Merge branch 'devel'

This commit is contained in:
Dominik Picheta 2015-10-27 23:36:00 +01:00
commit 3892969af4
1019 changed files with 35409 additions and 74200 deletions

2
.gitignore vendored
View file

@ -41,4 +41,4 @@ xcuserdata/
/testresults.html /testresults.html
/testresults.json /testresults.json
testament.db testament.db
/csources/ /csources

35
.travis.yml Normal file
View file

@ -0,0 +1,35 @@
sudo: false
language: c
os:
- linux
addons:
apt:
packages:
- libcurl4-openssl-dev
- libsdl1.2-dev
- libgc-dev
before_script:
- set -e
- wget http://flatassembler.net/fasm-1.71.39.tgz
- tar xvf fasm-1.71.39.tgz
- git clone --depth 1 https://github.com/nim-lang/csources.git
- cd csources
- sh build.sh
- cd ..
- sed -i -e 's,cc = gcc,cc = clang,' config/nim.cfg
- export PATH=$(pwd)/bin:$(pwd)/fasm:$PATH
script:
- nim c koch
- ./koch boot
- ./koch boot -d:release
- nim e install_nimble.nims
- nimble update
- nimble install zip
- nimble install opengl
- nimble install sdl1
- nimble install jester
- nimble install niminst
- nim c --taintMode:on tests/testament/tester
- tests/testament/tester --pedantic all
- ./koch csource
- ./koch xz

36
appveyor.yml Normal file
View file

@ -0,0 +1,36 @@
clone_depth: 5
artifacts:
- path: bin\nim.exe
platform:
- x64
before_build:
- git log -1
- C:\msys64\usr\bin\bash -lc "pacman --noconfirm -S zlib-devel"
- appveyor DownloadFile http://nim-lang.org/download/dlls.zip
- 7z e dlls.zip -odlls
- del dlls\libcurl.dll
- appveyor DownloadFile http://flatassembler.net/fasmw17139.zip
- 7z e fasmw17139.zip -obin fasm.exe
build_script:
- SET PATH=C:\msys64\mingw64\bin;dlls;bin;%PATH%
- gcc -v
- git clone -q --depth 1 https://github.com/nim-lang/csources
- cd csources
- build64.bat
- cd ..
- nim c koch
- koch boot
- koch boot -d:release
before_test:
- nim e install_nimble.nims
- nimble update
- nimble install zip
test_script:
- nim c --taintMode:on tests/testament/tester
- tests\testament\tester --pedantic all

View file

@ -1 +1 @@
This file keeps several tools from deleting this subdirectory. This file keeps several tools from deleting this subdirectory.

22
bootstrap.sh Executable file
View file

@ -0,0 +1,22 @@
#!/bin/sh
set -e
set -x
if [ ! -e csources/.git ]; then
git clone --depth 1 git://github.com/nim-lang/csources.git csources
fi
cd "csources"
sh build.sh
cd ".."
./bin/nim c koch
./koch boot -d:release
./koch geninstall
set +x
echo
echo 'Install Nim using "./install.sh <dir>" or "sudo ./install.sh <dir>".'
exit 0

View file

@ -1,19 +0,0 @@
#!/bin/sh
set -e
set -x
if [ ! -d "csources" ]; then
git clone --depth 1 https://github.com/nim-lang/csources.git
fi
cd "csources"
sh build.sh
cd ".."
./bin/nim c koch
./koch boot -d:release
cp -f install.sh.template install.sh
chmod +x install.sh
exit 0

View file

@ -1 +1 @@
This file keeps several tools from deleting this subdirectory. This file keeps several tools from deleting this subdirectory.

View file

@ -1,6 +1,6 @@
[Package] [Package]
name = "compiler" name = "compiler"
version = "0.10.3" version = "0.11.3"
author = "Andreas Rumpf" 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"
@ -8,4 +8,4 @@ license = "MIT"
InstallDirs = "doc, compiler" InstallDirs = "doc, compiler"
[Deps] [Deps]
Requires: "nim >= 0.10.3" Requires: "nim >= 0.11.3"

View file

@ -11,7 +11,7 @@
import import
ast, astalgo, types, trees, intsets, msgs ast, astalgo, types, trees, intsets, msgs
type type
TAnalysisResult* = enum TAnalysisResult* = enum
arNo, arMaybe, arYes arNo, arMaybe, arYes
@ -21,42 +21,42 @@ proc isPartOfAux(a, b: PType, marker: var IntSet): TAnalysisResult
proc isPartOfAux(n: PNode, b: PType, marker: var IntSet): TAnalysisResult = proc isPartOfAux(n: PNode, b: PType, marker: var IntSet): TAnalysisResult =
result = arNo result = arNo
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):
result = isPartOfAux(n.sons[i], b, marker) result = isPartOfAux(n.sons[i], b, marker)
if result == arYes: return if result == arYes: return
of nkRecCase: of nkRecCase:
assert(n.sons[0].kind == nkSym) assert(n.sons[0].kind == nkSym)
result = isPartOfAux(n.sons[0], b, marker) result = isPartOfAux(n.sons[0], b, marker)
if result == arYes: return if result == arYes: return
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:
result = isPartOfAux(lastSon(n.sons[i]), b, marker) result = isPartOfAux(lastSon(n.sons[i]), b, marker)
if result == arYes: return if result == arYes: return
else: internalError("isPartOfAux(record case branch)") else: internalError("isPartOfAux(record case branch)")
of nkSym: of nkSym:
result = isPartOfAux(n.sym.typ, b, marker) result = isPartOfAux(n.sym.typ, b, marker)
else: internalError(n.info, "isPartOfAux()") else: internalError(n.info, "isPartOfAux()")
proc isPartOfAux(a, b: PType, marker: var IntSet): TAnalysisResult = proc isPartOfAux(a, b: PType, marker: var IntSet): TAnalysisResult =
result = arNo result = arNo
if a == nil or b == nil: return if a == nil or b == nil: return
if containsOrIncl(marker, a.id): return if containsOrIncl(marker, a.id): return
if compareTypes(a, b, dcEqIgnoreDistinct): return arYes if compareTypes(a, b, dcEqIgnoreDistinct): return arYes
case a.kind case a.kind
of tyObject: of tyObject:
result = isPartOfAux(a.sons[0], b, marker) result = isPartOfAux(a.sons[0], b, marker)
if result == arNo: result = isPartOfAux(a.n, b, marker) if result == arNo: result = isPartOfAux(a.n, b, marker)
of tyGenericInst, tyDistinct: of tyGenericInst, tyDistinct:
result = isPartOfAux(lastSon(a), b, marker) result = isPartOfAux(lastSon(a), b, marker)
of tyArray, tyArrayConstr, tySet, tyTuple: of tyArray, tyArrayConstr, tySet, tyTuple:
for i in countup(0, sonsLen(a) - 1): for i in countup(0, sonsLen(a) - 1):
result = isPartOfAux(a.sons[i], b, marker) result = isPartOfAux(a.sons[i], b, marker)
if result == arYes: return if result == arYes: return
else: discard else: discard
proc isPartOf(a, b: PType): TAnalysisResult = proc isPartOf(a, b: PType): TAnalysisResult =
## checks iff 'a' can be part of 'b'. Iterates over VALUE types! ## checks iff 'a' can be part of 'b'. Iterates over VALUE types!
var marker = initIntSet() var marker = initIntSet()
# watch out: parameters reversed because I'm too lazy to change the code... # watch out: parameters reversed because I'm too lazy to change the code...
@ -70,14 +70,14 @@ proc isPartOf*(a, b: PNode): TAnalysisResult =
## type. Since however type analysis is more expensive, we perform it only ## type. Since however type analysis is more expensive, we perform it only
## if necessary. ## if necessary.
## ##
## cases: ## cases:
## ##
## YES-cases: ## YES-cases:
## x <| x # for general trees ## x <| x # for general trees
## x[] <| x ## x[] <| x
## x[i] <| x ## x[i] <| x
## x.f <| x ## x.f <| x
## ##
## NO-cases: ## NO-cases:
## x !<| y # depending on type and symbol kind ## x !<| y # depending on type and symbol kind
## x[constA] !<| x[constB] ## x[constA] !<| x[constB]
@ -88,16 +88,16 @@ proc isPartOf*(a, b: PNode): TAnalysisResult =
## ##
## x[] ?<| y[] iff compatible type ## x[] ?<| y[] iff compatible type
## ##
## ##
## x[] ?<| y depending on type ## x[] ?<| y depending on type
## ##
if a.kind == b.kind: if a.kind == b.kind:
case a.kind case a.kind
of nkSym: of nkSym:
const varKinds = {skVar, skTemp, skProc} const varKinds = {skVar, skTemp, skProc}
# same symbol: aliasing: # same symbol: aliasing:
if a.sym.id == b.sym.id: result = arYes if a.sym.id == b.sym.id: result = arYes
elif a.sym.kind in varKinds or b.sym.kind in varKinds: elif a.sym.kind in varKinds or b.sym.kind in varKinds:
# actually, a param could alias a var but we know that cannot happen # actually, a param could alias a var but we know that cannot happen
# here. XXX make this more generic # here. XXX make this more generic
result = arNo result = arNo
@ -110,11 +110,11 @@ proc isPartOf*(a, b: PNode): TAnalysisResult =
if len(a) >= 2 and len(b) >= 2: if len(a) >= 2 and len(b) >= 2:
# array accesses: # array accesses:
if result == arYes and isDeepConstExpr(a[1]) and isDeepConstExpr(b[1]): if result == arYes and isDeepConstExpr(a[1]) and isDeepConstExpr(b[1]):
# we know it's the same array and we have 2 constant indexes; # we know it's the same array and we have 2 constant indexes;
# if they are # if they are
var x = if a[1].kind == nkHiddenStdConv: a[1][1] else: a[1] var x = if a[1].kind == nkHiddenStdConv: a[1][1] else: a[1]
var y = if b[1].kind == nkHiddenStdConv: b[1][1] else: b[1] var y = if b[1].kind == nkHiddenStdConv: b[1][1] else: b[1]
if sameValue(x, y): result = arYes if sameValue(x, y): result = arYes
else: result = arNo else: result = arNo
# else: maybe and no are accurate # else: maybe and no are accurate
@ -122,7 +122,7 @@ proc isPartOf*(a, b: PNode): TAnalysisResult =
# pointer derefs: # pointer derefs:
if result != arYes: if result != arYes:
if isPartOf(a.typ, b.typ) != arNo: result = arMaybe if isPartOf(a.typ, b.typ) != arNo: result = arMaybe
of nkDotExpr: of nkDotExpr:
result = isPartOf(a[0], b[0]) result = isPartOf(a[0], b[0])
if result != arNo: if result != arNo:
@ -135,7 +135,7 @@ proc isPartOf*(a, b: PNode): TAnalysisResult =
# weaken because of indirection: # weaken because of indirection:
if result != arYes: if result != arYes:
if isPartOf(a.typ, b.typ) != arNo: result = arMaybe if isPartOf(a.typ, b.typ) != arNo: result = arMaybe
of nkHiddenStdConv, nkHiddenSubConv, nkConv: of nkHiddenStdConv, nkHiddenSubConv, nkConv:
result = isPartOf(a[1], b[1]) result = isPartOf(a[1], b[1])
of nkObjUpConv, nkObjDownConv, nkCheckedFieldExpr: of nkObjUpConv, nkObjDownConv, nkCheckedFieldExpr:
@ -144,26 +144,26 @@ proc isPartOf*(a, b: PNode): TAnalysisResult =
# Calls return a new location, so a default of ``arNo`` is fine. # Calls return a new location, so a default of ``arNo`` is fine.
else: else:
# go down recursively; this is quite demanding: # go down recursively; this is quite demanding:
const const
Ix0Kinds = {nkDotExpr, nkBracketExpr, nkObjUpConv, nkObjDownConv, Ix0Kinds = {nkDotExpr, nkBracketExpr, nkObjUpConv, nkObjDownConv,
nkCheckedFieldExpr} nkCheckedFieldExpr, nkHiddenAddr}
Ix1Kinds = {nkHiddenStdConv, nkHiddenSubConv, nkConv} Ix1Kinds = {nkHiddenStdConv, nkHiddenSubConv, nkConv}
DerefKinds = {nkHiddenDeref, nkDerefExpr} DerefKinds = {nkHiddenDeref, nkDerefExpr}
case b.kind case b.kind
of Ix0Kinds: of Ix0Kinds:
# a* !<| b.f iff a* !<| b # a* !<| b.f iff a* !<| b
result = isPartOf(a, b[0]) result = isPartOf(a, b[0])
of DerefKinds: of DerefKinds:
# a* !<| b[] iff # a* !<| b[] iff
if isPartOf(a.typ, b.typ) != arNo: if isPartOf(a.typ, b.typ) != arNo:
result = isPartOf(a, b[0]) result = isPartOf(a, b[0])
if result == arNo: result = arMaybe if result == arNo: result = arMaybe
of Ix1Kinds: of Ix1Kinds:
# a* !<| T(b) iff a* !<| b # a* !<| T(b) iff a* !<| b
result = isPartOf(a, b[1]) result = isPartOf(a, b[1])
of nkSym: of nkSym:
# b is an atom, so we have to check a: # b is an atom, so we have to check a:
case a.kind case a.kind
@ -172,7 +172,7 @@ proc isPartOf*(a, b: PNode): TAnalysisResult =
result = isPartOf(a[0], b) result = isPartOf(a[0], b)
of Ix1Kinds: of Ix1Kinds:
result = isPartOf(a[1], b) result = isPartOf(a[1], b)
of DerefKinds: of DerefKinds:
if isPartOf(a.typ, b.typ) != arNo: if isPartOf(a.typ, b.typ) != arNo:
result = isPartOf(a[0], b) result = isPartOf(a[0], b)

View file

@ -10,7 +10,7 @@
# abstract syntax tree + symbol table # abstract syntax tree + symbol table
import import
msgs, hashes, nversion, options, strutils, crc, ropes, idents, lists, msgs, hashes, nversion, options, strutils, securehash, ropes, idents, lists,
intsets, idgen intsets, idgen
type type
@ -291,12 +291,13 @@ const
sfNoForward* = sfRegister sfNoForward* = sfRegister
# forward declarations are not required (per module) # forward declarations are not required (per module)
sfNoRoot* = sfBorrow # a local variable is provably no root so it doesn't
# require RC ops
sfCompileToCpp* = sfInfixCall # compile the module as C++ code sfCompileToCpp* = sfInfixCall # compile the module as C++ code
sfCompileToObjc* = sfNamedParamCall # compile the module as Objective-C code sfCompileToObjc* = sfNamedParamCall # compile the module as Objective-C code
sfExperimental* = sfOverriden # module uses the .experimental switch sfExperimental* = sfOverriden # module uses the .experimental switch
sfGoto* = sfOverriden # var is used for 'goto' code generation sfGoto* = sfOverriden # var is used for 'goto' code generation
sfWrittenTo* = sfBorrow # param is assigned to
sfEscapes* = sfProcvar # param escapes
sfBase* = sfDiscriminant
const const
# getting ready for the future expr/stmt merge # getting ready for the future expr/stmt merge
@ -423,6 +424,7 @@ type
# but unfortunately it has measurable impact for compilation # but unfortunately it has measurable impact for compilation
# efficiency # efficiency
nfTransf, # node has been transformed nfTransf, # node has been transformed
nfNoRewrite # node should not be transformed anymore
nfSem # node has been checked for semantics nfSem # node has been checked for semantics
nfLL # node has gone through lambda lifting nfLL # node has gone through lambda lifting
nfDotField # the call can use a dot operator nfDotField # the call can use a dot operator
@ -475,6 +477,8 @@ type
# wildcard type. # wildcard type.
tfHasAsgn # type has overloaded assignment operator tfHasAsgn # type has overloaded assignment operator
tfBorrowDot # distinct type borrows '.' tfBorrowDot # distinct type borrows '.'
tfTriggersCompileTime # uses the NimNode type which make the proc
# implicitly '.compiletime'
TTypeFlags* = set[TTypeFlag] TTypeFlags* = set[TTypeFlag]
@ -521,6 +525,12 @@ const
tfUnion* = tfNoSideEffect tfUnion* = tfNoSideEffect
tfGcSafe* = tfThread tfGcSafe* = tfThread
tfObjHasKids* = tfEnumHasHoles tfObjHasKids* = tfEnumHasHoles
tfOldSchoolExprStmt* = tfVarargs # for now used to distinguish \
# 'varargs[expr]' from 'varargs[untyped]'. Eventually 'expr' will be
# deprecated and this mess can be cleaned up.
tfVoid* = tfVarargs # for historical reasons we conflated 'void' with
# 'empty' ('@[]' has the type 'seq[empty]').
tfReturnsNew* = tfInheritable
skError* = skUnknown skError* = skUnknown
# type flags that are essential for type equality: # type flags that are essential for type equality:
@ -529,40 +539,52 @@ const
type type
TMagic* = enum # symbols that require compiler magic: TMagic* = enum # symbols that require compiler magic:
mNone, mNone,
mDefined, mDefinedInScope, mCompiles, mDefined, mDefinedInScope, mCompiles, mArrGet, mArrPut, mAsgn,
mLow, mHigh, mSizeOf, mTypeTrait, mIs, mOf, mAddr, mTypeOf, mRoof, mPlugin, mLow, mHigh, mSizeOf, mTypeTrait, mIs, mOf, mAddr, mTypeOf, mRoof, mPlugin,
mEcho, mShallowCopy, mSlurp, mStaticExec, mEcho, mShallowCopy, mSlurp, mStaticExec,
mParseExprToAst, mParseStmtToAst, mExpandToAst, mQuoteAst, mParseExprToAst, mParseStmtToAst, mExpandToAst, mQuoteAst,
mUnaryLt, mInc, mDec, mOrd, mNew, mNewFinalize, mNewSeq, mLengthOpenArray, mUnaryLt, mInc, mDec, mOrd,
mLengthStr, mLengthArray, mLengthSeq, mXLenStr, mXLenSeq, mNew, mNewFinalize, mNewSeq,
mLengthOpenArray, mLengthStr, mLengthArray, mLengthSeq,
mXLenStr, mXLenSeq,
mIncl, mExcl, mCard, mChr, mIncl, mExcl, mCard, mChr,
mGCref, mGCunref, mGCref, mGCunref,
mAddI, mSubI, mMulI, mDivI, mModI,
mAddI, mSubI, mMulI, mDivI, mModI, mAddI64, mSubI64, mMulI64, mSucc, mPred,
mDivI64, mModI64, mSucc, mPred,
mAddF64, mSubF64, mMulF64, mDivF64, mAddF64, mSubF64, mMulF64, mDivF64,
mShrI, mShlI, mBitandI, mBitorI, mBitxorI,
mShrI, mShlI, mBitandI, mBitorI, mBitxorI, mMinI, mMaxI, mMinI, mMaxI,
mShrI64, mShlI64, mBitandI64, mBitorI64, mBitxorI64, mMinF64, mMaxF64,
mMinF64, mMaxF64, mAddU, mSubU, mMulU, mAddU, mSubU, mMulU, mDivU, mModU,
mDivU, mModU, mEqI, mLeI, mEqI, mLeI, mLtI,
mLtI, mEqF64, mLeF64, mLtF64,
mEqI64, mLeI64, mLtI64, mEqF64, mLeF64, mLtF64, mLeU, mLtU,
mLeU, mLtU, mLeU64, mLtU64, mLeU64, mLtU64,
mEqEnum, mLeEnum, mLtEnum, mEqCh, mLeCh, mLtCh, mEqB, mLeB, mLtB, mEqRef, mEqEnum, mLeEnum, mLtEnum,
mEqUntracedRef, mLePtr, mLtPtr, mEqCString, mXor, mEqProc, mUnaryMinusI, mEqCh, mLeCh, mLtCh,
mUnaryMinusI64, mAbsI, mAbsI64, mNot, mEqB, mLeB, mLtB,
mEqRef, mEqUntracedRef, mLePtr, mLtPtr, mEqCString,
mXor, mEqProc,
mUnaryMinusI, mUnaryMinusI64, mAbsI, mNot,
mUnaryPlusI, mBitnotI, mUnaryPlusI, mBitnotI,
mBitnotI64, mUnaryPlusF64, mUnaryMinusF64, mAbsF64, mZe8ToI, mZe8ToI64, mUnaryPlusF64, mUnaryMinusF64, mAbsF64,
mZe16ToI, mZe16ToI64, mZe32ToI64, mZeIToI64, mToU8, mToU16, mToU32, mZe8ToI, mZe8ToI64,
mToFloat, mToBiggestFloat, mToInt, mToBiggestInt, mCharToStr, mBoolToStr, mZe16ToI, mZe16ToI64,
mIntToStr, mInt64ToStr, mFloatToStr, mCStrToStr, mStrToStr, mEnumToStr, mZe32ToI64, mZeIToI64,
mAnd, mOr, mEqStr, mLeStr, mLtStr, mEqSet, mLeSet, mLtSet, mMulSet, mToU8, mToU16, mToU32,
mPlusSet, mMinusSet, mSymDiffSet, mConStrStr, mSlice, mToFloat, mToBiggestFloat,
mToInt, mToBiggestInt,
mCharToStr, mBoolToStr, mIntToStr, mInt64ToStr, mFloatToStr, mCStrToStr,
mStrToStr, mEnumToStr,
mAnd, mOr,
mEqStr, mLeStr, mLtStr,
mEqSet, mLeSet, mLtSet, mMulSet, mPlusSet, mMinusSet, mSymDiffSet,
mConStrStr, mSlice,
mDotDot, # this one is only necessary to give nice compile time warnings mDotDot, # this one is only necessary to give nice compile time warnings
mFields, mFieldPairs, mOmpParFor, mFields, mFieldPairs, mOmpParFor,
mAppendStrCh, mAppendStrStr, mAppendSeqElem, mAppendStrCh, mAppendStrStr, mAppendSeqElem,
mInRange, mInSet, mRepr, mExit, mSetLengthStr, mSetLengthSeq, mInRange, mInSet, mRepr, mExit,
mSetLengthStr, mSetLengthSeq,
mIsPartOf, mAstToStr, mParallel, mIsPartOf, mAstToStr, mParallel,
mSwap, mIsNil, mArrToSeq, mCopyStr, mCopyStrLast, mSwap, mIsNil, mArrToSeq, mCopyStr, mCopyStrLast,
mNewString, mNewStringOfCap, mParseBiggestFloat, mNewString, mNewStringOfCap, mParseBiggestFloat,
@ -584,42 +606,53 @@ type
mNSetFloatVal, mNSetSymbol, mNSetIdent, mNSetType, mNSetStrVal, mNLineInfo, mNSetFloatVal, mNSetSymbol, mNSetIdent, mNSetType, mNSetStrVal, mNLineInfo,
mNNewNimNode, mNCopyNimNode, mNCopyNimTree, mStrToIdent, mIdentToStr, mNNewNimNode, mNCopyNimNode, mNCopyNimTree, mStrToIdent, mIdentToStr,
mNBindSym, mLocals, mNCallSite, mNBindSym, mLocals, mNCallSite,
mEqIdent, mEqNimrodNode, mNHint, mNWarning, mNError, mEqIdent, mEqNimrodNode, mSameNodeType, mGetImpl,
mInstantiationInfo, mGetTypeInfo, mNGenSym mNHint, mNWarning, mNError,
mInstantiationInfo, mGetTypeInfo, mNGenSym,
mNimvm
# things that we can evaluate safely at compile time, even if not asked for it: # things that we can evaluate safely at compile time, even if not asked for it:
const const
ctfeWhitelist* = {mNone, mUnaryLt, mSucc, ctfeWhitelist* = {mNone, mUnaryLt, mSucc,
mPred, mInc, mDec, mOrd, mLengthOpenArray, mPred, mInc, mDec, mOrd, mLengthOpenArray,
mLengthStr, mLengthArray, mLengthSeq, mXLenStr, mXLenSeq, mLengthStr, mLengthArray, mLengthSeq, mXLenStr, mXLenSeq,
mArrGet, mArrPut, mAsgn,
mIncl, mExcl, mCard, mChr, mIncl, mExcl, mCard, mChr,
mAddI, mSubI, mMulI, mDivI, mModI, mAddI64, mSubI64, mMulI64, mAddI, mSubI, mMulI, mDivI, mModI,
mDivI64, mModI64, mAddF64, mSubF64, mMulF64, mDivF64, mAddF64, mSubF64, mMulF64, mDivF64,
mShrI, mShlI, mBitandI, mBitorI, mBitxorI, mMinI, mMaxI, mShrI, mShlI, mBitandI, mBitorI, mBitxorI,
mShrI64, mShlI64, mBitandI64, mBitorI64, mBitxorI64, mMinI, mMaxI,
mMinF64, mMaxF64, mAddU, mSubU, mMulU, mMinF64, mMaxF64,
mDivU, mModU, mEqI, mLeI, mAddU, mSubU, mMulU, mDivU, mModU,
mLtI, mEqI, mLeI, mLtI,
mEqI64, mLeI64, mLtI64, mEqF64, mLeF64, mLtF64, mEqF64, mLeF64, mLtF64,
mLeU, mLtU, mLeU64, mLtU64, mLeU, mLtU,
mEqEnum, mLeEnum, mLtEnum, mEqCh, mLeCh, mLtCh, mEqB, mLeB, mLtB, mEqRef, mLeU64, mLtU64,
mEqProc, mEqUntracedRef, mLePtr, mLtPtr, mEqCString, mXor, mUnaryMinusI, mEqEnum, mLeEnum, mLtEnum,
mUnaryMinusI64, mAbsI, mAbsI64, mNot, mEqCh, mLeCh, mLtCh,
mUnaryPlusI, mBitnotI, mEqB, mLeB, mLtB,
mBitnotI64, mUnaryPlusF64, mUnaryMinusF64, mAbsF64, mZe8ToI, mZe8ToI64, mEqRef, mEqProc, mEqUntracedRef, mLePtr, mLtPtr, mEqCString, mXor,
mZe16ToI, mZe16ToI64, mZe32ToI64, mZeIToI64, mToU8, mToU16, mToU32, mUnaryMinusI, mUnaryMinusI64, mAbsI, mNot, mUnaryPlusI, mBitnotI,
mToFloat, mToBiggestFloat, mToInt, mToBiggestInt, mCharToStr, mBoolToStr, mUnaryPlusF64, mUnaryMinusF64, mAbsF64,
mIntToStr, mInt64ToStr, mFloatToStr, mCStrToStr, mStrToStr, mEnumToStr, mZe8ToI, mZe8ToI64,
mAnd, mOr, mEqStr, mLeStr, mLtStr, mEqSet, mLeSet, mLtSet, mMulSet, mZe16ToI, mZe16ToI64,
mPlusSet, mMinusSet, mSymDiffSet, mConStrStr, mZe32ToI64, mZeIToI64,
mAppendStrCh, mAppendStrStr, mAppendSeqElem, mToU8, mToU16, mToU32,
mToFloat, mToBiggestFloat,
mToInt, mToBiggestInt,
mCharToStr, mBoolToStr, mIntToStr, mInt64ToStr, mFloatToStr, mCStrToStr,
mStrToStr, mEnumToStr,
mAnd, mOr,
mEqStr, mLeStr, mLtStr,
mEqSet, mLeSet, mLtSet, mMulSet, mPlusSet, mMinusSet, mSymDiffSet,
mConStrStr, mAppendStrCh, mAppendStrStr, mAppendSeqElem,
mInRange, mInSet, mRepr, mInRange, mInSet, mRepr,
mCopyStr, mCopyStrLast} mCopyStr, mCopyStrLast}
# magics that require special semantic checking and # magics that require special semantic checking and
# thus cannot be overloaded (also documented in the spec!): # thus cannot be overloaded (also documented in the spec!):
SpecialSemMagics* = { SpecialSemMagics* = {
mDefined, mDefinedInScope, mCompiles, mLow, mHigh, mSizeOf, mIs, mOf, mDefined, mDefinedInScope, mCompiles, mLow, mHigh, mSizeOf, mIs, mOf,
mEcho, mShallowCopy, mExpandToAst, mParallel, mSpawn, mAstToStr} mShallowCopy, mExpandToAst, mParallel, mSpawn, mAstToStr}
type type
PNode* = ref TNode PNode* = ref TNode
@ -679,6 +712,7 @@ type
lfSingleUse # no location yet and will only be used once lfSingleUse # no location yet and will only be used once
TStorageLoc* = enum TStorageLoc* = enum
OnUnknown, # location is unknown (stack, heap or static) OnUnknown, # location is unknown (stack, heap or static)
OnStatic, # in a static section
OnStack, # location is on hardware stack OnStack, # location is on hardware stack
OnHeap # location is on heap or global OnHeap # location is on heap or global
# (reference counting needed) # (reference counting needed)
@ -704,6 +738,8 @@ type
name*: Rope name*: Rope
path*: PNode # can be a string literal! path*: PNode # can be a string literal!
CompilesId* = int ## id that is used for the caching logic within
## ``system.compiles``. See the seminst module.
TInstantiation* = object TInstantiation* = object
sym*: PSym sym*: PSym
concreteTypes*: seq[PType] concreteTypes*: seq[PType]
@ -711,6 +747,7 @@ type
# needed in caas mode for purging the cache # needed in caas mode for purging the cache
# XXX: it's possible to switch to a # XXX: it's possible to switch to a
# simple ref count here # simple ref count here
compilesId*: CompilesId
PInstantiation* = ref TInstantiation PInstantiation* = ref TInstantiation
@ -747,6 +784,7 @@ type
tab*: TStrTable # interface table for modules tab*: TStrTable # interface table for modules
of skLet, skVar, skField, skForVar: of skLet, skVar, skField, skForVar:
guard*: PSym guard*: PSym
bitsize*: int
else: nil else: nil
magic*: TMagic magic*: TMagic
typ*: PType typ*: PType
@ -842,7 +880,7 @@ type
data*: TIdNodePairSeq data*: TIdNodePairSeq
TNodePair* = object TNodePair* = object
h*: THash # because it is expensive to compute! h*: Hash # because it is expensive to compute!
key*: PNode key*: PNode
val*: int val*: int
@ -947,6 +985,9 @@ proc add*(father, son: PNode) =
proc `[]`*(n: PNode, i: int): PNode {.inline.} = proc `[]`*(n: PNode, i: int): PNode {.inline.} =
result = n.sons[i] result = n.sons[i]
template `-|`*(b, s: expr): expr =
(if b >= 0: b else: s.len + b)
# son access operators with support for negative indices # son access operators with support for negative indices
template `{}`*(n: PNode, i: int): expr = n[i -| n] template `{}`*(n: PNode, i: int): expr = n[i -| n]
template `{}=`*(n: PNode, i: int, s: PNode): stmt = template `{}=`*(n: PNode, i: int, s: PNode): stmt =
@ -1000,7 +1041,8 @@ proc newSym*(symKind: TSymKind, name: PIdent, owner: PSym,
result.id = getID() result.id = getID()
when debugIds: when debugIds:
registerId(result) registerId(result)
#if result.id < 2000: #if result.id == 93289:
# writeStacktrace()
# MessageOut(name.s & " has id: " & toString(result.id)) # MessageOut(name.s & " has id: " & toString(result.id))
var emptyNode* = newNode(nkEmpty) var emptyNode* = newNode(nkEmpty)
@ -1198,23 +1240,11 @@ proc newSons*(father: PType, length: int) =
else: else:
setLen(father.sons, length) setLen(father.sons, length)
proc sonsLen*(n: PType): int = proc sonsLen*(n: PType): int = n.sons.len
if isNil(n.sons): result = 0 proc len*(n: PType): int = n.sons.len
else: result = len(n.sons) proc sonsLen*(n: PNode): int = n.sons.len
proc lastSon*(n: PNode): PNode = n.sons[^1]
proc len*(n: PType): int = proc lastSon*(n: PType): PType = n.sons[^1]
if isNil(n.sons): result = 0
else: result = len(n.sons)
proc sonsLen*(n: PNode): int =
if isNil(n.sons): result = 0
else: result = len(n.sons)
proc lastSon*(n: PNode): PNode =
result = n.sons[sonsLen(n) - 1]
proc lastSon*(n: PType): PType =
result = n.sons[sonsLen(n) - 1]
proc assignType*(dest, src: PType) = proc assignType*(dest, src: PType) =
dest.kind = src.kind dest.kind = src.kind
@ -1356,8 +1386,11 @@ proc propagateToOwner*(owner, elem: PType) =
o2.flags.incl tfHasAsgn o2.flags.incl tfHasAsgn
owner.flags.incl tfHasAsgn owner.flags.incl tfHasAsgn
if tfTriggersCompileTime in elem.flags:
owner.flags.incl tfTriggersCompileTime
if owner.kind notin {tyProc, tyGenericInst, tyGenericBody, if owner.kind notin {tyProc, tyGenericInst, tyGenericBody,
tyGenericInvocation}: tyGenericInvocation, tyPtr}:
let elemB = elem.skipTypes({tyGenericInst}) let elemB = elem.skipTypes({tyGenericInst})
if elemB.isGCedMem or tfHasGCedMem in elemB.flags: if elemB.isGCedMem or tfHasGCedMem in elemB.flags:
# for simplicity, we propagate this flag even to generics. We then # for simplicity, we propagate this flag even to generics. We then
@ -1496,6 +1529,9 @@ proc getFloat*(a: PNode): BiggestFloat =
proc getStr*(a: PNode): string = proc getStr*(a: PNode): string =
case a.kind case a.kind
of nkStrLit..nkTripleStrLit: result = a.strVal of nkStrLit..nkTripleStrLit: result = a.strVal
of nkNilLit:
# let's hope this fixes more problems than it creates:
result = nil
else: else:
internalError(a.info, "getStr") internalError(a.info, "getStr")
result = "" result = ""
@ -1553,6 +1589,14 @@ proc makeStmtList*(n: PNode): PNode =
result = newNodeI(nkStmtList, n.info) result = newNodeI(nkStmtList, n.info)
result.add n result.add n
proc skipStmtList*(n: PNode): PNode =
if n.kind in {nkStmtList, nkStmtListExpr}:
for i in 0 .. n.len-2:
if n[i].kind notin {nkEmpty, nkCommentStmt}: return n
result = n.lastSon
else:
result = n
proc createMagic*(name: string, m: TMagic): PSym = proc createMagic*(name: string, m: TMagic): PSym =
result = newSym(skProc, getIdent(name), nil, unknownLineInfo()) result = newSym(skProc, getIdent(name), nil, unknownLineInfo())
result.magic = m result.magic = m
@ -1560,3 +1604,10 @@ proc createMagic*(name: string, m: TMagic): PSym =
let let
opNot* = createMagic("not", mNot) opNot* = createMagic("not", mNot)
opContains* = createMagic("contains", mInSet) opContains* = createMagic("contains", mInSet)
when false:
proc containsNil*(n: PNode): bool =
# only for debugging
if n.isNil: return true
for i in 0 ..< n.safeLen:
if n[i].containsNil: return true

View file

@ -14,7 +14,7 @@
import import
ast, hashes, intsets, strutils, options, msgs, ropes, idents, rodutils ast, hashes, intsets, strutils, options, msgs, ropes, idents, rodutils
proc hashNode*(p: RootRef): THash proc hashNode*(p: RootRef): Hash
proc treeToYaml*(n: PNode, indent: int = 0, maxRecDepth: int = - 1): Rope proc treeToYaml*(n: PNode, indent: int = 0, maxRecDepth: int = - 1): Rope
# Convert a tree into its YAML representation; this is used by the # Convert a tree into its YAML representation; this is used by the
# YAML code generator and it is invaluable for debugging purposes. # YAML code generator and it is invaluable for debugging purposes.
@ -49,7 +49,7 @@ proc strTableGet*(t: TStrTable, name: PIdent): PSym
type type
TTabIter*{.final.} = object # consider all fields here private TTabIter*{.final.} = object # consider all fields here private
h*: THash # current hash h*: Hash # current hash
proc initTabIter*(ti: var TTabIter, tab: TStrTable): PSym proc initTabIter*(ti: var TTabIter, tab: TStrTable): PSym
proc nextIter*(ti: var TTabIter, tab: TStrTable): PSym proc nextIter*(ti: var TTabIter, tab: TStrTable): PSym
@ -65,7 +65,7 @@ proc nextIter*(ti: var TTabIter, tab: TStrTable): PSym
type type
TIdentIter*{.final.} = object # iterator over all syms with same identifier TIdentIter*{.final.} = object # iterator over all syms with same identifier
h*: THash # current hash h*: Hash # current hash
name*: PIdent name*: PIdent
@ -94,7 +94,7 @@ proc getSymFromList*(list: PNode, ident: PIdent, start: int = 0): PSym
proc lookupInRecord*(n: PNode, field: PIdent): PSym proc lookupInRecord*(n: PNode, field: PIdent): PSym
proc getModule*(s: PSym): PSym proc getModule*(s: PSym): PSym
proc mustRehash*(length, counter: int): bool proc mustRehash*(length, counter: int): bool
proc nextTry*(h, maxHash: THash): THash {.inline.} proc nextTry*(h, maxHash: Hash): Hash {.inline.}
# ------------- table[int, int] --------------------------------------------- # ------------- table[int, int] ---------------------------------------------
const const
@ -196,7 +196,7 @@ proc getSymFromList(list: PNode, ident: PIdent, start: int = 0): PSym =
else: internalError(list.info, "getSymFromList") else: internalError(list.info, "getSymFromList")
result = nil result = nil
proc hashNode(p: RootRef): THash = proc hashNode(p: RootRef): Hash =
result = hash(cast[pointer](p)) result = hash(cast[pointer](p))
proc mustRehash(length, counter: int): bool = proc mustRehash(length, counter: int): bool =
@ -452,7 +452,7 @@ proc debug(n: PSym) =
elif n.kind == skUnknown: elif n.kind == skUnknown:
msgWriteln("skUnknown") msgWriteln("skUnknown")
else: else:
#writeln(stdout, $symToYaml(n, 0, 1)) #writeLine(stdout, $symToYaml(n, 0, 1))
msgWriteln("$1_$2: $3, $4, $5, $6" % [ msgWriteln("$1_$2: $3, $4, $5, $6" % [
n.name.s, $n.id, $flagsToStr(n.flags), $flagsToStr(n.loc.flags), n.name.s, $n.id, $flagsToStr(n.flags), $flagsToStr(n.loc.flags),
$lineInfoToStr(n.info), $n.kind]) $lineInfoToStr(n.info), $n.kind])
@ -466,7 +466,7 @@ proc debug(n: PNode) =
const const
EmptySeq = @[] EmptySeq = @[]
proc nextTry(h, maxHash: THash): THash = proc nextTry(h, maxHash: Hash): Hash =
result = ((5 * h) + 1) and maxHash result = ((5 * h) + 1) and maxHash
# For any initial h in range(maxHash), repeating that maxHash times # For any initial h in range(maxHash), repeating that maxHash times
# generates each int in range(maxHash) exactly once (see any text on # generates each int in range(maxHash) exactly once (see any text on
@ -474,7 +474,7 @@ proc nextTry(h, maxHash: THash): THash =
proc objectSetContains(t: TObjectSet, obj: RootRef): bool = proc objectSetContains(t: TObjectSet, obj: RootRef): bool =
# returns true whether n is in t # returns true whether n is in t
var h: THash = hashNode(obj) and high(t.data) # start with real hash value var h: Hash = hashNode(obj) and high(t.data) # start with real hash value
while t.data[h] != nil: while t.data[h] != nil:
if t.data[h] == obj: if t.data[h] == obj:
return true return true
@ -482,7 +482,7 @@ proc objectSetContains(t: TObjectSet, obj: RootRef): bool =
result = false result = false
proc objectSetRawInsert(data: var TObjectSeq, obj: RootRef) = proc objectSetRawInsert(data: var TObjectSeq, obj: RootRef) =
var h: THash = hashNode(obj) and high(data) var h: Hash = hashNode(obj) and high(data)
while data[h] != nil: while data[h] != nil:
assert(data[h] != obj) assert(data[h] != obj)
h = nextTry(h, high(data)) h = nextTry(h, high(data))
@ -503,7 +503,7 @@ proc objectSetIncl(t: var TObjectSet, obj: RootRef) =
proc objectSetContainsOrIncl(t: var TObjectSet, obj: RootRef): bool = proc objectSetContainsOrIncl(t: var TObjectSet, obj: RootRef): bool =
# returns true if obj is already in the string table: # returns true if obj is already in the string table:
var h: THash = hashNode(obj) and high(t.data) var h: Hash = hashNode(obj) and high(t.data)
while true: while true:
var it = t.data[h] var it = t.data[h]
if it == nil: break if it == nil: break
@ -520,7 +520,7 @@ proc objectSetContainsOrIncl(t: var TObjectSet, obj: RootRef): bool =
result = false result = false
proc tableRawGet(t: TTable, key: RootRef): int = proc tableRawGet(t: TTable, key: RootRef): int =
var h: THash = hashNode(key) and high(t.data) # start with real hash value var h: Hash = hashNode(key) and high(t.data) # start with real hash value
while t.data[h].key != nil: while t.data[h].key != nil:
if t.data[h].key == key: if t.data[h].key == key:
return h return h
@ -529,7 +529,7 @@ proc tableRawGet(t: TTable, key: RootRef): int =
proc tableSearch(t: TTable, key, closure: RootRef, proc tableSearch(t: TTable, key, closure: RootRef,
comparator: TCmpProc): RootRef = comparator: TCmpProc): RootRef =
var h: THash = hashNode(key) and high(t.data) # start with real hash value var h: Hash = hashNode(key) and high(t.data) # start with real hash value
while t.data[h].key != nil: while t.data[h].key != nil:
if t.data[h].key == key: if t.data[h].key == key:
if comparator(t.data[h].val, closure): if comparator(t.data[h].val, closure):
@ -544,7 +544,7 @@ proc tableGet(t: TTable, key: RootRef): RootRef =
else: result = nil else: result = nil
proc tableRawInsert(data: var TPairSeq, key, val: RootRef) = proc tableRawInsert(data: var TPairSeq, key, val: RootRef) =
var h: THash = hashNode(key) and high(data) var h: Hash = hashNode(key) and high(data)
while data[h].key != nil: while data[h].key != nil:
assert(data[h].key != key) assert(data[h].key != key)
h = nextTry(h, high(data)) h = nextTry(h, high(data))
@ -569,7 +569,7 @@ proc tablePut(t: var TTable, key, val: RootRef) =
inc(t.counter) inc(t.counter)
proc strTableContains(t: TStrTable, n: PSym): bool = proc strTableContains(t: TStrTable, n: PSym): bool =
var h: THash = n.name.h and high(t.data) # start with real hash value var h: Hash = n.name.h and high(t.data) # start with real hash value
while t.data[h] != nil: while t.data[h] != nil:
if (t.data[h] == n): if (t.data[h] == n):
return true return true
@ -577,7 +577,7 @@ proc strTableContains(t: TStrTable, n: PSym): bool =
result = false result = false
proc strTableRawInsert(data: var TSymSeq, n: PSym) = proc strTableRawInsert(data: var TSymSeq, n: PSym) =
var h: THash = n.name.h and high(data) var h: Hash = n.name.h and high(data)
if sfImmediate notin n.flags: if sfImmediate notin n.flags:
# fast path: # fast path:
while data[h] != nil: while data[h] != nil:
@ -606,7 +606,7 @@ proc strTableRawInsert(data: var TSymSeq, n: PSym) =
proc symTabReplaceRaw(data: var TSymSeq, prevSym: PSym, newSym: PSym) = proc symTabReplaceRaw(data: var TSymSeq, prevSym: PSym, newSym: PSym) =
assert prevSym.name.h == newSym.name.h assert prevSym.name.h == newSym.name.h
var h: THash = prevSym.name.h and high(data) var h: Hash = prevSym.name.h and high(data)
while data[h] != nil: while data[h] != nil:
if data[h] == prevSym: if data[h] == prevSym:
data[h] = newSym data[h] = newSym
@ -640,7 +640,7 @@ proc strTableIncl*(t: var TStrTable, n: PSym): bool {.discardable.} =
# It is essential that `n` is written nevertheless! # It is essential that `n` is written nevertheless!
# This way the newest redefinition is picked by the semantic analyses! # This way the newest redefinition is picked by the semantic analyses!
assert n.name != nil assert n.name != nil
var h: THash = n.name.h and high(t.data) var h: Hash = n.name.h and high(t.data)
var replaceSlot = -1 var replaceSlot = -1
while true: while true:
var it = t.data[h] var it = t.data[h]
@ -666,7 +666,7 @@ proc strTableIncl*(t: var TStrTable, n: PSym): bool {.discardable.} =
result = false result = false
proc strTableGet(t: TStrTable, name: PIdent): PSym = proc strTableGet(t: TStrTable, name: PIdent): PSym =
var h: THash = name.h and high(t.data) var h: Hash = name.h and high(t.data)
while true: while true:
result = t.data[h] result = t.data[h]
if result == nil: break if result == nil: break
@ -694,7 +694,7 @@ proc nextIdentIter(ti: var TIdentIter, tab: TStrTable): PSym =
proc nextIdentExcluding*(ti: var TIdentIter, tab: TStrTable, proc nextIdentExcluding*(ti: var TIdentIter, tab: TStrTable,
excluding: IntSet): PSym = excluding: IntSet): PSym =
var h: THash = ti.h and high(tab.data) var h: Hash = ti.h and high(tab.data)
var start = h var start = h
result = tab.data[h] result = tab.data[h]
while result != nil: while result != nil:
@ -743,7 +743,7 @@ proc hasEmptySlot(data: TIdPairSeq): bool =
result = false result = false
proc idTableRawGet(t: TIdTable, key: int): int = proc idTableRawGet(t: TIdTable, key: int): int =
var h: THash var h: Hash
h = key and high(t.data) # start with real hash value h = key and high(t.data) # start with real hash value
while t.data[h].key != nil: while t.data[h].key != nil:
if t.data[h].key.id == key: if t.data[h].key.id == key:
@ -772,7 +772,7 @@ iterator pairs*(t: TIdTable): tuple[key: int, value: RootRef] =
yield (t.data[i].key.id, t.data[i].val) yield (t.data[i].key.id, t.data[i].val)
proc idTableRawInsert(data: var TIdPairSeq, key: PIdObj, val: RootRef) = proc idTableRawInsert(data: var TIdPairSeq, key: PIdObj, val: RootRef) =
var h: THash var h: Hash
h = key.id and high(data) h = key.id and high(data)
while data[h].key != nil: while data[h].key != nil:
assert(data[h].key.id != key.id) assert(data[h].key.id != key.id)
@ -805,7 +805,7 @@ iterator idTablePairs*(t: TIdTable): tuple[key: PIdObj, val: RootRef] =
if not isNil(t.data[i].key): yield (t.data[i].key, t.data[i].val) if not isNil(t.data[i].key): yield (t.data[i].key, t.data[i].val)
proc idNodeTableRawGet(t: TIdNodeTable, key: PIdObj): int = proc idNodeTableRawGet(t: TIdNodeTable, key: PIdObj): int =
var h: THash var h: Hash
h = key.id and high(t.data) # start with real hash value h = key.id and high(t.data) # start with real hash value
while t.data[h].key != nil: while t.data[h].key != nil:
if t.data[h].key.id == key.id: if t.data[h].key.id == key.id:
@ -824,7 +824,7 @@ proc idNodeTableGetLazy*(t: TIdNodeTable, key: PIdObj): PNode =
result = idNodeTableGet(t, key) result = idNodeTableGet(t, key)
proc idNodeTableRawInsert(data: var TIdNodePairSeq, key: PIdObj, val: PNode) = proc idNodeTableRawInsert(data: var TIdNodePairSeq, key: PIdObj, val: PNode) =
var h: THash var h: Hash
h = key.id and high(data) h = key.id and high(data)
while data[h].key != nil: while data[h].key != nil:
assert(data[h].key.id != key.id) assert(data[h].key.id != key.id)
@ -863,7 +863,7 @@ proc initIITable(x: var TIITable) =
for i in countup(0, StartSize - 1): x.data[i].key = InvalidKey for i in countup(0, StartSize - 1): x.data[i].key = InvalidKey
proc iiTableRawGet(t: TIITable, key: int): int = proc iiTableRawGet(t: TIITable, key: int): int =
var h: THash var h: Hash
h = key and high(t.data) # start with real hash value h = key and high(t.data) # start with real hash value
while t.data[h].key != InvalidKey: while t.data[h].key != InvalidKey:
if t.data[h].key == key: return h if t.data[h].key == key: return h
@ -876,7 +876,7 @@ proc iiTableGet(t: TIITable, key: int): int =
else: result = InvalidKey else: result = InvalidKey
proc iiTableRawInsert(data: var TIIPairSeq, key, val: int) = proc iiTableRawInsert(data: var TIIPairSeq, key, val: int) =
var h: THash var h: Hash
h = key and high(data) h = key and high(data)
while data[h].key != InvalidKey: while data[h].key != InvalidKey:
assert(data[h].key != key) assert(data[h].key != key)

View file

@ -10,12 +10,12 @@
# this unit handles Nim sets; it implements bit sets # this unit handles Nim sets; it implements bit sets
# the code here should be reused in the Nim standard library # the code here should be reused in the Nim standard library
type type
TBitSet* = seq[int8] # we use byte here to avoid issues with TBitSet* = seq[int8] # we use byte here to avoid issues with
# cross-compiling; uint would be more efficient # cross-compiling; uint would be more efficient
# however # however
const const
ElemSize* = sizeof(int8) * 8 ElemSize* = sizeof(int8) * 8
proc bitSetInit*(b: var TBitSet, length: int) proc bitSetInit*(b: var TBitSet, length: int)
@ -30,42 +30,42 @@ proc bitSetEquals*(x, y: TBitSet): bool
proc bitSetContains*(x, y: TBitSet): bool proc bitSetContains*(x, y: TBitSet): bool
# implementation # implementation
proc bitSetIn(x: TBitSet, e: BiggestInt): bool = proc bitSetIn(x: TBitSet, e: BiggestInt): bool =
result = (x[int(e div ElemSize)] and toU8(int(1 shl (e mod ElemSize)))) != result = (x[int(e div ElemSize)] and toU8(int(1 shl (e mod ElemSize)))) !=
toU8(0) toU8(0)
proc bitSetIncl(x: var TBitSet, elem: BiggestInt) = proc bitSetIncl(x: var TBitSet, elem: BiggestInt) =
assert(elem >= 0) assert(elem >= 0)
x[int(elem div ElemSize)] = x[int(elem div ElemSize)] or x[int(elem div ElemSize)] = x[int(elem div ElemSize)] or
toU8(int(1 shl (elem mod ElemSize))) toU8(int(1 shl (elem mod ElemSize)))
proc bitSetExcl(x: var TBitSet, elem: BiggestInt) = proc bitSetExcl(x: var TBitSet, elem: BiggestInt) =
x[int(elem div ElemSize)] = x[int(elem div ElemSize)] and x[int(elem div ElemSize)] = x[int(elem div ElemSize)] and
not toU8(int(1 shl (elem mod ElemSize))) not toU8(int(1 shl (elem mod ElemSize)))
proc bitSetInit(b: var TBitSet, length: int) = proc bitSetInit(b: var TBitSet, length: int) =
newSeq(b, length) newSeq(b, length)
proc bitSetUnion(x: var TBitSet, y: TBitSet) = proc bitSetUnion(x: var TBitSet, y: TBitSet) =
for i in countup(0, high(x)): x[i] = x[i] or y[i] for i in countup(0, high(x)): x[i] = x[i] or y[i]
proc bitSetDiff(x: var TBitSet, y: TBitSet) = proc bitSetDiff(x: var TBitSet, y: TBitSet) =
for i in countup(0, high(x)): x[i] = x[i] and not y[i] for i in countup(0, high(x)): x[i] = x[i] and not y[i]
proc bitSetSymDiff(x: var TBitSet, y: TBitSet) = proc bitSetSymDiff(x: var TBitSet, y: TBitSet) =
for i in countup(0, high(x)): x[i] = x[i] xor y[i] for i in countup(0, high(x)): x[i] = x[i] xor y[i]
proc bitSetIntersect(x: var TBitSet, y: TBitSet) = proc bitSetIntersect(x: var TBitSet, y: TBitSet) =
for i in countup(0, high(x)): x[i] = x[i] and y[i] for i in countup(0, high(x)): x[i] = x[i] and y[i]
proc bitSetEquals(x, y: TBitSet): bool = proc bitSetEquals(x, y: TBitSet): bool =
for i in countup(0, high(x)): for i in countup(0, high(x)):
if x[i] != y[i]: if x[i] != y[i]:
return false return false
result = true result = true
proc bitSetContains(x, y: TBitSet): bool = proc bitSetContains(x, y: TBitSet): bool =
for i in countup(0, high(x)): for i in countup(0, high(x)):
if (x[i] and not y[i]) != int8(0): if (x[i] and not y[i]) != int8(0):
return false return false
result = true result = true

View file

@ -30,39 +30,39 @@ type
# #
# This is a good compromise between correctness and brevity. ;-) # This is a good compromise between correctness and brevity. ;-)
const const
cb64 = [ cb64 = [
"A", "B", "C", "D", "E", "F", "G", "H", "I", "J", "K", "L", "M", "N", "A", "B", "C", "D", "E", "F", "G", "H", "I", "J", "K", "L", "M", "N",
"O", "P", "Q", "R", "S", "T" "U", "V", "W", "X", "Y", "Z", "O", "P", "Q", "R", "S", "T" "U", "V", "W", "X", "Y", "Z",
"a", "b", "c", "d", "e", "f", "g", "h", "i", "j", "k", "l", "m", "n", "a", "b", "c", "d", "e", "f", "g", "h", "i", "j", "k", "l", "m", "n",
"o", "p", "q", "r", "s", "t", "u", "v", "w", "x", "y", "z", "o", "p", "q", "r", "s", "t", "u", "v", "w", "x", "y", "z",
"0", "1", "2", "3", "4", "5", "6", "7", "8", "9", "0", "1", "2", "3", "4", "5", "6", "7", "8", "9",
"_A", "_B"] "_A", "_B"]
proc toBase64a(s: cstring, len: int): string = proc toBase64a(s: cstring, len: int): 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.
result = newStringOfCap(((len + 2) div 3) * 4) result = newStringOfCap(((len + 2) div 3) * 4)
var i = 0 var i = 0
while i < s.len - 2: while i < s.len - 2:
let a = ord(s[i]) let a = ord(s[i])
let b = ord(s[i+1]) let b = ord(s[i+1])
let c = ord(s[i+2]) let c = ord(s[i+2])
result.add cb64[a shr 2] result.add cb64[a shr 2]
result.add cb64[((a and 3) shl 4) or ((b and 0xF0) shr 4)] result.add cb64[((a and 3) shl 4) or ((b and 0xF0) shr 4)]
result.add cb64[((b and 0x0F) shl 2) or ((c and 0xC0) shr 6)] result.add cb64[((b and 0x0F) shl 2) or ((c and 0xC0) shr 6)]
result.add cb64[c and 0x3F] result.add cb64[c and 0x3F]
inc(i, 3) inc(i, 3)
if i < s.len-1: if i < s.len-1:
let a = ord(s[i]) let a = ord(s[i])
let b = ord(s[i+1]) let b = ord(s[i+1])
result.add cb64[a shr 2] result.add cb64[a shr 2]
result.add cb64[((a and 3) shl 4) or ((b and 0xF0) shr 4)] result.add cb64[((a and 3) shl 4) or ((b and 0xF0) shr 4)]
result.add cb64[((b and 0x0F) shl 2)] result.add cb64[((b and 0x0F) shl 2)]
elif i < s.len: elif i < s.len:
let a = ord(s[i]) let a = ord(s[i])
result.add cb64[a shr 2] result.add cb64[a shr 2]
result.add cb64[(a and 3) shl 4] result.add cb64[(a and 3) shl 4]
proc toBase64a(u: TUid): string = toBase64a(cast[cstring](u), sizeof(u)) proc toBase64a(u: TUid): string = toBase64a(cast[cstring](u), sizeof(u))
@ -73,7 +73,7 @@ proc hashSym(c: var MD5Context, s: PSym) =
c &= ":anon" c &= ":anon"
else: else:
var it = s.owner var it = s.owner
while it != nil: while it != nil:
hashSym(c, it) hashSym(c, it)
c &= "." c &= "."
it = s.owner it = s.owner
@ -106,18 +106,18 @@ proc hashTree(c: var MD5Context, n: PNode) =
proc hashType(c: var MD5Context, t: PType) = proc hashType(c: var MD5Context, t: PType) =
# modelled after 'typeToString' # modelled after 'typeToString'
if t == nil: if t == nil:
c &= "\254" c &= "\254"
return return
var k = t.kind var k = t.kind
md5Update(c, cast[cstring](addr(k)), 1) md5Update(c, cast[cstring](addr(k)), 1)
if t.sym != nil and sfAnon notin t.sym.flags: if t.sym != nil and sfAnon notin t.sym.flags:
# t.n for literals, but not for e.g. objects! # t.n for literals, but not for e.g. objects!
if t.kind in {tyFloat, tyInt}: c.hashNode(t.n) if t.kind in {tyFloat, tyInt}: c.hashNode(t.n)
c.hashSym(t.sym) c.hashSym(t.sym)
case t.kind case t.kind
of tyGenericBody, tyGenericInst, tyGenericInvocation: of tyGenericBody, tyGenericInst, tyGenericInvocation:
for i in countup(0, sonsLen(t) -1 -ord(t.kind != tyGenericInvocation)): for i in countup(0, sonsLen(t) -1 -ord(t.kind != tyGenericInvocation)):
@ -135,10 +135,10 @@ proc hashType(c: var MD5Context, t: PType) =
of tyArrayConstr: of tyArrayConstr:
c.hashTree(t.sons[0].n) c.hashTree(t.sons[0].n)
c.hashType(t.sons[1]) c.hashType(t.sons[1])
of tyTuple: of tyTuple:
if t.n != nil: if t.n != nil:
assert(sonsLen(t.n) == sonsLen(t)) assert(sonsLen(t.n) == sonsLen(t))
for i in countup(0, sonsLen(t.n) - 1): for i in countup(0, sonsLen(t.n) - 1):
assert(t.n.sons[i].kind == nkSym) assert(t.n.sons[i].kind == nkSym)
c &= t.n.sons[i].sym.name.s c &= t.n.sons[i].sym.name.s
c &= ":" c &= ":"
@ -184,18 +184,18 @@ proc pushSym(w: PRodWriter, s: PSym) =
if iiTableGet(w.index.tab, s.id) == InvalidKey: if iiTableGet(w.index.tab, s.id) == InvalidKey:
w.sstack.add(s) w.sstack.add(s)
proc encodeNode(w: PRodWriter, fInfo: TLineInfo, n: PNode, proc encodeNode(w: PRodWriter, fInfo: TLineInfo, n: PNode,
result: var string) = result: var string) =
if n == nil: if n == nil:
# nil nodes have to be stored too: # nil nodes have to be stored too:
result.add("()") result.add("()")
return return
result.add('(') result.add('(')
encodeVInt(ord(n.kind), result) encodeVInt(ord(n.kind), result)
# we do not write comments for now # we do not write comments for now
# Line information takes easily 20% or more of the filesize! Therefore we # Line information takes easily 20% or more of the filesize! Therefore we
# omit line information if it is the same as the father's line information: # omit line information if it is the same as the father's line information:
if fInfo.fileIndex != n.info.fileIndex: if fInfo.fileIndex != n.info.fileIndex:
result.add('?') result.add('?')
encodeVInt(n.info.col, result) encodeVInt(n.info.col, result)
result.add(',') result.add(',')
@ -211,7 +211,7 @@ proc encodeNode(w: PRodWriter, fInfo: TLineInfo, n: PNode,
result.add('?') result.add('?')
encodeVInt(n.info.col, result) encodeVInt(n.info.col, result)
var f = n.flags * PersistentNodeFlags var f = n.flags * PersistentNodeFlags
if f != {}: if f != {}:
result.add('$') result.add('$')
encodeVInt(cast[int32](f), result) encodeVInt(cast[int32](f), result)
if n.typ != nil: if n.typ != nil:
@ -219,16 +219,16 @@ proc encodeNode(w: PRodWriter, fInfo: TLineInfo, n: PNode,
encodeVInt(n.typ.id, result) encodeVInt(n.typ.id, result)
pushType(w, n.typ) pushType(w, n.typ)
case n.kind case n.kind
of nkCharLit..nkInt64Lit: of nkCharLit..nkInt64Lit:
if n.intVal != 0: if n.intVal != 0:
result.add('!') result.add('!')
encodeVBiggestInt(n.intVal, result) encodeVBiggestInt(n.intVal, result)
of nkFloatLit..nkFloat64Lit: of nkFloatLit..nkFloat64Lit:
if n.floatVal != 0.0: if n.floatVal != 0.0:
result.add('!') result.add('!')
encodeStr($n.floatVal, result) encodeStr($n.floatVal, result)
of nkStrLit..nkTripleStrLit: of nkStrLit..nkTripleStrLit:
if n.strVal != "": if n.strVal != "":
result.add('!') result.add('!')
encodeStr(n.strVal, result) encodeStr(n.strVal, result)
of nkIdent: of nkIdent:
@ -239,7 +239,7 @@ proc encodeNode(w: PRodWriter, fInfo: TLineInfo, n: PNode,
encodeVInt(n.sym.id, result) encodeVInt(n.sym.id, result)
pushSym(w, n.sym) pushSym(w, n.sym)
else: else:
for i in countup(0, sonsLen(n) - 1): for i in countup(0, sonsLen(n) - 1):
encodeNode(w, n.info, n.sons[i], result) encodeNode(w, n.info, n.sons[i], result)
add(result, ')') add(result, ')')
@ -268,9 +268,9 @@ proc encodeLoc(w: PRodWriter, loc: TLoc, result: var string) =
setLen(result, oldLen) setLen(result, oldLen)
else: else:
add(result, '>') add(result, '>')
proc encodeType(w: PRodWriter, t: PType, result: var string) = proc encodeType(w: PRodWriter, t: PType, result: var string) =
if t == nil: if t == nil:
# nil nodes have to be stored too: # nil nodes have to be stored too:
result.add("[]") result.add("[]")
return return
@ -282,38 +282,38 @@ proc encodeType(w: PRodWriter, t: PType, result: var string) =
encodeVInt(ord(t.kind), result) encodeVInt(ord(t.kind), result)
add(result, '+') add(result, '+')
encodeVInt(t.id, result) encodeVInt(t.id, result)
if t.n != nil: if t.n != nil:
encodeNode(w, unknownLineInfo(), t.n, result) encodeNode(w, unknownLineInfo(), t.n, result)
if t.flags != {}: if t.flags != {}:
add(result, '$') add(result, '$')
encodeVInt(cast[int32](t.flags), result) encodeVInt(cast[int32](t.flags), result)
if t.callConv != low(t.callConv): if t.callConv != low(t.callConv):
add(result, '?') add(result, '?')
encodeVInt(ord(t.callConv), result) encodeVInt(ord(t.callConv), result)
if t.owner != nil: if t.owner != nil:
add(result, '*') add(result, '*')
encodeVInt(t.owner.id, result) encodeVInt(t.owner.id, result)
pushSym(w, t.owner) pushSym(w, t.owner)
if t.sym != nil: if t.sym != nil:
add(result, '&') add(result, '&')
encodeVInt(t.sym.id, result) encodeVInt(t.sym.id, result)
pushSym(w, t.sym) pushSym(w, t.sym)
if t.size != - 1: if t.size != - 1:
add(result, '/') add(result, '/')
encodeVBiggestInt(t.size, result) encodeVBiggestInt(t.size, result)
if t.align != 2: if t.align != 2:
add(result, '=') add(result, '=')
encodeVInt(t.align, result) encodeVInt(t.align, result)
encodeLoc(w, t.loc, result) encodeLoc(w, t.loc, result)
for i in countup(0, sonsLen(t) - 1): for i in countup(0, sonsLen(t) - 1):
if t.sons[i] == nil: if t.sons[i] == nil:
add(result, "^()") add(result, "^()")
else: else:
add(result, '^') add(result, '^')
encodeVInt(t.sons[i].id, result) encodeVInt(t.sons[i].id, result)
pushType(w, t.sons[i]) pushType(w, t.sons[i])
proc encodeLib(w: PRodWriter, lib: PLib, info: TLineInfo, result: var string) = proc encodeLib(w: PRodWriter, lib: PLib, info: TLineInfo, result: var string) =
add(result, '|') add(result, '|')
encodeVInt(ord(lib.kind), result) encodeVInt(ord(lib.kind), result)
add(result, '|') add(result, '|')
@ -352,10 +352,10 @@ proc encodeSym(w: PRodWriter, s: PSym, result: var string) =
if s.magic != mNone: if s.magic != mNone:
result.add('@') result.add('@')
encodeVInt(ord(s.magic), result) encodeVInt(ord(s.magic), result)
if s.options != w.options: if s.options != w.options:
result.add('!') result.add('!')
encodeVInt(cast[int32](s.options), result) encodeVInt(cast[int32](s.options), result)
if s.position != 0: if s.position != 0:
result.add('%') result.add('%')
encodeVInt(s.position, result) encodeVInt(s.position, result)
if s.offset != - 1: if s.offset != - 1:
@ -383,7 +383,7 @@ proc createDb() =
fullpath varchar(256) not null, fullpath varchar(256) not null,
interfHash varchar(256) not null, interfHash varchar(256) not null,
fullHash varchar(256) not null, fullHash varchar(256) not null,
created timestamp not null default (DATETIME('now')), created timestamp not null default (DATETIME('now')),
);""") );""")
@ -397,7 +397,7 @@ proc createDb() =
foreign key (module) references module(id) foreign key (module) references module(id)
);""") );""")
db.exec(sql""" db.exec(sql"""
create table if not exists Type( create table if not exists Type(
id integer primary key, id integer primary key,

View file

@ -159,6 +159,17 @@ proc genArgNoParam(p: BProc, n: PNode): Rope =
initLocExprSingleUse(p, n, a) initLocExprSingleUse(p, n, a)
result = rdLoc(a) result = rdLoc(a)
template genParamLoop(params) {.dirty.} =
if i < sonsLen(typ):
assert(typ.n.sons[i].kind == nkSym)
let paramType = typ.n.sons[i]
if not paramType.typ.isCompileTimeOnly:
if params != nil: add(params, ~", ")
add(params, genArg(p, ri.sons[i], paramType.sym, ri))
else:
if params != nil: add(params, ~", ")
add(params, genArgNoParam(p, ri.sons[i]))
proc genPrefixCall(p: BProc, le, ri: PNode, d: var TLoc) = proc genPrefixCall(p: BProc, le, ri: PNode, d: var TLoc) =
var op: TLoc var op: TLoc
# this is a hotspot in the compiler # this is a hotspot in the compiler
@ -170,13 +181,7 @@ proc genPrefixCall(p: BProc, le, ri: PNode, d: var TLoc) =
assert(sonsLen(typ) == sonsLen(typ.n)) assert(sonsLen(typ) == sonsLen(typ.n))
var length = sonsLen(ri) var length = sonsLen(ri)
for i in countup(1, length - 1): for i in countup(1, length - 1):
if ri.sons[i].typ.isCompileTimeOnly: continue genParamLoop(params)
if params != nil: add(params, ~", ")
if i < sonsLen(typ):
assert(typ.n.sons[i].kind == nkSym)
add(params, genArg(p, ri.sons[i], typ.n.sons[i].sym, ri))
else:
add(params, genArgNoParam(p, ri.sons[i]))
fixupCall(p, le, ri, d, op.r, params) fixupCall(p, le, ri, d, op.r, params)
proc genClosureCall(p: BProc, le, ri: PNode, d: var TLoc) = proc genClosureCall(p: BProc, le, ri: PNode, d: var TLoc) =
@ -198,13 +203,7 @@ proc genClosureCall(p: BProc, le, ri: PNode, d: var TLoc) =
var length = sonsLen(ri) var length = sonsLen(ri)
for i in countup(1, length - 1): for i in countup(1, length - 1):
assert(sonsLen(typ) == sonsLen(typ.n)) assert(sonsLen(typ) == sonsLen(typ.n))
if ri.sons[i].typ.isCompileTimeOnly: continue genParamLoop(pl)
if i < sonsLen(typ):
assert(typ.n.sons[i].kind == nkSym)
add(pl, genArg(p, ri.sons[i], typ.n.sons[i].sym, ri))
else:
add(pl, genArgNoParam(p, ri.sons[i]))
if i < length - 1: add(pl, ~", ")
template genCallPattern {.dirty.} = template genCallPattern {.dirty.} =
lineF(p, cpsStmts, callPattern & ";$n", [op.r, pl, pl.addComma, rawProc]) lineF(p, cpsStmts, callPattern & ";$n", [op.r, pl, pl.addComma, rawProc])
@ -241,13 +240,14 @@ proc genClosureCall(p: BProc, le, ri: PNode, d: var TLoc) =
genCallPattern() genCallPattern()
proc genOtherArg(p: BProc; ri: PNode; i: int; typ: PType): Rope = proc genOtherArg(p: BProc; ri: PNode; i: int; typ: PType): Rope =
if ri.sons[i].typ.isCompileTimeOnly: if i < sonsLen(typ):
result = nil
elif i < sonsLen(typ):
# 'var T' is 'T&' in C++. This means we ignore the request of # 'var T' is 'T&' in C++. This means we ignore the request of
# any nkHiddenAddr when it's a 'var T'. # any nkHiddenAddr when it's a 'var T'.
assert(typ.n.sons[i].kind == nkSym) let paramType = typ.n.sons[i]
if typ.sons[i].kind == tyVar and ri.sons[i].kind == nkHiddenAddr: assert(paramType.kind == nkSym)
if paramType.typ.isCompileTimeOnly:
result = nil
elif typ.sons[i].kind == tyVar and ri.sons[i].kind == nkHiddenAddr:
result = genArgNoParam(p, ri.sons[i][0]) result = genArgNoParam(p, ri.sons[i][0])
else: else:
result = genArgNoParam(p, ri.sons[i]) #, typ.n.sons[i].sym) result = genArgNoParam(p, ri.sons[i]) #, typ.n.sons[i].sym)

View file

@ -221,7 +221,7 @@ proc optAsgnLoc(a: TLoc, t: PType, field: Rope): TLoc =
proc genOptAsgnTuple(p: BProc, dest, src: TLoc, flags: TAssignmentFlags) = proc genOptAsgnTuple(p: BProc, dest, src: TLoc, flags: TAssignmentFlags) =
let newflags = let newflags =
if src.k == locData: if src.s == OnStatic:
flags + {needToCopy} flags + {needToCopy}
elif tfShallow in dest.t.flags: elif tfShallow in dest.t.flags:
flags - {needToCopy} flags - {needToCopy}
@ -238,7 +238,7 @@ proc genOptAsgnObject(p: BProc, dest, src: TLoc, flags: TAssignmentFlags,
t: PNode) = t: PNode) =
if t == nil: return if t == nil: return
let newflags = let newflags =
if src.k == locData: if src.s == OnStatic:
flags + {needToCopy} flags + {needToCopy}
elif tfShallow in dest.t.flags: elif tfShallow in dest.t.flags:
flags - {needToCopy} flags - {needToCopy}
@ -287,13 +287,13 @@ proc genAssignment(p: BProc, dest, src: TLoc, flags: TAssignmentFlags) =
of tyRef: of tyRef:
genRefAssign(p, dest, src, flags) genRefAssign(p, dest, src, flags)
of tySequence: of tySequence:
if needToCopy notin flags and src.k != locData: if needToCopy notin flags and src.s != OnStatic:
genRefAssign(p, dest, src, flags) genRefAssign(p, dest, src, flags)
else: else:
linefmt(p, cpsStmts, "#genericSeqAssign($1, $2, $3);$n", linefmt(p, cpsStmts, "#genericSeqAssign($1, $2, $3);$n",
addrLoc(dest), rdLoc(src), genTypeInfo(p.module, dest.t)) addrLoc(dest), rdLoc(src), genTypeInfo(p.module, dest.t))
of tyString: of tyString:
if needToCopy notin flags and src.k != locData: if needToCopy notin flags and src.s != OnStatic:
genRefAssign(p, dest, src, flags) genRefAssign(p, dest, src, flags)
else: else:
if dest.s == OnStack or not usesNativeGC(): if dest.s == OnStack or not usesNativeGC():
@ -347,8 +347,8 @@ proc genAssignment(p: BProc, dest, src: TLoc, flags: TAssignmentFlags) =
else: else:
useStringh(p.module) useStringh(p.module)
linefmt(p, cpsStmts, linefmt(p, cpsStmts,
"memcpy((void*)$1, (NIM_CONST void*)$2, sizeof($1));$n", "memcpy((void*)$1, (NIM_CONST void*)$2, sizeof($3));$n",
rdLoc(dest), rdLoc(src)) rdLoc(dest), rdLoc(src), getTypeDesc(p.module, ty))
of tyOpenArray, tyVarargs: of tyOpenArray, tyVarargs:
# open arrays are always on the stack - really? What if a sequence is # open arrays are always on the stack - really? What if a sequence is
# passed to an open array? # passed to an open array?
@ -413,7 +413,7 @@ proc putDataIntoDest(p: BProc, d: var TLoc, t: PType, r: Rope) =
var a: TLoc var a: TLoc
if d.k != locNone: if d.k != locNone:
# need to generate an assignment here # need to generate an assignment here
initLoc(a, locData, t, OnUnknown) initLoc(a, locData, t, OnStatic)
a.r = r a.r = r
if lfNoDeepCopy in d.flags: genAssignment(p, d, a, {}) if lfNoDeepCopy in d.flags: genAssignment(p, d, a, {})
else: genAssignment(p, d, a, {needToCopy}) else: genAssignment(p, d, a, {needToCopy})
@ -424,11 +424,11 @@ proc putDataIntoDest(p: BProc, d: var TLoc, t: PType, r: Rope) =
d.t = t d.t = t
d.r = r d.r = r
proc putIntoDest(p: BProc, d: var TLoc, t: PType, r: Rope) = proc putIntoDest(p: BProc, d: var TLoc, t: PType, r: Rope; s=OnUnknown) =
var a: TLoc var a: TLoc
if d.k != locNone: if d.k != locNone:
# need to generate an assignment here # need to generate an assignment here
initLoc(a, locExpr, t, OnUnknown) initLoc(a, locExpr, t, s)
a.r = r a.r = r
if lfNoDeepCopy in d.flags: genAssignment(p, d, a, {}) if lfNoDeepCopy in d.flags: genAssignment(p, d, a, {})
else: genAssignment(p, d, a, {needToCopy}) else: genAssignment(p, d, a, {needToCopy})
@ -503,13 +503,13 @@ proc binaryArithOverflow(p: BProc, e: PNode, d: var TLoc, m: TMagic) =
"$# = #addInt($#, $#);$n", "$# = #subInt($#, $#);$n", "$# = #addInt($#, $#);$n", "$# = #subInt($#, $#);$n",
"$# = #mulInt($#, $#);$n", "$# = #divInt($#, $#);$n", "$# = #mulInt($#, $#);$n", "$# = #divInt($#, $#);$n",
"$# = #modInt($#, $#);$n", "$# = #modInt($#, $#);$n",
"$# = #addInt($#, $#);$n", "$# = #subInt($#, $#);$n"]
prc64: array[mAddI..mPred, string] = [
"$# = #addInt64($#, $#);$n", "$# = #subInt64($#, $#);$n", "$# = #addInt64($#, $#);$n", "$# = #subInt64($#, $#);$n",
"$# = #mulInt64($#, $#);$n", "$# = #divInt64($#, $#);$n", "$# = #mulInt64($#, $#);$n", "$# = #divInt64($#, $#);$n",
"$# = #modInt64($#, $#);$n", "$# = #modInt64($#, $#);$n",
"$# = #addInt($#, $#);$n", "$# = #subInt($#, $#);$n"] "$# = #addInt64($#, $#);$n", "$# = #subInt64($#, $#);$n"]
opr: array[mAddI..mPred, string] = [ opr: array[mAddI..mPred, string] = [
"($#)($# + $#)", "($#)($# - $#)", "($#)($# * $#)",
"($#)($# / $#)", "($#)($# % $#)",
"($#)($# + $#)", "($#)($# - $#)", "($#)($# * $#)", "($#)($# + $#)", "($#)($# - $#)", "($#)($# * $#)",
"($#)($# / $#)", "($#)($# % $#)", "($#)($# / $#)", "($#)($# % $#)",
"($#)($# + $#)", "($#)($# - $#)"] "($#)($# + $#)", "($#)($# - $#)"]
@ -525,16 +525,16 @@ proc binaryArithOverflow(p: BProc, e: PNode, d: var TLoc, m: TMagic) =
let res = opr[m] % [getTypeDesc(p.module, t), rdLoc(a), rdLoc(b)] let res = opr[m] % [getTypeDesc(p.module, t), rdLoc(a), rdLoc(b)]
putIntoDest(p, d, e.typ, res) putIntoDest(p, d, e.typ, res)
else: else:
let res = binaryArithOverflowRaw(p, t, a, b, prc[m]) let res = binaryArithOverflowRaw(p, t, a, b,
if t.kind == tyInt64: prc64[m] else: prc[m])
putIntoDest(p, d, e.typ, "($#)($#)" % [getTypeDesc(p.module, t), res]) putIntoDest(p, d, e.typ, "($#)($#)" % [getTypeDesc(p.module, t), res])
proc unaryArithOverflow(p: BProc, e: PNode, d: var TLoc, m: TMagic) = proc unaryArithOverflow(p: BProc, e: PNode, d: var TLoc, m: TMagic) =
const const
opr: array[mUnaryMinusI..mAbsI64, string] = [ opr: array[mUnaryMinusI..mAbsI, string] = [
mUnaryMinusI: "((NI$2)-($1))", mUnaryMinusI: "((NI$2)-($1))",
mUnaryMinusI64: "-($1)", mUnaryMinusI64: "-($1)",
mAbsI: "($1 > 0? ($1) : -($1))", mAbsI: "($1 > 0? ($1) : -($1))"]
mAbsI64: "($1 > 0? ($1) : -($1))"]
var var
a: TLoc a: TLoc
t: PType t: PType
@ -561,11 +561,6 @@ proc binaryArith(p: BProc, e: PNode, d: var TLoc, op: TMagic) =
"($4)($1 ^ $2)", # BitxorI "($4)($1 ^ $2)", # BitxorI
"(($1 <= $2) ? $1 : $2)", # MinI "(($1 <= $2) ? $1 : $2)", # MinI
"(($1 >= $2) ? $1 : $2)", # MaxI "(($1 >= $2) ? $1 : $2)", # MaxI
"($4)((NU64)($1) >> (NU64)($2))", # ShrI64
"($4)((NU64)($1) << (NU64)($2))", # ShlI64
"($4)($1 & $2)", # BitandI64
"($4)($1 | $2)", # BitorI64
"($4)($1 ^ $2)", # BitxorI64
"(($1 <= $2) ? $1 : $2)", # MinF64 "(($1 <= $2) ? $1 : $2)", # MinF64
"(($1 >= $2) ? $1 : $2)", # MaxF64 "(($1 >= $2) ? $1 : $2)", # MaxF64
"($4)((NU$3)($1) + (NU$3)($2))", # AddU "($4)((NU$3)($1) + (NU$3)($2))", # AddU
@ -576,9 +571,6 @@ proc binaryArith(p: BProc, e: PNode, d: var TLoc, op: TMagic) =
"($1 == $2)", # EqI "($1 == $2)", # EqI
"($1 <= $2)", # LeI "($1 <= $2)", # LeI
"($1 < $2)", # LtI "($1 < $2)", # LtI
"($1 == $2)", # EqI64
"($1 <= $2)", # LeI64
"($1 < $2)", # LtI64
"($1 == $2)", # EqF64 "($1 == $2)", # EqF64
"($1 <= $2)", # LeF64 "($1 <= $2)", # LeF64
"($1 < $2)", # LtF64 "($1 < $2)", # LtF64
@ -638,7 +630,6 @@ proc unaryArith(p: BProc, e: PNode, d: var TLoc, op: TMagic) =
unArithTab: array[mNot..mToBiggestInt, string] = ["!($1)", # Not unArithTab: array[mNot..mToBiggestInt, string] = ["!($1)", # Not
"$1", # UnaryPlusI "$1", # UnaryPlusI
"($3)((NU$2) ~($1))", # BitnotI "($3)((NU$2) ~($1))", # BitnotI
"($3)((NU$2) ~($1))", # BitnotI64
"$1", # UnaryPlusF64 "$1", # UnaryPlusF64
"-($1)", # UnaryMinusF64 "-($1)", # UnaryMinusF64
"($1 > 0? ($1) : -($1))", # AbsF64; BUGFIX: fabs() makes problems "($1 > 0? ($1) : -($1))", # AbsF64; BUGFIX: fabs() makes problems
@ -694,7 +685,7 @@ proc genDeref(p: BProc, e: PNode, d: var TLoc; enforceDeref=false) =
d.s = OnUnknown d.s = OnUnknown
if tfVarIsPtr notin typ.flags and p.module.compileToCpp and if tfVarIsPtr notin typ.flags and p.module.compileToCpp and
e.kind == nkHiddenDeref: e.kind == nkHiddenDeref:
putIntoDest(p, d, e.typ, rdLoc(a)) putIntoDest(p, d, e.typ, rdLoc(a), a.s)
return return
of tyPtr: of tyPtr:
d.s = OnUnknown # BUGFIX! d.s = OnUnknown # BUGFIX!
@ -703,7 +694,7 @@ proc genDeref(p: BProc, e: PNode, d: var TLoc; enforceDeref=false) =
let typ = skipTypes(a.t, abstractInst) let typ = skipTypes(a.t, abstractInst)
if typ.kind == tyVar and tfVarIsPtr notin typ.flags and if typ.kind == tyVar and tfVarIsPtr notin typ.flags and
e.kind == nkHiddenDeref: e.kind == nkHiddenDeref:
putIntoDest(p, d, e.typ, rdLoc(a)) putIntoDest(p, d, e.typ, rdLoc(a), a.s)
return return
if enforceDeref and mt == ctPtrToArray: if enforceDeref and mt == ctPtrToArray:
# we lie about the type for better C interop: 'ptr array[3,T]' is # we lie about the type for better C interop: 'ptr array[3,T]' is
@ -711,23 +702,23 @@ proc genDeref(p: BProc, e: PNode, d: var TLoc; enforceDeref=false) =
# See tmissingderef. So we get rid of the deref instead. The codegen # See tmissingderef. So we get rid of the deref instead. The codegen
# ends up using 'memcpy' for the array assignment, # ends up using 'memcpy' for the array assignment,
# so the '&' and '*' cancel out: # so the '&' and '*' cancel out:
putIntoDest(p, d, a.t.sons[0], rdLoc(a)) putIntoDest(p, d, a.t.sons[0], rdLoc(a), a.s)
else: else:
putIntoDest(p, d, e.typ, "(*$1)" % [rdLoc(a)]) putIntoDest(p, d, e.typ, "(*$1)" % [rdLoc(a)], a.s)
proc genAddr(p: BProc, e: PNode, d: var TLoc) = proc genAddr(p: BProc, e: PNode, d: var TLoc) =
# careful 'addr(myptrToArray)' needs to get the ampersand: # careful 'addr(myptrToArray)' needs to get the ampersand:
if e.sons[0].typ.skipTypes(abstractInst).kind in {tyRef, tyPtr}: if e.sons[0].typ.skipTypes(abstractInst).kind in {tyRef, tyPtr}:
var a: TLoc var a: TLoc
initLocExpr(p, e.sons[0], a) initLocExpr(p, e.sons[0], a)
putIntoDest(p, d, e.typ, "&" & a.r) putIntoDest(p, d, e.typ, "&" & a.r, a.s)
#Message(e.info, warnUser, "HERE NEW &") #Message(e.info, warnUser, "HERE NEW &")
elif mapType(e.sons[0].typ) == ctArray or isCppRef(p, e.sons[0].typ): elif mapType(e.sons[0].typ) == ctArray or isCppRef(p, e.sons[0].typ):
expr(p, e.sons[0], d) expr(p, e.sons[0], d)
else: else:
var a: TLoc var a: TLoc
initLocExpr(p, e.sons[0], a) initLocExpr(p, e.sons[0], a)
putIntoDest(p, d, e.typ, addrLoc(a)) putIntoDest(p, d, e.typ, addrLoc(a), a.s)
template inheritLocation(d: var TLoc, a: TLoc) = template inheritLocation(d: var TLoc, a: TLoc) =
if d.k == locNone: d.s = a.s if d.k == locNone: d.s = a.s
@ -754,7 +745,18 @@ proc genTupleElem(p: BProc, e: PNode, d: var TLoc) =
of nkIntLit..nkUInt64Lit: i = int(e.sons[1].intVal) of nkIntLit..nkUInt64Lit: i = int(e.sons[1].intVal)
else: internalError(e.info, "genTupleElem") else: internalError(e.info, "genTupleElem")
addf(r, ".Field$1", [rope(i)]) addf(r, ".Field$1", [rope(i)])
putIntoDest(p, d, ty.sons[i], r) putIntoDest(p, d, ty.sons[i], r, a.s)
proc lookupFieldAgain(p: BProc, ty: PType; field: PSym; r: var Rope): PSym =
var ty = ty
assert r != nil
while ty != nil:
assert(ty.kind in {tyTuple, tyObject})
result = lookupInRecord(ty.n, field.name)
if result != nil: break
if not p.module.compileToCpp: add(r, ".Sup")
ty = getUniqueType(ty.sons[0])
if result == nil: internalError(field.info, "genCheckedRecordField")
proc genRecordField(p: BProc, e: PNode, d: var TLoc) = proc genRecordField(p: BProc, e: PNode, d: var TLoc) =
var a: TLoc var a: TLoc
@ -765,25 +767,18 @@ proc genRecordField(p: BProc, e: PNode, d: var TLoc) =
# we found a unique tuple type which lacks field information # we found a unique tuple type which lacks field information
# so we use Field$i # so we use Field$i
addf(r, ".Field$1", [rope(f.position)]) addf(r, ".Field$1", [rope(f.position)])
putIntoDest(p, d, f.typ, r) putIntoDest(p, d, f.typ, r, a.s)
else: else:
var field: PSym = nil let field = lookupFieldAgain(p, ty, f, r)
while ty != nil:
if ty.kind notin {tyTuple, tyObject}:
internalError(e.info, "genRecordField")
field = lookupInRecord(ty.n, f.name)
if field != nil: break
if not p.module.compileToCpp: add(r, ".Sup")
ty = getUniqueType(ty.sons[0])
if field == nil: internalError(e.info, "genRecordField 2 ")
if field.loc.r == nil: internalError(e.info, "genRecordField 3") if field.loc.r == nil: internalError(e.info, "genRecordField 3")
addf(r, ".$1", [field.loc.r]) addf(r, ".$1", [field.loc.r])
putIntoDest(p, d, field.typ, r) putIntoDest(p, d, field.typ, r, a.s)
#d.s = a.s #d.s = a.s
proc genInExprAux(p: BProc, e: PNode, a, b, d: var TLoc) proc genInExprAux(p: BProc, e: PNode, a, b, d: var TLoc)
proc genFieldCheck(p: BProc, e: PNode, obj: Rope, field: PSym) = proc genFieldCheck(p: BProc, e: PNode, obj: Rope, field: PSym;
origTy: PType) =
var test, u, v: TLoc var test, u, v: TLoc
for i in countup(1, sonsLen(e) - 1): for i in countup(1, sonsLen(e) - 1):
var it = e.sons[i] var it = e.sons[i]
@ -795,8 +790,12 @@ proc genFieldCheck(p: BProc, e: PNode, obj: Rope, field: PSym) =
assert(disc.kind == nkSym) assert(disc.kind == nkSym)
initLoc(test, locNone, it.typ, OnStack) initLoc(test, locNone, it.typ, OnStack)
initLocExpr(p, it.sons[1], u) initLocExpr(p, it.sons[1], u)
initLoc(v, locExpr, disc.typ, OnUnknown) var o = obj
v.r = "$1.$2" % [obj, disc.sym.loc.r] let d = lookupFieldAgain(p, origTy, disc.sym, o)
initLoc(v, locExpr, d.typ, OnUnknown)
v.r = o
v.r.add(".")
v.r.add(d.loc.r)
genInExprAux(p, it, u, v, test) genInExprAux(p, it, u, v, test)
let id = nodeTableTestOrSet(p.module.dataCache, let id = nodeTableTestOrSet(p.module.dataCache,
newStrNode(nkStrLit, field.name.s), gBackendId) newStrNode(nkStrLit, field.name.s), gBackendId)
@ -813,27 +812,16 @@ proc genFieldCheck(p: BProc, e: PNode, obj: Rope, field: PSym) =
proc genCheckedRecordField(p: BProc, e: PNode, d: var TLoc) = proc genCheckedRecordField(p: BProc, e: PNode, d: var TLoc) =
if optFieldCheck in p.options: if optFieldCheck in p.options:
var var a: TLoc
a: TLoc let ty = genRecordFieldAux(p, e.sons[0], d, a)
f, field: PSym var r = rdLoc(a)
ty: PType let f = e.sons[0].sons[1].sym
r: Rope let field = lookupFieldAgain(p, ty, f, r)
ty = genRecordFieldAux(p, e.sons[0], d, a)
r = rdLoc(a)
f = e.sons[0].sons[1].sym
field = nil
while ty != nil:
assert(ty.kind in {tyTuple, tyObject})
field = lookupInRecord(ty.n, f.name)
if field != nil: break
if not p.module.compileToCpp: add(r, ".Sup")
ty = getUniqueType(ty.sons[0])
if field == nil: internalError(e.info, "genCheckedRecordField")
if field.loc.r == nil: if field.loc.r == nil:
internalError(e.info, "genCheckedRecordField") # generate the checks: internalError(e.info, "genCheckedRecordField") # generate the checks:
genFieldCheck(p, e, r, field) genFieldCheck(p, e, r, field, ty)
add(r, rfmt(nil, ".$1", field.loc.r)) add(r, rfmt(nil, ".$1", field.loc.r))
putIntoDest(p, d, field.typ, r) putIntoDest(p, d, field.typ, r, a.s)
else: else:
genRecordField(p, e.sons[0], d) genRecordField(p, e.sons[0], d)
@ -860,7 +848,7 @@ proc genArrayElem(p: BProc, x, y: PNode, d: var TLoc) =
localError(x.info, errIndexOutOfBounds) localError(x.info, errIndexOutOfBounds)
d.inheritLocation(a) d.inheritLocation(a)
putIntoDest(p, d, elemType(skipTypes(ty, abstractVar)), putIntoDest(p, d, elemType(skipTypes(ty, abstractVar)),
rfmt(nil, "$1[($2)- $3]", rdLoc(a), rdCharLoc(b), first)) rfmt(nil, "$1[($2)- $3]", rdLoc(a), rdCharLoc(b), first), a.s)
proc genCStringElem(p: BProc, x, y: PNode, d: var TLoc) = proc genCStringElem(p: BProc, x, y: PNode, d: var TLoc) =
var a, b: TLoc var a, b: TLoc
@ -869,7 +857,7 @@ proc genCStringElem(p: BProc, x, y: PNode, d: var TLoc) =
var ty = skipTypes(a.t, abstractVarRange) var ty = skipTypes(a.t, abstractVarRange)
if d.k == locNone: d.s = a.s if d.k == locNone: d.s = a.s
putIntoDest(p, d, elemType(skipTypes(ty, abstractVar)), putIntoDest(p, d, elemType(skipTypes(ty, abstractVar)),
rfmt(nil, "$1[$2]", rdLoc(a), rdCharLoc(b))) rfmt(nil, "$1[$2]", rdLoc(a), rdCharLoc(b)), a.s)
proc genOpenArrayElem(p: BProc, x, y: PNode, d: var TLoc) = proc genOpenArrayElem(p: BProc, x, y: PNode, d: var TLoc) =
var a, b: TLoc var a, b: TLoc
@ -880,7 +868,7 @@ proc genOpenArrayElem(p: BProc, x, y: PNode, d: var TLoc) =
rdLoc(b), rdLoc(a)) # BUGFIX: ``>=`` and not ``>``! rdLoc(b), rdLoc(a)) # BUGFIX: ``>=`` and not ``>``!
if d.k == locNone: d.s = a.s if d.k == locNone: d.s = a.s
putIntoDest(p, d, elemType(skipTypes(a.t, abstractVar)), putIntoDest(p, d, elemType(skipTypes(a.t, abstractVar)),
rfmt(nil, "$1[$2]", rdLoc(a), rdCharLoc(b))) rfmt(nil, "$1[$2]", rdLoc(a), rdCharLoc(b)), a.s)
proc genSeqElem(p: BProc, x, y: PNode, d: var TLoc) = proc genSeqElem(p: BProc, x, y: PNode, d: var TLoc) =
var a, b: TLoc var a, b: TLoc
@ -903,7 +891,7 @@ proc genSeqElem(p: BProc, x, y: PNode, d: var TLoc) =
if skipTypes(a.t, abstractVar).kind in {tyRef, tyPtr}: if skipTypes(a.t, abstractVar).kind in {tyRef, tyPtr}:
a.r = rfmt(nil, "(*$1)", a.r) a.r = rfmt(nil, "(*$1)", a.r)
putIntoDest(p, d, elemType(skipTypes(a.t, abstractVar)), putIntoDest(p, d, elemType(skipTypes(a.t, abstractVar)),
rfmt(nil, "$1->data[$2]", rdLoc(a), rdCharLoc(b))) rfmt(nil, "$1->data[$2]", rdLoc(a), rdCharLoc(b)), a.s)
proc genBracketExpr(p: BProc; n: PNode; d: var TLoc) = proc genBracketExpr(p: BProc; n: PNode; d: var TLoc) =
var ty = skipTypes(n.sons[0].typ, abstractVarRange) var ty = skipTypes(n.sons[0].typ, abstractVarRange)
@ -1061,9 +1049,9 @@ proc genSeqElemAppend(p: BProc, e: PNode, d: var TLoc) =
# seq = (typeof seq) incrSeq(&seq->Sup, sizeof(x)); # seq = (typeof seq) incrSeq(&seq->Sup, sizeof(x));
# seq->data[seq->len-1] = x; # seq->data[seq->len-1] = x;
let seqAppendPattern = if not p.module.compileToCpp: let seqAppendPattern = if not p.module.compileToCpp:
"$1 = ($2) #incrSeq(&($1)->Sup, sizeof($3));$n" "$1 = ($2) #incrSeqV2(&($1)->Sup, sizeof($3));$n"
else: else:
"$1 = ($2) #incrSeq($1, sizeof($3));$n" "$1 = ($2) #incrSeqV2($1, sizeof($3));$n"
var a, b, dest: TLoc var a, b, dest: TLoc
initLocExpr(p, e.sons[1], a) initLocExpr(p, e.sons[1], a)
initLocExpr(p, e.sons[2], b) initLocExpr(p, e.sons[2], b)
@ -1073,8 +1061,9 @@ proc genSeqElemAppend(p: BProc, e: PNode, d: var TLoc) =
getTypeDesc(p.module, skipTypes(e.sons[2].typ, abstractVar))]) getTypeDesc(p.module, skipTypes(e.sons[2].typ, abstractVar))])
keepAlive(p, a) keepAlive(p, a)
initLoc(dest, locExpr, b.t, OnHeap) initLoc(dest, locExpr, b.t, OnHeap)
dest.r = rfmt(nil, "$1->data[$1->$2-1]", rdLoc(a), lenField(p)) dest.r = rfmt(nil, "$1->data[$1->$2]", rdLoc(a), lenField(p))
genAssignment(p, dest, b, {needToCopy, afDestIsNil}) genAssignment(p, dest, b, {needToCopy, afDestIsNil})
lineCg(p, cpsStmts, "++$1->$2;$n", rdLoc(a), lenField(p))
gcUsage(e) gcUsage(e)
proc genReset(p: BProc, n: PNode) = proc genReset(p: BProc, n: PNode) =
@ -1158,20 +1147,15 @@ proc genObjConstr(p: BProc, e: PNode, d: var TLoc) =
else: else:
constructLoc(p, tmp) constructLoc(p, tmp)
discard getTypeDesc(p.module, t) discard getTypeDesc(p.module, t)
let ty = getUniqueType(t)
for i in 1 .. <e.len: for i in 1 .. <e.len:
let it = e.sons[i] let it = e.sons[i]
var tmp2: TLoc var tmp2: TLoc
tmp2.r = r tmp2.r = r
var field: PSym = nil let field = lookupFieldAgain(p, ty, it.sons[0].sym, tmp2.r)
var ty = getUniqueType(t) if field.loc.r == nil: internalError(e.info, "genObjConstr")
while ty != nil:
field = lookupInRecord(ty.n, it.sons[0].sym.name)
if field != nil: break
if not p.module.compileToCpp: add(tmp2.r, ".Sup")
ty = getUniqueType(ty.sons[0])
if field == nil or field.loc.r == nil: internalError(e.info, "genObjConstr")
if it.len == 3 and optFieldCheck in p.options: if it.len == 3 and optFieldCheck in p.options:
genFieldCheck(p, it.sons[2], tmp2.r, field) genFieldCheck(p, it.sons[2], r, field, ty)
add(tmp2.r, ".") add(tmp2.r, ".")
add(tmp2.r, field.loc.r) add(tmp2.r, field.loc.r)
tmp2.k = locTemp tmp2.k = locTemp
@ -1280,7 +1264,7 @@ proc genOf(p: BProc, x: PNode, typ: PType, d: var TLoc) =
r = rfmt(p.module, "(($1) && ($2))", nilCheck, genOfHelper(p, dest, r)) r = rfmt(p.module, "(($1) && ($2))", nilCheck, genOfHelper(p, dest, r))
else: else:
r = rfmt(p.module, "($1)", genOfHelper(p, dest, r)) r = rfmt(p.module, "($1)", genOfHelper(p, dest, r))
putIntoDest(p, d, getSysType(tyBool), r) putIntoDest(p, d, getSysType(tyBool), r, a.s)
proc genOf(p: BProc, n: PNode, d: var TLoc) = proc genOf(p: BProc, n: PNode, d: var TLoc) =
genOf(p, n.sons[1], n.sons[2].typ, d) genOf(p, n.sons[1], n.sons[2].typ, d)
@ -1292,45 +1276,47 @@ proc genRepr(p: BProc, e: PNode, d: var TLoc) =
case t.kind case t.kind
of tyInt..tyInt64, tyUInt..tyUInt64: of tyInt..tyInt64, tyUInt..tyUInt64:
putIntoDest(p, d, e.typ, putIntoDest(p, d, e.typ,
ropecg(p.module, "#reprInt((NI64)$1)", [rdLoc(a)])) ropecg(p.module, "#reprInt((NI64)$1)", [rdLoc(a)]), a.s)
of tyFloat..tyFloat128: of tyFloat..tyFloat128:
putIntoDest(p, d, e.typ, ropecg(p.module, "#reprFloat($1)", [rdLoc(a)])) putIntoDest(p, d, e.typ, ropecg(p.module, "#reprFloat($1)", [rdLoc(a)]), a.s)
of tyBool: of tyBool:
putIntoDest(p, d, e.typ, ropecg(p.module, "#reprBool($1)", [rdLoc(a)])) putIntoDest(p, d, e.typ, ropecg(p.module, "#reprBool($1)", [rdLoc(a)]), a.s)
of tyChar: of tyChar:
putIntoDest(p, d, e.typ, ropecg(p.module, "#reprChar($1)", [rdLoc(a)])) putIntoDest(p, d, e.typ, ropecg(p.module, "#reprChar($1)", [rdLoc(a)]), a.s)
of tyEnum, tyOrdinal: of tyEnum, tyOrdinal:
putIntoDest(p, d, e.typ, putIntoDest(p, d, e.typ,
ropecg(p.module, "#reprEnum($1, $2)", [ ropecg(p.module, "#reprEnum($1, $2)", [
rdLoc(a), genTypeInfo(p.module, t)])) rdLoc(a), genTypeInfo(p.module, t)]), a.s)
of tyString: of tyString:
putIntoDest(p, d, e.typ, ropecg(p.module, "#reprStr($1)", [rdLoc(a)])) putIntoDest(p, d, e.typ, ropecg(p.module, "#reprStr($1)", [rdLoc(a)]), a.s)
of tySet: of tySet:
putIntoDest(p, d, e.typ, ropecg(p.module, "#reprSet($1, $2)", [ putIntoDest(p, d, e.typ, ropecg(p.module, "#reprSet($1, $2)", [
addrLoc(a), genTypeInfo(p.module, t)])) addrLoc(a), genTypeInfo(p.module, t)]), a.s)
of tyOpenArray, tyVarargs: of tyOpenArray, tyVarargs:
var b: TLoc var b: TLoc
case a.t.kind case a.t.kind
of tyOpenArray, tyVarargs: of tyOpenArray, tyVarargs:
putIntoDest(p, b, e.typ, "$1, $1Len0" % [rdLoc(a)]) putIntoDest(p, b, e.typ, "$1, $1Len0" % [rdLoc(a)], a.s)
of tyString, tySequence: of tyString, tySequence:
putIntoDest(p, b, e.typ, putIntoDest(p, b, e.typ,
"$1->data, $1->$2" % [rdLoc(a), lenField(p)]) "$1->data, $1->$2" % [rdLoc(a), lenField(p)], a.s)
of tyArray, tyArrayConstr: of tyArray, tyArrayConstr:
putIntoDest(p, b, e.typ, putIntoDest(p, b, e.typ,
"$1, $2" % [rdLoc(a), rope(lengthOrd(a.t))]) "$1, $2" % [rdLoc(a), rope(lengthOrd(a.t))], a.s)
else: internalError(e.sons[0].info, "genRepr()") else: internalError(e.sons[0].info, "genRepr()")
putIntoDest(p, d, e.typ, putIntoDest(p, d, e.typ,
ropecg(p.module, "#reprOpenArray($1, $2)", [rdLoc(b), ropecg(p.module, "#reprOpenArray($1, $2)", [rdLoc(b),
genTypeInfo(p.module, elemType(t))])) genTypeInfo(p.module, elemType(t))]), a.s)
of tyCString, tyArray, tyArrayConstr, tyRef, tyPtr, tyPointer, tyNil, of tyCString, tyArray, tyArrayConstr, tyRef, tyPtr, tyPointer, tyNil,
tySequence: tySequence:
putIntoDest(p, d, e.typ, putIntoDest(p, d, e.typ,
ropecg(p.module, "#reprAny($1, $2)", [ ropecg(p.module, "#reprAny($1, $2)", [
rdLoc(a), genTypeInfo(p.module, t)])) rdLoc(a), genTypeInfo(p.module, t)]), a.s)
of tyEmpty:
localError(e.info, "'repr' doesn't support 'void' type")
else: else:
putIntoDest(p, d, e.typ, ropecg(p.module, "#reprAny($1, $2)", putIntoDest(p, d, e.typ, ropecg(p.module, "#reprAny($1, $2)",
[addrLoc(a), genTypeInfo(p.module, t)])) [addrLoc(a), genTypeInfo(p.module, t)]), a.s)
gcUsage(e) gcUsage(e)
proc genGetTypeInfo(p: BProc, e: PNode, d: var TLoc) = proc genGetTypeInfo(p: BProc, e: PNode, d: var TLoc) =
@ -1492,11 +1478,11 @@ proc genSetOp(p: BProc, e: PNode, d: var TLoc, op: TMagic) =
of 1, 2, 4, 8: of 1, 2, 4, 8:
case op case op
of mIncl: of mIncl:
var ts = "NI" & $(size * 8) var ts = "NU" & $(size * 8)
binaryStmtInExcl(p, e, d, binaryStmtInExcl(p, e, d,
"$1 |= ((" & ts & ")1)<<(($2)%(sizeof(" & ts & ")*8));$n") "$1 |= ((" & ts & ")1)<<(($2)%(sizeof(" & ts & ")*8));$n")
of mExcl: of mExcl:
var ts = "NI" & $(size * 8) var ts = "NU" & $(size * 8)
binaryStmtInExcl(p, e, d, "$1 &= ~(((" & ts & ")1) << (($2) % (sizeof(" & binaryStmtInExcl(p, e, d, "$1 &= ~(((" & ts & ")1) << (($2) % (sizeof(" &
ts & ")*8)));$n") ts & ")*8)));$n")
of mCard: of mCard:
@ -1555,13 +1541,13 @@ proc genSomeCast(p: BProc, e: PNode, d: var TLoc) =
let etyp = skipTypes(e.typ, abstractRange) let etyp = skipTypes(e.typ, abstractRange)
if etyp.kind in ValueTypes and lfIndirect notin a.flags: if etyp.kind in ValueTypes and lfIndirect notin a.flags:
putIntoDest(p, d, e.typ, "(*($1*) ($2))" % putIntoDest(p, d, e.typ, "(*($1*) ($2))" %
[getTypeDesc(p.module, e.typ), addrLoc(a)]) [getTypeDesc(p.module, e.typ), addrLoc(a)], a.s)
elif etyp.kind == tyProc and etyp.callConv == ccClosure: elif etyp.kind == tyProc and etyp.callConv == ccClosure:
putIntoDest(p, d, e.typ, "(($1) ($2))" % putIntoDest(p, d, e.typ, "(($1) ($2))" %
[getClosureType(p.module, etyp, clHalfWithEnv), rdCharLoc(a)]) [getClosureType(p.module, etyp, clHalfWithEnv), rdCharLoc(a)], a.s)
else: else:
putIntoDest(p, d, e.typ, "(($1) ($2))" % putIntoDest(p, d, e.typ, "(($1) ($2))" %
[getTypeDesc(p.module, e.typ), rdCharLoc(a)]) [getTypeDesc(p.module, e.typ), rdCharLoc(a)], a.s)
proc genCast(p: BProc, e: PNode, d: var TLoc) = proc genCast(p: BProc, e: PNode, d: var TLoc) =
const floatTypes = {tyFloat..tyFloat128} const floatTypes = {tyFloat..tyFloat128}
@ -1581,7 +1567,7 @@ proc genCast(p: BProc, e: PNode, d: var TLoc) =
tmp.s = OnStack tmp.s = OnStack
tmp.flags = {} tmp.flags = {}
expr(p, e.sons[1], tmp) expr(p, e.sons[1], tmp)
putIntoDest(p, d, e.typ, "LOC$#.dest" % [lbl]) putIntoDest(p, d, e.typ, "LOC$#.dest" % [lbl], tmp.s)
else: else:
# I prefer the shorter cast version for pointer types -> generate less # I prefer the shorter cast version for pointer types -> generate less
# C code; plus it's the right thing to do for closures: # C code; plus it's the right thing to do for closures:
@ -1591,16 +1577,17 @@ proc genRangeChck(p: BProc, n: PNode, d: var TLoc, magic: string) =
var a: TLoc var a: TLoc
var dest = skipTypes(n.typ, abstractVar) var dest = skipTypes(n.typ, abstractVar)
# range checks for unsigned turned out to be buggy and annoying: # range checks for unsigned turned out to be buggy and annoying:
if optRangeCheck notin p.options or dest.kind in {tyUInt..tyUInt64}: if optRangeCheck notin p.options or dest.skipTypes({tyRange}).kind in
{tyUInt..tyUInt64}:
initLocExpr(p, n.sons[0], a) initLocExpr(p, n.sons[0], a)
putIntoDest(p, d, n.typ, "(($1) ($2))" % putIntoDest(p, d, n.typ, "(($1) ($2))" %
[getTypeDesc(p.module, dest), rdCharLoc(a)]) [getTypeDesc(p.module, dest), rdCharLoc(a)], a.s)
else: else:
initLocExpr(p, n.sons[0], a) initLocExpr(p, n.sons[0], a)
putIntoDest(p, d, dest, ropecg(p.module, "(($1)#$5($2, $3, $4))", [ putIntoDest(p, d, dest, ropecg(p.module, "(($1)#$5($2, $3, $4))", [
getTypeDesc(p.module, dest), rdCharLoc(a), getTypeDesc(p.module, dest), rdCharLoc(a),
genLiteral(p, n.sons[1], dest), genLiteral(p, n.sons[2], dest), genLiteral(p, n.sons[1], dest), genLiteral(p, n.sons[2], dest),
rope(magic)])) rope(magic)]), a.s)
proc genConv(p: BProc, e: PNode, d: var TLoc) = proc genConv(p: BProc, e: PNode, d: var TLoc) =
let destType = e.typ.skipTypes({tyVar, tyGenericInst}) let destType = e.typ.skipTypes({tyVar, tyGenericInst})
@ -1612,13 +1599,13 @@ proc genConv(p: BProc, e: PNode, d: var TLoc) =
proc convStrToCStr(p: BProc, n: PNode, d: var TLoc) = proc convStrToCStr(p: BProc, n: PNode, d: var TLoc) =
var a: TLoc var a: TLoc
initLocExpr(p, n.sons[0], a) initLocExpr(p, n.sons[0], a)
putIntoDest(p, d, skipTypes(n.typ, abstractVar), "$1->data" % [rdLoc(a)]) putIntoDest(p, d, skipTypes(n.typ, abstractVar), "$1->data" % [rdLoc(a)], a.s)
proc convCStrToStr(p: BProc, n: PNode, d: var TLoc) = proc convCStrToStr(p: BProc, n: PNode, d: var TLoc) =
var a: TLoc var a: TLoc
initLocExpr(p, n.sons[0], a) initLocExpr(p, n.sons[0], a)
putIntoDest(p, d, skipTypes(n.typ, abstractVar), putIntoDest(p, d, skipTypes(n.typ, abstractVar),
ropecg(p.module, "#cstrToNimstr($1)", [rdLoc(a)])) ropecg(p.module, "#cstrToNimstr($1)", [rdLoc(a)]), a.s)
gcUsage(n) gcUsage(n)
proc genStrEquals(p: BProc, e: PNode, d: var TLoc) = proc genStrEquals(p: BProc, e: PNode, d: var TLoc) =
@ -1661,7 +1648,7 @@ proc genMagicExpr(p: BProc, e: PNode, d: var TLoc, op: TMagic) =
case op case op
of mOr, mAnd: genAndOr(p, e, d, op) of mOr, mAnd: genAndOr(p, e, d, op)
of mNot..mToBiggestInt: unaryArith(p, e, d, op) of mNot..mToBiggestInt: unaryArith(p, e, d, op)
of mUnaryMinusI..mAbsI64: unaryArithOverflow(p, e, d, op) of mUnaryMinusI..mAbsI: unaryArithOverflow(p, e, d, op)
of mAddF64..mDivF64: binaryFloatArith(p, e, d, op) of mAddF64..mDivF64: binaryFloatArith(p, e, d, op)
of mShrI..mXor: binaryArith(p, e, d, op) of mShrI..mXor: binaryArith(p, e, d, op)
of mEqProc: genEqProc(p, e, d) of mEqProc: genEqProc(p, e, d)
@ -1746,6 +1733,8 @@ proc genMagicExpr(p: BProc, e: PNode, d: var TLoc, op: TMagic) =
of mEcho: genEcho(p, e[1].skipConv) of mEcho: genEcho(p, e[1].skipConv)
of mArrToSeq: genArrToSeq(p, e, d) of mArrToSeq: genArrToSeq(p, e, d)
of mNLen..mNError, mSlurp..mQuoteAst: of mNLen..mNError, mSlurp..mQuoteAst:
echo "from here ", p.prc.name.s, " ", p.prc.info
writestacktrace()
localError(e.info, errXMustBeCompileTime, e.sons[0].sym.name.s) localError(e.info, errXMustBeCompileTime, e.sons[0].sym.name.s)
of mSpawn: of mSpawn:
let n = lowerings.wrapProcForSpawn(p.module.module, e, e.typ, nil, nil) let n = lowerings.wrapProcForSpawn(p.module.module, e, e.typ, nil, nil)
@ -1768,7 +1757,7 @@ proc handleConstExpr(p: BProc, n: PNode, d: var TLoc): bool =
var t = getUniqueType(n.typ) var t = getUniqueType(n.typ)
discard getTypeDesc(p.module, t) # so that any fields are initialized discard getTypeDesc(p.module, t) # so that any fields are initialized
var id = nodeTableTestOrSet(p.module.dataCache, n, gBackendId) var id = nodeTableTestOrSet(p.module.dataCache, n, gBackendId)
fillLoc(d, locData, t, "TMP" & rope(id), OnHeap) fillLoc(d, locData, t, "TMP" & rope(id), OnStatic)
if id == gBackendId: if id == gBackendId:
# expression not found in the cache: # expression not found in the cache:
inc(gBackendId) inc(gBackendId)
@ -1807,7 +1796,7 @@ proc genSetConstr(p: BProc, e: PNode, d: var TLoc) =
[rdLoc(d), rdSetElemLoc(a, e.typ)]) [rdLoc(d), rdSetElemLoc(a, e.typ)])
else: else:
# small set # small set
var ts = "NI" & $(getSize(e.typ) * 8) var ts = "NU" & $(getSize(e.typ) * 8)
lineF(p, cpsStmts, "$1 = 0;$n", [rdLoc(d)]) lineF(p, cpsStmts, "$1 = 0;$n", [rdLoc(d)])
for i in countup(0, sonsLen(e) - 1): for i in countup(0, sonsLen(e) - 1):
if e.sons[i].kind == nkRange: if e.sons[i].kind == nkRange:
@ -1815,13 +1804,13 @@ proc genSetConstr(p: BProc, e: PNode, d: var TLoc) =
initLocExpr(p, e.sons[i].sons[0], a) initLocExpr(p, e.sons[i].sons[0], a)
initLocExpr(p, e.sons[i].sons[1], b) initLocExpr(p, e.sons[i].sons[1], b)
lineF(p, cpsStmts, "for ($1 = $3; $1 <= $4; $1++) $n" & lineF(p, cpsStmts, "for ($1 = $3; $1 <= $4; $1++) $n" &
"$2 |=(1<<((" & ts & ")($1)%(sizeof(" & ts & ")*8)));$n", [ "$2 |=((" & ts & ")(1)<<(($1)%(sizeof(" & ts & ")*8)));$n", [
rdLoc(idx), rdLoc(d), rdSetElemLoc(a, e.typ), rdLoc(idx), rdLoc(d), rdSetElemLoc(a, e.typ),
rdSetElemLoc(b, e.typ)]) rdSetElemLoc(b, e.typ)])
else: else:
initLocExpr(p, e.sons[i], a) initLocExpr(p, e.sons[i], a)
lineF(p, cpsStmts, lineF(p, cpsStmts,
"$1 |=(1<<((" & ts & ")($2)%(sizeof(" & ts & ")*8)));$n", "$1 |=((" & ts & ")(1)<<(($2)%(sizeof(" & ts & ")*8)));$n",
[rdLoc(d), rdSetElemLoc(a, e.typ)]) [rdLoc(d), rdSetElemLoc(a, e.typ)])
proc genTupleConstr(p: BProc, n: PNode, d: var TLoc) = proc genTupleConstr(p: BProc, n: PNode, d: var TLoc) =
@ -1854,7 +1843,7 @@ proc genClosure(p: BProc, n: PNode, d: var TLoc) =
var tmp = "LOC" & rope(p.labels) var tmp = "LOC" & rope(p.labels)
addf(p.module.s[cfsData], "NIM_CONST $1 $2 = $3;$n", addf(p.module.s[cfsData], "NIM_CONST $1 $2 = $3;$n",
[getTypeDesc(p.module, n.typ), tmp, genConstExpr(p, n)]) [getTypeDesc(p.module, n.typ), tmp, genConstExpr(p, n)])
putIntoDest(p, d, n.typ, tmp) putIntoDest(p, d, n.typ, tmp, OnStatic)
else: else:
var tmp, a, b: TLoc var tmp, a, b: TLoc
initLocExpr(p, n.sons[0], a) initLocExpr(p, n.sons[0], a)
@ -1908,10 +1897,10 @@ proc upConv(p: BProc, n: PNode, d: var TLoc) =
r, genTypeInfo(p.module, dest)) r, genTypeInfo(p.module, dest))
if n.sons[0].typ.kind != tyObject: if n.sons[0].typ.kind != tyObject:
putIntoDest(p, d, n.typ, putIntoDest(p, d, n.typ,
"(($1) ($2))" % [getTypeDesc(p.module, n.typ), rdLoc(a)]) "(($1) ($2))" % [getTypeDesc(p.module, n.typ), rdLoc(a)], a.s)
else: else:
putIntoDest(p, d, n.typ, "(*($1*) ($2))" % putIntoDest(p, d, n.typ, "(*($1*) ($2))" %
[getTypeDesc(p.module, dest), addrLoc(a)]) [getTypeDesc(p.module, dest), addrLoc(a)], a.s)
proc downConv(p: BProc, n: PNode, d: var TLoc) = proc downConv(p: BProc, n: PNode, d: var TLoc) =
if p.module.compileToCpp: if p.module.compileToCpp:
@ -1943,9 +1932,9 @@ proc downConv(p: BProc, n: PNode, d: var TLoc) =
linefmt(p, cpsStmts, "$1 = &$2;$n", rdLoc(d), r) linefmt(p, cpsStmts, "$1 = &$2;$n", rdLoc(d), r)
else: else:
r = "&" & r r = "&" & r
putIntoDest(p, d, n.typ, r) putIntoDest(p, d, n.typ, r, a.s)
else: else:
putIntoDest(p, d, n.typ, r) putIntoDest(p, d, n.typ, r, a.s)
proc exprComplexConst(p: BProc, n: PNode, d: var TLoc) = proc exprComplexConst(p: BProc, n: PNode, d: var TLoc) =
var t = getUniqueType(n.typ) var t = getUniqueType(n.typ)
@ -1960,7 +1949,7 @@ proc exprComplexConst(p: BProc, n: PNode, d: var TLoc) =
[getTypeDesc(p.module, t), tmp, genConstExpr(p, n)]) [getTypeDesc(p.module, t), tmp, genConstExpr(p, n)])
if d.k == locNone: if d.k == locNone:
fillLoc(d, locData, t, tmp, OnHeap) fillLoc(d, locData, t, tmp, OnStatic)
else: else:
putDataIntoDest(p, d, t, tmp) putDataIntoDest(p, d, t, tmp)
@ -1978,6 +1967,9 @@ proc expr(p: BProc, n: PNode, d: var TLoc) =
genProc(p.module, sym) genProc(p.module, sym)
putLocIntoDest(p, d, sym.loc) putLocIntoDest(p, d, sym.loc)
of skProc, skConverter, skIterators: of skProc, skConverter, skIterators:
if sfCompileTime in sym.flags:
localError(n.info, "request to generate code for .compileTime proc: " &
sym.name.s)
genProc(p.module, sym) genProc(p.module, sym)
if sym.loc.r == nil or sym.loc.t == nil: if sym.loc.r == nil or sym.loc.t == nil:
internalError(n.info, "expr: proc not init " & sym.name.s) internalError(n.info, "expr: proc not init " & sym.name.s)
@ -1987,7 +1979,7 @@ proc expr(p: BProc, n: PNode, d: var TLoc) =
if sfGlobal in sym.flags: genVarPrototype(p.module, sym) if sfGlobal in sym.flags: genVarPrototype(p.module, sym)
putLocIntoDest(p, d, sym.loc) putLocIntoDest(p, d, sym.loc)
elif isSimpleConst(sym.typ): elif isSimpleConst(sym.typ):
putIntoDest(p, d, n.typ, genLiteral(p, sym.ast, sym.typ)) putIntoDest(p, d, n.typ, genLiteral(p, sym.ast, sym.typ), OnStatic)
else: else:
genComplexConst(p, sym, d) genComplexConst(p, sym, d)
of skEnumField: of skEnumField:
@ -2074,6 +2066,9 @@ proc expr(p: BProc, n: PNode, d: var TLoc) =
of nkStmtList: of nkStmtList:
for i in countup(0, sonsLen(n) - 1): genStmts(p, n.sons[i]) for i in countup(0, sonsLen(n) - 1): genStmts(p, n.sons[i])
of nkIfExpr, nkIfStmt: genIf(p, n, d) of nkIfExpr, nkIfStmt: genIf(p, n, d)
of nkWhen:
# This should be a "when nimvm" node.
expr(p, n.sons[1].sons[0], d)
of nkObjDownConv: downConv(p, n, d) of nkObjDownConv: downConv(p, n, d)
of nkObjUpConv: upConv(p, n, d) of nkObjUpConv: upConv(p, n, d)
of nkChckRangeF: genRangeChck(p, n, d, "chckRangeF") of nkChckRangeF: genRangeChck(p, n, d, "chckRangeF")
@ -2128,7 +2123,7 @@ proc expr(p: BProc, n: PNode, d: var TLoc) =
# due to a bug/limitation in the lambda lifting, unused inner procs # due to a bug/limitation in the lambda lifting, unused inner procs
# are not transformed correctly. We work around this issue (#411) here # are not transformed correctly. We work around this issue (#411) here
# by ensuring it's no inner proc (owner is a module): # by ensuring it's no inner proc (owner is a module):
if prc.skipGenericOwner.kind == skModule: if prc.skipGenericOwner.kind == skModule and sfCompileTime notin prc.flags:
if (optDeadCodeElim notin gGlobalOptions and if (optDeadCodeElim notin gGlobalOptions and
sfDeadCodeElim notin getModule(prc).flags) or sfDeadCodeElim notin getModule(prc).flags) or
({sfExportc, sfCompilerProc} * prc.flags == {sfExportc}) or ({sfExportc, sfCompilerProc} * prc.flags == {sfExportc}) or
@ -2150,7 +2145,7 @@ proc genNamedConstExpr(p: BProc, n: PNode): Rope =
proc genConstSimpleList(p: BProc, n: PNode): Rope = proc genConstSimpleList(p: BProc, n: PNode): Rope =
var length = sonsLen(n) var length = sonsLen(n)
result = rope("{") result = rope("{")
for i in countup(0, length - 2): for i in countup(ord(n.kind == nkObjConstr), length - 2):
addf(result, "$1,$n", [genNamedConstExpr(p, n.sons[i])]) addf(result, "$1,$n", [genNamedConstExpr(p, n.sons[i])])
if length > 0: add(result, genNamedConstExpr(p, n.sons[length - 1])) if length > 0: add(result, genNamedConstExpr(p, n.sons[length - 1]))
addf(result, "}$n", []) addf(result, "}$n", [])

View file

@ -102,7 +102,7 @@ proc assignLabel(b: var TBlock): Rope {.inline.} =
proc blockBody(b: var TBlock): Rope = proc blockBody(b: var TBlock): Rope =
result = b.sections[cpsLocals] result = b.sections[cpsLocals]
if b.frameLen > 0: if b.frameLen > 0:
result.addf("F.len+=$1;$n", [b.frameLen.rope]) result.addf("FR.len+=$1;$n", [b.frameLen.rope])
result.add(b.sections[cpsInit]) result.add(b.sections[cpsInit])
result.add(b.sections[cpsStmts]) result.add(b.sections[cpsStmts])
@ -123,7 +123,7 @@ proc endBlock(p: BProc) =
~"}$n" ~"}$n"
let frameLen = p.blocks[topBlock].frameLen let frameLen = p.blocks[topBlock].frameLen
if frameLen > 0: if frameLen > 0:
blockEnd.addf("F.len-=$1;$n", [frameLen.rope]) blockEnd.addf("FR.len-=$1;$n", [frameLen.rope])
endBlock(p, blockEnd) endBlock(p, blockEnd)
proc genSimpleBlock(p: BProc, stmts: PNode) {.inline.} = proc genSimpleBlock(p: BProc, stmts: PNode) {.inline.} =
@ -1002,8 +1002,10 @@ proc genAsmStmt(p: BProc, t: PNode) =
proc determineSection(n: PNode): TCFileSection = proc determineSection(n: PNode): TCFileSection =
result = cfsProcHeaders result = cfsProcHeaders
if n.len >= 1 and n.sons[0].kind in {nkStrLit..nkTripleStrLit}: if n.len >= 1 and n.sons[0].kind in {nkStrLit..nkTripleStrLit}:
if n.sons[0].strVal.startsWith("/*TYPESECTION*/"): result = cfsTypes let sec = n.sons[0].strVal
elif n.sons[0].strVal.startsWith("/*VARSECTION*/"): result = cfsVars if sec.startsWith("/*TYPESECTION*/"): result = cfsTypes
elif sec.startsWith("/*VARSECTION*/"): result = cfsVars
elif sec.startsWith("/*INCLUDESECTION*/"): result = cfsHeaders
proc genEmit(p: BProc, t: PNode) = proc genEmit(p: BProc, t: PNode) =
var s = genAsmOrEmitStmt(p, t.sons[1]) var s = genAsmOrEmitStmt(p, t.sons[1])
@ -1099,7 +1101,10 @@ proc genAsgn(p: BProc, e: PNode, fastAsgn: bool) =
genGotoVar(p, e.sons[1]) genGotoVar(p, e.sons[1])
elif not fieldDiscriminantCheckNeeded(p, e): elif not fieldDiscriminantCheckNeeded(p, e):
var a: TLoc var a: TLoc
initLocExpr(p, e.sons[0], a) if e[0].kind in {nkDerefExpr, nkHiddenDeref}:
genDeref(p, e[0], a, enforceDeref=true)
else:
initLocExpr(p, e.sons[0], a)
if fastAsgn: incl(a.flags, lfNoDeepCopy) if fastAsgn: incl(a.flags, lfNoDeepCopy)
assert(a.t != nil) assert(a.t != nil)
loadInto(p, e.sons[0], e.sons[1], a) loadInto(p, e.sons[0], e.sons[1], a)

View file

@ -323,7 +323,8 @@ proc paramStorageLoc(param: PSym): TStorageLoc =
result = OnUnknown result = OnUnknown
proc genProcParams(m: BModule, t: PType, rettype, params: var Rope, proc genProcParams(m: BModule, t: PType, rettype, params: var Rope,
check: var IntSet, declareEnvironment=true) = check: var IntSet, declareEnvironment=true;
weakDep=false) =
params = nil params = nil
if (t.sons[0] == nil) or isInvalidReturnType(t.sons[0]): if (t.sons[0] == nil) or isInvalidReturnType(t.sons[0]):
rettype = ~"void" rettype = ~"void"
@ -341,6 +342,8 @@ proc genProcParams(m: BModule, t: PType, rettype, params: var Rope,
add(params, ~"*") add(params, ~"*")
incl(param.loc.flags, lfIndirect) incl(param.loc.flags, lfIndirect)
param.loc.s = OnUnknown param.loc.s = OnUnknown
elif weakDep:
add(params, getTypeDescWeak(m, param.typ, check))
else: else:
add(params, getTypeDescAux(m, param.typ, check)) add(params, getTypeDescAux(m, param.typ, check))
add(params, ~" ") add(params, ~" ")
@ -438,6 +441,8 @@ proc genRecordFieldsAux(m: BModule, n: PNode,
elif fieldType.kind == tySequence: elif fieldType.kind == tySequence:
# we need to use a weak dependency here for trecursive_table. # we need to use a weak dependency here for trecursive_table.
addf(result, "$1 $2;$n", [getTypeDescWeak(m, field.loc.t, check), sname]) addf(result, "$1 $2;$n", [getTypeDescWeak(m, field.loc.t, check), sname])
elif field.bitsize != 0:
addf(result, "$1 $2:$3;$n", [getTypeDescAux(m, field.loc.t, check), sname, rope($field.bitsize)])
else: else:
# don't use fieldType here because we need the # don't use fieldType here because we need the
# tyGenericInst for C++ template support # tyGenericInst for C++ template support
@ -577,7 +582,7 @@ proc getTypeDescAux(m: BModule, typ: PType, check: var IntSet): Rope =
result = getTypeName(t) result = getTypeName(t)
idTablePut(m.typeCache, t, result) idTablePut(m.typeCache, t, result)
var rettype, desc: Rope var rettype, desc: Rope
genProcParams(m, t, rettype, desc, check) genProcParams(m, t, rettype, desc, check, true, true)
if not isImportedType(t): if not isImportedType(t):
if t.callConv != ccClosure: # procedure vars may need a closure! if t.callConv != ccClosure: # procedure vars may need a closure!
addf(m.s[cfsTypes], "typedef $1_PTR($2, $3) $4;$n", addf(m.s[cfsTypes], "typedef $1_PTR($2, $3) $4;$n",

View file

@ -99,7 +99,10 @@ proc getUniqueType*(key: PType): PType =
gCanonicalTypes[k] = key gCanonicalTypes[k] = key
result = key result = key
of tyTypeDesc, tyTypeClasses, tyGenericParam, tyFromExpr, tyFieldAccessor: of tyTypeDesc, tyTypeClasses, tyGenericParam, tyFromExpr, tyFieldAccessor:
internalError("getUniqueType") if key.sym != nil:
internalError(key.sym.info, "metatype not eliminated")
else:
internalError("metatype not eliminated")
of tyDistinct: of tyDistinct:
if key.deepCopy != nil: result = key if key.deepCopy != nil: result = key
else: result = getUniqueType(lastSon(key)) else: result = getUniqueType(lastSon(key))

View file

@ -10,12 +10,11 @@
## This module implements the C code generator. ## This module implements the C code generator.
import import
ast, astalgo, hashes, trees, platform, magicsys, extccomp, ast, astalgo, hashes, trees, platform, magicsys, extccomp, options, intsets,
options, intsets, nversion, nimsets, msgs, securehash, bitsets, idents, lists, types,
nversion, nimsets, msgs, crc, bitsets, idents, lists, types, ccgutils, os, ccgutils, os, ropes, math, passes, rodread, wordrecg, treetab, cgmeth,
ropes, math, passes, rodread, wordrecg, treetab, cgmeth, condsyms, condsyms, rodutils, renderer, idgen, cgendata, ccgmerge, semfold, aliases,
rodutils, renderer, idgen, cgendata, ccgmerge, semfold, aliases, lowerings, lowerings, semparallel
semparallel
import strutils except `%` # collides with ropes.`%` import strutils except `%` # collides with ropes.`%`
@ -376,7 +375,7 @@ proc localDebugInfo(p: BProc, s: PSym) =
var a = "&" & s.loc.r var a = "&" & s.loc.r
if s.kind == skParam and ccgIntroducedPtr(s): a = s.loc.r if s.kind == skParam and ccgIntroducedPtr(s): a = s.loc.r
lineF(p, cpsInit, lineF(p, cpsInit,
"F.s[$1].address = (void*)$3; F.s[$1].typ = $4; F.s[$1].name = $2;$n", "FR.s[$1].address = (void*)$3; FR.s[$1].typ = $4; FR.s[$1].name = $2;$n",
[p.maxFrameLen.rope, makeCString(normalize(s.name.s)), a, [p.maxFrameLen.rope, makeCString(normalize(s.name.s)), a,
genTypeInfo(p.module, s.loc.t)]) genTypeInfo(p.module, s.loc.t)])
inc(p.maxFrameLen) inc(p.maxFrameLen)
@ -506,8 +505,7 @@ proc loadDynamicLib(m: BModule, lib: PLib) =
if lib.path.kind in {nkStrLit..nkTripleStrLit}: if lib.path.kind in {nkStrLit..nkTripleStrLit}:
var s: TStringSeq = @[] var s: TStringSeq = @[]
libCandidates(lib.path.strVal, s) libCandidates(lib.path.strVal, s)
if gVerbosity >= 2: rawMessage(hintDependency, lib.path.strVal)
msgWriteln("Dependency: " & lib.path.strVal)
var loadlib: Rope = nil var loadlib: Rope = nil
for i in countup(0, high(s)): for i in countup(0, high(s)):
inc(m.labels) inc(m.labels)
@ -599,7 +597,7 @@ proc cgsym(m: BModule, name: string): Rope =
of skProc, skMethod, skConverter, skIterators: genProc(m, sym) of skProc, skMethod, skConverter, skIterators: genProc(m, sym)
of skVar, skResult, skLet: genVarPrototype(m, sym) of skVar, skResult, skLet: genVarPrototype(m, sym)
of skType: discard getTypeDesc(m, sym.typ) of skType: discard getTypeDesc(m, sym.typ)
else: internalError("cgsym: " & name) else: internalError("cgsym: " & name & ": " & $sym.kind)
else: else:
# we used to exclude the system module from this check, but for DLL # we used to exclude the system module from this check, but for DLL
# generation support this sloppyness leads to hard to detect bugs, so # generation support this sloppyness leads to hard to detect bugs, so
@ -611,10 +609,12 @@ proc generateHeaders(m: BModule) =
add(m.s[cfsHeaders], tnl & "#include \"nimbase.h\"" & tnl) add(m.s[cfsHeaders], tnl & "#include \"nimbase.h\"" & tnl)
var it = PStrEntry(m.headerFiles.head) var it = PStrEntry(m.headerFiles.head)
while it != nil: while it != nil:
if it.data[0] notin {'\"', '<'}: if it.data[0] == '#':
addf(m.s[cfsHeaders], "$N#include \"$1\"$N", [rope(it.data)]) add(m.s[cfsHeaders], rope(it.data.replace('`', '"') & tnl))
elif it.data[0] notin {'\"', '<'}:
addf(m.s[cfsHeaders], "#include \"$1\"$N", [rope(it.data)])
else: else:
addf(m.s[cfsHeaders], "$N#include $1$N", [rope(it.data)]) addf(m.s[cfsHeaders], "#include $1$N", [rope(it.data)])
it = PStrEntry(it.next) it = PStrEntry(it.next)
proc retIsNotVoid(s: PSym): bool = proc retIsNotVoid(s: PSym): bool =
@ -623,7 +623,7 @@ proc retIsNotVoid(s: PSym): bool =
proc initFrame(p: BProc, procname, filename: Rope): Rope = proc initFrame(p: BProc, procname, filename: Rope): Rope =
discard cgsym(p.module, "nimFrame") discard cgsym(p.module, "nimFrame")
if p.maxFrameLen > 0: if p.maxFrameLen > 0:
discard cgsym(p.module, "TVarSlot") discard cgsym(p.module, "VarSlot")
result = rfmt(nil, "\tnimfrs($1, $2, $3, $4)$N", result = rfmt(nil, "\tnimfrs($1, $2, $3, $4)$N",
procname, filename, p.maxFrameLen.rope, procname, filename, p.maxFrameLen.rope,
p.blocks[0].frameLen.rope) p.blocks[0].frameLen.rope)
@ -676,8 +676,11 @@ proc genProcAux(m: BModule, prc: PSym) =
closureSetup(p, prc) closureSetup(p, prc)
genStmts(p, prc.getBody) # modifies p.locals, p.init, etc. genStmts(p, prc.getBody) # modifies p.locals, p.init, etc.
var generatedProc: Rope var generatedProc: Rope
if sfNoReturn in prc.flags:
if hasDeclspec in extccomp.CC[extccomp.cCompiler].props:
header = "__declspec(noreturn) " & header
if sfPure in prc.flags: if sfPure in prc.flags:
if hasNakedDeclspec in extccomp.CC[extccomp.cCompiler].props: if hasDeclspec in extccomp.CC[extccomp.cCompiler].props:
header = "__declspec(naked) " & header header = "__declspec(naked) " & header
generatedProc = rfmt(nil, "$N$1 {$n$2$3$4}$N$N", generatedProc = rfmt(nil, "$N$1 {$n$2$3$4}$N$N",
header, p.s(cpsLocals), p.s(cpsInit), p.s(cpsStmts)) header, p.s(cpsLocals), p.s(cpsInit), p.s(cpsStmts))
@ -718,10 +721,14 @@ proc genProcPrototype(m: BModule, sym: PSym) =
getTypeDesc(m, sym.loc.t), mangleDynLibProc(sym))) getTypeDesc(m, sym.loc.t), mangleDynLibProc(sym)))
elif not containsOrIncl(m.declaredProtos, sym.id): elif not containsOrIncl(m.declaredProtos, sym.id):
var header = genProcHeader(m, sym) var header = genProcHeader(m, sym)
if sfNoReturn in sym.flags and hasDeclspec in extccomp.CC[cCompiler].props:
header = "__declspec(noreturn) " & header
if sym.typ.callConv != ccInline and crossesCppBoundary(m, sym): if sym.typ.callConv != ccInline and crossesCppBoundary(m, sym):
header = "extern \"C\" " & header header = "extern \"C\" " & header
if sfPure in sym.flags and hasNakedAttribute in CC[cCompiler].props: if sfPure in sym.flags and hasAttribute in CC[cCompiler].props:
header.add(" __attribute__((naked))") header.add(" __attribute__((naked))")
if sfNoReturn in sym.flags and hasAttribute in CC[cCompiler].props:
header.add(" __attribute__((noreturn))")
add(m.s[cfsProcHeaders], rfmt(nil, "$1;$n", header)) add(m.s[cfsProcHeaders], rfmt(nil, "$1;$n", header))
proc genProcNoForward(m: BModule, prc: PSym) = proc genProcNoForward(m: BModule, prc: PSym) =
@ -753,7 +760,7 @@ proc requestConstImpl(p: BProc, sym: PSym) =
var m = p.module var m = p.module
useHeader(m, sym) useHeader(m, sym)
if sym.loc.k == locNone: if sym.loc.k == locNone:
fillLoc(sym.loc, locData, sym.typ, mangleName(sym), OnUnknown) fillLoc(sym.loc, locData, sym.typ, mangleName(sym), OnStatic)
if lfNoDecl in sym.loc.flags: return if lfNoDecl in sym.loc.flags: return
# declare implementation: # declare implementation:
var q = findPendingModule(m, sym) var q = findPendingModule(m, sym)
@ -808,7 +815,7 @@ proc genVarPrototype(m: BModule, sym: PSym) =
genVarPrototypeAux(m, sym) genVarPrototypeAux(m, sym)
proc addIntTypes(result: var Rope) {.inline.} = proc addIntTypes(result: var Rope) {.inline.} =
addf(result, "#define NIM_INTBITS $1", [ addf(result, "#define NIM_INTBITS $1" & tnl, [
platform.CPU[targetCPU].intSize.rope]) platform.CPU[targetCPU].intSize.rope])
proc getCopyright(cfile: string): Rope = proc getCopyright(cfile: string): Rope =
@ -846,14 +853,14 @@ proc genMainProc(m: BModule) =
# functions, which might otherwise merge their stack frames. # functions, which might otherwise merge their stack frames.
PreMainBody = PreMainBody =
"void PreMainInner() {$N" & "void PreMainInner() {$N" &
"\tsystemInit();$N" & "\tsystemInit000();$N" &
"$1" & "$1" &
"$2" & "$2" &
"$3" & "$3" &
"}$N$N" & "}$N$N" &
"void PreMain() {$N" & "void PreMain() {$N" &
"\tvoid (*volatile inner)();$N" & "\tvoid (*volatile inner)();$N" &
"\tsystemDatInit();$N" & "\tsystemDatInit000();$N" &
"\tinner = PreMainInner;$N" & "\tinner = PreMainInner;$N" &
"$4$5" & "$4$5" &
"\t(*inner)();$N" & "\t(*inner)();$N" &
@ -943,7 +950,7 @@ proc genMainProc(m: BModule) =
gBreakpoints.add(m.genFilenames) gBreakpoints.add(m.genFilenames)
let initStackBottomCall = let initStackBottomCall =
if platform.targetOS == osStandalone: "".rope if platform.targetOS == osStandalone or gSelectedGC == 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, [
@ -967,8 +974,8 @@ proc getSomeInitName(m: PSym, suffix: string): Rope =
result.add m.name.s result.add m.name.s
result.add suffix result.add suffix
proc getInitName(m: PSym): Rope = getSomeInitName(m, "Init") proc getInitName(m: PSym): Rope = getSomeInitName(m, "Init000")
proc getDatInitName(m: PSym): Rope = getSomeInitName(m, "DatInit") proc getDatInitName(m: PSym): Rope = getSomeInitName(m, "DatInit000")
proc registerModuleToMain(m: PSym) = proc registerModuleToMain(m: PSym) =
var var
@ -1010,7 +1017,7 @@ proc genInitCode(m: BModule) =
var procname = makeCString(m.module.name.s) var procname = makeCString(m.module.name.s)
add(prc, initFrame(m.initProc, procname, m.module.info.quotedFilename)) add(prc, initFrame(m.initProc, procname, m.module.info.quotedFilename))
else: else:
add(prc, ~"\tTFrame F; F.len = 0;$N") add(prc, ~"\tTFrame F; FR.len = 0;$N")
add(prc, genSectionStart(cpsInit)) add(prc, genSectionStart(cpsInit))
add(prc, m.preInitProc.s(cpsInit)) add(prc, m.preInitProc.s(cpsInit))
@ -1103,7 +1110,7 @@ proc rawNewModule(module: PSym, filename: string): BModule =
proc nullify[T](arr: var T) = proc nullify[T](arr: var T) =
for i in low(arr)..high(arr): for i in low(arr)..high(arr):
arr[i] = nil arr[i] = Rope(nil)
proc resetModule*(m: BModule) = proc resetModule*(m: BModule) =
# between two compilations in CAAS mode, we can throw # between two compilations in CAAS mode, we can throw
@ -1322,4 +1329,3 @@ proc cgenWriteModules* =
if generatedHeader != nil: writeHeader(generatedHeader) if generatedHeader != nil: writeHeader(generatedHeader)
const cgenPass* = makePass(myOpen, myOpenCached, myProcess, myClose) const cgenPass* = makePass(myOpen, myOpenCached, myProcess, myClose)

View file

@ -9,33 +9,33 @@
## This module implements code generation for multi methods. ## This module implements code generation for multi methods.
import import
intsets, options, ast, astalgo, msgs, idents, renderer, types, magicsys, intsets, options, ast, astalgo, msgs, idents, renderer, types, magicsys,
sempass2, strutils sempass2, strutils
proc genConv(n: PNode, d: PType, downcast: bool): PNode = proc genConv(n: PNode, d: PType, downcast: bool): PNode =
var dest = skipTypes(d, abstractPtrs) var dest = skipTypes(d, abstractPtrs)
var source = skipTypes(n.typ, abstractPtrs) var source = skipTypes(n.typ, abstractPtrs)
if (source.kind == tyObject) and (dest.kind == tyObject): if (source.kind == tyObject) and (dest.kind == tyObject):
var diff = inheritanceDiff(dest, source) var diff = inheritanceDiff(dest, source)
if diff == high(int): internalError(n.info, "cgmeth.genConv") if diff == high(int): internalError(n.info, "cgmeth.genConv")
if diff < 0: if diff < 0:
result = newNodeIT(nkObjUpConv, n.info, d) result = newNodeIT(nkObjUpConv, n.info, d)
addSon(result, n) addSon(result, n)
if downcast: internalError(n.info, "cgmeth.genConv: no upcast allowed") if downcast: internalError(n.info, "cgmeth.genConv: no upcast allowed")
elif diff > 0: elif diff > 0:
result = newNodeIT(nkObjDownConv, n.info, d) result = newNodeIT(nkObjDownConv, n.info, d)
addSon(result, n) addSon(result, n)
if not downcast: if not downcast:
internalError(n.info, "cgmeth.genConv: no downcast allowed") internalError(n.info, "cgmeth.genConv: no downcast allowed")
else: else:
result = n result = n
else: else:
result = n result = n
proc methodCall*(n: PNode): PNode = proc methodCall*(n: PNode): PNode =
result = n result = n
# replace ordinary method by dispatcher method: # replace ordinary method by dispatcher method:
var disp = lastSon(result.sons[0].sym.ast).sym var disp = lastSon(result.sons[0].sym.ast).sym
assert sfDispatcher in disp.flags assert sfDispatcher in disp.flags
result.sons[0].sym = disp result.sons[0].sym = disp
@ -47,30 +47,35 @@ proc methodCall*(n: PNode): PNode =
var var
gMethods: seq[tuple[methods: TSymSeq, dispatcher: PSym]] = @[] gMethods: seq[tuple[methods: TSymSeq, dispatcher: PSym]] = @[]
proc sameMethodBucket(a, b: PSym): bool = type
result = false MethodResult = enum No, Invalid, Yes
if a.name.id != b.name.id: return
if sonsLen(a.typ) != sonsLen(b.typ): proc sameMethodBucket(a, b: PSym): MethodResult =
if a.name.id != b.name.id: return
if sonsLen(a.typ) != sonsLen(b.typ):
return # check for return type: return # check for return type:
if not sameTypeOrNil(a.typ.sons[0], b.typ.sons[0]): return if not sameTypeOrNil(a.typ.sons[0], b.typ.sons[0]): return
for i in countup(1, sonsLen(a.typ) - 1): for i in countup(1, sonsLen(a.typ) - 1):
var aa = a.typ.sons[i] var aa = a.typ.sons[i]
var bb = b.typ.sons[i] var bb = b.typ.sons[i]
while true: while true:
aa = skipTypes(aa, {tyGenericInst}) aa = skipTypes(aa, {tyGenericInst})
bb = skipTypes(bb, {tyGenericInst}) bb = skipTypes(bb, {tyGenericInst})
if (aa.kind == bb.kind) and (aa.kind in {tyVar, tyPtr, tyRef}): if (aa.kind == bb.kind) and (aa.kind in {tyVar, tyPtr, tyRef}):
aa = aa.lastSon aa = aa.lastSon
bb = bb.lastSon bb = bb.lastSon
else: else:
break break
if sameType(aa, bb) or if sameType(aa, bb):
(aa.kind == tyObject) and (bb.kind == tyObject) and if aa.kind == tyObject and result != Invalid: result = Yes
(inheritanceDiff(bb, aa) < 0): elif aa.kind == tyObject and bb.kind == tyObject:
discard let diff = inheritanceDiff(bb, aa)
if diff < 0:
if result != Invalid: result = Yes
elif diff != high(int):
result = Invalid
else: else:
return return No
result = true
proc attachDispatcher(s: PSym, dispatcher: PNode) = proc attachDispatcher(s: PSym, dispatcher: PNode) =
var L = s.ast.len-1 var L = s.ast.len-1
@ -133,18 +138,31 @@ proc fixupDispatcher(meth, disp: PSym) =
proc methodDef*(s: PSym, fromCache: bool) = proc methodDef*(s: PSym, fromCache: bool) =
var L = len(gMethods) var L = len(gMethods)
var witness: PSym
for i in countup(0, L - 1): for i in countup(0, L - 1):
var disp = gMethods[i].dispatcher var disp = gMethods[i].dispatcher
if sameMethodBucket(disp, s): case sameMethodBucket(disp, s)
of Yes:
add(gMethods[i].methods, s) add(gMethods[i].methods, s)
attachDispatcher(s, lastSon(disp.ast)) attachDispatcher(s, lastSon(disp.ast))
fixupDispatcher(s, disp) fixupDispatcher(s, disp)
when useEffectSystem: checkMethodEffects(disp, s) when useEffectSystem: checkMethodEffects(disp, s)
return if sfBase in s.flags and gMethods[i].methods[0] != s:
# already exists due to forwarding definition?
localError(s.info, "method is not a base")
return
of No: discard
of Invalid:
if witness.isNil: witness = gMethods[i].methods[0]
# create a new dispatcher: # create a new dispatcher:
add(gMethods, (methods: @[s], dispatcher: createDispatcher(s))) add(gMethods, (methods: @[s], dispatcher: createDispatcher(s)))
if fromCache: if fromCache:
internalError(s.info, "no method dispatcher found") internalError(s.info, "no method dispatcher found")
if witness != nil:
localError(s.info, "invalid declaration order; cannot attach '" & s.name.s &
"' to method defined here: " & $witness.info)
elif sfBase notin s.flags:
message(s.info, warnUseBase)
proc relevantCol(methods: TSymSeq, col: int): bool = proc relevantCol(methods: TSymSeq, col: int): bool =
# returns true iff the position is relevant # returns true iff the position is relevant
@ -154,35 +172,35 @@ proc relevantCol(methods: TSymSeq, col: int): bool =
let t2 = skipTypes(methods[i].typ.sons[col], skipPtrs) let t2 = skipTypes(methods[i].typ.sons[col], skipPtrs)
if not sameType(t2, t): if not sameType(t2, t):
return true return true
proc cmpSignatures(a, b: PSym, relevantCols: IntSet): int = proc cmpSignatures(a, b: PSym, relevantCols: IntSet): int =
for col in countup(1, sonsLen(a.typ) - 1): for col in countup(1, sonsLen(a.typ) - 1):
if contains(relevantCols, col): if contains(relevantCols, col):
var aa = skipTypes(a.typ.sons[col], skipPtrs) var aa = skipTypes(a.typ.sons[col], skipPtrs)
var bb = skipTypes(b.typ.sons[col], skipPtrs) var bb = skipTypes(b.typ.sons[col], skipPtrs)
var d = inheritanceDiff(aa, bb) var d = inheritanceDiff(aa, bb)
if (d != high(int)): if (d != high(int)):
return d return d
proc sortBucket(a: var TSymSeq, relevantCols: IntSet) = proc sortBucket(a: var TSymSeq, relevantCols: IntSet) =
# we use shellsort here; fast and simple # we use shellsort here; fast and simple
var n = len(a) var n = len(a)
var h = 1 var h = 1
while true: while true:
h = 3 * h + 1 h = 3 * h + 1
if h > n: break if h > n: break
while true: while true:
h = h div 3 h = h div 3
for i in countup(h, n - 1): for i in countup(h, n - 1):
var v = a[i] var v = a[i]
var j = i var j = i
while cmpSignatures(a[j - h], v, relevantCols) >= 0: while cmpSignatures(a[j - h], v, relevantCols) >= 0:
a[j] = a[j - h] a[j] = a[j - h]
j = j - h j = j - h
if j < h: break if j < h: break
a[j] = v a[j] = v
if h == 1: break if h == 1: break
proc genDispatcher(methods: TSymSeq, relevantCols: IntSet): PSym = proc genDispatcher(methods: TSymSeq, relevantCols: IntSet): PSym =
var base = lastSon(methods[0].ast).sym var base = lastSon(methods[0].ast).sym
result = base result = base
@ -199,7 +217,7 @@ proc genDispatcher(methods: TSymSeq, relevantCols: IntSet): PSym =
addSon(isn, newSymNode(iss)) addSon(isn, newSymNode(iss))
addSon(isn, newSymNode(base.typ.n.sons[col].sym)) addSon(isn, newSymNode(base.typ.n.sons[col].sym))
addSon(isn, newNodeIT(nkType, base.info, curr.typ.sons[col])) addSon(isn, newNodeIT(nkType, base.info, curr.typ.sons[col]))
if cond != nil: if cond != nil:
var a = newNodeIT(nkCall, base.info, getSysType(tyBool)) var a = newNodeIT(nkCall, base.info, getSysType(tyBool))
addSon(a, newSymNode(ands)) addSon(a, newSymNode(ands))
addSon(a, cond) addSon(a, cond)
@ -209,12 +227,12 @@ proc genDispatcher(methods: TSymSeq, relevantCols: IntSet): PSym =
cond = isn cond = isn
var call = newNodeI(nkCall, base.info) var call = newNodeI(nkCall, base.info)
addSon(call, newSymNode(curr)) addSon(call, newSymNode(curr))
for col in countup(1, paramLen - 1): for col in countup(1, paramLen - 1):
addSon(call, genConv(newSymNode(base.typ.n.sons[col].sym), addSon(call, genConv(newSymNode(base.typ.n.sons[col].sym),
curr.typ.sons[col], false)) curr.typ.sons[col], false))
var ret: PNode var ret: PNode
if base.typ.sons[0] != nil: if base.typ.sons[0] != nil:
var a = newNodeI(nkAsgn, base.info) var a = newNodeI(nkFastAsgn, base.info)
addSon(a, newSymNode(base.ast.sons[resultPos].sym)) addSon(a, newSymNode(base.ast.sons[resultPos].sym))
addSon(a, call) addSon(a, call)
ret = newNodeI(nkReturnStmt, base.info) ret = newNodeI(nkReturnStmt, base.info)
@ -230,13 +248,12 @@ proc genDispatcher(methods: TSymSeq, relevantCols: IntSet): PSym =
disp = ret disp = ret
result.ast.sons[bodyPos] = disp result.ast.sons[bodyPos] = disp
proc generateMethodDispatchers*(): PNode = proc generateMethodDispatchers*(): PNode =
result = newNode(nkStmtList) result = newNode(nkStmtList)
for bucket in countup(0, len(gMethods) - 1): for bucket in countup(0, len(gMethods) - 1):
var relevantCols = initIntSet() var relevantCols = initIntSet()
for col in countup(1, sonsLen(gMethods[bucket].methods[0].typ) - 1): for col in countup(1, sonsLen(gMethods[bucket].methods[0].typ) - 1):
if relevantCol(gMethods[bucket].methods, col): incl(relevantCols, col) if relevantCol(gMethods[bucket].methods, col): incl(relevantCols, col)
sortBucket(gMethods[bucket].methods, relevantCols) sortBucket(gMethods[bucket].methods, relevantCols)
addSon(result, addSon(result,
newSymNode(genDispatcher(gMethods[bucket].methods, relevantCols))) newSymNode(genDispatcher(gMethods[bucket].methods, relevantCols)))

View file

@ -17,11 +17,12 @@ template bootSwitch(name, expr, userString: expr): expr =
const name = if expr: " " & userString else: "" const name = if expr: " " & userString else: ""
bootSwitch(usedRelease, defined(release), "-d:release") bootSwitch(usedRelease, defined(release), "-d:release")
bootSwitch(usedGnuReadline, defined(useGnuReadline), "-d:useGnuReadline") bootSwitch(usedGnuReadline, defined(useLinenoise), "-d:useLinenoise")
bootSwitch(usedNoCaas, defined(noCaas), "-d:noCaas") bootSwitch(usedNoCaas, defined(noCaas), "-d:noCaas")
bootSwitch(usedBoehm, defined(boehmgc), "--gc:boehm") bootSwitch(usedBoehm, defined(boehmgc), "--gc:boehm")
bootSwitch(usedMarkAndSweep, defined(gcmarkandsweep), "--gc:markAndSweep") bootSwitch(usedMarkAndSweep, defined(gcmarkandsweep), "--gc:markAndSweep")
bootSwitch(usedGenerational, defined(gcgenerational), "--gc:generational") bootSwitch(usedGenerational, defined(gcgenerational), "--gc:generational")
bootSwitch(usedGoGC, defined(gogc), "--gc:go")
bootSwitch(usedNoGC, defined(nogc), "--gc:none") bootSwitch(usedNoGC, defined(nogc), "--gc:none")
import import
@ -64,14 +65,15 @@ proc getCommandLineDesc(): string =
proc helpOnError(pass: TCmdLinePass) = proc helpOnError(pass: TCmdLinePass) =
if pass == passCmd1: if pass == passCmd1:
msgWriteln(getCommandLineDesc()) msgWriteln(getCommandLineDesc(), {msgStdout})
msgQuit(0) msgQuit(0)
proc writeAdvancedUsage(pass: TCmdLinePass) = proc writeAdvancedUsage(pass: TCmdLinePass) =
if pass == passCmd1: if pass == passCmd1:
msgWriteln(`%`(HelpMessage, [VersionAsString, msgWriteln(`%`(HelpMessage, [VersionAsString,
platform.OS[platform.hostOS].name, platform.OS[platform.hostOS].name,
CPU[platform.hostCPU].name]) & AdvancedUsage) CPU[platform.hostCPU].name]) & AdvancedUsage,
{msgStdout})
msgQuit(0) msgQuit(0)
proc writeVersionInfo(pass: TCmdLinePass) = proc writeVersionInfo(pass: TCmdLinePass) =
@ -86,7 +88,7 @@ proc writeVersionInfo(pass: TCmdLinePass) =
msgWriteln("active boot switches:" & usedRelease & usedAvoidTimeMachine & msgWriteln("active boot switches:" & usedRelease & usedAvoidTimeMachine &
usedTinyC & usedGnuReadline & usedNativeStacktrace & usedNoCaas & usedTinyC & usedGnuReadline & usedNativeStacktrace & usedNoCaas &
usedFFI & usedBoehm & usedMarkAndSweep & usedGenerational & usedNoGC) usedFFI & usedBoehm & usedMarkAndSweep & usedGenerational & usedGoGC & usedNoGC)
msgQuit(0) msgQuit(0)
var var
@ -94,7 +96,7 @@ var
proc writeCommandLineUsage() = proc writeCommandLineUsage() =
if not helpWritten: if not helpWritten:
msgWriteln(getCommandLineDesc()) msgWriteln(getCommandLineDesc(), {msgStdout})
helpWritten = true helpWritten = true
proc addPrefix(switch: string): string = proc addPrefix(switch: string): string =
@ -127,6 +129,18 @@ proc processOnOffSwitch(op: TOptions, arg: string, pass: TCmdLinePass,
of wOff: gOptions = gOptions - op of wOff: gOptions = gOptions - op
else: localError(info, errOnOrOffExpectedButXFound, arg) else: localError(info, errOnOrOffExpectedButXFound, arg)
proc processOnOffSwitchOrList(op: TOptions, arg: string, pass: TCmdLinePass,
info: TLineInfo): bool =
result = false
case whichKeyword(arg)
of wOn: gOptions = gOptions + op
of wOff: gOptions = gOptions - op
else:
if arg == "list":
result = true
else:
localError(info, errOnOffOrListExpectedButXFound, arg)
proc processOnOffSwitchG(op: TGlobalOptions, arg: string, pass: TCmdLinePass, proc processOnOffSwitchG(op: TGlobalOptions, arg: string, pass: TCmdLinePass,
info: TLineInfo) = info: TLineInfo) =
case whichKeyword(arg) case whichKeyword(arg)
@ -140,6 +154,10 @@ proc expectArg(switch, arg: string, pass: TCmdLinePass, info: TLineInfo) =
proc expectNoArg(switch, arg: string, pass: TCmdLinePass, info: TLineInfo) = proc expectNoArg(switch, arg: string, pass: TCmdLinePass, info: TLineInfo) =
if arg != "": localError(info, errCmdLineNoArgExpected, addPrefix(switch)) if arg != "": localError(info, errCmdLineNoArgExpected, addPrefix(switch))
var
enableNotes: TNoteKinds
disableNotes: TNoteKinds
proc processSpecificNote(arg: string, state: TSpecialWord, pass: TCmdLinePass, proc processSpecificNote(arg: string, state: TSpecialWord, pass: TCmdLinePass,
info: TLineInfo; orig: string) = info: TLineInfo; orig: string) =
var id = "" # arg = "X]:on|off" var id = "" # arg = "X]:on|off"
@ -161,8 +179,12 @@ proc processSpecificNote(arg: string, state: TSpecialWord, pass: TCmdLinePass,
if x >= 0: n = TNoteKind(x + ord(warnMin)) if x >= 0: n = TNoteKind(x + ord(warnMin))
else: localError(info, "unknown warning: " & id) else: localError(info, "unknown warning: " & id)
case whichKeyword(substr(arg, i)) case whichKeyword(substr(arg, i))
of wOn: incl(gNotes, n) of wOn:
of wOff: excl(gNotes, n) incl(gNotes, n)
incl(enableNotes, n)
of wOff:
excl(gNotes, n)
incl(disableNotes, n)
else: localError(info, errOnOrOffExpectedButXFound, arg) else: localError(info, errOnOrOffExpectedButXFound, arg)
proc processCompile(filename: string) = proc processCompile(filename: string) =
@ -181,6 +203,7 @@ proc testCompileOptionArg*(switch, arg: string, info: TLineInfo): bool =
of "v2": result = gSelectedGC == gcV2 of "v2": result = gSelectedGC == gcV2
of "markandsweep": result = gSelectedGC == gcMarkAndSweep of "markandsweep": result = gSelectedGC == gcMarkAndSweep
of "generational": result = gSelectedGC == gcGenerational of "generational": result = gSelectedGC == gcGenerational
of "go": result = gSelectedGC == gcGo
of "none": result = gSelectedGC == gcNone of "none": result = gSelectedGC == gcNone
else: localError(info, errNoneBoehmRefcExpectedButXFound, arg) else: localError(info, errNoneBoehmRefcExpectedButXFound, arg)
of "opt": of "opt":
@ -229,7 +252,8 @@ proc testCompileOption*(switch: string, info: TLineInfo): bool =
of "experimental": result = gExperimentalMode of "experimental": result = gExperimentalMode
else: invalidCmdLineOption(passCmd1, switch, info) else: invalidCmdLineOption(passCmd1, switch, info)
proc processPath(path: string, notRelativeToProj = false): string = proc processPath(path: string, notRelativeToProj = false,
cfginfo = unknownLineInfo()): string =
let p = if notRelativeToProj or os.isAbsolute(path) or let p = if notRelativeToProj or os.isAbsolute(path) or
'$' in path or path[0] == '.': '$' in path or path[0] == '.':
path path
@ -239,6 +263,7 @@ proc processPath(path: string, notRelativeToProj = false): string =
"nim", getPrefixDir(), "nim", getPrefixDir(),
"lib", libpath, "lib", libpath,
"home", removeTrailingDirSep(os.getHomeDir()), "home", removeTrailingDirSep(os.getHomeDir()),
"config", cfginfo.toFullPath().splitFile().dir,
"projectname", options.gProjectName, "projectname", options.gProjectName,
"projectpath", options.gProjectPath]) "projectpath", options.gProjectPath])
@ -281,7 +306,7 @@ proc processSwitch(switch, arg: string, pass: TCmdLinePass, info: TLineInfo) =
case switch.normalize case switch.normalize
of "path", "p": of "path", "p":
expectArg(switch, arg, pass, info) expectArg(switch, arg, pass, info)
addPath(processPath(arg), info) addPath(processPath(arg, cfginfo=info), info)
of "nimblepath", "babelpath": of "nimblepath", "babelpath":
# keep the old name for compat # keep the old name for compat
if pass in {passCmd2, passPP} and not options.gNoNimblePath: if pass in {passCmd2, passPP} and not options.gNoNimblePath:
@ -363,14 +388,19 @@ proc processSwitch(switch, arg: string, pass: TCmdLinePass, info: TLineInfo) =
of "generational": of "generational":
gSelectedGC = gcGenerational gSelectedGC = gcGenerational
defineSymbol("gcgenerational") defineSymbol("gcgenerational")
of "go":
gSelectedGC = gcGo
defineSymbol("gogc")
of "none": of "none":
gSelectedGC = gcNone gSelectedGC = gcNone
defineSymbol("nogc") defineSymbol("nogc")
else: localError(info, errNoneBoehmRefcExpectedButXFound, arg) else: localError(info, errNoneBoehmRefcExpectedButXFound, arg)
of "warnings", "w": processOnOffSwitch({optWarns}, arg, pass, info) of "warnings", "w":
if processOnOffSwitchOrList({optWarns}, arg, pass, info): listWarnings()
of "warning": processSpecificNote(arg, wWarning, pass, info, switch) of "warning": processSpecificNote(arg, wWarning, pass, info, switch)
of "hint": processSpecificNote(arg, wHint, pass, info, switch) of "hint": processSpecificNote(arg, wHint, pass, info, switch)
of "hints": processOnOffSwitch({optHints}, arg, pass, info) of "hints":
if processOnOffSwitchOrList({optHints}, arg, pass, info): listHints()
of "threadanalysis": processOnOffSwitchG({optThreadAnalysis}, arg, pass, info) of "threadanalysis": processOnOffSwitchG({optThreadAnalysis}, arg, pass, info)
of "stacktrace": processOnOffSwitch({optStackTrace}, arg, pass, info) of "stacktrace": processOnOffSwitch({optStackTrace}, arg, pass, info)
of "linetrace": processOnOffSwitch({optLineTrace}, arg, pass, info) of "linetrace": processOnOffSwitch({optLineTrace}, arg, pass, info)
@ -405,6 +435,8 @@ proc processSwitch(switch, arg: string, pass: TCmdLinePass, info: TLineInfo) =
of "linedir": processOnOffSwitch({optLineDir}, arg, pass, info) of "linedir": processOnOffSwitch({optLineDir}, arg, pass, info)
of "assertions", "a": processOnOffSwitch({optAssert}, arg, pass, info) of "assertions", "a": processOnOffSwitch({optAssert}, arg, pass, info)
of "deadcodeelim": processOnOffSwitchG({optDeadCodeElim}, arg, pass, info) of "deadcodeelim": processOnOffSwitchG({optDeadCodeElim}, arg, pass, info)
of "reportconceptfailures":
processOnOffSwitchG({optReportConceptFailures}, arg, pass, info)
of "threads": of "threads":
processOnOffSwitchG({optThreads}, arg, pass, info) processOnOffSwitchG({optThreads}, arg, pass, info)
#if optThreads in gGlobalOptions: incl(gNotes, warnGcUnsafe) #if optThreads in gGlobalOptions: incl(gNotes, warnGcUnsafe)
@ -501,6 +533,9 @@ proc processSwitch(switch, arg: string, pass: TCmdLinePass, info: TLineInfo) =
of "verbosity": of "verbosity":
expectArg(switch, arg, pass, info) expectArg(switch, arg, pass, info)
gVerbosity = parseInt(arg) gVerbosity = parseInt(arg)
gNotes = NotesVerbosity[gVerbosity]
incl(gNotes, enableNotes)
excl(gNotes, disableNotes)
of "parallelbuild": of "parallelbuild":
expectArg(switch, arg, pass, info) expectArg(switch, arg, pass, info)
gNumberOfProcessors = parseInt(arg) gNumberOfProcessors = parseInt(arg)
@ -530,6 +565,7 @@ proc processSwitch(switch, arg: string, pass: TCmdLinePass, info: TLineInfo) =
of "genscript": of "genscript":
expectNoArg(switch, arg, pass, info) expectNoArg(switch, arg, pass, info)
incl(gGlobalOptions, optGenScript) incl(gGlobalOptions, optGenScript)
of "colors": processOnOffSwitchG({optUseColors}, arg, pass, info)
of "lib": of "lib":
expectArg(switch, arg, pass, info) expectArg(switch, arg, pass, info)
libpath = processPath(arg, notRelativeToProj=true) libpath = processPath(arg, notRelativeToProj=true)
@ -579,6 +615,10 @@ proc processSwitch(switch, arg: string, pass: TCmdLinePass, info: TLineInfo) =
of "experimental": of "experimental":
expectNoArg(switch, arg, pass, info) expectNoArg(switch, arg, pass, info)
gExperimentalMode = true gExperimentalMode = true
of "assembler":
cAssembler = nameToCC(arg)
if cAssembler notin cValidAssemblers:
localError(info, errGenerated, "'$1' is not a valid assembler." % [arg])
else: else:
if strutils.find(switch, '.') >= 0: options.setConfigVar(switch, arg) if strutils.find(switch, '.') >= 0: options.setConfigVar(switch, arg)
else: invalidCmdLineOption(pass, switch, info) else: invalidCmdLineOption(pass, switch, info)
@ -608,11 +648,18 @@ proc processSwitch*(pass: TCmdLinePass; p: OptParser) =
proc processArgument*(pass: TCmdLinePass; p: OptParser; proc processArgument*(pass: TCmdLinePass; p: OptParser;
argsCount: var int): bool = argsCount: var int): bool =
if argsCount == 0: if argsCount == 0:
options.command = p.key # nim filename.nims is the same as "nim e filename.nims":
if p.key.endswith(".nims"):
options.command = "e"
options.gProjectName = unixToNativePath(p.key)
arguments = cmdLineRest(p)
result = true
elif pass != passCmd2:
options.command = p.key
else: else:
if pass == passCmd1: options.commandArgs.add p.key if pass == passCmd1: options.commandArgs.add p.key
if argsCount == 1: if argsCount == 1:
# support UNIX style filenames anywhere for portable build scripts: # support UNIX style filenames everywhere for portable build scripts:
options.gProjectName = unixToNativePath(p.key) options.gProjectName = unixToNativePath(p.key)
arguments = cmdLineRest(p) arguments = cmdLineRest(p)
result = true result = true

View file

@ -88,3 +88,8 @@ proc initDefines*() =
defineSymbol("nimalias") defineSymbol("nimalias")
defineSymbol("nimlocks") defineSymbol("nimlocks")
defineSymbol("nimnode") defineSymbol("nimnode")
defineSymbol("nimnomagic64")
defineSymbol("nimvarargstyped")
defineSymbol("nimtypedescfixed")
defineSymbol("nimKnowsNimvm")
defineSymbol("nimArrIdx")

View file

@ -1,147 +0,0 @@
#
#
# The Nim Compiler
# (c) Copyright 2012 Andreas Rumpf
#
# See the file "copying.txt", included in this
# distribution, for details about the copyright.
#
import
strutils
type
TCrc32* = int32
const
InitCrc32* = TCrc32(- 1)
InitAdler32* = int32(1)
proc updateCrc32*(val: int8, crc: TCrc32): TCrc32 {.inline.}
proc updateCrc32*(val: char, crc: TCrc32): TCrc32 {.inline.}
proc crcFromBuf*(buf: pointer, length: int): TCrc32
proc strCrc32*(s: string): TCrc32
proc crcFromFile*(filename: string): TCrc32
proc updateAdler32*(adler: int32, buf: pointer, length: int): int32
# implementation
type
TCRC_TabEntry = int
const
crc32table: array[0..255, TCRC_TabEntry] = [0, 1996959894, - 301047508,
- 1727442502, 124634137, 1886057615, - 379345611, - 1637575261, 249268274,
2044508324, - 522852066, - 1747789432, 162941995, 2125561021, - 407360249,
- 1866523247, 498536548, 1789927666, - 205950648, - 2067906082, 450548861,
1843258603, - 187386543, - 2083289657, 325883990, 1684777152, - 43845254,
- 1973040660, 335633487, 1661365465, - 99664541, - 1928851979, 997073096,
1281953886, - 715111964, - 1570279054, 1006888145, 1258607687, - 770865667,
- 1526024853, 901097722, 1119000684, - 608450090, - 1396901568, 853044451,
1172266101, - 589951537, - 1412350631, 651767980, 1373503546, - 925412992,
- 1076862698, 565507253, 1454621731, - 809855591, - 1195530993, 671266974,
1594198024, - 972236366, - 1324619484, 795835527, 1483230225, - 1050600021,
- 1234817731, 1994146192, 31158534, - 1731059524, - 271249366, 1907459465,
112637215, - 1614814043, - 390540237, 2013776290, 251722036, - 1777751922,
- 519137256, 2137656763, 141376813, - 1855689577, - 429695999, 1802195444,
476864866, - 2056965928, - 228458418, 1812370925, 453092731, - 2113342271,
- 183516073, 1706088902, 314042704, - 1950435094, - 54949764, 1658658271,
366619977, - 1932296973, - 69972891, 1303535960, 984961486, - 1547960204,
- 725929758, 1256170817, 1037604311, - 1529756563, - 740887301, 1131014506,
879679996, - 1385723834, - 631195440, 1141124467, 855842277, - 1442165665,
- 586318647, 1342533948, 654459306, - 1106571248, - 921952122, 1466479909,
544179635, - 1184443383, - 832445281, 1591671054, 702138776, - 1328506846,
- 942167884, 1504918807, 783551873, - 1212326853, - 1061524307, - 306674912,
- 1698712650, 62317068, 1957810842, - 355121351, - 1647151185, 81470997,
1943803523, - 480048366, - 1805370492, 225274430, 2053790376, - 468791541,
- 1828061283, 167816743, 2097651377, - 267414716, - 2029476910, 503444072,
1762050814, - 144550051, - 2140837941, 426522225, 1852507879, - 19653770,
- 1982649376, 282753626, 1742555852, - 105259153, - 1900089351, 397917763,
1622183637, - 690576408, - 1580100738, 953729732, 1340076626, - 776247311,
- 1497606297, 1068828381, 1219638859, - 670225446, - 1358292148, 906185462,
1090812512, - 547295293, - 1469587627, 829329135, 1181335161, - 882789492,
- 1134132454, 628085408, 1382605366, - 871598187, - 1156888829, 570562233,
1426400815, - 977650754, - 1296233688, 733239954, 1555261956, - 1026031705,
- 1244606671, 752459403, 1541320221, - 1687895376, - 328994266, 1969922972,
40735498, - 1677130071, - 351390145, 1913087877, 83908371, - 1782625662,
- 491226604, 2075208622, 213261112, - 1831694693, - 438977011, 2094854071,
198958881, - 2032938284, - 237706686, 1759359992, 534414190, - 2118248755,
- 155638181, 1873836001, 414664567, - 2012718362, - 15766928, 1711684554,
285281116, - 1889165569, - 127750551, 1634467795, 376229701, - 1609899400,
- 686959890, 1308918612, 956543938, - 1486412191, - 799009033, 1231636301,
1047427035, - 1362007478, - 640263460, 1088359270, 936918000, - 1447252397,
- 558129467, 1202900863, 817233897, - 1111625188, - 893730166, 1404277552,
615818150, - 1160759803, - 841546093, 1423857449, 601450431, - 1285129682,
- 1000256840, 1567103746, 711928724, - 1274298825, - 1022587231, 1510334235,
755167117]
proc updateCrc32(val: int8, crc: TCrc32): TCrc32 =
result = TCrc32(crc32table[(int(crc) xor (int(val) and 0x000000FF)) and
0x000000FF]) xor (crc shr TCrc32(8))
proc updateCrc32(val: char, crc: TCrc32): TCrc32 =
result = updateCrc32(toU8(ord(val)), crc)
proc strCrc32(s: string): TCrc32 =
result = InitCrc32
for i in countup(0, len(s) - 1): result = updateCrc32(s[i], result)
proc `><`*(c: TCrc32, s: string): TCrc32 =
result = c
for i in 0..len(s)-1: result = updateCrc32(s[i], result)
type
TByteArray = array[0..10000000, int8]
PByteArray = ref TByteArray
proc crcFromBuf(buf: pointer, length: int): TCrc32 =
var p = cast[PByteArray](buf)
result = InitCrc32
for i in countup(0, length - 1): result = updateCrc32(p[i], result)
proc crcFromFile(filename: string): TCrc32 =
const
bufSize = 8000 # don't use 8K for the memory allocator!
var
bin: File
result = InitCrc32
if not open(bin, filename):
return # not equal if file does not exist
var buf = alloc(bufSize)
var p = cast[PByteArray](buf)
while true:
var readBytes = readBuffer(bin, buf, bufSize)
for i in countup(0, readBytes - 1): result = updateCrc32(p[i], result)
if readBytes != bufSize: break
dealloc(buf)
close(bin)
const
base = int32(65521) # largest prime smaller than 65536
# NMAX = 5552; original code with unsigned 32 bit integer
# NMAX is the largest n
# such that 255n(n+1)/2 + (n+1)(BASE-1) <= 2^32-1
nmax = 3854 # code with signed 32 bit integer
# NMAX is the largest n such that
# 255n(n+1)/2 + (n+1)(BASE-1) <= 2^31-1
# The penalty is the time loss in the extra MOD-calls.
proc updateAdler32(adler: int32, buf: pointer, length: int): int32 =
var
s1, s2: int32
L, k, b: int
s1 = adler and int32(0x0000FFFF)
s2 = (adler shr int32(16)) and int32(0x0000FFFF)
L = length
b = 0
while (L > 0):
if L < nmax: k = L
else: k = nmax
dec(L, k)
while (k > 0):
s1 = s1 +% int32((cast[cstring](buf))[b])
s2 = s2 +% s1
inc(b)
dec(k)
s1 = `%%`(s1, base)
s2 = `%%`(s2, base)
result = (s2 shl int32(16)) or s1

View file

@ -18,7 +18,7 @@ import
type type
TSections = array[TSymKind, Rope] TSections = array[TSymKind, Rope]
TDocumentor = object of rstgen.TRstGenerator TDocumentor = object of rstgen.RstGenerator
modDesc: Rope # module description modDesc: Rope # module description
id: int # for generating IDs id: int # for generating IDs
toc, section: TSections toc, section: TSections
@ -29,7 +29,7 @@ type
PDoc* = ref TDocumentor ## Alias to type less. PDoc* = ref TDocumentor ## Alias to type less.
proc compilerMsgHandler(filename: string, line, col: int, proc compilerMsgHandler(filename: string, line, col: int,
msgKind: rst.TMsgKind, arg: string) {.procvar.} = msgKind: rst.MsgKind, arg: string) {.procvar.} =
# translate msg kind: # translate msg kind:
var k: msgs.TMsgKind var k: msgs.TMsgKind
case msgKind case msgKind
@ -53,7 +53,7 @@ proc docgenFindFile(s: string): string {.procvar.} =
proc parseRst(text, filename: string, proc parseRst(text, filename: string,
line, column: int, hasToc: var bool, line, column: int, hasToc: var bool,
rstOptions: TRstParseOptions): PRstNode = rstOptions: RstParseOptions): PRstNode =
result = rstParse(text, filename, line, column, hasToc, rstOptions, result = rstParse(text, filename, line, column, hasToc, rstOptions,
docgenFindFile, compilerMsgHandler) docgenFindFile, compilerMsgHandler)
@ -75,7 +75,7 @@ proc newDocumentor*(filename: string, config: StringTableRef): PDoc =
ga('send', 'pageview'); ga('send', 'pageview');
</script> </script>
""" % [config["doc.googleAnalytics"]] """ % [config.getOrDefault"doc.googleAnalytics"]
else: else:
result.analytics = "" result.analytics = ""
@ -158,7 +158,7 @@ proc genRecComment(d: PDoc, n: PNode): Rope =
if n == nil: return nil if n == nil: return nil
result = genComment(d, n).rope result = genComment(d, n).rope
if result == nil: if result == nil:
if n.kind notin {nkEmpty..nkNilLit}: if n.kind notin {nkEmpty..nkNilLit, nkEnumTy}:
for i in countup(0, len(n)-1): for i in countup(0, len(n)-1):
result = genRecComment(d, n.sons[i]) result = genRecComment(d, n.sons[i])
if result != nil: return if result != nil: return

View file

@ -10,10 +10,10 @@
# This module implements a new documentation generator that runs after # This module implements a new documentation generator that runs after
# semantic checking. # semantic checking.
import import
os, options, ast, astalgo, msgs, ropes, idents, passes, docgen os, options, ast, astalgo, msgs, ropes, idents, passes, docgen
type type
TGen = object of TPassContext TGen = object of TPassContext
doc: PDoc doc: PDoc
module: PSym module: PSym
@ -29,12 +29,12 @@ proc close(p: PPassContext, n: PNode): PNode =
except IOError: except IOError:
discard discard
proc processNode(c: PPassContext, n: PNode): PNode = proc processNode(c: PPassContext, n: PNode): PNode =
result = n result = n
var g = PGen(c) var g = PGen(c)
generateDoc(g.doc, n) generateDoc(g.doc, n)
proc myOpen(module: PSym): PPassContext = proc myOpen(module: PSym): PPassContext =
var g: PGen var g: PGen
new(g) new(g)
g.module = module g.module = module
@ -45,5 +45,5 @@ proc myOpen(module: PSym): PPassContext =
const docgen2Pass* = makePass(open = myOpen, process = processNode, close = close) const docgen2Pass* = makePass(open = myOpen, process = processNode, close = close)
proc finishDoc2Pass*(project: string) = proc finishDoc2Pass*(project: string) =
discard discard

View file

@ -25,16 +25,21 @@ proc copyNode(ctx: TemplCtx, a, b: PNode): PNode =
if ctx.instLines: result.info = b.info if ctx.instLines: result.info = b.info
proc evalTemplateAux(templ, actual: PNode, c: var TemplCtx, result: PNode) = proc evalTemplateAux(templ, actual: PNode, c: var TemplCtx, result: PNode) =
template handleParam(param) =
let x = param
if x.kind == nkArgList:
for y in items(x): result.add(y)
else:
result.add copyTree(x)
case templ.kind case templ.kind
of nkSym: of nkSym:
var s = templ.sym var s = templ.sym
if s.owner.id == c.owner.id: if s.owner.id == c.owner.id:
if s.kind == skParam and sfGenSym notin s.flags: if s.kind == skParam and sfGenSym notin s.flags:
let x = actual.sons[s.position] handleParam actual.sons[s.position]
if x.kind == nkArgList: elif s.kind == skGenericParam:
for y in items(x): result.add(y) handleParam actual.sons[s.owner.typ.len + s.position - 1]
else:
result.add copyTree(x)
else: else:
internalAssert sfGenSym in s.flags internalAssert sfGenSym in s.flags
var x = PSym(idTableGet(c.mapping, s)) var x = PSym(idTableGet(c.mapping, s))
@ -56,20 +61,44 @@ proc evalTemplateAux(templ, actual: PNode, c: var TemplCtx, result: PNode) =
proc evalTemplateArgs(n: PNode, s: PSym): PNode = proc evalTemplateArgs(n: PNode, s: PSym): PNode =
# if the template has zero arguments, it can be called without ``()`` # if the template has zero arguments, it can be called without ``()``
# `n` is then a nkSym or something similar # `n` is then a nkSym or something similar
var a: int var totalParams = case n.kind
case n.kind of nkCall, nkInfix, nkPrefix, nkPostfix, nkCommand, nkCallStrLit: n.len-1
of nkCall, nkInfix, nkPrefix, nkPostfix, nkCommand, nkCallStrLit: else: 0
a = sonsLen(n)
else: a = 0 var
var f = s.typ.sonsLen # XXX: Since immediate templates are not subjected to the
if a > f: globalError(n.info, errWrongNumberOfArguments) # standard sigmatching algorithm, they will have a number
# of deficiencies when it comes to generic params:
# Type dependencies between the parameters won't be honoured
# and the bound generic symbols won't be resolvable within
# their bodies. We could try to fix this, but it may be
# wiser to just deprecate immediate templates and macros
# now that we have working untyped parameters.
genericParams = if sfImmediate in s.flags: 0
else: s.ast[genericParamsPos].len
expectedRegularParams = <s.typ.len
givenRegularParams = totalParams - genericParams
if totalParams > expectedRegularParams + genericParams:
globalError(n.info, errWrongNumberOfArguments)
result = newNodeI(nkArgList, n.info) result = newNodeI(nkArgList, n.info)
for i in countup(1, f - 1): for i in 1 .. givenRegularParams:
var arg = if i < a: n.sons[i] else: copyTree(s.typ.n.sons[i].sym.ast) result.addSon n.sons[i]
if arg == nil or arg.kind == nkEmpty:
# handle parameters with default values, which were
# not supplied by the user
for i in givenRegularParams+1 .. expectedRegularParams:
let default = s.typ.n.sons[i].sym.ast
if default.isNil or default.kind == nkEmpty:
localError(n.info, errWrongNumberOfArguments) localError(n.info, errWrongNumberOfArguments)
addSon(result, arg) addSon(result, ast.emptyNode)
else:
addSon(result, default.copyTree)
# add any generic paramaters
for i in 1 .. genericParams:
result.addSon n.sons[givenRegularParams + i]
var evalTemplateCounter* = 0 var evalTemplateCounter* = 0
# to prevent endless recursion in templates instantiation # to prevent endless recursion in templates instantiation

View file

@ -13,12 +13,13 @@
# nim files. # nim files.
import import
lists, ropes, os, strutils, osproc, platform, condsyms, options, msgs, crc lists, ropes, os, strutils, osproc, platform, condsyms, options, msgs,
securehash, streams
type type
TSystemCC* = enum TSystemCC* = enum
ccNone, ccGcc, ccLLVM_Gcc, ccCLang, ccLcc, ccBcc, ccDmc, ccWcc, ccVcc, ccNone, ccGcc, ccLLVM_Gcc, ccCLang, ccLcc, ccBcc, ccDmc, ccWcc, ccVcc,
ccTcc, ccPcc, ccUcc, ccIcl ccTcc, ccPcc, ccUcc, ccIcl, asmFasm
TInfoCCProp* = enum # properties of the C compiler: TInfoCCProp* = enum # properties of the C compiler:
hasSwitchRange, # CC allows ranges in switch statements (GNU C) hasSwitchRange, # CC allows ranges in switch statements (GNU C)
hasComputedGoto, # CC has computed goto (GNU C extension) hasComputedGoto, # CC has computed goto (GNU C extension)
@ -26,8 +27,8 @@ type
hasAssume, # CC has __assume (Visual C extension) hasAssume, # CC has __assume (Visual C extension)
hasGcGuard, # CC supports GC_GUARD to keep stack roots hasGcGuard, # CC supports GC_GUARD to keep stack roots
hasGnuAsm, # CC's asm uses the absurd GNU assembler syntax hasGnuAsm, # CC's asm uses the absurd GNU assembler syntax
hasNakedDeclspec, # CC has __declspec(naked) hasDeclspec, # CC has __declspec(X)
hasNakedAttribute # CC has __attribute__((naked)) hasAttribute, # CC has __attribute__((X))
TInfoCCProps* = set[TInfoCCProp] TInfoCCProps* = set[TInfoCCProp]
TInfoCC* = tuple[ TInfoCC* = tuple[
name: string, # the short name of the compiler name: string, # the short name of the compiler
@ -85,7 +86,7 @@ compiler gcc:
structStmtFmt: "$1 $3 $2 ", # struct|union [packed] $name structStmtFmt: "$1 $3 $2 ", # struct|union [packed] $name
packedPragma: "__attribute__((__packed__))", packedPragma: "__attribute__((__packed__))",
props: {hasSwitchRange, hasComputedGoto, hasCpp, hasGcGuard, hasGnuAsm, props: {hasSwitchRange, hasComputedGoto, hasCpp, hasGcGuard, hasGnuAsm,
hasNakedAttribute}) hasAttribute})
# LLVM Frontend for GCC/G++ # LLVM Frontend for GCC/G++
compiler llvmGcc: compiler llvmGcc:
@ -122,12 +123,12 @@ compiler vcc:
includeCmd: " /I", includeCmd: " /I",
linkDirCmd: " /LIBPATH:", linkDirCmd: " /LIBPATH:",
linkLibCmd: " $1.lib", linkLibCmd: " $1.lib",
debug: " /GZ /Zi ", debug: " /RTC1 /Z7 ",
pic: "", pic: "",
asmStmtFrmt: "__asm{$n$1$n}$n", asmStmtFrmt: "__asm{$n$1$n}$n",
structStmtFmt: "$3$n$1 $2", structStmtFmt: "$3$n$1 $2",
packedPragma: "#pragma pack(1)", packedPragma: "#pragma pack(1)",
props: {hasCpp, hasAssume, hasNakedDeclspec}) props: {hasCpp, hasAssume, hasDeclspec})
# Intel C/C++ Compiler # Intel C/C++ Compiler
compiler icl: compiler icl:
@ -317,6 +318,31 @@ compiler ucc:
packedPragma: "", # XXX: not supported yet packedPragma: "", # XXX: not supported yet
props: {}) props: {})
# fasm assembler
compiler fasm:
result = (
name: "fasm",
objExt: "o",
optSpeed: "",
optSize: "",
compilerExe: "fasm",
cppCompiler: "fasm",
compileTmpl: "$file $objfile",
buildGui: "",
buildDll: "",
buildLib: "",
linkerExe: "",
linkTmpl: "",
includeCmd: "",
linkDirCmd: "",
linkLibCmd: "",
debug: "",
pic: "",
asmStmtFrmt: "",
structStmtFmt: "",
packedPragma: "",
props: {})
const const
CC*: array[succ(low(TSystemCC))..high(TSystemCC), TInfoCC] = [ CC*: array[succ(low(TSystemCC))..high(TSystemCC), TInfoCC] = [
gcc(), gcc(),
@ -330,17 +356,22 @@ const
tcc(), tcc(),
pcc(), pcc(),
ucc(), ucc(),
icl()] icl(),
fasm()]
hExt* = ".h" hExt* = ".h"
var var
cCompiler* = ccGcc # the used compiler cCompiler* = ccGcc # the used compiler
cAssembler* = ccNone
gMixedMode*: bool # true if some module triggered C++ codegen gMixedMode*: bool # true if some module triggered C++ codegen
cIncludes*: seq[string] = @[] # directories to search for included files cIncludes*: seq[string] = @[] # directories to search for included files
cLibs*: seq[string] = @[] # directories to search for lib files cLibs*: seq[string] = @[] # directories to search for lib files
cLinkedLibs*: seq[string] = @[] # libraries to link cLinkedLibs*: seq[string] = @[] # libraries to link
const
cValidAssemblers* = {asmFasm}
# implementation # implementation
proc libNameTmpl(): string {.inline.} = proc libNameTmpl(): string {.inline.} =
@ -428,7 +459,7 @@ proc resetCompilationLists* =
initLinkedList(toCompile) initLinkedList(toCompile)
## XXX: we must associate these with their originating module ## XXX: we must associate these with their originating module
# when the module is loaded/unloaded it adds/removes its items # when the module is loaded/unloaded it adds/removes its items
# That's because we still need to CRC check the external files # That's because we still need to hash check the external files
# Maybe we can do that in checkDep on the other hand? # Maybe we can do that in checkDep on the other hand?
initLinkedList(externalToCompile) initLinkedList(externalToCompile)
initLinkedList(toLink) initLinkedList(toLink)
@ -437,17 +468,13 @@ proc addFileToLink*(filename: string) =
prependStr(toLink, filename) prependStr(toLink, filename)
# BUGFIX: was ``appendStr`` # BUGFIX: was ``appendStr``
proc execWithEcho(cmd: string, prettyCmd = ""): int = proc execWithEcho(cmd: string, msg = hintExecuting): int =
if optListCmd in gGlobalOptions or gVerbosity > 0: rawMessage(msg, cmd)
if prettyCmd != "":
msgWriteln(prettyCmd)
else:
msgWriteln(cmd)
result = execCmd(cmd) result = execCmd(cmd)
proc execExternalProgram*(cmd: string, prettyCmd = "") = proc execExternalProgram*(cmd: string, msg = hintExecuting) =
if execWithEcho(cmd, prettyCmd) != 0: if execWithEcho(cmd, msg) != 0:
rawMessage(errExecutionOfProgramFailed, "") rawMessage(errExecutionOfProgramFailed, cmd)
proc generateScript(projectFile: string, script: Rope) = proc generateScript(projectFile: string, script: Rope) =
let (dir, name, ext) = splitFile(projectFile) let (dir, name, ext) = splitFile(projectFile)
@ -530,6 +557,21 @@ proc getLinkerExe(compiler: TSystemCC): string =
proc getCompileCFileCmd*(cfilename: string, isExternal = false): string = proc getCompileCFileCmd*(cfilename: string, isExternal = false): string =
var c = cCompiler var c = cCompiler
if cfilename.endswith(".asm"):
var customAssembler = getConfigVar("assembler")
if customAssembler.len > 0:
c = nameToCC(customAssembler)
else:
if targetCPU == cpuI386 or targetCPU == cpuAmd64:
c = asmFasm
else:
c = ccNone
if c == ccNone:
rawMessage(errExternalAssemblerNotFound, "")
elif c notin cValidAssemblers:
rawMessage(errExternalAssemblerNotValid, customAssembler)
var options = cFileSpecificOptions(cfilename) var options = cFileSpecificOptions(cfilename)
var exe = getConfigVar(c, ".exe") var exe = getConfigVar(c, ".exe")
if exe.len == 0: exe = c.getCompilerExe if exe.len == 0: exe = c.getCompilerExe
@ -572,32 +614,30 @@ proc getCompileCFileCmd*(cfilename: string, isExternal = false): string =
"nim", quoteShell(getPrefixDir()), "nim", quoteShell(getPrefixDir()),
"lib", quoteShell(libpath)]) "lib", quoteShell(libpath)])
proc footprint(filename: string): TCrc32 = proc footprint(filename: string): SecureHash =
# note, '><' further modifies a crc value with a string. result = secureHash(
result = crcFromFile(filename) >< $secureHashFile(filename) &
platform.OS[targetOS].name >< platform.OS[targetOS].name &
platform.CPU[targetCPU].name >< platform.CPU[targetCPU].name &
extccomp.CC[extccomp.cCompiler].name >< extccomp.CC[extccomp.cCompiler].name &
getCompileCFileCmd(filename, true) getCompileCFileCmd(filename, true))
proc externalFileChanged(filename: string): bool = proc externalFileChanged(filename: string): bool =
if gCmd notin {cmdCompileToC, cmdCompileToCpp, cmdCompileToOC, cmdCompileToLLVM}: if gCmd notin {cmdCompileToC, cmdCompileToCpp, cmdCompileToOC, cmdCompileToLLVM}:
return false return false
var crcFile = toGeneratedFile(filename.withPackageName, "crc") var hashFile = toGeneratedFile(filename.withPackageName, "sha1")
var currentCrc = int(footprint(filename)) var currentHash = footprint(filename)
var f: File var f: File
if open(f, crcFile, fmRead): if open(f, hashFile, fmRead):
var line = newStringOfCap(40) let oldHash = parseSecureHash(f.readLine())
if not f.readLine(line): line = "0"
close(f) close(f)
var oldCrc = parseInt(line) result = oldHash != currentHash
result = oldCrc != currentCrc
else: else:
result = true result = true
if result: if result:
if open(f, crcFile, fmWrite): if open(f, hashFile, fmWrite):
f.writeln($currentCrc) f.writeLine($currentHash)
close(f) close(f)
proc addExternalFileToCompile*(filename: string) = proc addExternalFileToCompile*(filename: string) =
@ -632,6 +672,12 @@ proc callCCompiler*(projectfile: string) =
var prettyCmds: TStringSeq = @[] var prettyCmds: TStringSeq = @[]
let prettyCb = proc (idx: int) = let prettyCb = proc (idx: int) =
echo prettyCmds[idx] echo prettyCmds[idx]
let runCb = proc (idx: int, p: Process) =
let exitCode = p.peekExitCode
if exitCode != 0:
rawMessage(errGenerated, "execution of an external compiler program '" &
cmds[idx] & "' failed with exit code: " & $exitCode & "\n\n" &
p.outputStream.readAll.strip)
compileCFile(toCompile, script, cmds, prettyCmds, false) compileCFile(toCompile, script, cmds, prettyCmds, false)
compileCFile(externalToCompile, script, cmds, prettyCmds, true) compileCFile(externalToCompile, script, cmds, prettyCmds, true)
if optCompileOnly notin gGlobalOptions: if optCompileOnly notin gGlobalOptions:
@ -640,22 +686,19 @@ proc callCCompiler*(projectfile: string) =
if gNumberOfProcessors <= 1: if gNumberOfProcessors <= 1:
for i in countup(0, high(cmds)): for i in countup(0, high(cmds)):
res = execWithEcho(cmds[i]) res = execWithEcho(cmds[i])
if res != 0: rawMessage(errExecutionOfProgramFailed, []) if res != 0: rawMessage(errExecutionOfProgramFailed, cmds[i])
elif optListCmd in gGlobalOptions or gVerbosity > 1: elif optListCmd in gGlobalOptions or gVerbosity > 1:
res = execProcesses(cmds, {poEchoCmd, poUsePath, poParentStreams}, res = execProcesses(cmds, {poEchoCmd, poStdErrToStdOut, poUsePath, poParentStreams},
gNumberOfProcessors) gNumberOfProcessors, afterRunEvent=runCb)
elif gVerbosity == 1: elif gVerbosity == 1:
res = execProcesses(cmds, {poUsePath, poParentStreams}, res = execProcesses(cmds, {poStdErrToStdOut, poUsePath, poParentStreams},
gNumberOfProcessors, prettyCb) gNumberOfProcessors, prettyCb, afterRunEvent=runCb)
else: else:
res = execProcesses(cmds, {poUsePath, poParentStreams}, res = execProcesses(cmds, {poStdErrToStdOut, poUsePath, poParentStreams},
gNumberOfProcessors) gNumberOfProcessors, afterRunEvent=runCb)
if res != 0: if res != 0:
if gNumberOfProcessors <= 1: if gNumberOfProcessors <= 1:
rawMessage(errExecutionOfProgramFailed, []) rawMessage(errExecutionOfProgramFailed, cmds.join())
else:
rawMessage(errGenerated, " execution of an external program failed; " &
"rerun with --parallelBuild:1 to see the error message")
if optNoLinking notin gGlobalOptions: if optNoLinking notin gGlobalOptions:
# call the linker: # call the linker:
var it = PStrEntry(toLink.head) var it = PStrEntry(toLink.head)
@ -668,7 +711,8 @@ proc callCCompiler*(projectfile: string) =
it = PStrEntry(it.next) it = PStrEntry(it.next)
if optGenStaticLib in gGlobalOptions: if optGenStaticLib in gGlobalOptions:
linkCmd = CC[c].buildLib % ["libfile", (libNameTmpl() % gProjectName), let name = splitFile(gProjectName).name
linkCmd = CC[c].buildLib % ["libfile", (libNameTmpl() % name),
"objfiles", objfiles] "objfiles", objfiles]
else: else:
var linkerExe = getConfigVar(c, ".linkerexe") var linkerExe = getConfigVar(c, ".linkerexe")
@ -703,10 +747,8 @@ proc callCCompiler*(projectfile: string) =
"nim", quoteShell(getPrefixDir()), "nim", quoteShell(getPrefixDir()),
"lib", quoteShell(libpath)]) "lib", quoteShell(libpath)])
if optCompileOnly notin gGlobalOptions: if optCompileOnly notin gGlobalOptions:
if gVerbosity == 1: execExternalProgram(linkCmd,
execExternalProgram(linkCmd, "[Linking]") if gVerbosity > 1: hintExecuting else: hintLinking)
else:
execExternalProgram(linkCmd)
else: else:
linkCmd = "" linkCmd = ""
if optGenScript in gGlobalOptions: if optGenScript in gGlobalOptions:

View file

@ -9,18 +9,18 @@
# This module implements Nim's standard template filter. # This module implements Nim's standard template filter.
import import
llstream, os, wordrecg, idents, strutils, ast, astalgo, msgs, options, llstream, os, wordrecg, idents, strutils, ast, astalgo, msgs, options,
renderer, filters renderer, filters
proc filterTmpl*(stdin: PLLStream, filename: string, call: PNode): PLLStream proc filterTmpl*(stdin: PLLStream, filename: string, call: PNode): PLLStream
# #! template(subsChar='$', metaChar='#') | standard(version="0.7.2") # #! template(subsChar='$', metaChar='#') | standard(version="0.7.2")
# implementation # implementation
type type
TParseState = enum TParseState = enum
psDirective, psTempl psDirective, psTempl
TTmplParser{.final.} = object TTmplParser{.final.} = object
inp: PLLStream inp: PLLStream
state: TParseState state: TParseState
info: TLineInfo info: TLineInfo
@ -33,18 +33,18 @@ type
pendingExprLine: bool pendingExprLine: bool
const const
PatternChars = {'a'..'z', 'A'..'Z', '0'..'9', '\x80'..'\xFF', '.', '_'} PatternChars = {'a'..'z', 'A'..'Z', '0'..'9', '\x80'..'\xFF', '.', '_'}
proc newLine(p: var TTmplParser) = proc newLine(p: var TTmplParser) =
llStreamWrite(p.outp, repeat(')', p.emitPar)) llStreamWrite(p.outp, repeat(')', p.emitPar))
p.emitPar = 0 p.emitPar = 0
if p.info.line > int16(1): llStreamWrite(p.outp, "\n") if p.info.line > int16(1): llStreamWrite(p.outp, "\n")
if p.pendingExprLine: if p.pendingExprLine:
llStreamWrite(p.outp, spaces(2)) llStreamWrite(p.outp, spaces(2))
p.pendingExprLine = false p.pendingExprLine = false
proc scanPar(p: var TTmplParser, d: int) = proc scanPar(p: var TTmplParser, d: int) =
var i = d var i = d
while true: while true:
case p.x[i] case p.x[i]
@ -58,44 +58,44 @@ proc scanPar(p: var TTmplParser, d: int) =
else: discard else: discard
inc(i) inc(i)
proc withInExpr(p: TTmplParser): bool {.inline.} = proc withInExpr(p: TTmplParser): bool {.inline.} =
result = p.par > 0 or p.bracket > 0 or p.curly > 0 result = p.par > 0 or p.bracket > 0 or p.curly > 0
proc parseLine(p: var TTmplParser) = proc parseLine(p: var TTmplParser) =
var var
d, j, curly: int d, j, curly: int
keyw: string keyw: string
j = 0 j = 0
while p.x[j] == ' ': inc(j) while p.x[j] == ' ': inc(j)
if (p.x[0] == p.nimDirective) and (p.x[0 + 1] == '!'): if p.x[0] == p.nimDirective and p.x[1] in {'?', '!'}:
newLine(p) newLine(p)
elif (p.x[j] == p.nimDirective): elif p.x[j] == p.nimDirective:
newLine(p) newLine(p)
inc(j) inc(j)
while p.x[j] == ' ': inc(j) while p.x[j] == ' ': inc(j)
d = j d = j
keyw = "" keyw = ""
while p.x[j] in PatternChars: while p.x[j] in PatternChars:
add(keyw, p.x[j]) add(keyw, p.x[j])
inc(j) inc(j)
scanPar(p, j) scanPar(p, j)
p.pendingExprLine = withInExpr(p) or llstream.endsWithOpr(p.x) p.pendingExprLine = withInExpr(p) or llstream.endsWithOpr(p.x)
case whichKeyword(keyw) case whichKeyword(keyw)
of wEnd: of wEnd:
if p.indent >= 2: if p.indent >= 2:
dec(p.indent, 2) dec(p.indent, 2)
else: else:
p.info.col = int16(j) p.info.col = int16(j)
localError(p.info, errXNotAllowedHere, "end") localError(p.info, errXNotAllowedHere, "end")
llStreamWrite(p.outp, spaces(p.indent)) llStreamWrite(p.outp, spaces(p.indent))
llStreamWrite(p.outp, "#end") llStreamWrite(p.outp, "#end")
of wIf, wWhen, wTry, wWhile, wFor, wBlock, wCase, wProc, wIterator, of wIf, wWhen, wTry, wWhile, wFor, wBlock, wCase, wProc, wIterator,
wConverter, wMacro, wTemplate, wMethod: wConverter, wMacro, wTemplate, wMethod:
llStreamWrite(p.outp, spaces(p.indent)) llStreamWrite(p.outp, spaces(p.indent))
llStreamWrite(p.outp, substr(p.x, d)) llStreamWrite(p.outp, substr(p.x, d))
inc(p.indent, 2) inc(p.indent, 2)
of wElif, wOf, wElse, wExcept, wFinally: of wElif, wOf, wElse, wExcept, wFinally:
llStreamWrite(p.outp, spaces(p.indent - 2)) llStreamWrite(p.outp, spaces(p.indent - 2))
llStreamWrite(p.outp, substr(p.x, d)) llStreamWrite(p.outp, substr(p.x, d))
of wLet, wVar, wConst, wType: of wLet, wVar, wConst, wType:
@ -108,7 +108,7 @@ proc parseLine(p: var TTmplParser) =
llStreamWrite(p.outp, spaces(p.indent)) llStreamWrite(p.outp, spaces(p.indent))
llStreamWrite(p.outp, substr(p.x, d)) llStreamWrite(p.outp, substr(p.x, d))
p.state = psDirective p.state = psDirective
else: else:
# data line # data line
# reset counters # reset counters
p.par = 0 p.par = 0
@ -116,42 +116,42 @@ proc parseLine(p: var TTmplParser) =
p.bracket = 0 p.bracket = 0
j = 0 j = 0
case p.state case p.state
of psTempl: of psTempl:
# next line of string literal: # next line of string literal:
llStreamWrite(p.outp, p.conc) llStreamWrite(p.outp, p.conc)
llStreamWrite(p.outp, "\n") llStreamWrite(p.outp, "\n")
llStreamWrite(p.outp, spaces(p.indent + 2)) llStreamWrite(p.outp, spaces(p.indent + 2))
llStreamWrite(p.outp, "\"") llStreamWrite(p.outp, "\"")
of psDirective: of psDirective:
newLine(p) newLine(p)
llStreamWrite(p.outp, spaces(p.indent)) llStreamWrite(p.outp, spaces(p.indent))
llStreamWrite(p.outp, p.emit) llStreamWrite(p.outp, p.emit)
llStreamWrite(p.outp, "(\"") llStreamWrite(p.outp, "(\"")
inc(p.emitPar) inc(p.emitPar)
p.state = psTempl p.state = psTempl
while true: while true:
case p.x[j] case p.x[j]
of '\0': of '\0':
break break
of '\x01'..'\x1F', '\x80'..'\xFF': of '\x01'..'\x1F', '\x80'..'\xFF':
llStreamWrite(p.outp, "\\x") llStreamWrite(p.outp, "\\x")
llStreamWrite(p.outp, toHex(ord(p.x[j]), 2)) llStreamWrite(p.outp, toHex(ord(p.x[j]), 2))
inc(j) inc(j)
of '\\': of '\\':
llStreamWrite(p.outp, "\\\\") llStreamWrite(p.outp, "\\\\")
inc(j) inc(j)
of '\'': of '\'':
llStreamWrite(p.outp, "\\\'") llStreamWrite(p.outp, "\\\'")
inc(j) inc(j)
of '\"': of '\"':
llStreamWrite(p.outp, "\\\"") llStreamWrite(p.outp, "\\\"")
inc(j) inc(j)
else: else:
if p.x[j] == p.subsChar: if p.x[j] == p.subsChar:
# parse Nim expression: # parse Nim expression:
inc(j) inc(j)
case p.x[j] case p.x[j]
of '{': of '{':
p.info.col = int16(j) p.info.col = int16(j)
llStreamWrite(p.outp, '\"') llStreamWrite(p.outp, '\"')
llStreamWrite(p.outp, p.conc) llStreamWrite(p.outp, p.conc)
@ -159,50 +159,50 @@ proc parseLine(p: var TTmplParser) =
llStreamWrite(p.outp, '(') llStreamWrite(p.outp, '(')
inc(j) inc(j)
curly = 0 curly = 0
while true: while true:
case p.x[j] case p.x[j]
of '\0': of '\0':
localError(p.info, errXExpected, "}") localError(p.info, errXExpected, "}")
break break
of '{': of '{':
inc(j) inc(j)
inc(curly) inc(curly)
llStreamWrite(p.outp, '{') llStreamWrite(p.outp, '{')
of '}': of '}':
inc(j) inc(j)
if curly == 0: break if curly == 0: break
if curly > 0: dec(curly) if curly > 0: dec(curly)
llStreamWrite(p.outp, '}') llStreamWrite(p.outp, '}')
else: else:
llStreamWrite(p.outp, p.x[j]) llStreamWrite(p.outp, p.x[j])
inc(j) inc(j)
llStreamWrite(p.outp, ')') llStreamWrite(p.outp, ')')
llStreamWrite(p.outp, p.conc) llStreamWrite(p.outp, p.conc)
llStreamWrite(p.outp, '\"') llStreamWrite(p.outp, '\"')
of 'a'..'z', 'A'..'Z', '\x80'..'\xFF': of 'a'..'z', 'A'..'Z', '\x80'..'\xFF':
llStreamWrite(p.outp, '\"') llStreamWrite(p.outp, '\"')
llStreamWrite(p.outp, p.conc) llStreamWrite(p.outp, p.conc)
llStreamWrite(p.outp, p.toStr) llStreamWrite(p.outp, p.toStr)
llStreamWrite(p.outp, '(') llStreamWrite(p.outp, '(')
while p.x[j] in PatternChars: while p.x[j] in PatternChars:
llStreamWrite(p.outp, p.x[j]) llStreamWrite(p.outp, p.x[j])
inc(j) inc(j)
llStreamWrite(p.outp, ')') llStreamWrite(p.outp, ')')
llStreamWrite(p.outp, p.conc) llStreamWrite(p.outp, p.conc)
llStreamWrite(p.outp, '\"') llStreamWrite(p.outp, '\"')
else: else:
if p.x[j] == p.subsChar: if p.x[j] == p.subsChar:
llStreamWrite(p.outp, p.subsChar) llStreamWrite(p.outp, p.subsChar)
inc(j) inc(j)
else: else:
p.info.col = int16(j) p.info.col = int16(j)
localError(p.info, errInvalidExpression, "$") localError(p.info, errInvalidExpression, "$")
else: else:
llStreamWrite(p.outp, p.x[j]) llStreamWrite(p.outp, p.x[j])
inc(j) inc(j)
llStreamWrite(p.outp, "\\n\"") llStreamWrite(p.outp, "\\n\"")
proc filterTmpl(stdin: PLLStream, filename: string, call: PNode): PLLStream = proc filterTmpl(stdin: PLLStream, filename: string, call: PNode): PLLStream =
var p: TTmplParser var p: TTmplParser
p.info = newLineInfo(filename, 0, 0) p.info = newLineInfo(filename, 0, 0)
p.outp = llStreamOpen("") p.outp = llStreamOpen("")

View file

@ -10,7 +10,7 @@
# This module implements Nim's simple filters and helpers for filters. # This module implements Nim's simple filters and helpers for filters.
import import
llstream, os, wordrecg, idents, strutils, ast, astalgo, msgs, options, llstream, os, wordrecg, idents, strutils, ast, astalgo, msgs, options,
renderer renderer
proc filterReplace*(stdin: PLLStream, filename: string, call: PNode): PLLStream proc filterReplace*(stdin: PLLStream, filename: string, call: PNode): PLLStream
@ -21,40 +21,40 @@ proc strArg*(n: PNode, name: string, pos: int, default: string): string
proc boolArg*(n: PNode, name: string, pos: int, default: bool): bool proc boolArg*(n: PNode, name: string, pos: int, default: bool): bool
# implementation # implementation
proc invalidPragma(n: PNode) = proc invalidPragma(n: PNode) =
localError(n.info, errXNotAllowedHere, renderTree(n, {renderNoComments})) localError(n.info, errXNotAllowedHere, renderTree(n, {renderNoComments}))
proc getArg(n: PNode, name: string, pos: int): PNode = proc getArg(n: PNode, name: string, pos: int): PNode =
result = nil result = nil
if n.kind in {nkEmpty..nkNilLit}: return if n.kind in {nkEmpty..nkNilLit}: return
for i in countup(1, sonsLen(n) - 1): for i in countup(1, sonsLen(n) - 1):
if n.sons[i].kind == nkExprEqExpr: if n.sons[i].kind == nkExprEqExpr:
if n.sons[i].sons[0].kind != nkIdent: invalidPragma(n) if n.sons[i].sons[0].kind != nkIdent: invalidPragma(n)
if identEq(n.sons[i].sons[0].ident, name): if identEq(n.sons[i].sons[0].ident, name):
return n.sons[i].sons[1] return n.sons[i].sons[1]
elif i == pos: elif i == pos:
return n.sons[i] return n.sons[i]
proc charArg(n: PNode, name: string, pos: int, default: char): char = proc charArg(n: PNode, name: string, pos: int, default: char): char =
var x = getArg(n, name, pos) var x = getArg(n, name, pos)
if x == nil: result = default if x == nil: result = default
elif x.kind == nkCharLit: result = chr(int(x.intVal)) elif x.kind == nkCharLit: result = chr(int(x.intVal))
else: invalidPragma(n) else: invalidPragma(n)
proc strArg(n: PNode, name: string, pos: int, default: string): string = proc strArg(n: PNode, name: string, pos: int, default: string): string =
var x = getArg(n, name, pos) var x = getArg(n, name, pos)
if x == nil: result = default if x == nil: result = default
elif x.kind in {nkStrLit..nkTripleStrLit}: result = x.strVal elif x.kind in {nkStrLit..nkTripleStrLit}: result = x.strVal
else: invalidPragma(n) else: invalidPragma(n)
proc boolArg(n: PNode, name: string, pos: int, default: bool): bool = proc boolArg(n: PNode, name: string, pos: int, default: bool): bool =
var x = getArg(n, name, pos) var x = getArg(n, name, pos)
if x == nil: result = default if x == nil: result = default
elif (x.kind == nkIdent) and identEq(x.ident, "true"): result = true elif (x.kind == nkIdent) and identEq(x.ident, "true"): result = true
elif (x.kind == nkIdent) and identEq(x.ident, "false"): result = false elif (x.kind == nkIdent) and identEq(x.ident, "false"): result = false
else: invalidPragma(n) else: invalidPragma(n)
proc filterStrip(stdin: PLLStream, filename: string, call: PNode): PLLStream = proc filterStrip(stdin: PLLStream, filename: string, call: PNode): PLLStream =
var pattern = strArg(call, "startswith", 1, "") var pattern = strArg(call, "startswith", 1, "")
var leading = boolArg(call, "leading", 2, true) var leading = boolArg(call, "leading", 2, true)
var trailing = boolArg(call, "trailing", 3, true) var trailing = boolArg(call, "trailing", 3, true)
@ -62,13 +62,13 @@ proc filterStrip(stdin: PLLStream, filename: string, call: PNode): PLLStream =
var line = newStringOfCap(80) var line = newStringOfCap(80)
while llStreamReadLine(stdin, line): while llStreamReadLine(stdin, line):
var stripped = strip(line, leading, trailing) var stripped = strip(line, leading, trailing)
if (len(pattern) == 0) or startsWith(stripped, pattern): if (len(pattern) == 0) or startsWith(stripped, pattern):
llStreamWriteln(result, stripped) llStreamWriteln(result, stripped)
else: else:
llStreamWriteln(result, line) llStreamWriteln(result, line)
llStreamClose(stdin) llStreamClose(stdin)
proc filterReplace(stdin: PLLStream, filename: string, call: PNode): PLLStream = proc filterReplace(stdin: PLLStream, filename: string, call: PNode): PLLStream =
var sub = strArg(call, "sub", 1, "") var sub = strArg(call, "sub", 1, "")
if len(sub) == 0: invalidPragma(call) if len(sub) == 0: invalidPragma(call)
var by = strArg(call, "by", 2, "") var by = strArg(call, "by", 2, "")

View file

@ -12,13 +12,13 @@
import ast, astalgo import ast, astalgo
const const
someCmp = {mEqI, mEqI64, mEqF64, mEqEnum, mEqCh, mEqB, mEqRef, mEqProc, someCmp = {mEqI, mEqF64, mEqEnum, mEqCh, mEqB, mEqRef, mEqProc,
mEqUntracedRef, mLeI, mLeI64, mLeF64, mLeU, mLeU64, mLeEnum, mEqUntracedRef, mLeI, mLeF64, mLeU, mLeU64, mLeEnum,
mLeCh, mLeB, mLePtr, mLtI, mLtI64, mLtF64, mLtU, mLtU64, mLtEnum, mLeCh, mLeB, mLePtr, mLtI, mLtF64, mLtU, mLtU64, mLtEnum,
mLtCh, mLtB, mLtPtr} mLtCh, mLtB, mLtPtr}
proc isCounter(s: PSym): bool {.inline.} = proc isCounter(s: PSym): bool {.inline.} =
s.kind in {skResult, skVar, skLet, skTemp} and s.kind in {skResult, skVar, skLet, skTemp} and
{sfGlobal, sfAddrTaken} * s.flags == {} {sfGlobal, sfAddrTaken} * s.flags == {}
proc isCall(n: PNode): bool {.inline.} = proc isCall(n: PNode): bool {.inline.} =
@ -29,7 +29,7 @@ proc fromSystem(op: PSym): bool = sfSystemModule in getModule(op).flags
proc getCounter(lastStmt: PNode): PSym = proc getCounter(lastStmt: PNode): PSym =
if lastStmt.isCall: if lastStmt.isCall:
let op = lastStmt.sym let op = lastStmt.sym
if op.magic in {mDec, mInc} or if op.magic in {mDec, mInc} or
((op.name.s == "+=" or op.name.s == "-=") and op.fromSystem): ((op.name.s == "+=" or op.name.s == "-=") and op.fromSystem):
if op[1].kind == nkSym and isCounter(op[1].sym): if op[1].kind == nkSym and isCounter(op[1].sym):
result = op[1].sym result = op[1].sym
@ -67,7 +67,7 @@ proc extractForLoop*(loop, fullTree: PNode): ForLoop =
if not cond.isCall: return if not cond.isCall: return
if cond[0].sym.magic notin someCmp: return if cond[0].sym.magic notin someCmp: return
var lastStmt = loop[1] var lastStmt = loop[1]
while lastStmt.kind in {nkStmtList, nkStmtListExpr}: while lastStmt.kind in {nkStmtList, nkStmtListExpr}:
lastStmt = lastStmt.lastSon lastStmt = lastStmt.lastSon
@ -76,7 +76,7 @@ proc extractForLoop*(loop, fullTree: PNode): ForLoop =
if counter.isNil or counter.ast.isNil: return if counter.isNil or counter.ast.isNil: return
template `=~`(a, b): expr = a.kind == nkSym and a.sym == b template `=~`(a, b): expr = a.kind == nkSym and a.sym == b
if cond[1] =~ counter or cond[2] =~ counter: if cond[1] =~ counter or cond[2] =~ counter:
# ok, now check 'counter' is not used *after* the loop # ok, now check 'counter' is not used *after* the loop
if counterInTree(fullTree, loop, counter): return if counterInTree(fullTree, loop, counter): return

View file

@ -13,13 +13,13 @@ import ast, astalgo, msgs, magicsys, nimsets, trees, types, renderer, idents,
saturate saturate
const const
someEq = {mEqI, mEqI64, mEqF64, mEqEnum, mEqCh, mEqB, mEqRef, mEqProc, someEq = {mEqI, mEqF64, mEqEnum, mEqCh, mEqB, mEqRef, mEqProc,
mEqUntracedRef, mEqStr, mEqSet, mEqCString} mEqUntracedRef, mEqStr, mEqSet, mEqCString}
# set excluded here as the semantics are vastly different: # set excluded here as the semantics are vastly different:
someLe = {mLeI, mLeI64, mLeF64, mLeU, mLeU64, mLeEnum, someLe = {mLeI, mLeF64, mLeU, mLeU64, mLeEnum,
mLeCh, mLeB, mLePtr, mLeStr} mLeCh, mLeB, mLePtr, mLeStr}
someLt = {mLtI, mLtI64, mLtF64, mLtU, mLtU64, mLtEnum, someLt = {mLtI, mLtF64, mLtU, mLtU64, mLtEnum,
mLtCh, mLtB, mLtPtr, mLtStr} mLtCh, mLtB, mLtPtr, mLtStr}
someLen = {mLengthOpenArray, mLengthStr, mLengthArray, mLengthSeq, someLen = {mLengthOpenArray, mLengthStr, mLengthArray, mLengthSeq,
@ -30,13 +30,14 @@ const
someHigh = {mHigh} someHigh = {mHigh}
# we don't list unsigned here because wrap around semantics suck for # we don't list unsigned here because wrap around semantics suck for
# proving anything: # proving anything:
someAdd = {mAddI, mAddI64, mAddF64, mSucc} someAdd = {mAddI, mAddF64, mSucc}
someSub = {mSubI, mSubI64, mSubF64, mPred} someSub = {mSubI, mSubF64, mPred}
someMul = {mMulI, mMulI64, mMulF64} someMul = {mMulI, mMulF64}
someDiv = {mDivI, mDivI64, mDivF64} someDiv = {mDivI, mDivF64}
someMod = {mModI, mModI64} someMod = {mModI}
someMax = {mMaxI, mMaxF64} someMax = {mMaxI, mMaxF64}
someMin = {mMinI, mMinF64} someMin = {mMinI, mMinF64}
someBinaryOp = someAdd+someSub+someMul+someMax+someMin
proc isValue(n: PNode): bool = n.kind in {nkCharLit..nkNilLit} proc isValue(n: PNode): bool = n.kind in {nkCharLit..nkNilLit}
proc isLocation(n: PNode): bool = not n.isValue proc isLocation(n: PNode): bool = not n.isValue
@ -165,11 +166,21 @@ proc `|+|`(a, b: PNode): PNode =
if a.kind in {nkCharLit..nkUInt64Lit}: result.intVal = a.intVal |+| b.intVal if a.kind in {nkCharLit..nkUInt64Lit}: result.intVal = a.intVal |+| b.intVal
else: result.floatVal = a.floatVal + b.floatVal else: result.floatVal = a.floatVal + b.floatVal
proc `|-|`(a, b: PNode): PNode =
result = copyNode(a)
if a.kind in {nkCharLit..nkUInt64Lit}: result.intVal = a.intVal |-| b.intVal
else: result.floatVal = a.floatVal - b.floatVal
proc `|*|`(a, b: PNode): PNode = proc `|*|`(a, b: PNode): PNode =
result = copyNode(a) result = copyNode(a)
if a.kind in {nkCharLit..nkUInt64Lit}: result.intVal = a.intVal |*| b.intVal if a.kind in {nkCharLit..nkUInt64Lit}: result.intVal = a.intVal |*| b.intVal
else: result.floatVal = a.floatVal * b.floatVal else: result.floatVal = a.floatVal * b.floatVal
proc `|div|`(a, b: PNode): PNode =
result = copyNode(a)
if a.kind in {nkCharLit..nkUInt64Lit}: result.intVal = a.intVal div b.intVal
else: result.floatVal = a.floatVal / b.floatVal
proc negate(a, b, res: PNode): PNode = proc negate(a, b, res: PNode): PNode =
if b.kind in {nkCharLit..nkUInt64Lit} and b.intVal != low(BiggestInt): if b.kind in {nkCharLit..nkUInt64Lit} and b.intVal != low(BiggestInt):
var b = copyNode(b) var b = copyNode(b)
@ -213,10 +224,16 @@ proc reassociation(n: PNode): PNode =
if result[2].isValue and if result[2].isValue and
result[1].getMagic in someAdd and result[1][2].isValue: result[1].getMagic in someAdd and result[1][2].isValue:
result = opAdd.buildCall(result[1][1], result[1][2] |+| result[2]) result = opAdd.buildCall(result[1][1], result[1][2] |+| result[2])
if result[2].intVal == 0:
result = result[1]
of someMul: of someMul:
if result[2].isValue and if result[2].isValue and
result[1].getMagic in someMul and result[1][2].isValue: result[1].getMagic in someMul and result[1][2].isValue:
result = opAdd.buildCall(result[1][1], result[1][2] |*| result[2]) result = opMul.buildCall(result[1][1], result[1][2] |*| result[2])
if result[2].intVal == 1:
result = result[1]
elif result[2].intVal == 0:
result = zero()
else: discard else: discard
proc pred(n: PNode): PNode = proc pred(n: PNode): PNode =
@ -234,7 +251,7 @@ proc canon*(n: PNode): PNode =
result.sons[i] = canon(n.sons[i]) result.sons[i] = canon(n.sons[i])
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.ast.getMagic in (someEq + someAdd + someMul + someMin +
someMax + someHigh + {mUnaryLt} + someSub + someLen): someMax + someHigh + {mUnaryLt} + someSub + someLen + someDiv):
result = n.sym.ast.copyTree result = n.sym.ast.copyTree
else: else:
result = n result = n
@ -248,7 +265,7 @@ proc canon*(n: PNode): PNode =
# high == len+(-1) # high == len+(-1)
result = opAdd.buildCall(opLen.buildCall(result[1]), minusOne()) result = opAdd.buildCall(opLen.buildCall(result[1]), minusOne())
of mUnaryLt: of mUnaryLt:
result = buildCall(opAdd, result[1], newIntNode(nkIntLit, -1)) result = buildCall(opAdd, result[1], minusOne())
of someSub: of someSub:
# x - 4 --> x + (-4) # x - 4 --> x + (-4)
result = negate(result[1], result[2], result) result = negate(result[1], result[2], result)
@ -294,6 +311,16 @@ proc canon*(n: PNode): PNode =
if plus != nil and not isLetLocation(x, true): if plus != nil and not isLetLocation(x, true):
result = buildCall(result[0].sym, plus, y[1]) result = buildCall(result[0].sym, plus, y[1])
else: discard else: discard
elif x.isValue and y.getMagic in someAdd and y[2].isValue:
# 0 <= a.len + 3
# -3 <= a.len
result.sons[1] = x |-| y[2]
result.sons[2] = y[1]
elif x.isValue and y.getMagic in someSub and y[2].isValue:
# 0 <= a.len - 3
# 3 <= a.len
result.sons[1] = x |+| y[2]
result.sons[2] = y[1]
else: discard else: discard
proc `+@`*(a: PNode; b: BiggestInt): PNode = proc `+@`*(a: PNode; b: BiggestInt): PNode =
@ -313,6 +340,9 @@ proc usefulFact(n: PNode): PNode =
if isLetLocation(n.sons[1], true) or isLetLocation(n.sons[2], true): if isLetLocation(n.sons[1], true) or isLetLocation(n.sons[2], true):
# XXX algebraic simplifications! 'i-1 < a.len' --> 'i < a.len+1' # XXX algebraic simplifications! 'i-1 < a.len' --> 'i < a.len+1'
result = n result = n
elif n[1].getMagic in someLen or n[2].getMagic in someLen:
# XXX Rethink this whole idea of 'usefulFact' for semparallel
result = n
of mIsNil: of mIsNil:
if isLetLocation(n.sons[1], false) or isVar(n.sons[1]): if isLetLocation(n.sons[1], false) or isVar(n.sons[1]):
result = n result = n
@ -366,8 +396,8 @@ proc usefulFact(n: PNode): PNode =
type type
TModel* = seq[PNode] # the "knowledge base" TModel* = seq[PNode] # the "knowledge base"
proc addFact*(m: var TModel, n: PNode) = proc addFact*(m: var TModel, nn: PNode) =
let n = usefulFact(n) let n = usefulFact(nn)
if n != nil: m.add n if n != nil: m.add n
proc addFactNeg*(m: var TModel, n: PNode) = proc addFactNeg*(m: var TModel, n: PNode) =
@ -697,10 +727,57 @@ proc simpleSlice*(a, b: PNode): BiggestInt =
else: else:
result = -1 result = -1
template isMul(x): expr = x.getMagic in someMul
template isDiv(x): expr = x.getMagic in someDiv
template isAdd(x): expr = x.getMagic in someAdd
template isSub(x): expr = x.getMagic in someSub
template isVal(x): expr = x.kind in {nkCharLit..nkUInt64Lit}
template isIntVal(x, y): expr = x.intVal == y
import macros
macro `=~`(x: PNode, pat: untyped): bool =
proc m(x, pat, conds: NimNode) =
case pat.kind
of nnkInfix:
case $pat[0]
of "*": conds.add getAst(isMul(x))
of "/": conds.add getAst(isDiv(x))
of "+": conds.add getAst(isAdd(x))
of "-": conds.add getAst(isSub(x))
else:
error("invalid pattern")
m(newTree(nnkBracketExpr, x, newLit(1)), pat[1], conds)
m(newTree(nnkBracketExpr, x, newLit(2)), pat[2], conds)
of nnkPar:
if pat.len == 1:
m(x, pat[0], conds)
else:
error("invalid pattern")
of nnkIdent:
let c = newTree(nnkStmtListExpr, newLetStmt(pat, x))
conds.add c
if ($pat)[^1] == 'c': c.add(getAst(isVal(pat)))
else: c.add bindSym"true"
of nnkIntLit:
conds.add(getAst(isIntVal(pat.intVal)))
else:
error("invalid pattern")
var conds = newTree(nnkBracket)
m(x, pat, conds)
result = nestList(!"and", conds)
proc isMinusOne(n: PNode): bool =
n.kind in {nkCharLit..nkUInt64Lit} and n.intVal == -1
proc pleViaModel(model: TModel; aa, bb: PNode): TImplication proc pleViaModel(model: TModel; aa, bb: PNode): TImplication
proc ple(m: TModel; a, b: PNode): TImplication = proc ple(m: TModel; a, b: PNode): TImplication =
template `<=?`(a,b): expr = ple(m,a,b) == impYes template `<=?`(a,b): expr = ple(m,a,b) == impYes
template `>=?`(a,b): expr = ple(m, nkIntLit.newIntNode(b), a) == impYes
# 0 <= 3 # 0 <= 3
if a.isValue and b.isValue: if a.isValue and b.isValue:
@ -721,6 +798,7 @@ proc ple(m: TModel; a, b: PNode): TImplication =
if a.intVal <= 0: return impYes if a.intVal <= 0: return impYes
# x <= y+c if 0 <= c and x <= y # x <= y+c if 0 <= c and x <= y
# x <= y+(-c) if c <= 0 and y >= x
if b.getMagic in someAdd and zero() <=? b[2] and a <=? b[1]: return impYes if b.getMagic in someAdd and zero() <=? b[2] and a <=? b[1]: return impYes
# x+c <= y if c <= 0 and x <= y # x+c <= y if c <= 0 and x <= y
@ -730,10 +808,44 @@ proc ple(m: TModel; a, b: PNode): TImplication =
if b.getMagic in someMul: if b.getMagic in someMul:
if a <=? b[1] and one() <=? b[2] and zero() <=? b[1]: return impYes if a <=? b[1] and one() <=? b[2] and zero() <=? b[1]: return impYes
if a.getMagic in someMul and a[2].isValue and a[1].getMagic in someDiv and
a[1][2].isValue:
# simplify (x div 4) * 2 <= y to x div (c div d) <= y
if ple(m, buildCall(opDiv, a[1][1], `|div|`(a[1][2], a[2])), b) == impYes:
return impYes
# x*3 + x == x*4. It follows that:
# x*3 + y <= x*4 if y <= x and 3 <= 4
if a =~ x*dc + y and b =~ x2*ec:
if sameTree(x, x2):
let ec1 = opAdd.buildCall(ec, minusOne())
if x >=? 1 and ec >=? 1 and dc >=? 1 and dc <=? ec1 and y <=? x:
return impYes
elif a =~ x*dc and b =~ x2*ec + y:
#echo "BUG cam ehrer e ", a, " <=? ", b
if sameTree(x, x2):
let ec1 = opAdd.buildCall(ec, minusOne())
if x >=? 1 and ec >=? 1 and dc >=? 1 and dc <=? ec1 and y <=? zero():
return impYes
# x+c <= x+d if c <= d. Same for *, - etc.
if a.getMagic in someBinaryOp and a.getMagic == b.getMagic:
if sameTree(a[1], b[1]) and a[2] <=? b[2]: return impYes
elif sameTree(a[2], b[2]) and a[1] <=? b[1]: return impYes
# x div c <= y if 1 <= c and 0 <= y and x <= y: # x div c <= y if 1 <= c and 0 <= y and x <= y:
if a.getMagic in someDiv: if a.getMagic in someDiv:
if one() <=? a[2] and zero() <=? b and a[1] <=? b: return impYes if one() <=? a[2] and zero() <=? b and a[1] <=? b: return impYes
# x div c <= x div d if d <= c
if b.getMagic in someDiv:
if sameTree(a[1], b[1]) and b[2] <=? a[2]: return impYes
# x div z <= x - 1 if z <= x
if a[2].isValue and b.getMagic in someAdd and b[2].isMinusOne:
if a[2] <=? a[1] and sameTree(a[1], b[1]): return impYes
# slightly subtle: # slightly subtle:
# x <= max(y, z) iff x <= y or x <= z # x <= max(y, z) iff x <= y or x <= z
# note that 'x <= max(x, z)' is a special case of the above rule # note that 'x <= max(x, z)' is a special case of the above rule
@ -769,11 +881,19 @@ proc pleViaModelRec(m: var TModel; a, b: PNode): TImplication =
for i in 0..m.high: for i in 0..m.high:
let fact = m[i] let fact = m[i]
if fact != nil and fact.getMagic in someLe: if fact != nil and fact.getMagic in someLe:
# x <= y implies a <= b if a <= x and y <= b
let x = fact[1]
let y = fact[2]
# mark as used: # mark as used:
m[i] = nil m[i] = nil
# i <= len-100
# i <=? len-1
# --> true if (len-100) <= (len-1)
let x = fact[1]
let y = fact[2]
if sameTree(x, a) and y.getMagic in someAdd and b.getMagic in someAdd and
sameTree(y[1], b[1]):
if ple(m, b[2], y[2]) == impYes:
return impYes
# x <= y implies a <= b if a <= x and y <= b
if ple(m, a, x) == impYes: if ple(m, a, x) == impYes:
if ple(m, y, b) == impYes: if ple(m, y, b) == impYes:
return impYes return impYes

View file

@ -28,7 +28,7 @@ proc evalPattern(c: PContext, n, orig: PNode): PNode =
else: else:
result = semDirectOp(c, n, {}) result = semDirectOp(c, n, {})
if optHints in gOptions and hintPattern in gNotes: if optHints in gOptions and hintPattern in gNotes:
message(orig.info, hintPattern, rule & " --> '" & message(orig.info, hintPattern, rule & " --> '" &
renderTree(result, {renderNoComments}) & "'") renderTree(result, {renderNoComments}) & "'")
proc applyPatterns(c: PContext, n: PNode): PNode = proc applyPatterns(c: PContext, n: PNode): PNode =
@ -68,7 +68,7 @@ proc hlo(c: PContext, n: PNode): PNode =
result = n result = n
else: else:
if n.kind in {nkFastAsgn, nkAsgn, nkIdentDefs, nkVarTuple} and if n.kind in {nkFastAsgn, nkAsgn, nkIdentDefs, nkVarTuple} and
n.sons[0].kind == nkSym and n.sons[0].kind == nkSym and
{sfGlobal, sfPure} * n.sons[0].sym.flags == {sfGlobal, sfPure}: {sfGlobal, sfPure} * n.sons[0].sym.flags == {sfGlobal, sfPure}:
# do not optimize 'var g {.global} = re(...)' again! # do not optimize 'var g {.global} = re(...)' again!
return n return n

View file

@ -11,19 +11,19 @@
# An identifier is a shared immutable string that can be compared by its # An identifier is a shared immutable string that can be compared by its
# id. This module is essential for the compiler's performance. # id. This module is essential for the compiler's performance.
import import
hashes, strutils hashes, strutils, etcpriv
type type
TIdObj* = object of RootObj TIdObj* = object of RootObj
id*: int # unique id; use this for comparisons and not the pointers id*: int # unique id; use this for comparisons and not the pointers
PIdObj* = ref TIdObj PIdObj* = ref TIdObj
PIdent* = ref TIdent PIdent* = ref TIdent
TIdent*{.acyclic.} = object of TIdObj TIdent*{.acyclic.} = object of TIdObj
s*: string s*: string
next*: PIdent # for hash-table chaining next*: PIdent # for hash-table chaining
h*: THash # hash value of s h*: Hash # hash value of s
var firstCharIsCS*: bool = true var firstCharIsCS*: bool = true
var buckets*: array[0..4096 * 2 - 1, PIdent] var buckets*: array[0..4096 * 2 - 1, PIdent]
@ -37,18 +37,20 @@ proc cmpIgnoreStyle(a, b: cstring, blen: int): int =
while j < blen: while j < blen:
while a[i] == '_': inc(i) while a[i] == '_': inc(i)
while b[j] == '_': inc(j) while b[j] == '_': inc(j)
while isMagicIdentSeparatorRune(a, i): inc(i, magicIdentSeparatorRuneByteWidth)
while isMagicIdentSeparatorRune(b, j): inc(j, magicIdentSeparatorRuneByteWidth)
# tolower inlined: # tolower inlined:
var aa = a[i] var aa = a[i]
var bb = b[j] var bb = b[j]
if aa >= 'A' and aa <= 'Z': aa = chr(ord(aa) + (ord('a') - ord('A'))) if aa >= 'A' and aa <= 'Z': aa = chr(ord(aa) + (ord('a') - ord('A')))
if bb >= 'A' and bb <= 'Z': bb = chr(ord(bb) + (ord('a') - ord('A'))) if bb >= 'A' and bb <= 'Z': bb = chr(ord(bb) + (ord('a') - ord('A')))
result = ord(aa) - ord(bb) result = ord(aa) - ord(bb)
if (result != 0) or (aa == '\0'): break if (result != 0) or (aa == '\0'): break
inc(i) inc(i)
inc(j) inc(j)
if result == 0: if result == 0:
if a[i] != '\0': result = 1 if a[i] != '\0': result = 1
proc cmpExact(a, b: cstring, blen: int): int = proc cmpExact(a, b: cstring, blen: int): int =
var i = 0 var i = 0
var j = 0 var j = 0
@ -57,27 +59,27 @@ proc cmpExact(a, b: cstring, blen: int): int =
var aa = a[i] var aa = a[i]
var bb = b[j] var bb = b[j]
result = ord(aa) - ord(bb) result = ord(aa) - ord(bb)
if (result != 0) or (aa == '\0'): break if (result != 0) or (aa == '\0'): break
inc(i) inc(i)
inc(j) inc(j)
if result == 0: if result == 0:
if a[i] != '\0': result = 1 if a[i] != '\0': result = 1
var wordCounter = 1 var wordCounter = 1
proc getIdent*(identifier: cstring, length: int, h: THash): PIdent = proc getIdent*(identifier: cstring, length: int, h: Hash): PIdent =
var idx = h and high(buckets) var idx = h and high(buckets)
result = buckets[idx] result = buckets[idx]
var last: PIdent = nil var last: PIdent = nil
var id = 0 var id = 0
while result != nil: while result != nil:
if cmpExact(cstring(result.s), identifier, length) == 0: if cmpExact(cstring(result.s), identifier, length) == 0:
if last != nil: if last != nil:
# make access to last looked up identifier faster: # make access to last looked up identifier faster:
last.next = result.next last.next = result.next
result.next = buckets[idx] result.next = buckets[idx]
buckets[idx] = result buckets[idx] = result
return return
elif cmpIgnoreStyle(cstring(result.s), identifier, length) == 0: elif cmpIgnoreStyle(cstring(result.s), identifier, length) == 0:
assert((id == 0) or (id == result.id)) assert((id == 0) or (id == result.id))
id = result.id id = result.id
@ -89,20 +91,20 @@ proc getIdent*(identifier: cstring, length: int, h: THash): PIdent =
for i in countup(0, length - 1): result.s[i] = identifier[i] for i in countup(0, length - 1): result.s[i] = identifier[i]
result.next = buckets[idx] result.next = buckets[idx]
buckets[idx] = result buckets[idx] = result
if id == 0: if id == 0:
inc(wordCounter) inc(wordCounter)
result.id = -wordCounter result.id = -wordCounter
else: else:
result.id = id result.id = id
proc getIdent*(identifier: string): PIdent = proc getIdent*(identifier: string): PIdent =
result = getIdent(cstring(identifier), len(identifier), result = getIdent(cstring(identifier), len(identifier),
hashIgnoreStyle(identifier)) hashIgnoreStyle(identifier))
proc getIdent*(identifier: string, h: THash): PIdent = proc getIdent*(identifier: string, h: Hash): PIdent =
result = getIdent(cstring(identifier), len(identifier), h) result = getIdent(cstring(identifier), len(identifier), h)
proc identEq*(id: PIdent, name: string): bool = proc identEq*(id: PIdent, name: string): bool =
result = id.id == getIdent(name).id result = id.id == getIdent(name).id
var idAnon* = getIdent":anonymous" var idAnon* = getIdent":anonymous"

View file

@ -18,26 +18,26 @@ const
when debugIds: when debugIds:
import intsets import intsets
var usedIds = initIntSet() var usedIds = initIntSet()
proc registerID*(id: PIdObj) = proc registerID*(id: PIdObj) =
when debugIds: when debugIds:
if id.id == -1 or containsOrIncl(usedIds, id.id): if id.id == -1 or containsOrIncl(usedIds, id.id):
internalError("ID already used: " & $id.id) internalError("ID already used: " & $id.id)
proc getID*(): int {.inline.} = proc getID*(): int {.inline.} =
result = gFrontEndId result = gFrontEndId
inc(gFrontEndId) inc(gFrontEndId)
proc backendId*(): int {.inline.} = proc backendId*(): int {.inline.} =
result = gBackendId result = gBackendId
inc(gBackendId) inc(gBackendId)
proc setId*(id: int) {.inline.} = proc setId*(id: int) {.inline.} =
gFrontEndId = max(gFrontEndId, id + 1) gFrontEndId = max(gFrontEndId, id + 1)
proc idSynchronizationPoint*(idRange: int) = proc idSynchronizationPoint*(idRange: int) =
gFrontEndId = (gFrontEndId div idRange + 1) * idRange + 1 gFrontEndId = (gFrontEndId div idRange + 1) * idRange + 1
proc toGid(f: string): string = proc toGid(f: string): string =
@ -48,10 +48,10 @@ proc toGid(f: string): string =
proc saveMaxIds*(project: string) = proc saveMaxIds*(project: string) =
var f = open(project.toGid, fmWrite) var f = open(project.toGid, fmWrite)
f.writeln($gFrontEndId) f.writeLine($gFrontEndId)
f.writeln($gBackendId) f.writeLine($gBackendId)
f.close() f.close()
proc loadMaxIds*(project: string) = proc loadMaxIds*(project: string) =
var f: File var f: File
if open(f, project.toGid, fmRead): if open(f, project.toGid, fmRead):

View file

@ -121,7 +121,7 @@ proc importAllSymbolsExcept(c: PContext, fromMod: PSym, exceptSet: IntSet) =
if s.kind != skEnumField: if s.kind != skEnumField:
if s.kind notin ExportableSymKinds: if s.kind notin ExportableSymKinds:
internalError(s.info, "importAllSymbols: " & $s.kind) internalError(s.info, "importAllSymbols: " & $s.kind)
if exceptSet.empty or s.name.id notin exceptSet: if exceptSet.isNil or s.name.id notin exceptSet:
rawImportSymbol(c, s) rawImportSymbol(c, s)
s = nextIter(i, fromMod.tab) s = nextIter(i, fromMod.tab)
@ -138,7 +138,7 @@ proc importForwarded(c: PContext, n: PNode, exceptSet: IntSet) =
let s = a.sym let s = a.sym
if s.kind == skModule: if s.kind == skModule:
importAllSymbolsExcept(c, s, exceptSet) importAllSymbolsExcept(c, s, exceptSet)
elif exceptSet.empty or s.name.id notin exceptSet: elif exceptSet.isNil or s.name.id notin exceptSet:
rawImportSymbol(c, s) rawImportSymbol(c, s)
of nkExportExceptStmt: of nkExportExceptStmt:
localError(n.info, errGenerated, "'export except' not implemented") localError(n.info, errGenerated, "'export except' not implemented")

View file

@ -6,7 +6,7 @@ Name: "Nim"
Version: "$version" Version: "$version"
Platforms: """ Platforms: """
windows: i386;amd64 windows: i386;amd64
linux: i386;amd64;powerpc64;arm;sparc;mips;powerpc linux: i386;amd64;powerpc64;arm;sparc;mips;mipsel;powerpc;powerpc64el;arm64
macosx: i386;amd64;powerpc64 macosx: i386;amd64;powerpc64
solaris: i386;amd64;sparc solaris: i386;amd64;sparc
freebsd: i386;amd64 freebsd: i386;amd64
@ -99,9 +99,13 @@ Files: "lib/pure/concurrency/*.nim"
Files: "lib/pure/unidecode/*.nim" Files: "lib/pure/unidecode/*.nim"
Files: "lib/pure/concurrency/*.cfg" Files: "lib/pure/concurrency/*.cfg"
Files: "lib/impure/*.nim" Files: "lib/impure/*.nim"
Files: "lib/impure/nre/private/*.nim"
Files: "lib/wrappers/*.nim" Files: "lib/wrappers/*.nim"
Files: "lib/wrappers/readline/*.nim" Files: "lib/wrappers/readline/*.nim"
Files: "lib/wrappers/linenoise/*.nim"
Files: "lib/wrappers/linenoise/*.c"
Files: "lib/wrappers/linenoise/*.h"
Files: "lib/wrappers/sdl/*.nim" Files: "lib/wrappers/sdl/*.nim"
Files: "lib/wrappers/zip/*.nim" Files: "lib/wrappers/zip/*.nim"
Files: "lib/wrappers/zip/libzip_all.c" Files: "lib/wrappers/zip/libzip_all.c"
@ -111,6 +115,9 @@ Files: "lib/posix/*.nim"
Files: "lib/js/*.nim" Files: "lib/js/*.nim"
Files: "lib/packages/docutils/*.nim" Files: "lib/packages/docutils/*.nim"
Files: "lib/deprecated/core/*.nim"
Files: "lib/deprecated/pure/*.nim"
Files: "lib/deprecated/pure/*.cfg"
[Other] [Other]
Files: "examples/*.nim" Files: "examples/*.nim"
@ -203,7 +210,11 @@ Files: "tests/stdlib/*.nim"
Files: "tests/system/*.nim" Files: "tests/system/*.nim"
Files: "tests/template/*.nim" Files: "tests/template/*.nim"
Files: "tests/testament/*.nim" Files: "tests/testament/*.nim"
Files: "tests/testdata/*.nim" Files: "tests/testdata/*.csv"
Files: "tests/testdata/*.html"
Files: "tests/testdata/*.json"
Files: "tests/testdata/*.txt"
Files: "tests/testdata/*.xml"
Files: "tests/threads/*.nim" Files: "tests/threads/*.nim"
Files: "tests/threads/*.cfg" Files: "tests/threads/*.cfg"
Files: "tests/trmacros/*.nim" Files: "tests/trmacros/*.nim"
@ -236,6 +247,7 @@ BinPath: r"bin;dist\mingw\bin;dist"
; Section | dir | zipFile | size hint (in KB) | url | exe start menu entry ; Section | dir | zipFile | size hint (in KB) | url | exe start menu entry
Download: r"Documentation|doc|docs.zip|13824|http://nim-lang.org/download/docs-${version}.zip|overview.html" Download: r"Documentation|doc|docs.zip|13824|http://nim-lang.org/download/docs-${version}.zip|overview.html"
Download: r"C Compiler (MingW)|dist|mingw.zip|82944|http://nim-lang.org/download/${mingw}.zip" Download: r"C Compiler (MingW)|dist|mingw.zip|82944|http://nim-lang.org/download/${mingw}.zip"
Download: r"Support DLL's|bin|nim_dlls.zip|479|http://nim-lang.org/download/dlls.zip"
Download: r"Aporia IDE|dist|aporia.zip|97997|http://nim-lang.org/download/aporia-0.3.0.zip|aporia-0.3.0\bin\aporia.exe" Download: r"Aporia IDE|dist|aporia.zip|97997|http://nim-lang.org/download/aporia-0.3.0.zip|aporia-0.3.0\bin\aporia.exe"
; for now only NSIS supports optional downloads ; for now only NSIS supports optional downloads

View file

@ -30,8 +30,8 @@ implements the required case distinction.
import import
ast, astalgo, strutils, hashes, trees, platform, magicsys, extccomp, ast, astalgo, strutils, hashes, trees, platform, magicsys, extccomp, options,
options, nversion, nimsets, msgs, crc, bitsets, idents, lists, types, os, nversion, nimsets, msgs, securehash, bitsets, idents, lists, types, os,
times, ropes, math, passes, ccgutils, wordrecg, renderer, rodread, rodutils, times, ropes, math, passes, ccgutils, wordrecg, renderer, rodread, rodutils,
intsets, cgmeth, lowerings intsets, cgmeth, lowerings
@ -136,7 +136,7 @@ proc mapType(typ: PType): TJSTypeKind =
result = etyBaseIndex result = etyBaseIndex
of tyPointer: of tyPointer:
# treat a tyPointer like a typed pointer to an array of bytes # treat a tyPointer like a typed pointer to an array of bytes
result = etyInt result = etyBaseIndex
of tyRange, tyDistinct, tyOrdinal, tyConst, tyMutable, tyIter, tyProxy: of tyRange, tyDistinct, tyOrdinal, tyConst, tyMutable, tyIter, tyProxy:
result = mapType(t.sons[0]) result = mapType(t.sons[0])
of tyInt..tyInt64, tyUInt..tyUInt64, tyEnum, tyChar: result = etyInt of tyInt..tyInt64, tyUInt..tyUInt64, tyEnum, tyChar: result = etyInt
@ -197,10 +197,10 @@ proc isSimpleExpr(n: PNode): bool =
elif n.isAtom: elif n.isAtom:
result = true result = true
proc getTemp(p: PProc): Rope = proc getTemp(p: PProc, defineInLocals: bool = true): Rope =
inc(p.unique) inc(p.unique)
result = "Tmp$1" % [rope(p.unique)] result = "Tmp$1" % [rope(p.unique)]
addf(p.locals, "var $1;$n" | "local $1;$n", [result]) if defineInLocals: addf(p.locals, "var $1;$n" | "local $1;$n", [result])
proc genAnd(p: PProc, a, b: PNode, r: var TCompRes) = proc genAnd(p: PProc, a, b: PNode, r: var TCompRes) =
assert r.kind == resNone assert r.kind == resNone
@ -258,11 +258,6 @@ const # magic checked op; magic unchecked op; checked op; unchecked op
["mulInt", "", "mulInt($1, $2)", "($1 * $2)"], # MulI ["mulInt", "", "mulInt($1, $2)", "($1 * $2)"], # MulI
["divInt", "", "divInt($1, $2)", "Math.floor($1 / $2)"], # DivI ["divInt", "", "divInt($1, $2)", "Math.floor($1 / $2)"], # DivI
["modInt", "", "modInt($1, $2)", "Math.floor($1 % $2)"], # ModI ["modInt", "", "modInt($1, $2)", "Math.floor($1 % $2)"], # ModI
["addInt64", "", "addInt64($1, $2)", "($1 + $2)"], # AddI64
["subInt64", "", "subInt64($1, $2)", "($1 - $2)"], # SubI64
["mulInt64", "", "mulInt64($1, $2)", "($1 * $2)"], # MulI64
["divInt64", "", "divInt64($1, $2)", "Math.floor($1 / $2)"], # DivI64
["modInt64", "", "modInt64($1, $2)", "Math.floor($1 % $2)"], # ModI64
["addInt", "", "addInt($1, $2)", "($1 + $2)"], # Succ ["addInt", "", "addInt($1, $2)", "($1 + $2)"], # Succ
["subInt", "", "subInt($1, $2)", "($1 - $2)"], # Pred ["subInt", "", "subInt($1, $2)", "($1 - $2)"], # Pred
["", "", "($1 + $2)", "($1 + $2)"], # AddF64 ["", "", "($1 + $2)", "($1 + $2)"], # AddF64
@ -276,11 +271,6 @@ const # magic checked op; magic unchecked op; checked op; unchecked op
["", "", "($1 ^ $2)", "($1 ^ $2)"], # BitxorI ["", "", "($1 ^ $2)", "($1 ^ $2)"], # BitxorI
["nimMin", "nimMin", "nimMin($1, $2)", "nimMin($1, $2)"], # MinI ["nimMin", "nimMin", "nimMin($1, $2)", "nimMin($1, $2)"], # MinI
["nimMax", "nimMax", "nimMax($1, $2)", "nimMax($1, $2)"], # MaxI ["nimMax", "nimMax", "nimMax($1, $2)", "nimMax($1, $2)"], # MaxI
["", "", "($1 >>> $2)", "($1 >>> $2)"], # ShrI64
["", "", "($1 << $2)", "($1 << $2)"], # ShlI64
["", "", "($1 & $2)", "($1 & $2)"], # BitandI64
["", "", "($1 | $2)", "($1 | $2)"], # BitorI64
["", "", "($1 ^ $2)", "($1 ^ $2)"], # BitxorI64
["nimMin", "nimMin", "nimMin($1, $2)", "nimMin($1, $2)"], # MinF64 ["nimMin", "nimMin", "nimMin($1, $2)", "nimMin($1, $2)"], # MinF64
["nimMax", "nimMax", "nimMax($1, $2)", "nimMax($1, $2)"], # MaxF64 ["nimMax", "nimMax", "nimMax($1, $2)", "nimMax($1, $2)"], # MaxF64
["addU", "addU", "addU($1, $2)", "addU($1, $2)"], # addU ["addU", "addU", "addU($1, $2)", "addU($1, $2)"], # addU
@ -291,9 +281,6 @@ const # magic checked op; magic unchecked op; checked op; unchecked op
["", "", "($1 == $2)", "($1 == $2)"], # EqI ["", "", "($1 == $2)", "($1 == $2)"], # EqI
["", "", "($1 <= $2)", "($1 <= $2)"], # LeI ["", "", "($1 <= $2)", "($1 <= $2)"], # LeI
["", "", "($1 < $2)", "($1 < $2)"], # LtI ["", "", "($1 < $2)", "($1 < $2)"], # LtI
["", "", "($1 == $2)", "($1 == $2)"], # EqI64
["", "", "($1 <= $2)", "($1 <= $2)"], # LeI64
["", "", "($1 < $2)", "($1 < $2)"], # LtI64
["", "", "($1 == $2)", "($1 == $2)"], # EqF64 ["", "", "($1 == $2)", "($1 == $2)"], # EqF64
["", "", "($1 <= $2)", "($1 <= $2)"], # LeF64 ["", "", "($1 <= $2)", "($1 <= $2)"], # LeF64
["", "", "($1 < $2)", "($1 < $2)"], # LtF64 ["", "", "($1 < $2)", "($1 < $2)"], # LtF64
@ -320,11 +307,9 @@ const # magic checked op; magic unchecked op; checked op; unchecked op
["negInt", "", "negInt($1)", "-($1)"], # UnaryMinusI ["negInt", "", "negInt($1)", "-($1)"], # UnaryMinusI
["negInt64", "", "negInt64($1)", "-($1)"], # UnaryMinusI64 ["negInt64", "", "negInt64($1)", "-($1)"], # UnaryMinusI64
["absInt", "", "absInt($1)", "Math.abs($1)"], # AbsI ["absInt", "", "absInt($1)", "Math.abs($1)"], # AbsI
["absInt64", "", "absInt64($1)", "Math.abs($1)"], # AbsI64
["", "", "!($1)", "!($1)"], # Not ["", "", "!($1)", "!($1)"], # Not
["", "", "+($1)", "+($1)"], # UnaryPlusI ["", "", "+($1)", "+($1)"], # UnaryPlusI
["", "", "~($1)", "~($1)"], # BitnotI ["", "", "~($1)", "~($1)"], # BitnotI
["", "", "~($1)", "~($1)"], # BitnotI64
["", "", "+($1)", "+($1)"], # UnaryPlusF64 ["", "", "+($1)", "+($1)"], # UnaryPlusF64
["", "", "-($1)", "-($1)"], # UnaryMinusF64 ["", "", "-($1)", "-($1)"], # UnaryMinusF64
["", "", "Math.abs($1)", "Math.abs($1)"], # AbsF64 ["", "", "Math.abs($1)", "Math.abs($1)"], # AbsF64
@ -357,11 +342,6 @@ const # magic checked op; magic unchecked op; checked op; unchecked op
["mulInt", "", "mulInt($1, $2)", "($1 * $2)"], # MulI ["mulInt", "", "mulInt($1, $2)", "($1 * $2)"], # MulI
["divInt", "", "divInt($1, $2)", "Math.floor($1 / $2)"], # DivI ["divInt", "", "divInt($1, $2)", "Math.floor($1 / $2)"], # DivI
["modInt", "", "modInt($1, $2)", "Math.floor($1 % $2)"], # ModI ["modInt", "", "modInt($1, $2)", "Math.floor($1 % $2)"], # ModI
["addInt64", "", "addInt64($1, $2)", "($1 + $2)"], # AddI64
["subInt64", "", "subInt64($1, $2)", "($1 - $2)"], # SubI64
["mulInt64", "", "mulInt64($1, $2)", "($1 * $2)"], # MulI64
["divInt64", "", "divInt64($1, $2)", "Math.floor($1 / $2)"], # DivI64
["modInt64", "", "modInt64($1, $2)", "Math.floor($1 % $2)"], # ModI64
["addInt", "", "addInt($1, $2)", "($1 + $2)"], # Succ ["addInt", "", "addInt($1, $2)", "($1 + $2)"], # Succ
["subInt", "", "subInt($1, $2)", "($1 - $2)"], # Pred ["subInt", "", "subInt($1, $2)", "($1 - $2)"], # Pred
["", "", "($1 + $2)", "($1 + $2)"], # AddF64 ["", "", "($1 + $2)", "($1 + $2)"], # AddF64
@ -375,11 +355,6 @@ const # magic checked op; magic unchecked op; checked op; unchecked op
["", "", "($1 ^ $2)", "($1 ^ $2)"], # BitxorI ["", "", "($1 ^ $2)", "($1 ^ $2)"], # BitxorI
["nimMin", "nimMin", "nimMin($1, $2)", "nimMin($1, $2)"], # MinI ["nimMin", "nimMin", "nimMin($1, $2)", "nimMin($1, $2)"], # MinI
["nimMax", "nimMax", "nimMax($1, $2)", "nimMax($1, $2)"], # MaxI ["nimMax", "nimMax", "nimMax($1, $2)", "nimMax($1, $2)"], # MaxI
["", "", "($1 >>> $2)", "($1 >>> $2)"], # ShrI64
["", "", "($1 << $2)", "($1 << $2)"], # ShlI64
["", "", "($1 & $2)", "($1 & $2)"], # BitandI64
["", "", "($1 | $2)", "($1 | $2)"], # BitorI64
["", "", "($1 ^ $2)", "($1 ^ $2)"], # BitxorI64
["nimMin", "nimMin", "nimMin($1, $2)", "nimMin($1, $2)"], # MinF64 ["nimMin", "nimMin", "nimMin($1, $2)", "nimMin($1, $2)"], # MinF64
["nimMax", "nimMax", "nimMax($1, $2)", "nimMax($1, $2)"], # MaxF64 ["nimMax", "nimMax", "nimMax($1, $2)", "nimMax($1, $2)"], # MaxF64
["addU", "addU", "addU($1, $2)", "addU($1, $2)"], # addU ["addU", "addU", "addU($1, $2)", "addU($1, $2)"], # addU
@ -390,9 +365,6 @@ const # magic checked op; magic unchecked op; checked op; unchecked op
["", "", "($1 == $2)", "($1 == $2)"], # EqI ["", "", "($1 == $2)", "($1 == $2)"], # EqI
["", "", "($1 <= $2)", "($1 <= $2)"], # LeI ["", "", "($1 <= $2)", "($1 <= $2)"], # LeI
["", "", "($1 < $2)", "($1 < $2)"], # LtI ["", "", "($1 < $2)", "($1 < $2)"], # LtI
["", "", "($1 == $2)", "($1 == $2)"], # EqI64
["", "", "($1 <= $2)", "($1 <= $2)"], # LeI64
["", "", "($1 < $2)", "($1 < $2)"], # LtI64
["", "", "($1 == $2)", "($1 == $2)"], # EqF64 ["", "", "($1 == $2)", "($1 == $2)"], # EqF64
["", "", "($1 <= $2)", "($1 <= $2)"], # LeF64 ["", "", "($1 <= $2)", "($1 <= $2)"], # LeF64
["", "", "($1 < $2)", "($1 < $2)"], # LtF64 ["", "", "($1 < $2)", "($1 < $2)"], # LtF64
@ -419,11 +391,9 @@ const # magic checked op; magic unchecked op; checked op; unchecked op
["negInt", "", "negInt($1)", "-($1)"], # UnaryMinusI ["negInt", "", "negInt($1)", "-($1)"], # UnaryMinusI
["negInt64", "", "negInt64($1)", "-($1)"], # UnaryMinusI64 ["negInt64", "", "negInt64($1)", "-($1)"], # UnaryMinusI64
["absInt", "", "absInt($1)", "Math.abs($1)"], # AbsI ["absInt", "", "absInt($1)", "Math.abs($1)"], # AbsI
["absInt64", "", "absInt64($1)", "Math.abs($1)"], # AbsI64
["", "", "not ($1)", "not ($1)"], # Not ["", "", "not ($1)", "not ($1)"], # Not
["", "", "+($1)", "+($1)"], # UnaryPlusI ["", "", "+($1)", "+($1)"], # UnaryPlusI
["", "", "~($1)", "~($1)"], # BitnotI ["", "", "~($1)", "~($1)"], # BitnotI
["", "", "~($1)", "~($1)"], # BitnotI64
["", "", "+($1)", "+($1)"], # UnaryPlusF64 ["", "", "+($1)", "+($1)"], # UnaryPlusF64
["", "", "-($1)", "-($1)"], # UnaryMinusF64 ["", "", "-($1)", "-($1)"], # UnaryMinusF64
["", "", "Math.abs($1)", "Math.abs($1)"], # AbsF64 ["", "", "Math.abs($1)", "Math.abs($1)"], # AbsF64
@ -535,12 +505,12 @@ proc moveInto(p: PProc, src: var TCompRes, dest: TCompRes) =
proc genTry(p: PProc, n: PNode, r: var TCompRes) = proc genTry(p: PProc, n: PNode, r: var TCompRes) =
# code to generate: # code to generate:
# #
# var sp = {prev: excHandler, exc: null}; # ++excHandler;
# excHandler = sp;
# try { # try {
# stmts; # stmts;
# TMP = e # } catch (EXC) {
# } catch (e) { # var prevJSError = lastJSError; lastJSError = EXC;
# --excHandler;
# if (e.typ && e.typ == NTI433 || e.typ == NTI2321) { # if (e.typ && e.typ == NTI433 || e.typ == NTI2321) {
# stmts; # stmts;
# } else if (e.typ && e.typ == NTI32342) { # } else if (e.typ && e.typ == NTI32342) {
@ -548,35 +518,41 @@ proc genTry(p: PProc, n: PNode, r: var TCompRes) =
# } else { # } else {
# stmts; # stmts;
# } # }
# lastJSError = prevJSError;
# } finally { # } finally {
# stmts; # stmts;
# excHandler = excHandler.prev;
# } # }
genLineDir(p, n) genLineDir(p, n)
if not isEmptyType(n.typ): if not isEmptyType(n.typ):
r.kind = resVal r.kind = resVal
r.res = getTemp(p) r.res = getTemp(p)
inc(p.unique) inc(p.unique)
var i = 1
var length = sonsLen(n)
var catchBranchesExist = length > 1 and n.sons[i].kind == nkExceptBranch
if catchBranchesExist:
add(p.body, "++excHandler;" & tnl)
var safePoint = "Tmp$1" % [rope(p.unique)] var safePoint = "Tmp$1" % [rope(p.unique)]
addf(p.body, addf(p.body,
"var $1 = {prev: excHandler, exc: null};$nexcHandler = $1;$n" | "" |
"local $1 = pcall(", "local $1 = pcall(",
[safePoint]) [safePoint])
if optStackTrace in p.options: add(p.body, "framePtr = F;" & tnl) if optStackTrace in p.options: add(p.body, "framePtr = F;" & tnl)
addf(p.body, "try {$n" | "function()$n", []) addf(p.body, "try {$n" | "function()$n", [])
var length = sonsLen(n)
var a: TCompRes var a: TCompRes
gen(p, n.sons[0], a) gen(p, n.sons[0], a)
moveInto(p, a, r) moveInto(p, a, r)
var i = 1 var generalCatchBranchExists = false
if p.target == targetJS and length > 1 and n.sons[i].kind == nkExceptBranch: if p.target == targetJS and catchBranchesExist:
addf(p.body, "} catch (EXC) {$n lastJSError = EXC;$n", []) addf(p.body, "} catch (EXC) {$n var prevJSError = lastJSError;$n" &
" lastJSError = EXC;$n --excHandler;$n", [])
elif p.target == targetLua: elif p.target == targetLua:
addf(p.body, "end)$n", []) addf(p.body, "end)$n", [])
while i < length and n.sons[i].kind == nkExceptBranch: while i < length and n.sons[i].kind == nkExceptBranch:
let blen = sonsLen(n.sons[i]) let blen = sonsLen(n.sons[i])
if blen == 1: if blen == 1:
# general except section: # general except section:
generalCatchBranchExists = true
if i > 1: addf(p.body, "else {$n" | "else$n", []) if i > 1: addf(p.body, "else {$n" | "else$n", [])
gen(p, n.sons[i].sons[0], a) gen(p, n.sons[i].sons[0], a)
moveInto(p, a, r) moveInto(p, a, r)
@ -588,17 +564,22 @@ proc genTry(p: PProc, n: PNode, r: var TCompRes) =
if n.sons[i].sons[j].kind != nkType: if n.sons[i].sons[j].kind != nkType:
internalError(n.info, "genTryStmt") internalError(n.info, "genTryStmt")
if orExpr != nil: add(orExpr, "||" | " or ") if orExpr != nil: add(orExpr, "||" | " or ")
addf(orExpr, "isObj($1.exc.m_type, $2)", addf(orExpr, "isObj(lastJSError.m_type, $1)",
[safePoint, genTypeInfo(p, n.sons[i].sons[j].typ)]) [genTypeInfo(p, n.sons[i].sons[j].typ)])
if i > 1: add(p.body, "else ") if i > 1: add(p.body, "else ")
addf(p.body, "if ($1.exc && ($2)) {$n" | "if $1.exc and ($2) then$n", addf(p.body, "if (lastJSError && ($2)) {$n" | "if $1.exc and ($2) then$n",
[safePoint, orExpr]) [safePoint, orExpr])
gen(p, n.sons[i].sons[blen - 1], a) gen(p, n.sons[i].sons[blen - 1], a)
moveInto(p, a, r) moveInto(p, a, r)
addf(p.body, "}$n" | "end$n", []) addf(p.body, "}$n" | "end$n", [])
inc(i) inc(i)
if p.target == targetJS: if p.target == targetJS:
add(p.body, "} finally {" & tnl & "excHandler = excHandler.prev;" & tnl) if catchBranchesExist:
if not generalCatchBranchExists:
useMagic(p, "reraiseException")
add(p.body, "else {" & tnl & "reraiseException();" & tnl & "}" & tnl)
add(p.body, "lastJSError = prevJSError;" & tnl)
add(p.body, "} finally {" & tnl)
if i < length and n.sons[i].kind == nkFinally: if i < length and n.sons[i].kind == nkFinally:
genStmt(p, n.sons[i].sons[0]) genStmt(p, n.sons[i].sons[0])
if p.target == targetJS: if p.target == targetJS:
@ -748,14 +729,13 @@ proc genBreakStmt(p: PProc, n: PNode) =
p.blocks[idx].id = abs(p.blocks[idx].id) # label is used p.blocks[idx].id = abs(p.blocks[idx].id) # label is used
addf(p.body, "break L$1;$n" | "goto ::L$1::;$n", [rope(p.blocks[idx].id)]) addf(p.body, "break L$1;$n" | "goto ::L$1::;$n", [rope(p.blocks[idx].id)])
proc genAsmStmt(p: PProc, n: PNode) = proc genAsmOrEmitStmt(p: PProc, n: PNode) =
genLineDir(p, n) genLineDir(p, n)
assert(n.kind == nkAsmStmt)
for i in countup(0, sonsLen(n) - 1): for i in countup(0, sonsLen(n) - 1):
case n.sons[i].kind case n.sons[i].kind
of nkStrLit..nkTripleStrLit: add(p.body, n.sons[i].strVal) of nkStrLit..nkTripleStrLit: add(p.body, n.sons[i].strVal)
of nkSym: add(p.body, mangleName(n.sons[i].sym)) of nkSym: add(p.body, mangleName(n.sons[i].sym))
else: internalError(n.sons[i].info, "jsgen: genAsmStmt()") else: internalError(n.sons[i].info, "jsgen: genAsmOrEmitStmt()")
proc genIf(p: PProc, n: PNode, r: var TCompRes) = proc genIf(p: PProc, n: PNode, r: var TCompRes) =
var cond, stmt: TCompRes var cond, stmt: TCompRes
@ -810,19 +790,36 @@ proc needsNoCopy(y: PNode): bool =
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
gen(p, x, a) gen(p, x, a)
let xtyp = mapType(x.typ)
if x.kind == nkHiddenDeref and x.sons[0].kind == nkCall and xtyp != etyObject:
gen(p, x.sons[0], a)
let tmp = p.getTemp(false)
addf(p.body, "var $1 = $2;$n", [tmp, a.rdLoc])
a.res = "$1[0][$1[1]]" % [tmp]
else:
gen(p, x, a)
gen(p, y, b) gen(p, y, b)
case mapType(x.typ)
case xtyp
of etyObject: of etyObject:
if needsNoCopy(y) or noCopyNeeded: if (needsNoCopy(y) and needsNoCopy(x)) or noCopyNeeded:
addf(p.body, "$1 = $2;$n", [a.rdLoc, b.rdLoc]) addf(p.body, "$1 = $2;$n", [a.rdLoc, b.rdLoc])
else: else:
useMagic(p, "nimCopy") useMagic(p, "nimCopy")
addf(p.body, "$1 = nimCopy($2, $3);$n", addf(p.body, "nimCopy($1, $2, $3);$n",
[a.res, b.res, genTypeInfo(p, y.typ)]) [a.res, b.res, genTypeInfo(p, y.typ)])
of etyBaseIndex: of etyBaseIndex:
if a.typ != etyBaseIndex or b.typ != etyBaseIndex: if a.typ != etyBaseIndex or b.typ != etyBaseIndex:
internalError(x.info, "genAsgn") if y.kind == nkCall:
addf(p.body, "$1 = $2; $3 = $4;$n", [a.address, b.address, a.res, b.res]) let tmp = p.getTemp(false)
addf(p.body, "var $1 = $4; $2 = $1[0]; $3 = $1[1];$n", [tmp, a.address, a.res, b.rdLoc])
else:
internalError(x.info, "genAsgn")
else:
addf(p.body, "$1 = $2; $3 = $4;$n", [a.address, b.address, a.res, b.res])
else: else:
addf(p.body, "$1 = $2;$n", [a.res, b.res]) addf(p.body, "$1 = $2;$n", [a.res, b.res])
@ -838,11 +835,9 @@ proc genSwap(p: PProc, n: PNode) =
var a, b: TCompRes var a, b: TCompRes
gen(p, n.sons[1], a) gen(p, n.sons[1], a)
gen(p, n.sons[2], b) gen(p, n.sons[2], b)
inc(p.unique) var tmp = p.getTemp(false)
var tmp = "Tmp$1" % [rope(p.unique)]
if mapType(skipTypes(n.sons[1].typ, abstractVar)) == etyBaseIndex: if mapType(skipTypes(n.sons[1].typ, abstractVar)) == etyBaseIndex:
inc(p.unique) let tmp2 = p.getTemp(false)
let tmp2 = "Tmp$1" % [rope(p.unique)]
if a.typ != etyBaseIndex or b.typ != etyBaseIndex: if a.typ != etyBaseIndex or b.typ != etyBaseIndex:
internalError(n.info, "genSwap") internalError(n.info, "genSwap")
addf(p.body, "var $1 = $2; $2 = $3; $3 = $1;$n" | addf(p.body, "var $1 = $2; $2 = $3; $3 = $1;$n" |
@ -880,7 +875,7 @@ proc genFieldAccess(p: PProc, n: PNode, r: var TCompRes) =
if skipTypes(n.sons[0].typ, abstractVarRange).kind == tyTuple: if skipTypes(n.sons[0].typ, abstractVarRange).kind == tyTuple:
r.res = "$1.Field$2" % [r.res, getFieldPosition(n.sons[1]).rope] r.res = "$1.Field$2" % [r.res, getFieldPosition(n.sons[1]).rope]
else: else:
if n.sons[1].kind != nkSym: internalError(n.sons[1].info, "genFieldAddr") if n.sons[1].kind != nkSym: internalError(n.sons[1].info, "genFieldAccess")
var f = n.sons[1].sym var f = n.sons[1].sym
if f.loc.r == nil: f.loc.r = mangleName(f) if f.loc.r == nil: f.loc.r = mangleName(f)
r.res = "$1.$2" % [r.res, f.loc.r] r.res = "$1.$2" % [r.res, f.loc.r]
@ -970,18 +965,35 @@ proc genAddr(p: PProc, n: PNode, r: var TCompRes) =
of nkCheckedFieldExpr: of nkCheckedFieldExpr:
genCheckedFieldAddr(p, n, r) genCheckedFieldAddr(p, n, r)
of nkDotExpr: of nkDotExpr:
genFieldAddr(p, n.sons[0], r) if mapType(n.typ) == etyBaseIndex:
genFieldAddr(p, n.sons[0], r)
else:
genFieldAccess(p, n.sons[0], r)
of nkBracketExpr: of nkBracketExpr:
var ty = skipTypes(n.sons[0].typ, abstractVarRange) var ty = skipTypes(n.sons[0].typ, abstractVarRange)
if ty.kind in {tyRef, tyPtr}: ty = skipTypes(ty.lastSon, abstractVarRange) if ty.kind in MappedToObject:
case ty.kind gen(p, n.sons[0], r)
of tyArray, tyArrayConstr, tyOpenArray, tySequence, tyString, tyCString, else:
tyVarargs, tyChar: let kindOfIndexedExpr = skipTypes(n.sons[0].sons[0].typ, abstractVarRange).kind
genArrayAddr(p, n.sons[0], r) case kindOfIndexedExpr
of tyTuple: of tyArray, tyArrayConstr, tyOpenArray, tySequence, tyString, tyCString,
genFieldAddr(p, n.sons[0], r) tyVarargs:
else: internalError(n.sons[0].info, "expr(nkBracketExpr, " & $ty.kind & ')') genArrayAddr(p, n.sons[0], r)
else: internalError(n.sons[0].info, "genAddr") of tyTuple:
genFieldAddr(p, n.sons[0], r)
else: internalError(n.sons[0].info, "expr(nkBracketExpr, " & $kindOfIndexedExpr & ')')
of nkObjDownConv:
gen(p, n.sons[0], r)
else: internalError(n.sons[0].info, "genAddr: " & $n.sons[0].kind)
proc genProcForSymIfNeeded(p: PProc, s: PSym) =
if not p.g.generatedSyms.containsOrIncl(s.id):
let newp = genProc(p, s)
var owner = p
while owner != nil and owner.prc != s.owner:
owner = owner.up
if owner != nil: add(owner.locals, newp)
else: add(p.g.code, newp)
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
@ -1018,13 +1030,8 @@ proc genSym(p: PProc, n: PNode, r: var TCompRes) =
discard discard
elif sfForward in s.flags: elif sfForward in s.flags:
p.g.forwarded.add(s) p.g.forwarded.add(s)
elif not p.g.generatedSyms.containsOrIncl(s.id): else:
let newp = genProc(p, s) genProcForSymIfNeeded(p, s)
var owner = p
while owner != nil and owner.prc != s.owner:
owner = owner.up
if owner != nil: add(owner.locals, newp)
else: add(p.g.code, newp)
else: else:
if s.loc.r == nil: if s.loc.r == nil:
internalError(n.info, "symbol has no generated name: " & s.name.s) internalError(n.info, "symbol has no generated name: " & s.name.s)
@ -1037,10 +1044,15 @@ proc genDeref(p: PProc, n: PNode, r: var TCompRes) =
else: else:
var a: TCompRes var a: TCompRes
gen(p, n.sons[0], a) gen(p, n.sons[0], a)
if a.typ != etyBaseIndex: internalError(n.info, "genDeref") if a.typ == etyBaseIndex:
r.res = "$1[$2]" % [a.address, a.res] r.res = "$1[$2]" % [a.address, a.res]
elif n.sons[0].kind == nkCall:
let tmp = p.getTemp
r.res = "($1 = $2, $1[0][$1[1]])" % [tmp, a.res]
else:
internalError(n.info, "genDeref")
proc genArg(p: PProc, n: PNode, r: var TCompRes) = proc genArgNoParam(p: PProc, n: PNode, r: var TCompRes) =
var a: TCompRes var a: TCompRes
gen(p, n, a) gen(p, n, a)
if a.typ == etyBaseIndex: if a.typ == etyBaseIndex:
@ -1050,13 +1062,42 @@ proc genArg(p: PProc, n: PNode, r: var TCompRes) =
else: else:
add(r.res, a.res) add(r.res, a.res)
proc genArg(p: PProc, n: PNode, param: PSym, r: var TCompRes) =
var a: TCompRes
gen(p, n, a)
if skipTypes(param.typ, abstractVar).kind in {tyOpenArray, tyVarargs} and
a.typ == etyBaseIndex:
add(r.res, "$1[$2]" % [a.address, a.res])
elif a.typ == etyBaseIndex:
add(r.res, a.address)
add(r.res, ", ")
add(r.res, a.res)
else:
add(r.res, a.res)
proc genArgs(p: PProc, n: PNode, r: var TCompRes) = proc genArgs(p: PProc, n: PNode, r: var TCompRes) =
add(r.res, "(") add(r.res, "(")
var hasArgs = false
var typ = skipTypes(n.sons[0].typ, abstractInst)
assert(typ.kind == tyProc)
assert(sonsLen(typ) == sonsLen(typ.n))
for i in countup(1, sonsLen(n) - 1): for i in countup(1, sonsLen(n) - 1):
let it = n.sons[i] let it = n.sons[i]
if it.typ.isCompileTimeOnly: continue var paramType : PNode = nil
if i > 1: add(r.res, ", ") if i < sonsLen(typ):
genArg(p, it, r) assert(typ.n.sons[i].kind == nkSym)
paramType = typ.n.sons[i]
if paramType.typ.isCompileTimeOnly: continue
if hasArgs: add(r.res, ", ")
if paramType.isNil:
genArgNoParam(p, it, r)
else:
genArg(p, it, paramType.sym, r)
hasArgs = true
add(r.res, ")") add(r.res, ")")
r.kind = resExpr r.kind = resExpr
@ -1080,11 +1121,12 @@ proc genInfixCall(p: PProc, n: PNode, r: var TCompRes) =
add(r.res, "(") add(r.res, "(")
for i in countup(2, sonsLen(n) - 1): for i in countup(2, sonsLen(n) - 1):
if i > 2: add(r.res, ", ") if i > 2: add(r.res, ", ")
genArg(p, n.sons[i], r) genArgNoParam(p, n.sons[i], r)
add(r.res, ")") add(r.res, ")")
r.kind = resExpr r.kind = resExpr
proc genEcho(p: PProc, n: PNode, r: var TCompRes) = proc genEcho(p: PProc, n: PNode, r: var TCompRes) =
useMagic(p, "toJSStr") # Used in rawEcho
useMagic(p, "rawEcho") useMagic(p, "rawEcho")
add(r.res, "rawEcho(") add(r.res, "rawEcho(")
let n = n[1].skipConv let n = n[1].skipConv
@ -1093,7 +1135,7 @@ proc genEcho(p: PProc, n: PNode, r: var TCompRes) =
let it = n.sons[i] let it = n.sons[i]
if it.typ.isCompileTimeOnly: continue if it.typ.isCompileTimeOnly: continue
if i > 0: add(r.res, ", ") if i > 0: add(r.res, ", ")
genArg(p, it, r) genArgNoParam(p, it, r)
add(r.res, ")") add(r.res, ")")
r.kind = resExpr r.kind = resExpr
@ -1102,24 +1144,32 @@ proc putToSeq(s: string, indirect: bool): Rope =
if indirect: result = "[$1]" % [result] if indirect: result = "[$1]" % [result]
proc createVar(p: PProc, typ: PType, indirect: bool): Rope proc createVar(p: PProc, typ: PType, indirect: bool): Rope
proc createRecordVarAux(p: PProc, rec: PNode, c: var int): Rope = proc createRecordVarAux(p: PProc, rec: PNode, excludedFieldIDs: IntSet, output: var Rope) =
result = nil
case rec.kind case rec.kind
of nkRecList: of nkRecList:
for i in countup(0, sonsLen(rec) - 1): for i in countup(0, sonsLen(rec) - 1):
add(result, createRecordVarAux(p, rec.sons[i], c)) createRecordVarAux(p, rec.sons[i], excludedFieldIDs, output)
of nkRecCase: of nkRecCase:
add(result, createRecordVarAux(p, rec.sons[0], c)) createRecordVarAux(p, rec.sons[0], excludedFieldIDs, output)
for i in countup(1, sonsLen(rec) - 1): for i in countup(1, sonsLen(rec) - 1):
add(result, createRecordVarAux(p, lastSon(rec.sons[i]), c)) createRecordVarAux(p, lastSon(rec.sons[i]), excludedFieldIDs, output)
of nkSym: of nkSym:
if c > 0: add(result, ", ") if rec.sym.id notin excludedFieldIDs:
add(result, mangleName(rec.sym)) if output.len > 0: output.add(", ")
add(result, ": ") output.add(mangleName(rec.sym))
add(result, createVar(p, rec.sym.typ, false)) output.add(": ")
inc(c) output.add(createVar(p, rec.sym.typ, false))
else: internalError(rec.info, "createRecordVarAux") else: internalError(rec.info, "createRecordVarAux")
proc createObjInitList(p: PProc, typ: PType, excludedFieldIDs: IntSet, output: var Rope) =
var t = typ
if tfFinal notin t.flags or t.sons[0] != nil:
if output.len > 0: output.add(", ")
addf(output, "m_type: $1" | "m_type = $#", [genTypeInfo(p, t)])
while t != nil:
createRecordVarAux(p, t.n, excludedFieldIDs, output)
t = t.sons[0]
proc createVar(p: PProc, typ: PType, indirect: bool): Rope = proc createVar(p: PProc, typ: PType, indirect: bool): Rope =
var t = skipTypes(typ, abstractInst) var t = skipTypes(typ, abstractInst)
case t.kind case t.kind
@ -1160,15 +1210,9 @@ proc createVar(p: PProc, typ: PType, indirect: bool): Rope =
add(result, "}") add(result, "}")
if indirect: result = "[$1]" % [result] if indirect: result = "[$1]" % [result]
of tyObject: of tyObject:
result = rope("{") var initList : Rope
var c = 0 createObjInitList(p, t, initIntSet(), initList)
if tfFinal notin t.flags or t.sons[0] != nil: result = "{$1}" % [initList]
inc(c)
addf(result, "m_type: $1" | "m_type = $#", [genTypeInfo(p, t)])
while t != nil:
add(result, createRecordVarAux(p, t.n, c))
t = t.sons[0]
add(result, "}")
if indirect: result = "[$1]" % [result] if indirect: result = "[$1]" % [result]
of tyVar, tyPtr, tyRef: of tyVar, tyPtr, tyRef:
if mapType(t) == etyBaseIndex: if mapType(t) == etyBaseIndex:
@ -1197,7 +1241,7 @@ proc genVarInit(p: PProc, v: PSym, n: PNode) =
s = a.res s = a.res
else: else:
useMagic(p, "nimCopy") useMagic(p, "nimCopy")
s = "nimCopy($1, $2)" % [a.res, genTypeInfo(p, n.typ)] s = "nimCopy(null, $1, $2)" % [a.res, genTypeInfo(p, n.typ)]
of etyBaseIndex: of etyBaseIndex:
if (a.typ != etyBaseIndex): internalError(n.info, "genVarInit") if (a.typ != etyBaseIndex): internalError(n.info, "genVarInit")
if {sfAddrTaken, sfGlobal} * v.flags != {}: if {sfAddrTaken, sfGlobal} * v.flags != {}:
@ -1389,6 +1433,12 @@ proc genMagic(p: PProc, n: PNode, r: var TCompRes) =
of mCopyStrLast: ternaryExpr(p, n, r, "", "($1.slice($2, ($3)+1).concat(0))") of mCopyStrLast: ternaryExpr(p, n, r, "", "($1.slice($2, ($3)+1).concat(0))")
of mNewString: unaryExpr(p, n, r, "mnewString", "mnewString($1)") of mNewString: unaryExpr(p, n, r, "mnewString", "mnewString($1)")
of mNewStringOfCap: unaryExpr(p, n, r, "mnewString", "mnewString(0)") of mNewStringOfCap: unaryExpr(p, n, r, "mnewString", "mnewString(0)")
of mDotDot:
genProcForSymIfNeeded(p, n.sons[0].sym)
genCall(p, n, r)
of mParseBiggestFloat:
useMagic(p, "nimParseBiggestFloat")
genCall(p, n, r)
else: else:
genCall(p, n, r) genCall(p, n, r)
#else internalError(e.info, 'genMagic: ' + magicToStr[op]); #else internalError(e.info, 'genMagic: ' + magicToStr[op]);
@ -1434,19 +1484,22 @@ proc genTupleConstr(p: PProc, n: PNode, r: var TCompRes) =
r.res.add("}") r.res.add("}")
proc genObjConstr(p: PProc, n: PNode, r: var TCompRes) = proc genObjConstr(p: PProc, n: PNode, r: var TCompRes) =
# XXX inheritance?
var a: TCompRes var a: TCompRes
r.res = rope("{")
r.kind = resExpr r.kind = resExpr
var initList : Rope
var fieldIDs = initIntSet()
for i in countup(1, sonsLen(n) - 1): for i in countup(1, sonsLen(n) - 1):
if i > 1: add(r.res, ", ") if i > 1: add(initList, ", ")
var it = n.sons[i] var it = n.sons[i]
internalAssert it.kind == nkExprColonExpr internalAssert it.kind == nkExprColonExpr
gen(p, it.sons[1], a) gen(p, it.sons[1], a)
var f = it.sons[0].sym var f = it.sons[0].sym
if f.loc.r == nil: f.loc.r = mangleName(f) if f.loc.r == nil: f.loc.r = mangleName(f)
addf(r.res, "$#: $#" | "$# = $#" , [f.loc.r, a.res]) fieldIDs.incl(f.id)
r.res.add("}") addf(initList, "$#: $#" | "$# = $#" , [f.loc.r, a.res])
let t = skipTypes(n.typ, abstractInst + skipPtrs)
createObjInitList(p, t, fieldIDs, initList)
r.res = "{$1}" % [initList]
proc genConv(p: PProc, n: PNode, r: var TCompRes) = proc genConv(p: PProc, n: PNode, r: var TCompRes) =
var dest = skipTypes(n.typ, abstractVarRange) var dest = skipTypes(n.typ, abstractVarRange)
@ -1551,7 +1604,10 @@ proc genProc(oldProc: PProc, prc: PSym): Rope =
mangleName(resultSym), mangleName(resultSym),
createVar(p, resultSym.typ, isIndirect(resultSym))] createVar(p, resultSym.typ, isIndirect(resultSym))]
gen(p, prc.ast.sons[resultPos], a) gen(p, prc.ast.sons[resultPos], a)
returnStmt = "return $#;$n" % [a.res] if mapType(resultSym.typ) == etyBaseIndex:
returnStmt = "return [$#, $#];$n" % [a.address, a.res]
else:
returnStmt = "return $#;$n" % [a.res]
genStmt(p, prc.getBody) genStmt(p, prc.getBody)
result = ("function $#($#) {$n$#$#$#$#}$n" | result = ("function $#($#) {$n$#$#$#$#}$n" |
"function $#($#) $n$#$#$#$#$nend$n") % "function $#($#) $n$#$#$#$#$nend$n") %
@ -1565,6 +1621,12 @@ proc genStmt(p: PProc, n: PNode) =
gen(p, n, r) gen(p, n, r)
if r.res != nil: addf(p.body, "$#;$n", [r.res]) if r.res != nil: addf(p.body, "$#;$n", [r.res])
proc genPragma(p: PProc, n: PNode) =
for it in n.sons:
case whichPragma(it)
of wEmit: genAsmOrEmitStmt(p, it.sons[1])
else: discard
proc gen(p: PProc, n: PNode, r: var TCompRes) = proc gen(p: PProc, n: PNode, r: var TCompRes) =
r.typ = etyNone r.typ = etyNone
r.kind = resNone r.kind = resNone
@ -1648,6 +1710,9 @@ proc gen(p: PProc, n: PNode, r: var TCompRes) =
gen(p, lastSon(n), r) gen(p, lastSon(n), r)
of nkBlockStmt, nkBlockExpr: genBlock(p, n, r) of nkBlockStmt, nkBlockExpr: genBlock(p, n, r)
of nkIfStmt, nkIfExpr: genIf(p, n, r) of nkIfStmt, nkIfExpr: genIf(p, n, r)
of nkWhen:
# This is "when nimvm" node
gen(p, n.sons[1].sons[0], r)
of nkWhileStmt: genWhileStmt(p, n) of nkWhileStmt: genWhileStmt(p, n)
of nkVarSection, nkLetSection: genVarStmt(p, n) of nkVarSection, nkLetSection: genVarStmt(p, n)
of nkConstSection: discard of nkConstSection: discard
@ -1664,12 +1729,13 @@ proc gen(p: PProc, n: PNode, r: var TCompRes) =
if n.sons[0].kind != nkEmpty: if n.sons[0].kind != nkEmpty:
genLineDir(p, n) genLineDir(p, n)
gen(p, n.sons[0], r) gen(p, n.sons[0], r)
of nkAsmStmt: genAsmStmt(p, n) of nkAsmStmt: genAsmOrEmitStmt(p, n)
of nkTryStmt: genTry(p, n, r) of nkTryStmt: genTry(p, n, r)
of nkRaiseStmt: genRaiseStmt(p, n) of nkRaiseStmt: genRaiseStmt(p, n)
of nkTypeSection, nkCommentStmt, nkIteratorDef, nkIncludeStmt, of nkTypeSection, nkCommentStmt, nkIteratorDef, nkIncludeStmt,
nkImportStmt, nkImportExceptStmt, nkExportStmt, nkExportExceptStmt, nkImportStmt, nkImportExceptStmt, nkExportStmt, nkExportExceptStmt,
nkFromStmt, nkTemplateDef, nkMacroDef, nkPragma: discard nkFromStmt, nkTemplateDef, nkMacroDef: discard
of nkPragma: genPragma(p, n)
of nkProcDef, nkMethodDef, nkConverterDef: of nkProcDef, nkMethodDef, nkConverterDef:
var s = n.sons[namePos].sym var s = n.sons[namePos].sym
if {sfExportc, sfCompilerProc} * s.flags == {sfExportc}: if {sfExportc, sfCompilerProc} * s.flags == {sfExportc}:
@ -1691,7 +1757,7 @@ proc genHeader(): Rope =
result = ("/* Generated by the Nim Compiler v$1 */$n" & result = ("/* Generated by the Nim Compiler v$1 */$n" &
"/* (c) 2015 Andreas Rumpf */$n$n" & "/* (c) 2015 Andreas Rumpf */$n$n" &
"var framePtr = null;$n" & "var framePtr = null;$n" &
"var excHandler = null;$n" & "var excHandler = 0;$n" &
"var lastJSError = null;$n") % "var lastJSError = null;$n") %
[rope(VersionAsString)] [rope(VersionAsString)]

View file

@ -116,13 +116,12 @@ proc genEnumInfo(p: PProc, typ: PType, name: Rope) =
[name, genTypeInfo(p, typ.sons[0])]) [name, genTypeInfo(p, typ.sons[0])])
proc genTypeInfo(p: PProc, typ: PType): Rope = proc genTypeInfo(p: PProc, typ: PType): Rope =
var t = typ let t = typ.skipTypes({tyGenericInst})
if t.kind == tyGenericInst: t = lastSon(t)
result = "NTI$1" % [rope(t.id)] result = "NTI$1" % [rope(t.id)]
if containsOrIncl(p.g.typeInfoGenerated, t.id): return if containsOrIncl(p.g.typeInfoGenerated, t.id): return
case t.kind case t.kind
of tyDistinct: of tyDistinct:
result = genTypeInfo(p, typ.sons[0]) result = genTypeInfo(p, t.sons[0])
of tyPointer, tyProc, tyBool, tyChar, tyCString, tyString, tyInt..tyUInt64: of tyPointer, tyProc, tyBool, tyChar, tyCString, tyString, tyInt..tyUInt64:
var s = var s =
"var $1 = {size: 0,kind: $2,base: null,node: null,finalizer: null};$n" % "var $1 = {size: 0,kind: $2,base: null,node: null,finalizer: null};$n" %
@ -134,14 +133,14 @@ proc genTypeInfo(p: PProc, typ: PType): Rope =
[result, rope(ord(t.kind))] [result, rope(ord(t.kind))]
prepend(p.g.typeInfo, s) prepend(p.g.typeInfo, s)
addf(p.g.typeInfo, "$1.base = $2;$n", addf(p.g.typeInfo, "$1.base = $2;$n",
[result, genTypeInfo(p, typ.lastSon)]) [result, genTypeInfo(p, t.lastSon)])
of tyArrayConstr, tyArray: of tyArrayConstr, tyArray:
var s = var s =
"var $1 = {size: 0,kind: $2,base: null,node: null,finalizer: null};$n" % "var $1 = {size: 0,kind: $2,base: null,node: null,finalizer: null};$n" %
[result, rope(ord(t.kind))] [result, rope(ord(t.kind))]
prepend(p.g.typeInfo, s) prepend(p.g.typeInfo, s)
addf(p.g.typeInfo, "$1.base = $2;$n", addf(p.g.typeInfo, "$1.base = $2;$n",
[result, genTypeInfo(p, typ.sons[1])]) [result, genTypeInfo(p, t.sons[1])])
of tyEnum: genEnumInfo(p, t, result) of tyEnum: genEnumInfo(p, t, result)
of tyObject: genObjectInfo(p, t, result) of tyObject: genObjectInfo(p, t, result)
of tyTuple: genTupleInfo(p, t, result) of tyTuple: genTupleInfo(p, t, result)

View file

@ -184,7 +184,7 @@ proc addHiddenParam(routine: PSym, param: PSym) =
var params = routine.ast.sons[paramsPos] var params = routine.ast.sons[paramsPos]
# -1 is correct here as param.position is 0 based but we have at position 0 # -1 is correct here as param.position is 0 based but we have at position 0
# some nkEffect node: # some nkEffect node:
param.position = params.len-1 param.position = routine.typ.n.len-1
addSon(params, newSymNode(param)) addSon(params, newSymNode(param))
incl(routine.typ.flags, tfCapturesEnv) incl(routine.typ.flags, tfCapturesEnv)
assert sfFromGeneric in param.flags assert sfFromGeneric in param.flags
@ -859,11 +859,17 @@ proc transformOuterProc(o: POuterContext, n: PNode; it: TIter): PNode =
return indirectAccess(newSymNode(it.closureParam), local, n.info) return indirectAccess(newSymNode(it.closureParam), local, n.info)
if local.kind == skClosureIterator: if local.kind == skClosureIterator:
# bug #3354; allow for
#iterator iter(): int {.closure.}=
# s.add(iter)
# yield 1
#if local == o.fn or local == it.fn:
# message(n.info, errRecursiveDependencyX, local.name.s)
# consider: [i1, i2, i1] Since we merged the iterator's closure # consider: [i1, i2, i1] Since we merged the iterator's closure
# with the captured owning variables, we need to generate the # with the captured owning variables, we need to generate the
# closure generation code again: # closure generation code again:
if local == o.fn or local == it.fn:
message(n.info, errRecursiveDependencyX, local.name.s)
# XXX why doesn't this work? # XXX why doesn't this work?
var closure = PEnv(idTableGet(o.lambdasToEnv, local)) var closure = PEnv(idTableGet(o.lambdasToEnv, local))
if closure.isNil: if closure.isNil:
@ -946,7 +952,11 @@ proc transformOuterProc(o: POuterContext, n: PNode; it: TIter): PNode =
proc liftLambdas*(fn: PSym, body: PNode): PNode = proc liftLambdas*(fn: PSym, body: PNode): PNode =
# XXX gCmd == cmdCompileToJS does not suffice! The compiletime stuff needs # XXX gCmd == cmdCompileToJS does not suffice! The compiletime stuff needs
# the transformation even when compiling to JS ... # the transformation even when compiling to JS ...
if body.kind == nkEmpty or gCmd == cmdCompileToJS or
# However we can do lifting for the stuff which is *only* compiletime.
let isCompileTime = sfCompileTime in fn.flags or fn.kind == skMacro
if body.kind == nkEmpty or (gCmd == cmdCompileToJS and not isCompileTime) or
fn.skipGenericOwner.kind != skModule: fn.skipGenericOwner.kind != skModule:
# ignore forward declaration: # ignore forward declaration:
result = body result = body
@ -1012,7 +1022,9 @@ proc liftForLoop*(body: PNode): PNode =
... ...
""" """
var L = body.len var L = body.len
internalAssert body.kind == nkForStmt and body[L-2].kind in nkCallKinds if not (body.kind == nkForStmt and body[L-2].kind in nkCallKinds):
localError(body.info, "ignored invalid for loop")
return body
var call = body[L-2] var call = body[L-2]
result = newNodeI(nkStmtList, body.info) result = newNodeI(nkStmtList, body.info)

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 wordrecg, etcpriv
const const
MaxLineLength* = 80 # lines longer than this lead to a warning MaxLineLength* = 80 # lines longer than this lead to a warning
@ -140,10 +140,12 @@ proc isKeyword*(kind: TTokType): bool =
proc isNimIdentifier*(s: string): bool = proc isNimIdentifier*(s: string): bool =
if s[0] in SymStartChars: if s[0] in SymStartChars:
var i = 1 var i = 1
while i < s.len: var sLen = s.len
while i < sLen:
if s[i] == '_': if s[i] == '_':
inc(i) inc(i)
if s[i] notin SymChars: return elif isMagicIdentSeparatorRune(cstring s, i):
inc(i, magicIdentSeparatorRuneByteWidth)
if s[i] notin SymChars: return if s[i] notin SymChars: return
inc(i) inc(i)
result = true result = true
@ -229,23 +231,6 @@ proc lexMessagePos(L: var TLexer, msg: TMsgKind, pos: int, arg = "") =
var info = newLineInfo(L.fileIdx, L.lineNumber, pos - L.lineStart) var info = newLineInfo(L.fileIdx, L.lineNumber, pos - L.lineStart)
L.dispMessage(info, msg, arg) L.dispMessage(info, msg, arg)
proc matchUnderscoreChars(L: var TLexer, tok: var TToken, chars: set[char]) =
var pos = L.bufpos # use registers for pos, buf
var buf = L.buf
while true:
if buf[pos] in chars:
add(tok.literal, buf[pos])
inc(pos)
else:
break
if buf[pos] == '_':
if buf[pos+1] notin chars:
lexMessage(L, errInvalidToken, "_")
break
add(tok.literal, '_')
inc(pos)
L.bufpos = pos
proc matchTwoChars(L: TLexer, first: char, second: set[char]): bool = proc matchTwoChars(L: TLexer, first: char, second: set[char]): bool =
result = (L.buf[L.bufpos] == first) and (L.buf[L.bufpos + 1] in second) result = (L.buf[L.bufpos] == first) and (L.buf[L.bufpos + 1] in second)
@ -268,136 +253,200 @@ proc unsafeParseUInt(s: string, b: var BiggestInt, start = 0): int =
result = i - start result = i - start
{.pop.} # overflowChecks {.pop.} # overflowChecks
template eatChar(L: var TLexer, t: var TToken, replacementChar: char) =
add(t.literal, replacementChar)
inc(L.bufpos)
template eatChar(L: var TLexer, t: var TToken) =
add(t.literal, L.buf[L.bufpos])
inc(L.bufpos)
proc getNumber(L: var TLexer): TToken = proc getNumber(L: var TLexer): TToken =
proc matchUnderscoreChars(L: var TLexer, tok: var TToken, chars: set[char]) =
var pos = L.bufpos # use registers for pos, buf
var buf = L.buf
while true:
if buf[pos] in chars:
add(tok.literal, buf[pos])
inc(pos)
else:
break
if buf[pos] == '_':
if buf[pos+1] notin chars:
lexMessage(L, errInvalidToken, "_")
break
add(tok.literal, '_')
inc(pos)
L.bufpos = pos
proc matchChars(L: var TLexer, tok: var TToken, chars: set[char]) =
var pos = L.bufpos # use registers for pos, buf
var buf = L.buf
while buf[pos] in chars:
add(tok.literal, buf[pos])
inc(pos)
L.bufpos = pos
proc lexMessageLitNum(L: var TLexer, msg: TMsgKind, startpos: int) =
# Used to get slightly human friendlier err messages.
# Note: the erroneous 'O' char in the character set is intentional
const literalishChars = {'A'..'F', 'a'..'f', '0'..'9', 'X', 'x', 'o', 'O',
'c', 'C', 'b', 'B', '_', '.', '\'', 'd', 'i', 'u'}
var msgPos = L.bufpos
var t: TToken
t.literal = ""
L.bufpos = startpos # Use L.bufpos as pos because of matchChars
matchChars(L, t, literalishChars)
# We must verify +/- specifically so that we're not past the literal
if L.buf[L.bufpos] in {'+', '-'} and
L.buf[L.bufpos - 1] in {'e', 'E'}:
add(t.literal, L.buf[L.bufpos])
inc(L.bufpos)
matchChars(L, t, literalishChars)
if L.buf[L.bufpos] in {'\'', 'f', 'F', 'd', 'D', 'i', 'I', 'u', 'U'}:
inc(L.bufpos)
add(t.literal, L.buf[L.bufpos])
matchChars(L, t, {'0'..'9'})
L.bufpos = msgPos
lexMessage(L, msg, t.literal)
var var
pos, endpos: int startpos, endpos: int
xi: BiggestInt xi: BiggestInt
# get the base: isBase10 = true
const
baseCodeChars = {'X', 'x', 'o', 'c', 'C', 'b', 'B'}
literalishChars = baseCodeChars + {'A'..'F', 'a'..'f', '0'..'9', '_', '\''}
floatTypes = {tkFloatLit, tkFloat32Lit, tkFloat64Lit, tkFloat128Lit}
result.tokType = tkIntLit # int literal until we know better result.tokType = tkIntLit # int literal until we know better
result.literal = "" result.literal = ""
result.base = base10 # BUGFIX result.base = base10
pos = L.bufpos # make sure the literal is correct for error messages: startpos = L.bufpos
var eallowed = false
if L.buf[pos] == '0' and L.buf[pos+1] in {'X', 'x'}: # First stage: find out base, make verifications, build token literal string
matchUnderscoreChars(L, result, {'A'..'F', 'a'..'f', '0'..'9', 'X', 'x'}) if L.buf[L.bufpos] == '0' and L.buf[L.bufpos + 1] in baseCodeChars + {'O'}:
isBase10 = false
eatChar(L, result, '0')
case L.buf[L.bufpos]
of 'O':
lexMessageLitNum(L, errInvalidNumberOctalCode, startpos)
of 'x', 'X':
eatChar(L, result, 'x')
matchUnderscoreChars(L, result, {'0'..'9', 'a'..'f', 'A'..'F'})
of 'o', 'c', 'C':
eatChar(L, result, 'c')
matchUnderscoreChars(L, result, {'0'..'7'})
of 'b', 'B':
eatChar(L, result, 'b')
matchUnderscoreChars(L, result, {'0'..'1'})
else:
internalError(getLineInfo(L), "getNumber")
else: else:
matchUnderscoreChars(L, result, {'0'..'9', 'b', 'B', 'o', 'c', 'C'})
eallowed = true
if (L.buf[L.bufpos] == '.') and (L.buf[L.bufpos + 1] in {'0'..'9'}):
add(result.literal, '.')
inc(L.bufpos)
matchUnderscoreChars(L, result, {'0'..'9'})
eallowed = true
if eallowed and L.buf[L.bufpos] in {'e', 'E'}:
add(result.literal, 'e')
inc(L.bufpos)
if L.buf[L.bufpos] in {'+', '-'}:
add(result.literal, L.buf[L.bufpos])
inc(L.bufpos)
matchUnderscoreChars(L, result, {'0'..'9'}) matchUnderscoreChars(L, result, {'0'..'9'})
if (L.buf[L.bufpos] == '.') and (L.buf[L.bufpos + 1] in {'0'..'9'}):
result.tokType = tkFloat64Lit
eatChar(L, result, '.')
matchUnderscoreChars(L, result, {'0'..'9'})
if L.buf[L.bufpos] in {'e', 'E'}:
result.tokType = tkFloat64Lit
eatChar(L, result, 'e')
if L.buf[L.bufpos] in {'+', '-'}:
eatChar(L, result)
matchUnderscoreChars(L, result, {'0'..'9'})
endpos = L.bufpos endpos = L.bufpos
if L.buf[endpos] in {'\'', 'f', 'F', 'i', 'I', 'u', 'U'}:
if L.buf[endpos] == '\'': inc(endpos) # Second stage, find out if there's a datatype suffix and handle it
L.bufpos = pos # restore position var postPos = endpos
case L.buf[endpos] if L.buf[postPos] in {'\'', 'f', 'F', 'd', 'D', 'i', 'I', 'u', 'U'}:
if L.buf[postPos] == '\'':
inc(postPos)
case L.buf[postPos]
of 'f', 'F': of 'f', 'F':
inc(endpos) inc(postPos)
if (L.buf[endpos] == '3') and (L.buf[endpos + 1] == '2'): if (L.buf[postPos] == '3') and (L.buf[postPos + 1] == '2'):
result.tokType = tkFloat32Lit result.tokType = tkFloat32Lit
inc(endpos, 2) inc(postPos, 2)
elif (L.buf[endpos] == '6') and (L.buf[endpos + 1] == '4'): elif (L.buf[postPos] == '6') and (L.buf[postPos + 1] == '4'):
result.tokType = tkFloat64Lit result.tokType = tkFloat64Lit
inc(endpos, 2) inc(postPos, 2)
elif (L.buf[endpos] == '1') and elif (L.buf[postPos] == '1') and
(L.buf[endpos + 1] == '2') and (L.buf[postPos + 1] == '2') and
(L.buf[endpos + 2] == '8'): (L.buf[postPos + 2] == '8'):
result.tokType = tkFloat128Lit result.tokType = tkFloat128Lit
inc(endpos, 3) inc(postPos, 3)
else: else: # "f" alone defaults to float32
lexMessage(L, errInvalidNumber, result.literal & "'f" & L.buf[endpos]) result.tokType = tkFloat32Lit
of 'd', 'D': # ad hoc convenience shortcut for f64
inc(postPos)
result.tokType = tkFloat64Lit
of 'i', 'I': of 'i', 'I':
inc(endpos) inc(postPos)
if (L.buf[endpos] == '6') and (L.buf[endpos + 1] == '4'): if (L.buf[postPos] == '6') and (L.buf[postPos + 1] == '4'):
result.tokType = tkInt64Lit result.tokType = tkInt64Lit
inc(endpos, 2) inc(postPos, 2)
elif (L.buf[endpos] == '3') and (L.buf[endpos + 1] == '2'): elif (L.buf[postPos] == '3') and (L.buf[postPos + 1] == '2'):
result.tokType = tkInt32Lit result.tokType = tkInt32Lit
inc(endpos, 2) inc(postPos, 2)
elif (L.buf[endpos] == '1') and (L.buf[endpos + 1] == '6'): elif (L.buf[postPos] == '1') and (L.buf[postPos + 1] == '6'):
result.tokType = tkInt16Lit result.tokType = tkInt16Lit
inc(endpos, 2) inc(postPos, 2)
elif (L.buf[endpos] == '8'): elif (L.buf[postPos] == '8'):
result.tokType = tkInt8Lit result.tokType = tkInt8Lit
inc(endpos) inc(postPos)
else: else:
lexMessage(L, errInvalidNumber, result.literal & "'i" & L.buf[endpos]) lexMessageLitNum(L, errInvalidNumber, startpos)
of 'u', 'U': of 'u', 'U':
inc(endpos) inc(postPos)
if (L.buf[endpos] == '6') and (L.buf[endpos + 1] == '4'): if (L.buf[postPos] == '6') and (L.buf[postPos + 1] == '4'):
result.tokType = tkUInt64Lit result.tokType = tkUInt64Lit
inc(endpos, 2) inc(postPos, 2)
elif (L.buf[endpos] == '3') and (L.buf[endpos + 1] == '2'): elif (L.buf[postPos] == '3') and (L.buf[postPos + 1] == '2'):
result.tokType = tkUInt32Lit result.tokType = tkUInt32Lit
inc(endpos, 2) inc(postPos, 2)
elif (L.buf[endpos] == '1') and (L.buf[endpos + 1] == '6'): elif (L.buf[postPos] == '1') and (L.buf[postPos + 1] == '6'):
result.tokType = tkUInt16Lit result.tokType = tkUInt16Lit
inc(endpos, 2) inc(postPos, 2)
elif (L.buf[endpos] == '8'): elif (L.buf[postPos] == '8'):
result.tokType = tkUInt8Lit result.tokType = tkUInt8Lit
inc(endpos) inc(postPos)
else: else:
result.tokType = tkUIntLit result.tokType = tkUIntLit
else: lexMessage(L, errInvalidNumber, result.literal & "'" & L.buf[endpos]) else:
else: lexMessageLitNum(L, errInvalidNumber, startpos)
L.bufpos = pos # restore position
# Is there still a literalish char awaiting? Then it's an error!
if L.buf[postPos] in literalishChars or
(L.buf[postPos] == '.' and L.buf[postPos + 1] in {'0'..'9'}):
lexMessageLitNum(L, errInvalidNumber, startpos)
# Third stage, extract actual number
L.bufpos = startpos # restore position
var pos: int = startpos
try: try:
if (L.buf[pos] == '0') and if (L.buf[pos] == '0') and (L.buf[pos + 1] in baseCodeChars):
(L.buf[pos + 1] in {'x', 'X', 'b', 'B', 'o', 'O', 'c', 'C'}):
inc(pos, 2) inc(pos, 2)
xi = 0 # it may be a base prefix xi = 0 # it is a base prefix
case L.buf[pos - 1] # now look at the optional type suffix:
case L.buf[pos - 1]
of 'b', 'B': of 'b', 'B':
result.base = base2 result.base = base2
while true: while pos < endpos:
case L.buf[pos] if L.buf[pos] != '_':
of '2'..'9', '.':
lexMessage(L, errInvalidNumber, result.literal)
inc(pos)
of '_':
if L.buf[pos+1] notin {'0'..'1'}:
lexMessage(L, errInvalidToken, "_")
break
inc(pos)
of '0', '1':
xi = `shl`(xi, 1) or (ord(L.buf[pos]) - ord('0')) xi = `shl`(xi, 1) or (ord(L.buf[pos]) - ord('0'))
inc(pos) inc(pos)
else: break
of 'o', 'c', 'C': of 'o', 'c', 'C':
result.base = base8 result.base = base8
while true: while pos < endpos:
case L.buf[pos] if L.buf[pos] != '_':
of '8'..'9', '.':
lexMessage(L, errInvalidNumber, result.literal)
inc(pos)
of '_':
if L.buf[pos+1] notin {'0'..'7'}:
lexMessage(L, errInvalidToken, "_")
break
inc(pos)
of '0'..'7':
xi = `shl`(xi, 3) or (ord(L.buf[pos]) - ord('0')) xi = `shl`(xi, 3) or (ord(L.buf[pos]) - ord('0'))
inc(pos) inc(pos)
else: break
of 'O':
lexMessage(L, errInvalidNumber, result.literal)
of 'x', 'X': of 'x', 'X':
result.base = base16 result.base = base16
while true: while pos < endpos:
case L.buf[pos] case L.buf[pos]
of '_': of '_':
if L.buf[pos+1] notin {'0'..'9', 'a'..'f', 'A'..'F'}:
lexMessage(L, errInvalidToken, "_")
break
inc(pos) inc(pos)
of '0'..'9': of '0'..'9':
xi = `shl`(xi, 4) or (ord(L.buf[pos]) - ord('0')) xi = `shl`(xi, 4) or (ord(L.buf[pos]) - ord('0'))
@ -408,51 +457,81 @@ proc getNumber(L: var TLexer): TToken =
of 'A'..'F': of 'A'..'F':
xi = `shl`(xi, 4) or (ord(L.buf[pos]) - ord('A') + 10) xi = `shl`(xi, 4) or (ord(L.buf[pos]) - ord('A') + 10)
inc(pos) inc(pos)
else: break else:
else: internalError(getLineInfo(L), "getNumber") break
else:
internalError(getLineInfo(L), "getNumber")
case result.tokType case result.tokType
of tkIntLit, tkInt64Lit: result.iNumber = xi of tkIntLit, tkInt64Lit: result.iNumber = xi
of tkInt8Lit: result.iNumber = BiggestInt(int8(toU8(int(xi)))) of tkInt8Lit: result.iNumber = BiggestInt(int8(toU8(int(xi))))
of tkInt16Lit: result.iNumber = BiggestInt(toU16(int(xi))) of tkInt16Lit: result.iNumber = BiggestInt(int16(toU16(int(xi))))
of tkInt32Lit: result.iNumber = BiggestInt(toU32(xi)) of tkInt32Lit: result.iNumber = BiggestInt(int32(toU32(int64(xi))))
of tkUIntLit, tkUInt64Lit: result.iNumber = xi of tkUIntLit, tkUInt64Lit: result.iNumber = xi
of tkUInt8Lit: result.iNumber = BiggestInt(int8(toU8(int(xi)))) of tkUInt8Lit: result.iNumber = BiggestInt(uint8(toU8(int(xi))))
of tkUInt16Lit: result.iNumber = BiggestInt(toU16(int(xi))) of tkUInt16Lit: result.iNumber = BiggestInt(uint16(toU16(int(xi))))
of tkUInt32Lit: result.iNumber = BiggestInt(toU32(xi)) of tkUInt32Lit: result.iNumber = BiggestInt(uint32(toU32(int64(xi))))
of tkFloat32Lit: of tkFloat32Lit:
result.fNumber = (cast[PFloat32](addr(xi)))[] result.fNumber = (cast[PFloat32](addr(xi)))[]
# note: this code is endian neutral! # note: this code is endian neutral!
# XXX: Test this on big endian machine! # XXX: Test this on big endian machine!
of tkFloat64Lit: result.fNumber = (cast[PFloat64](addr(xi)))[] of tkFloat64Lit: result.fNumber = (cast[PFloat64](addr(xi)))[]
else: internalError(getLineInfo(L), "getNumber") else: internalError(getLineInfo(L), "getNumber")
elif isFloatLiteral(result.literal) or (result.tokType == tkFloat32Lit) or
(result.tokType == tkFloat64Lit): # Bounds checks. Non decimal literals are allowed to overflow the range of
result.fNumber = parseFloat(result.literal) # the datatype as long as their pattern don't overflow _bitwise_, hence
if result.tokType == tkIntLit: result.tokType = tkFloatLit # below checks of signed sizes against uint*.high is deliberate:
elif result.tokType == tkUint64Lit: # (0x80'u8 = 128, 0x80'i8 = -128, etc == OK)
xi = 0 if result.tokType notin floatTypes:
let len = unsafeParseUInt(result.literal, xi) let outOfRange = case result.tokType:
if len != result.literal.len or len == 0: of tkUInt8Lit, tkUInt16Lit, tkUInt32Lit: result.iNumber != xi
raise newException(ValueError, "invalid integer: " & $xi) of tkInt8Lit: (xi > BiggestInt(uint8.high))
result.iNumber = xi of tkInt16Lit: (xi > BiggestInt(uint16.high))
of tkInt32Lit: (xi > BiggestInt(uint32.high))
else: false
if outOfRange:
#echo "out of range num: ", result.iNumber, " vs ", xi
lexMessageLitNum(L, errNumberOutOfRange, startpos)
else: else:
result.iNumber = parseBiggestInt(result.literal) case result.tokType
of floatTypes:
result.fNumber = parseFloat(result.literal)
of tkUint64Lit:
xi = 0
let len = unsafeParseUInt(result.literal, xi)
if len != result.literal.len or len == 0:
raise newException(ValueError, "invalid integer: " & $xi)
result.iNumber = xi
else:
result.iNumber = parseBiggestInt(result.literal)
# Explicit bounds checks
let outOfRange = case result.tokType:
of tkInt8Lit: (result.iNumber < int8.low or result.iNumber > int8.high)
of tkUInt8Lit: (result.iNumber < BiggestInt(uint8.low) or
result.iNumber > BiggestInt(uint8.high))
of tkInt16Lit: (result.iNumber < int16.low or result.iNumber > int16.high)
of tkUInt16Lit: (result.iNumber < BiggestInt(uint16.low) or
result.iNumber > BiggestInt(uint16.high))
of tkInt32Lit: (result.iNumber < int32.low or result.iNumber > int32.high)
of tkUInt32Lit: (result.iNumber < BiggestInt(uint32.low) or
result.iNumber > BiggestInt(uint32.high))
else: false
if outOfRange: lexMessageLitNum(L, errNumberOutOfRange, startpos)
# Promote int literal to int64? Not always necessary, but more consistent
if result.tokType == tkIntLit:
if (result.iNumber < low(int32)) or (result.iNumber > high(int32)): if (result.iNumber < low(int32)) or (result.iNumber > high(int32)):
if result.tokType == tkIntLit: result.tokType = tkInt64Lit
result.tokType = tkInt64Lit
elif result.tokType in {tkInt8Lit, tkInt16Lit, tkInt32Lit}:
lexMessage(L, errNumberOutOfRange, result.literal)
elif result.tokType == tkInt8Lit and
(result.iNumber < int8.low or result.iNumber > int8.high):
lexMessage(L, errNumberOutOfRange, result.literal)
elif result.tokType == tkInt16Lit and
(result.iNumber < int16.low or result.iNumber > int16.high):
lexMessage(L, errNumberOutOfRange, result.literal)
except ValueError: except ValueError:
lexMessage(L, errInvalidNumber, result.literal) lexMessageLitNum(L, errInvalidNumber, startpos)
except OverflowError, RangeError: except OverflowError, RangeError:
lexMessage(L, errNumberOutOfRange, result.literal) lexMessageLitNum(L, errNumberOutOfRange, startpos)
L.bufpos = endpos L.bufpos = postPos
proc handleHexChar(L: var TLexer, xi: var int) = proc handleHexChar(L: var TLexer, xi: var int) =
case L.buf[L.bufpos] case L.buf[L.bufpos]
@ -625,23 +704,34 @@ proc getCharacter(L: var TLexer, tok: var TToken) =
inc(L.bufpos) # skip ' inc(L.bufpos) # skip '
proc getSymbol(L: var TLexer, tok: var TToken) = proc getSymbol(L: var TLexer, tok: var TToken) =
var h: THash = 0 var h: Hash = 0
var pos = L.bufpos var pos = L.bufpos
var buf = L.buf var buf = L.buf
while true: while true:
var c = buf[pos] var c = buf[pos]
case c case c
of 'a'..'z', '0'..'9', '\x80'..'\xFF': of 'a'..'z', '0'..'9', '\x80'..'\xFF':
h = h !& ord(c) if c == '\226' and
buf[pos+1] == '\128' and
buf[pos+2] == '\147': # It's a 'magic separator' en-dash Unicode
if buf[pos + magicIdentSeparatorRuneByteWidth] notin SymChars:
lexMessage(L, errInvalidToken, "–")
break
inc(pos, magicIdentSeparatorRuneByteWidth)
else:
h = h !& ord(c)
inc(pos)
of 'A'..'Z': of 'A'..'Z':
c = chr(ord(c) + (ord('a') - ord('A'))) # toLower() c = chr(ord(c) + (ord('a') - ord('A'))) # toLower()
h = h !& ord(c) h = h !& ord(c)
inc(pos)
of '_': of '_':
if buf[pos+1] notin SymChars: if buf[pos+1] notin SymChars:
lexMessage(L, errInvalidToken, "_") lexMessage(L, errInvalidToken, "_")
break break
inc(pos)
else: break else: break
inc(pos)
h = !$h h = !$h
tok.ident = getIdent(addr(L.buf[L.bufpos]), pos - L.bufpos, h) tok.ident = getIdent(addr(L.buf[L.bufpos]), pos - L.bufpos, h)
L.bufpos = pos L.bufpos = pos
@ -652,7 +742,7 @@ proc getSymbol(L: var TLexer, tok: var TToken) =
tok.tokType = TTokType(tok.ident.id + ord(tkSymbol)) tok.tokType = TTokType(tok.ident.id + ord(tkSymbol))
proc endOperator(L: var TLexer, tok: var TToken, pos: int, proc endOperator(L: var TLexer, tok: var TToken, pos: int,
hash: THash) {.inline.} = hash: Hash) {.inline.} =
var h = !$hash var h = !$hash
tok.ident = getIdent(addr(L.buf[L.bufpos]), pos - L.bufpos, h) tok.ident = getIdent(addr(L.buf[L.bufpos]), pos - L.bufpos, h)
if (tok.ident.id < oprLow) or (tok.ident.id > oprHigh): tok.tokType = tkOpr if (tok.ident.id < oprLow) or (tok.ident.id > oprHigh): tok.tokType = tkOpr
@ -662,7 +752,7 @@ proc endOperator(L: var TLexer, tok: var TToken, pos: int,
proc getOperator(L: var TLexer, tok: var TToken) = proc getOperator(L: var TLexer, tok: var TToken) =
var pos = L.bufpos var pos = L.bufpos
var buf = L.buf var buf = L.buf
var h: THash = 0 var h: Hash = 0
while true: while true:
var c = buf[pos] var c = buf[pos]
if c notin OpChars: break if c notin OpChars: break

View file

@ -10,7 +10,7 @@
# This module implements a generic doubled linked list. # This module implements a generic doubled linked list.
# TODO Remove this and replace it with something sensible # TODO Remove this and replace it with something sensible
import os import os
type type
PListEntry* = ref TListEntry PListEntry* = ref TListEntry
TListEntry* = object of RootObj TListEntry* = object of RootObj
prev*, next*: PListEntry prev*, next*: PListEntry
@ -25,68 +25,68 @@ type
TCompareProc* = proc (entry: PListEntry, closure: pointer): bool {.nimcall.} TCompareProc* = proc (entry: PListEntry, closure: pointer): bool {.nimcall.}
proc initLinkedList*(list: var TLinkedList) = proc initLinkedList*(list: var TLinkedList) =
list.counter = 0 list.counter = 0
list.head = nil list.head = nil
list.tail = nil list.tail = nil
proc append*(list: var TLinkedList, entry: PListEntry) = proc append*(list: var TLinkedList, entry: PListEntry) =
inc(list.counter) inc(list.counter)
entry.next = nil entry.next = nil
entry.prev = list.tail entry.prev = list.tail
if list.tail != nil: if list.tail != nil:
assert(list.tail.next == nil) assert(list.tail.next == nil)
list.tail.next = entry list.tail.next = entry
list.tail = entry list.tail = entry
if list.head == nil: list.head = entry if list.head == nil: list.head = entry
proc contains*(list: TLinkedList, data: string): bool = proc contains*(list: TLinkedList, data: string): bool =
var it = list.head var it = list.head
while it != nil: while it != nil:
if PStrEntry(it).data == data: if PStrEntry(it).data == data:
return true return true
it = it.next it = it.next
proc newStrEntry(data: string): PStrEntry = proc newStrEntry(data: string): PStrEntry =
new(result) new(result)
result.data = data result.data = data
proc appendStr*(list: var TLinkedList, data: string) = proc appendStr*(list: var TLinkedList, data: string) =
append(list, newStrEntry(data)) append(list, newStrEntry(data))
proc includeStr*(list: var TLinkedList, data: string): bool = proc includeStr*(list: var TLinkedList, data: string): bool =
if contains(list, data): return true if contains(list, data): return true
appendStr(list, data) # else: add to list appendStr(list, data) # else: add to list
proc prepend*(list: var TLinkedList, entry: PListEntry) = proc prepend*(list: var TLinkedList, entry: PListEntry) =
inc(list.counter) inc(list.counter)
entry.prev = nil entry.prev = nil
entry.next = list.head entry.next = list.head
if list.head != nil: if list.head != nil:
assert(list.head.prev == nil) assert(list.head.prev == nil)
list.head.prev = entry list.head.prev = entry
list.head = entry list.head = entry
if list.tail == nil: list.tail = entry if list.tail == nil: list.tail = entry
proc prependStr*(list: var TLinkedList, data: string) = proc prependStr*(list: var TLinkedList, data: string) =
prepend(list, newStrEntry(data)) prepend(list, newStrEntry(data))
proc insertBefore*(list: var TLinkedList, pos, entry: PListEntry) = proc insertBefore*(list: var TLinkedList, pos, entry: PListEntry) =
assert(pos != nil) assert(pos != nil)
if pos == list.head: if pos == list.head:
prepend(list, entry) prepend(list, entry)
else: else:
inc(list.counter) inc(list.counter)
entry.next = pos entry.next = pos
entry.prev = pos.prev entry.prev = pos.prev
if pos.prev != nil: pos.prev.next = entry if pos.prev != nil: pos.prev.next = entry
pos.prev = entry pos.prev = entry
proc remove*(list: var TLinkedList, entry: PListEntry) = proc remove*(list: var TLinkedList, entry: PListEntry) =
dec(list.counter) dec(list.counter)
if entry == list.tail: if entry == list.tail:
list.tail = entry.prev list.tail = entry.prev
if entry == list.head: if entry == list.head:
list.head = entry.next list.head = entry.next
if entry.next != nil: entry.next.prev = entry.prev if entry.next != nil: entry.next.prev = entry.prev
if entry.prev != nil: entry.prev.next = entry.next if entry.prev != nil: entry.prev.next = entry.next
@ -112,8 +112,8 @@ proc excludePath*(list: var TLinkedList, data: string) =
remove(list, it) remove(list, it)
it = nxt it = nxt
proc find*(list: TLinkedList, fn: TCompareProc, closure: pointer): PListEntry = proc find*(list: TLinkedList, fn: TCompareProc, closure: pointer): PListEntry =
result = list.head result = list.head
while result != nil: while result != nil:
if fn(result, closure): return if fn(result, closure): return
result = result.next result = result.next

View file

@ -9,13 +9,14 @@
## Low-level streams for high performance. ## Low-level streams for high performance.
import import
strutils strutils
when not defined(windows) and defined(useGnuReadline): # support '-d:useGnuReadline' for backwards compatibility:
when not defined(windows) and (defined(useGnuReadline) or defined(useLinenoise)):
import rdstdin import rdstdin
type type
TLLStreamKind* = enum # enum of different stream implementations TLLStreamKind* = enum # enum of different stream implementations
llsNone, # null stream: reading and writing has no effect llsNone, # null stream: reading and writing has no effect
llsString, # stream encapsulates a string llsString, # stream encapsulates a string
@ -27,42 +28,42 @@ type
s*: string s*: string
rd*, wr*: int # for string streams rd*, wr*: int # for string streams
lineOffset*: int # for fake stdin line numbers lineOffset*: int # for fake stdin line numbers
PLLStream* = ref TLLStream PLLStream* = ref TLLStream
proc llStreamOpen*(data: string): PLLStream = proc llStreamOpen*(data: string): PLLStream =
new(result) new(result)
result.s = data result.s = data
result.kind = llsString result.kind = llsString
proc llStreamOpen*(f: File): PLLStream = proc llStreamOpen*(f: File): PLLStream =
new(result) new(result)
result.f = f result.f = f
result.kind = llsFile result.kind = llsFile
proc llStreamOpen*(filename: string, mode: FileMode): PLLStream = proc llStreamOpen*(filename: string, mode: FileMode): PLLStream =
new(result) new(result)
result.kind = llsFile result.kind = llsFile
if not open(result.f, filename, mode): result = nil if not open(result.f, filename, mode): result = nil
proc llStreamOpen*(): PLLStream = proc llStreamOpen*(): PLLStream =
new(result) new(result)
result.kind = llsNone result.kind = llsNone
proc llStreamOpenStdIn*(): PLLStream = proc llStreamOpenStdIn*(): PLLStream =
new(result) new(result)
result.kind = llsStdIn result.kind = llsStdIn
result.s = "" result.s = ""
result.lineOffset = -1 result.lineOffset = -1
proc llStreamClose*(s: PLLStream) = proc llStreamClose*(s: PLLStream) =
case s.kind case s.kind
of llsNone, llsString, llsStdIn: of llsNone, llsString, llsStdIn:
discard discard
of llsFile: of llsFile:
close(s.f) close(s.f)
when not declared(readLineFromStdin): when not declared(readLineFromStdin):
# fallback implementation: # fallback implementation:
proc readLineFromStdin(prompt: string, line: var string): bool = proc readLineFromStdin(prompt: string, line: var string): bool =
stdout.write(prompt) stdout.write(prompt)
@ -77,7 +78,7 @@ proc endsWith*(x: string, s: set[char]): bool =
if i >= 0 and x[i] in s: if i >= 0 and x[i] in s:
result = true result = true
const const
LineContinuationOprs = {'+', '-', '*', '/', '\\', '<', '>', '!', '?', '^', LineContinuationOprs = {'+', '-', '*', '/', '\\', '<', '>', '!', '?', '^',
'|', '%', '&', '$', '@', '~', ','} '|', '%', '&', '$', '@', '~', ','}
AdditionalLineContinuationOprs = {'#', ':', '='} AdditionalLineContinuationOprs = {'#', ':', '='}
@ -104,31 +105,31 @@ proc llReadFromStdin(s: PLLStream, buf: pointer, bufLen: int): int =
s.rd = 0 s.rd = 0
var line = newStringOfCap(120) var line = newStringOfCap(120)
var triples = 0 var triples = 0
while readLineFromStdin(if s.s.len == 0: ">>> " else: "... ", line): while readLineFromStdin(if s.s.len == 0: ">>> " else: "... ", line):
add(s.s, line) add(s.s, line)
add(s.s, "\n") add(s.s, "\n")
inc triples, countTriples(line) inc triples, countTriples(line)
if not continueLine(line, (triples and 1) == 1): break if not continueLine(line, (triples and 1) == 1): break
inc(s.lineOffset) inc(s.lineOffset)
result = min(bufLen, len(s.s) - s.rd) result = min(bufLen, len(s.s) - s.rd)
if result > 0: if result > 0:
copyMem(buf, addr(s.s[s.rd]), result) copyMem(buf, addr(s.s[s.rd]), result)
inc(s.rd, result) inc(s.rd, result)
proc llStreamRead*(s: PLLStream, buf: pointer, bufLen: int): int = proc llStreamRead*(s: PLLStream, buf: pointer, bufLen: int): int =
case s.kind case s.kind
of llsNone: of llsNone:
result = 0 result = 0
of llsString: of llsString:
result = min(bufLen, len(s.s) - s.rd) result = min(bufLen, len(s.s) - s.rd)
if result > 0: if result > 0:
copyMem(buf, addr(s.s[0 + s.rd]), result) copyMem(buf, addr(s.s[0 + s.rd]), result)
inc(s.rd, result) inc(s.rd, result)
of llsFile: of llsFile:
result = readBuffer(s.f, buf, bufLen) result = readBuffer(s.f, buf, bufLen)
of llsStdIn: of llsStdIn:
result = llReadFromStdin(s, buf, bufLen) result = llReadFromStdin(s, buf, bufLen)
proc llStreamReadLine*(s: PLLStream, line: var string): bool = proc llStreamReadLine*(s: PLLStream, line: var string): bool =
setLen(line, 0) setLen(line, 0)
case s.kind case s.kind
@ -152,60 +153,60 @@ proc llStreamReadLine*(s: PLLStream, line: var string): bool =
result = readLine(s.f, line) result = readLine(s.f, line)
of llsStdIn: of llsStdIn:
result = readLine(stdin, line) result = readLine(stdin, line)
proc llStreamWrite*(s: PLLStream, data: string) = proc llStreamWrite*(s: PLLStream, data: string) =
case s.kind case s.kind
of llsNone, llsStdIn: of llsNone, llsStdIn:
discard discard
of llsString: of llsString:
add(s.s, data) add(s.s, data)
inc(s.wr, len(data)) inc(s.wr, len(data))
of llsFile: of llsFile:
write(s.f, data) write(s.f, data)
proc llStreamWriteln*(s: PLLStream, data: string) = proc llStreamWriteln*(s: PLLStream, data: string) =
llStreamWrite(s, data) llStreamWrite(s, data)
llStreamWrite(s, "\n") llStreamWrite(s, "\n")
proc llStreamWrite*(s: PLLStream, data: char) = proc llStreamWrite*(s: PLLStream, data: char) =
var c: char var c: char
case s.kind case s.kind
of llsNone, llsStdIn: of llsNone, llsStdIn:
discard discard
of llsString: of llsString:
add(s.s, data) add(s.s, data)
inc(s.wr) inc(s.wr)
of llsFile: of llsFile:
c = data c = data
discard writeBuffer(s.f, addr(c), sizeof(c)) discard writeBuffer(s.f, addr(c), sizeof(c))
proc llStreamWrite*(s: PLLStream, buf: pointer, buflen: int) = proc llStreamWrite*(s: PLLStream, buf: pointer, buflen: int) =
case s.kind case s.kind
of llsNone, llsStdIn: of llsNone, llsStdIn:
discard discard
of llsString: of llsString:
if buflen > 0: if buflen > 0:
setLen(s.s, len(s.s) + buflen) setLen(s.s, len(s.s) + buflen)
copyMem(addr(s.s[0 + s.wr]), buf, buflen) copyMem(addr(s.s[0 + s.wr]), buf, buflen)
inc(s.wr, buflen) inc(s.wr, buflen)
of llsFile: of llsFile:
discard writeBuffer(s.f, buf, buflen) discard writeBuffer(s.f, buf, buflen)
proc llStreamReadAll*(s: PLLStream): string = proc llStreamReadAll*(s: PLLStream): string =
const const
bufSize = 2048 bufSize = 2048
case s.kind case s.kind
of llsNone, llsStdIn: of llsNone, llsStdIn:
result = "" result = ""
of llsString: of llsString:
if s.rd == 0: result = s.s if s.rd == 0: result = s.s
else: result = substr(s.s, s.rd) else: result = substr(s.s, s.rd)
s.rd = len(s.s) s.rd = len(s.s)
of llsFile: of llsFile:
result = newString(bufSize) result = newString(bufSize)
var bytes = readBuffer(s.f, addr(result[0]), bufSize) var bytes = readBuffer(s.f, addr(result[0]), bufSize)
var i = bytes var i = bytes
while bytes == bufSize: while bytes == bufSize:
setLen(result, i + bufSize) setLen(result, i + bufSize)
bytes = readBuffer(s.f, addr(result[i + 0]), bufSize) bytes = readBuffer(s.f, addr(result[i + 0]), bufSize)
inc(i, bytes) inc(i, bytes)

View file

@ -116,7 +116,7 @@ proc errorSym*(c: PContext, n: PNode): PSym =
result.typ = errorType(c) result.typ = errorType(c)
incl(result.flags, sfDiscardable) incl(result.flags, sfDiscardable)
# pretend it's imported from some unknown module to prevent cascading errors: # pretend it's imported from some unknown module to prevent cascading errors:
if gCmd != cmdInteractive and c.inCompilesContext == 0: if gCmd != cmdInteractive and c.compilesContextId == 0:
c.importTable.addSym(result) c.importTable.addSym(result)
type type

View file

@ -63,6 +63,52 @@ proc lowerTupleUnpacking*(n: PNode; owner: PSym): PNode =
if n.sons[i].kind == nkSym: v.addVar(n.sons[i]) if n.sons[i].kind == nkSym: v.addVar(n.sons[i])
result.add newAsgnStmt(n.sons[i], newTupleAccess(tempAsNode, i)) result.add newAsgnStmt(n.sons[i], newTupleAccess(tempAsNode, i))
proc newTupleAccessRaw*(tup: PNode, i: int): PNode =
result = newNodeI(nkBracketExpr, tup.info)
addSon(result, copyTree(tup))
var lit = newNodeI(nkIntLit, tup.info)
lit.intVal = i
addSon(result, lit)
proc lowerTupleUnpackingForAsgn*(n: PNode; owner: PSym): PNode =
let value = n.lastSon
result = newNodeI(nkStmtList, n.info)
var temp = newSym(skTemp, getIdent(genPrefix), owner, value.info)
var v = newNodeI(nkLetSection, value.info)
let tempAsNode = newIdentNode(getIdent(genPrefix & $temp.id), value.info)
var vpart = newNodeI(nkIdentDefs, tempAsNode.info, 3)
vpart.sons[0] = tempAsNode
vpart.sons[1] = ast.emptyNode
vpart.sons[2] = value
addSon(v, vpart)
result.add(v)
let lhs = n.sons[0]
for i in 0 .. lhs.len-1:
result.add newAsgnStmt(lhs.sons[i], newTupleAccessRaw(tempAsNode, i))
proc lowerSwap*(n: PNode; owner: PSym): PNode =
result = newNodeI(nkStmtList, n.info)
# note: cannot use 'skTemp' here cause we really need the copy for the VM :-(
var temp = newSym(skVar, getIdent(genPrefix), owner, n.info)
temp.typ = n.sons[1].typ
incl(temp.flags, sfFromGeneric)
var v = newNodeI(nkVarSection, n.info)
let tempAsNode = newSymNode(temp)
var vpart = newNodeI(nkIdentDefs, v.info, 3)
vpart.sons[0] = tempAsNode
vpart.sons[1] = ast.emptyNode
vpart.sons[2] = n[1]
addSon(v, vpart)
result.add(v)
result.add newFastAsgnStmt(n[1], n[2])
result.add newFastAsgnStmt(n[2], tempAsNode)
proc createObj*(owner: PSym, info: TLineInfo): PType = proc createObj*(owner: PSym, info: TLineInfo): PType =
result = newType(tyObject, owner) result = newType(tyObject, owner)
rawAddSon(result, nil) rawAddSon(result, nil)
@ -167,7 +213,7 @@ proc genDeref*(n: PNode): PNode =
result.add n result.add n
proc callCodegenProc*(name: string, arg1: PNode; proc callCodegenProc*(name: string, arg1: PNode;
arg2, arg3: PNode = nil): PNode = arg2, arg3, optionalArgs: PNode = nil): PNode =
result = newNodeI(nkCall, arg1.info) result = newNodeI(nkCall, arg1.info)
let sym = magicsys.getCompilerProc(name) let sym = magicsys.getCompilerProc(name)
if sym == nil: if sym == nil:
@ -177,6 +223,9 @@ proc callCodegenProc*(name: string, arg1: PNode;
result.add arg1 result.add arg1
if arg2 != nil: result.add arg2 if arg2 != nil: result.add arg2
if arg3 != nil: result.add arg3 if arg3 != nil: result.add arg3
if optionalArgs != nil:
for i in 1..optionalArgs.len-3:
result.add optionalArgs[i]
result.typ = sym.typ.sons[0] result.typ = sym.typ.sons[0]
proc callProc(a: PNode): PNode = proc callProc(a: PNode): PNode =
@ -483,7 +532,7 @@ proc wrapProcForSpawn*(owner: PSym; spawnExpr: PNode; retType: PType;
barrier, dest: PNode = nil): PNode = barrier, dest: PNode = nil): PNode =
# if 'barrier' != nil, then it is in a 'parallel' section and we # if 'barrier' != nil, then it is in a 'parallel' section and we
# generate quite different code # generate quite different code
let n = spawnExpr[1] let n = spawnExpr[^2]
let spawnKind = spawnResult(retType, barrier!=nil) let spawnKind = spawnResult(retType, barrier!=nil)
case spawnKind case spawnKind
of srVoid: of srVoid:
@ -569,7 +618,7 @@ proc wrapProcForSpawn*(owner: PSym; spawnExpr: PNode; retType: PType;
fvField = newDotExpr(scratchObj, field) fvField = newDotExpr(scratchObj, field)
fvAsExpr = indirectAccess(castExpr, field, n.info) fvAsExpr = indirectAccess(castExpr, field, n.info)
# create flowVar: # create flowVar:
result.add newFastAsgnStmt(fvField, callProc(spawnExpr[2])) result.add newFastAsgnStmt(fvField, callProc(spawnExpr[^1]))
if barrier == nil: if barrier == nil:
result.add callCodegenProc("nimFlowVarCreateSemaphore", fvField) result.add callCodegenProc("nimFlowVarCreateSemaphore", fvField)
@ -584,7 +633,7 @@ proc wrapProcForSpawn*(owner: PSym; spawnExpr: PNode; retType: PType;
let wrapper = createWrapperProc(fn, threadParam, argsParam, let wrapper = createWrapperProc(fn, threadParam, argsParam,
varSection, varInit, call, varSection, varInit, call,
barrierAsExpr, fvAsExpr, spawnKind) barrierAsExpr, fvAsExpr, spawnKind)
result.add callCodegenProc("nimSpawn", wrapper.newSymNode, result.add callCodegenProc("nimSpawn" & $spawnExpr.len, wrapper.newSymNode,
genAddrOf(scratchObj.newSymNode)) genAddrOf(scratchObj.newSymNode), nil, spawnExpr)
if spawnKind == srFlowVar: result.add fvField if spawnKind == srFlowVar: result.add fvField

View file

@ -9,43 +9,35 @@
# Built-in types and compilerprocs are registered here. # Built-in types and compilerprocs are registered here.
import import
ast, astalgo, hashes, msgs, platform, nversion, times, idents, rodread ast, astalgo, hashes, msgs, platform, nversion, times, idents, rodread
var systemModule*: PSym var systemModule*: PSym
proc registerSysType*(t: PType)
# magic symbols in the system module:
proc getSysType*(kind: TTypeKind): PType
proc getCompilerProc*(name: string): PSym
proc registerCompilerProc*(s: PSym)
proc finishSystem*(tab: TStrTable)
proc getSysSym*(name: string): PSym
# implementation
var var
gSysTypes: array[TTypeKind, PType] gSysTypes: array[TTypeKind, PType]
compilerprocs: TStrTable compilerprocs: TStrTable
exposed: TStrTable
proc nilOrSysInt*: PType = gSysTypes[tyInt] proc nilOrSysInt*: PType = gSysTypes[tyInt]
proc registerSysType(t: PType) = proc registerSysType*(t: PType) =
if gSysTypes[t.kind] == nil: gSysTypes[t.kind] = t if gSysTypes[t.kind] == nil: gSysTypes[t.kind] = t
proc newSysType(kind: TTypeKind, size: int): PType = proc newSysType(kind: TTypeKind, size: int): PType =
result = newType(kind, systemModule) result = newType(kind, systemModule)
result.size = size result.size = size
result.align = size.int16 result.align = size.int16
proc getSysSym(name: string): PSym = proc getSysSym*(name: string): PSym =
result = strTableGet(systemModule.tab, getIdent(name)) result = strTableGet(systemModule.tab, getIdent(name))
if result == nil: if result == nil:
rawMessage(errSystemNeeds, name) rawMessage(errSystemNeeds, name)
result = newSym(skError, getIdent(name), systemModule, systemModule.info) result = newSym(skError, getIdent(name), systemModule, systemModule.info)
result.typ = newType(tyError, systemModule) result.typ = newType(tyError, systemModule)
if result.kind == skStub: loadStub(result) if result.kind == skStub: loadStub(result)
if result.kind == skAlias: result = result.owner if result.kind == skAlias: result = result.owner
proc getSysMagic*(name: string, m: TMagic): PSym = proc getSysMagic*(name: string, m: TMagic): PSym =
var ti: TIdentIter var ti: TIdentIter
let id = getIdent(name) let id = getIdent(name)
@ -57,13 +49,13 @@ proc getSysMagic*(name: string, m: TMagic): PSym =
rawMessage(errSystemNeeds, name) rawMessage(errSystemNeeds, name)
result = newSym(skError, id, systemModule, systemModule.info) result = newSym(skError, id, systemModule, systemModule.info)
result.typ = newType(tyError, systemModule) result.typ = newType(tyError, systemModule)
proc sysTypeFromName*(name: string): PType = proc sysTypeFromName*(name: string): PType =
result = getSysSym(name).typ result = getSysSym(name).typ
proc getSysType(kind: TTypeKind): PType = proc getSysType*(kind: TTypeKind): PType =
result = gSysTypes[kind] result = gSysTypes[kind]
if result == nil: if result == nil:
case kind case kind
of tyInt: result = sysTypeFromName("int") of tyInt: result = sysTypeFromName("int")
of tyInt8: result = sysTypeFromName("int8") of tyInt8: result = sysTypeFromName("int8")
@ -87,7 +79,7 @@ proc getSysType(kind: TTypeKind): PType =
of tyNil: result = newSysType(tyNil, ptrSize) of tyNil: result = newSysType(tyNil, ptrSize)
else: internalError("request for typekind: " & $kind) else: internalError("request for typekind: " & $kind)
gSysTypes[kind] = result gSysTypes[kind] = result
if result.kind != kind: if result.kind != kind:
internalError("wanted: " & $kind & " got: " & $result.kind) internalError("wanted: " & $kind & " got: " & $result.kind)
if result == nil: internalError("type not found: " & $kind) if result == nil: internalError("type not found: " & $kind)
@ -97,6 +89,7 @@ var
proc resetSysTypes* = proc resetSysTypes* =
systemModule = nil systemModule = nil
initStrTable(compilerprocs) initStrTable(compilerprocs)
initStrTable(exposed)
for i in low(gSysTypes)..high(gSysTypes): for i in low(gSysTypes)..high(gSysTypes):
gSysTypes[i] = nil gSysTypes[i] = nil
@ -163,8 +156,8 @@ proc setIntLitType*(result: PNode) =
result.typ = getSysType(tyInt64) result.typ = getSysType(tyInt64)
else: internalError(result.info, "invalid int size") else: internalError(result.info, "invalid int size")
proc getCompilerProc(name: string): PSym = proc getCompilerProc*(name: string): PSym =
var ident = getIdent(name, hashIgnoreStyle(name)) let ident = getIdent(name)
result = strTableGet(compilerprocs, ident) result = strTableGet(compilerprocs, ident)
if result == nil: if result == nil:
result = strTableGet(rodCompilerprocs, ident) result = strTableGet(rodCompilerprocs, ident)
@ -173,9 +166,22 @@ proc getCompilerProc(name: string): PSym =
if result.kind == skStub: loadStub(result) if result.kind == skStub: loadStub(result)
if result.kind == skAlias: result = result.owner if result.kind == skAlias: result = result.owner
proc registerCompilerProc(s: PSym) = proc registerCompilerProc*(s: PSym) =
strTableAdd(compilerprocs, s) strTableAdd(compilerprocs, s)
proc finishSystem(tab: TStrTable) = discard proc registerNimScriptSymbol*(s: PSym) =
# Nimscript symbols must be al unique:
let conflict = strTableGet(exposed, s.name)
if conflict == nil:
strTableAdd(exposed, s)
else:
localError(s.info, "symbol conflicts with other .exportNims symbol at: " &
$conflict.info)
proc getNimScriptSymbol*(name: string): PSym =
strTableGet(exposed, getIdent(name))
proc resetNimScriptSymbols*() = initStrTable(exposed)
initStrTable(compilerprocs) initStrTable(compilerprocs)
initStrTable(exposed)

View file

@ -15,7 +15,7 @@ import
wordrecg, sem, semdata, idents, passes, docgen, extccomp, wordrecg, sem, semdata, idents, passes, docgen, extccomp,
cgen, jsgen, json, nversion, cgen, jsgen, json, nversion,
platform, nimconf, importer, passaux, depends, vm, vmdef, types, idgen, platform, nimconf, importer, passaux, depends, vm, vmdef, types, idgen,
tables, docgen2, service, parser, modules, ccgutils, sigmatch, ropes, lists docgen2, service, parser, modules, ccgutils, sigmatch, ropes, lists
from magicsys import systemModule, resetSysTypes from magicsys import systemModule, resetSysTypes
@ -63,14 +63,14 @@ proc commandCompileToC =
compileProject() compileProject()
cgenWriteModules() cgenWriteModules()
if gCmd != cmdRun: if gCmd != cmdRun:
extccomp.callCCompiler(if gProjectName == "-": "stdinfile" else: changeFileExt(gProjectFull, "")) extccomp.callCCompiler(changeFileExt(gProjectFull, ""))
if isServing: if isServing:
# caas will keep track only of the compilation commands # caas will keep track only of the compilation commands
lastCaasCmd = curCaasCmd lastCaasCmd = curCaasCmd
resetCgenModules() resetCgenModules()
for i in 0 .. <gMemCacheData.len: for i in 0 .. <gMemCacheData.len:
gMemCacheData[i].crcStatus = crcCached gMemCacheData[i].hashStatus = hashCached
gMemCacheData[i].needsRecompile = Maybe gMemCacheData[i].needsRecompile = Maybe
# XXX: clean these global vars # XXX: clean these global vars
@ -116,7 +116,7 @@ proc interactivePasses =
#incl(gGlobalOptions, optSafeCode) #incl(gGlobalOptions, optSafeCode)
#setTarget(osNimrodVM, cpuNimrodVM) #setTarget(osNimrodVM, cpuNimrodVM)
initDefines() initDefines()
defineSymbol("nimrodvm") defineSymbol("nimscript")
when hasFFI: defineSymbol("nimffi") when hasFFI: defineSymbol("nimffi")
registerPass(verbosePass) registerPass(verbosePass)
registerPass(semPass) registerPass(semPass)
@ -190,7 +190,6 @@ proc resetMemory =
resetRopeCache() resetRopeCache()
resetSysTypes() resetSysTypes()
gOwners = @[] gOwners = @[]
rangeDestructorProc = nil
for i in low(buckets)..high(buckets): for i in low(buckets)..high(buckets):
buckets[i] = nil buckets[i] = nil
idAnon = nil idAnon = nil
@ -237,7 +236,7 @@ proc mainCommand* =
when SimulateCaasMemReset: when SimulateCaasMemReset:
gGlobalOptions.incl(optCaasEnabled) gGlobalOptions.incl(optCaasEnabled)
# In "nimrod serve" scenario, each command must reset the registered passes # In "nim serve" scenario, each command must reset the registered passes
clearPasses() clearPasses()
gLastCmdTime = epochTime() gLastCmdTime = epochTime()
appendStr(searchPaths, options.libpath) appendStr(searchPaths, options.libpath)
@ -318,11 +317,12 @@ proc mainCommand* =
(key: "lib_paths", val: libpaths) (key: "lib_paths", val: libpaths)
] ]
outWriteln($dumpdata) msgWriteln($dumpdata, {msgStdout, msgSkipHook})
else: else:
outWriteln("-- list of currently defined symbols --") msgWriteln("-- list of currently defined symbols --",
for s in definedSymbolNames(): outWriteln(s) {msgStdout, msgSkipHook})
outWriteln("-- end of list --") for s in definedSymbolNames(): msgWriteln(s, {msgStdout, msgSkipHook})
msgWriteln("-- end of list --", {msgStdout, msgSkipHook})
for it in iterSearchPath(searchPaths): msgWriteln(it) for it in iterSearchPath(searchPaths): msgWriteln(it)
of "check": of "check":
@ -337,7 +337,7 @@ proc mainCommand* =
wantMainModule() wantMainModule()
commandScan() commandScan()
msgWriteln("Beware: Indentation tokens depend on the parser\'s state!") msgWriteln("Beware: Indentation tokens depend on the parser\'s state!")
of "i": of "secret":
gCmd = cmdInteractive gCmd = cmdInteractive
commandInteractive() commandInteractive()
of "e": of "e":
@ -356,12 +356,14 @@ proc mainCommand* =
gGlobalOptions.incl(optCaasEnabled) gGlobalOptions.incl(optCaasEnabled)
msgs.gErrorMax = high(int) # do not stop after first error msgs.gErrorMax = high(int) # do not stop after first error
serve(mainCommand) serve(mainCommand)
of "nop", "help":
# prevent the "success" message:
gCmd = cmdDump
else: else:
rawMessage(errInvalidCommandX, command) rawMessage(errInvalidCommandX, command)
if (msgs.gErrorCounter == 0 and if msgs.gErrorCounter == 0 and
gCmd notin {cmdInterpret, cmdRun, cmdDump} and gCmd notin {cmdInterpret, cmdRun, cmdDump}:
gVerbosity > 0):
rawMessage(hintSuccessX, [$gLinesCompiled, rawMessage(hintSuccessX, [$gLinesCompiled,
formatFloat(epochTime() - gLastCmdTime, ffDecimal, 3), formatFloat(epochTime() - gLastCmdTime, ffDecimal, 3),
formatSize(getTotalMem()), formatSize(getTotalMem()),
@ -379,3 +381,4 @@ proc mainCommand* =
when SimulateCaasMemReset: when SimulateCaasMemReset:
resetMemory() resetMemory()
resetAttributes()

View file

@ -10,19 +10,19 @@
## implements the module handling ## implements the module handling
import import
ast, astalgo, magicsys, crc, rodread, msgs, cgendata, sigmatch, options, ast, astalgo, magicsys, securehash, rodread, msgs, cgendata, sigmatch, options,
idents, os, lexer, idgen, passes, syntaxes idents, os, lexer, idgen, passes, syntaxes, llstream
type type
TNeedRecompile* = enum Maybe, No, Yes, Probing, Recompiled TNeedRecompile* = enum Maybe, No, Yes, Probing, Recompiled
TCrcStatus* = enum crcNotTaken, crcCached, crcHasChanged, crcNotChanged THashStatus* = enum hashNotTaken, hashCached, hashHasChanged, hashNotChanged
TModuleInMemory* = object TModuleInMemory* = object
compiledAt*: float compiledAt*: float
crc*: TCrc32 hash*: SecureHash
deps*: seq[int32] ## XXX: slurped files are currently not tracked deps*: seq[int32] ## XXX: slurped files are currently not tracked
needsRecompile*: TNeedRecompile needsRecompile*: TNeedRecompile
crcStatus*: TCrcStatus hashStatus*: THashStatus
var var
gCompiledModules: seq[PSym] = @[] gCompiledModules: seq[PSym] = @[]
@ -34,36 +34,36 @@ proc getModule(fileIdx: int32): PSym =
if fileIdx >= 0 and fileIdx < gCompiledModules.len: if fileIdx >= 0 and fileIdx < gCompiledModules.len:
result = gCompiledModules[fileIdx] result = gCompiledModules[fileIdx]
template crc(x: PSym): expr = template hash(x: PSym): expr =
gMemCacheData[x.position].crc gMemCacheData[x.position].hash
proc crcChanged(fileIdx: int32): bool = proc hashChanged(fileIdx: int32): bool =
internalAssert fileIdx >= 0 and fileIdx < gMemCacheData.len internalAssert fileIdx >= 0 and fileIdx < gMemCacheData.len
template updateStatus = template updateStatus =
gMemCacheData[fileIdx].crcStatus = if result: crcHasChanged gMemCacheData[fileIdx].hashStatus = if result: hashHasChanged
else: crcNotChanged else: hashNotChanged
# echo "TESTING CRC: ", fileIdx.toFilename, " ", result # echo "TESTING Hash: ", fileIdx.toFilename, " ", result
case gMemCacheData[fileIdx].crcStatus: case gMemCacheData[fileIdx].hashStatus:
of crcHasChanged: of hashHasChanged:
result = true result = true
of crcNotChanged: of hashNotChanged:
result = false result = false
of crcCached: of hashCached:
let newCrc = crcFromFile(fileIdx.toFilename) let newHash = secureHashFile(fileIdx.toFullPath)
result = newCrc != gMemCacheData[fileIdx].crc result = newHash != gMemCacheData[fileIdx].hash
gMemCacheData[fileIdx].crc = newCrc gMemCacheData[fileIdx].hash = newHash
updateStatus() updateStatus()
of crcNotTaken: of hashNotTaken:
gMemCacheData[fileIdx].crc = crcFromFile(fileIdx.toFilename) gMemCacheData[fileIdx].hash = secureHashFile(fileIdx.toFullPath)
result = true result = true
updateStatus() updateStatus()
proc doCRC(fileIdx: int32) = proc doHash(fileIdx: int32) =
if gMemCacheData[fileIdx].crcStatus == crcNotTaken: if gMemCacheData[fileIdx].hashStatus == hashNotTaken:
# echo "FIRST CRC: ", fileIdx.ToFilename # echo "FIRST Hash: ", fileIdx.ToFilename
gMemCacheData[fileIdx].crc = crcFromFile(fileIdx.toFilename) gMemCacheData[fileIdx].hash = secureHashFile(fileIdx.toFullPath)
proc addDep(x: PSym, dep: int32) = proc addDep(x: PSym, dep: int32) =
growCache gMemCacheData, dep growCache gMemCacheData, dep
@ -78,6 +78,13 @@ proc resetModule*(fileIdx: int32) =
if fileIdx <% cgendata.gModules.len: if fileIdx <% cgendata.gModules.len:
cgendata.gModules[fileIdx] = nil cgendata.gModules[fileIdx] = nil
proc resetModule*(module: PSym) =
let conflict = getModule(module.position.int32)
if conflict == nil: return
doAssert conflict == module
resetModule(module.position.int32)
initStrTable(module.tab)
proc resetAllModules* = proc resetAllModules* =
for i in 0..gCompiledModules.high: for i in 0..gCompiledModules.high:
if gCompiledModules[i] != nil: if gCompiledModules[i] != nil:
@ -85,6 +92,14 @@ proc resetAllModules* =
resetPackageCache() resetPackageCache()
# for m in cgenModules(): echo "CGEN MODULE FOUND" # for m in cgenModules(): echo "CGEN MODULE FOUND"
proc resetAllModulesHard* =
resetPackageCache()
gCompiledModules.setLen 0
gMemCacheData.setLen 0
magicsys.resetSysTypes()
# XXX
#gOwners = @[]
proc checkDepMem(fileIdx: int32): TNeedRecompile = proc checkDepMem(fileIdx: int32): TNeedRecompile =
template markDirty = template markDirty =
resetModule(fileIdx) resetModule(fileIdx)
@ -94,9 +109,9 @@ proc checkDepMem(fileIdx: int32): TNeedRecompile =
return gMemCacheData[fileIdx].needsRecompile return gMemCacheData[fileIdx].needsRecompile
if optForceFullMake in gGlobalOptions or if optForceFullMake in gGlobalOptions or
crcChanged(fileIdx): hashChanged(fileIdx):
markDirty markDirty
if gMemCacheData[fileIdx].deps != nil: if gMemCacheData[fileIdx].deps != nil:
gMemCacheData[fileIdx].needsRecompile = Probing gMemCacheData[fileIdx].needsRecompile = Probing
for dep in gMemCacheData[fileIdx].deps: for dep in gMemCacheData[fileIdx].deps:
@ -104,30 +119,30 @@ proc checkDepMem(fileIdx: int32): TNeedRecompile =
if d in {Yes, Recompiled}: if d in {Yes, Recompiled}:
# echo fileIdx.toFilename, " depends on ", dep.toFilename, " ", d # echo fileIdx.toFilename, " depends on ", dep.toFilename, " ", d
markDirty markDirty
gMemCacheData[fileIdx].needsRecompile = No gMemCacheData[fileIdx].needsRecompile = No
return No return No
proc newModule(fileIdx: int32): PSym = proc newModule(fileIdx: int32): PSym =
# We cannot call ``newSym`` here, because we have to circumvent the ID # We cannot call ``newSym`` here, because we have to circumvent the ID
# mechanism, which we do in order to assign each module a persistent ID. # mechanism, which we do in order to assign each module a persistent ID.
new(result) new(result)
result.id = - 1 # for better error checking result.id = - 1 # for better error checking
result.kind = skModule result.kind = skModule
let filename = fileIdx.toFullPath let filename = fileIdx.toFullPath
result.name = getIdent(splitFile(filename).name) result.name = getIdent(splitFile(filename).name)
if result.name.s != "-" and not isNimIdentifier(result.name.s): if not isNimIdentifier(result.name.s):
rawMessage(errInvalidModuleName, result.name.s) rawMessage(errInvalidModuleName, result.name.s)
result.info = newLineInfo(fileIdx, 1, 1) result.info = newLineInfo(fileIdx, 1, 1)
result.owner = newSym(skPackage, getIdent(getPackageName(filename)), nil, result.owner = newSym(skPackage, getIdent(getPackageName(filename)), nil,
result.info) result.info)
result.position = fileIdx result.position = fileIdx
growCache gMemCacheData, fileIdx growCache gMemCacheData, fileIdx
growCache gCompiledModules, fileIdx growCache gCompiledModules, fileIdx
gCompiledModules[result.position] = result gCompiledModules[result.position] = result
incl(result.flags, sfUsed) incl(result.flags, sfUsed)
initStrTable(result.tab) initStrTable(result.tab)
strTableAdd(result.tab, result) # a module knows itself strTableAdd(result.tab, result) # a module knows itself
@ -143,16 +158,19 @@ proc compileModule*(fileIdx: int32, flags: TSymFlags): PSym =
result.flags = result.flags + flags result.flags = result.flags + flags
if gCmd in {cmdCompileToC, cmdCompileToCpp, cmdCheck, cmdIdeTools}: if gCmd in {cmdCompileToC, cmdCompileToCpp, cmdCheck, cmdIdeTools}:
rd = handleSymbolFile(result) rd = handleSymbolFile(result)
if result.id < 0: if result.id < 0:
internalError("handleSymbolFile should have set the module\'s ID") internalError("handleSymbolFile should have set the module\'s ID")
return return
else: else:
result.id = getID() result.id = getID()
processModule(result, nil, rd) if sfMainModule in flags and gProjectIsStdin:
processModule(result, llStreamOpen(stdin), rd)
else:
processModule(result, nil, rd)
if optCaasEnabled in gGlobalOptions: if optCaasEnabled in gGlobalOptions:
gMemCacheData[fileIdx].compiledAt = gLastCmdTime gMemCacheData[fileIdx].compiledAt = gLastCmdTime
gMemCacheData[fileIdx].needsRecompile = Recompiled gMemCacheData[fileIdx].needsRecompile = Recompiled
doCRC fileIdx doHash fileIdx
else: else:
if checkDepMem(fileIdx) == Yes: if checkDepMem(fileIdx) == Yes:
result = compileModule(fileIdx, flags) result = compileModule(fileIdx, flags)
@ -171,7 +189,7 @@ proc includeModule*(s: PSym, fileIdx: int32): PNode {.procvar.} =
if optCaasEnabled in gGlobalOptions: if optCaasEnabled in gGlobalOptions:
growCache gMemCacheData, fileIdx growCache gMemCacheData, fileIdx
addDep(s, fileIdx) addDep(s, fileIdx)
doCRC(fileIdx) doHash(fileIdx)
proc `==^`(a, b: string): bool = proc `==^`(a, b: string): bool =
try: try:
@ -202,9 +220,8 @@ proc compileProject*(projectFileIdx = -1'i32) =
compileSystemModule() compileSystemModule()
discard compileModule(projectFile, {sfMainModule}) discard compileModule(projectFile, {sfMainModule})
var stdinModule: PSym proc makeModule*(filename: string): PSym =
proc makeStdinModule*(): PSym = result = newModule(fileInfoIdx filename)
if stdinModule == nil: result.id = getID()
stdinModule = newModule(fileInfoIdx"stdin")
stdinModule.id = getID() proc makeStdinModule*(): PSym = makeModule"stdin"
result = stdinModule

View file

@ -8,7 +8,7 @@
# #
import import
options, strutils, os, tables, ropes, platform options, strutils, os, tables, ropes, platform, terminal, macros
type type
TMsgKind* = enum TMsgKind* = enum
@ -17,10 +17,9 @@ type
errIntLiteralExpected, errInvalidCharacterConstant, errIntLiteralExpected, errInvalidCharacterConstant,
errClosingTripleQuoteExpected, errClosingQuoteExpected, errClosingTripleQuoteExpected, errClosingQuoteExpected,
errTabulatorsAreNotAllowed, errInvalidToken, errLineTooLong, errTabulatorsAreNotAllowed, errInvalidToken, errLineTooLong,
errInvalidNumber, errNumberOutOfRange, errNnotAllowedInCharacter, errInvalidNumber, errInvalidNumberOctalCode, errNumberOutOfRange,
errClosingBracketExpected, errMissingFinalQuote, errIdentifierExpected, errNnotAllowedInCharacter, errClosingBracketExpected, errMissingFinalQuote,
errNewlineExpected, errIdentifierExpected, errNewlineExpected, errInvalidModuleName,
errInvalidModuleName,
errOperatorExpected, errTokenExpected, errStringAfterIncludeExpected, errOperatorExpected, errTokenExpected, errStringAfterIncludeExpected,
errRecursiveDependencyX, errOnOrOffExpected, errNoneSpeedOrSizeExpected, errRecursiveDependencyX, errOnOrOffExpected, errNoneSpeedOrSizeExpected,
errInvalidPragma, errUnknownPragma, errInvalidDirectiveX, errInvalidPragma, errUnknownPragma, errInvalidDirectiveX,
@ -31,11 +30,14 @@ type
errStmtInvalidAfterReturn, errStmtExpected, errInvalidLabel, errStmtInvalidAfterReturn, errStmtExpected, errInvalidLabel,
errInvalidCmdLineOption, errCmdLineArgExpected, errCmdLineNoArgExpected, errInvalidCmdLineOption, errCmdLineArgExpected, errCmdLineNoArgExpected,
errInvalidVarSubstitution, errUnknownVar, errUnknownCcompiler, errInvalidVarSubstitution, errUnknownVar, errUnknownCcompiler,
errOnOrOffExpectedButXFound, errNoneBoehmRefcExpectedButXFound, errOnOrOffExpectedButXFound, errOnOffOrListExpectedButXFound,
errNoneBoehmRefcExpectedButXFound,
errNoneSpeedOrSizeExpectedButXFound, errGuiConsoleOrLibExpectedButXFound, errNoneSpeedOrSizeExpectedButXFound, errGuiConsoleOrLibExpectedButXFound,
errUnknownOS, errUnknownCPU, errGenOutExpectedButXFound, errUnknownOS, errUnknownCPU, errGenOutExpectedButXFound,
errArgsNeedRunOption, errInvalidMultipleAsgn, errColonOrEqualsExpected, errArgsNeedRunOption, errInvalidMultipleAsgn, errColonOrEqualsExpected,
errExprExpected, errUndeclaredIdentifier, errUseQualifier, errTypeExpected, errExprExpected, errUndeclaredIdentifier, errUndeclaredField,
errUndeclaredRoutine, errUseQualifier,
errTypeExpected,
errSystemNeeds, errExecutionOfProgramFailed, errNotOverloadable, errSystemNeeds, errExecutionOfProgramFailed, errNotOverloadable,
errInvalidArgForX, errStmtHasNoEffect, errXExpectsTypeOrValue, errInvalidArgForX, errStmtHasNoEffect, errXExpectsTypeOrValue,
errXExpectsArrayType, errIteratorCannotBeInstantiated, errExprXAmbiguous, errXExpectsArrayType, errIteratorCannotBeInstantiated, errExprXAmbiguous,
@ -80,7 +82,7 @@ type
errArrayExpectsTwoTypeParams, errInvalidVisibilityX, errInitHereNotAllowed, errArrayExpectsTwoTypeParams, errInvalidVisibilityX, errInitHereNotAllowed,
errXCannotBeAssignedTo, errIteratorNotAllowed, errXNeedsReturnType, errXCannotBeAssignedTo, errIteratorNotAllowed, errXNeedsReturnType,
errNoReturnTypeDeclared, errNoReturnTypeDeclared,
errInvalidCommandX, errXOnlyAtModuleScope, errNoCommand, errInvalidCommandX, errXOnlyAtModuleScope,
errXNeedsParamObjectType, errXNeedsParamObjectType,
errTemplateInstantiationTooNested, errInstantiationFrom, errTemplateInstantiationTooNested, errInstantiationFrom,
errInvalidIndexValueForTuple, errCommandExpectsFilename, errInvalidIndexValueForTuple, errCommandExpectsFilename,
@ -106,6 +108,8 @@ type
errCannotInferReturnType, errCannotInferReturnType,
errGenericLambdaNotAllowed, errGenericLambdaNotAllowed,
errCompilerDoesntSupportTarget, errCompilerDoesntSupportTarget,
errExternalAssemblerNotFound,
errExternalAssemblerNotValid,
errUser, errUser,
warnCannotOpenFile, warnCannotOpenFile,
warnOctalEscape, warnXIsNeverRead, warnXmightNotBeenInit, warnOctalEscape, warnXIsNeverRead, warnXmightNotBeenInit,
@ -114,7 +118,7 @@ type
warnUnknownSubstitutionX, warnLanguageXNotSupported, warnUnknownSubstitutionX, warnLanguageXNotSupported,
warnFieldXNotSupported, warnCommentXIgnored, warnFieldXNotSupported, warnCommentXIgnored,
warnNilStatement, warnTypelessParam, warnNilStatement, warnTypelessParam,
warnDifferentHeaps, warnWriteToForeignHeap, warnUnsafeCode, warnUseBase, warnWriteToForeignHeap, warnUnsafeCode,
warnEachIdentIsTuple, warnShadowIdent, warnEachIdentIsTuple, warnShadowIdent,
warnProveInit, warnProveField, warnProveIndex, warnGcUnsafe, warnGcUnsafe2, warnProveInit, warnProveField, warnProveIndex, warnGcUnsafe, warnGcUnsafe2,
warnUninit, warnGcMem, warnDestructor, warnLockLevel, warnResultShadowed, warnUninit, warnGcMem, warnDestructor, warnLockLevel, warnResultShadowed,
@ -124,6 +128,8 @@ type
hintConvFromXtoItselfNotNeeded, hintExprAlwaysX, hintQuitCalled, hintConvFromXtoItselfNotNeeded, hintExprAlwaysX, hintQuitCalled,
hintProcessing, hintCodeBegin, hintCodeEnd, hintConf, hintPath, hintProcessing, hintCodeBegin, hintCodeEnd, hintConf, hintPath,
hintConditionAlwaysTrue, hintName, hintPattern, hintConditionAlwaysTrue, hintName, hintPattern,
hintExecuting, hintLinking, hintDependency,
hintSource, hintStackTrace, hintGCStats,
hintUser hintUser
const const
@ -143,6 +149,7 @@ const
errInvalidToken: "invalid token: $1", errInvalidToken: "invalid token: $1",
errLineTooLong: "line too long", errLineTooLong: "line too long",
errInvalidNumber: "$1 is not a valid number", errInvalidNumber: "$1 is not a valid number",
errInvalidNumberOctalCode: "$1 is not a valid number; did you mean octal? Then use one of '0o', '0c' or '0C'.",
errNumberOutOfRange: "number $1 out of valid range", errNumberOutOfRange: "number $1 out of valid range",
errNnotAllowedInCharacter: "\\n not allowed in character literal", errNnotAllowedInCharacter: "\\n not allowed in character literal",
errClosingBracketExpected: "closing ']' expected, but end of file reached", errClosingBracketExpected: "closing ']' expected, but end of file reached",
@ -179,6 +186,7 @@ const
errUnknownVar: "unknown variable: \'$1\'", errUnknownVar: "unknown variable: \'$1\'",
errUnknownCcompiler: "unknown C compiler: \'$1\'", errUnknownCcompiler: "unknown C compiler: \'$1\'",
errOnOrOffExpectedButXFound: "\'on\' or \'off\' expected, but \'$1\' found", errOnOrOffExpectedButXFound: "\'on\' or \'off\' expected, but \'$1\' found",
errOnOffOrListExpectedButXFound: "\'on\', \'off\' or \'list\' expected, but \'$1\' found",
errNoneBoehmRefcExpectedButXFound: "'none', 'boehm' or 'refc' expected, but '$1' found", errNoneBoehmRefcExpectedButXFound: "'none', 'boehm' or 'refc' expected, but '$1' found",
errNoneSpeedOrSizeExpectedButXFound: "'none', 'speed' or 'size' expected, but '$1' found", errNoneSpeedOrSizeExpectedButXFound: "'none', 'speed' or 'size' expected, but '$1' found",
errGuiConsoleOrLibExpectedButXFound: "'gui', 'console' or 'lib' expected, but '$1' found", errGuiConsoleOrLibExpectedButXFound: "'gui', 'console' or 'lib' expected, but '$1' found",
@ -190,10 +198,12 @@ const
errColonOrEqualsExpected: "\':\' or \'=\' expected, but found \'$1\'", errColonOrEqualsExpected: "\':\' or \'=\' expected, but found \'$1\'",
errExprExpected: "expression expected, but found \'$1\'", errExprExpected: "expression expected, but found \'$1\'",
errUndeclaredIdentifier: "undeclared identifier: \'$1\'", errUndeclaredIdentifier: "undeclared identifier: \'$1\'",
errUndeclaredField: "undeclared field: \'$1\'",
errUndeclaredRoutine: "attempting to call undeclared routine: \'$1\'",
errUseQualifier: "ambiguous identifier: \'$1\' -- use a qualifier", errUseQualifier: "ambiguous identifier: \'$1\' -- use a qualifier",
errTypeExpected: "type expected", errTypeExpected: "type expected",
errSystemNeeds: "system module needs \'$1\'", errSystemNeeds: "system module needs \'$1\'",
errExecutionOfProgramFailed: "execution of an external program failed", errExecutionOfProgramFailed: "execution of an external program failed: '$1'",
errNotOverloadable: "overloaded \'$1\' leads to ambiguous calls", errNotOverloadable: "overloaded \'$1\' leads to ambiguous calls",
errInvalidArgForX: "invalid argument for \'$1\'", errInvalidArgForX: "invalid argument for \'$1\'",
errStmtHasNoEffect: "statement has no effect", errStmtHasNoEffect: "statement has no effect",
@ -308,6 +318,7 @@ const
errIteratorNotAllowed: "iterators can only be defined at the module\'s top level", errIteratorNotAllowed: "iterators can only be defined at the module\'s top level",
errXNeedsReturnType: "$1 needs a return type", errXNeedsReturnType: "$1 needs a return type",
errNoReturnTypeDeclared: "no return type declared", errNoReturnTypeDeclared: "no return type declared",
errNoCommand: "no command given",
errInvalidCommandX: "invalid command: \'$1\'", errInvalidCommandX: "invalid command: \'$1\'",
errXOnlyAtModuleScope: "\'$1\' is only allowed at top level", errXOnlyAtModuleScope: "\'$1\' is only allowed at top level",
errXNeedsParamObjectType: "'$1' needs a parameter that has an object type", errXNeedsParamObjectType: "'$1' needs a parameter that has an object type",
@ -362,55 +373,63 @@ const
"it is used as an operand to another routine and the types " & "it is used as an operand to another routine and the types " &
"of the generic paramers can be inferred from the expected signature.", "of the generic paramers can be inferred from the expected signature.",
errCompilerDoesntSupportTarget: "The current compiler \'$1\' doesn't support the requested compilation target", errCompilerDoesntSupportTarget: "The current compiler \'$1\' doesn't support the requested compilation target",
errExternalAssemblerNotFound: "External assembler not found",
errExternalAssemblerNotValid: "External assembler '$1' is not a valid assembler",
errUser: "$1", errUser: "$1",
warnCannotOpenFile: "cannot open \'$1\' [CannotOpenFile]", warnCannotOpenFile: "cannot open \'$1\'",
warnOctalEscape: "octal escape sequences do not exist; leading zero is ignored [OctalEscape]", warnOctalEscape: "octal escape sequences do not exist; leading zero is ignored",
warnXIsNeverRead: "\'$1\' is never read [XIsNeverRead]", warnXIsNeverRead: "\'$1\' is never read",
warnXmightNotBeenInit: "\'$1\' might not have been initialized [XmightNotBeenInit]", warnXmightNotBeenInit: "\'$1\' might not have been initialized",
warnDeprecated: "$1 is deprecated [Deprecated]", warnDeprecated: "$1 is deprecated",
warnConfigDeprecated: "config file '$1' is deprecated [ConfigDeprecated]", warnConfigDeprecated: "config file '$1' is deprecated",
warnSmallLshouldNotBeUsed: "\'l\' should not be used as an identifier; may look like \'1\' (one) [SmallLshouldNotBeUsed]", warnSmallLshouldNotBeUsed: "\'l\' should not be used as an identifier; may look like \'1\' (one)",
warnUnknownMagic: "unknown magic \'$1\' might crash the compiler [UnknownMagic]", warnUnknownMagic: "unknown magic \'$1\' might crash the compiler",
warnRedefinitionOfLabel: "redefinition of label \'$1\' [RedefinitionOfLabel]", warnRedefinitionOfLabel: "redefinition of label \'$1\'",
warnUnknownSubstitutionX: "unknown substitution \'$1\' [UnknownSubstitutionX]", warnUnknownSubstitutionX: "unknown substitution \'$1\'",
warnLanguageXNotSupported: "language \'$1\' not supported [LanguageXNotSupported]", warnLanguageXNotSupported: "language \'$1\' not supported",
warnFieldXNotSupported: "field \'$1\' not supported [FieldXNotSupported]", warnFieldXNotSupported: "field \'$1\' not supported",
warnCommentXIgnored: "comment \'$1\' ignored [CommentXIgnored]", warnCommentXIgnored: "comment \'$1\' ignored",
warnNilStatement: "'nil' statement is deprecated; use an empty 'discard' statement instead [NilStmt]", warnNilStatement: "'nil' statement is deprecated; use an empty 'discard' statement instead",
warnTypelessParam: "'$1' has no type. Typeless parameters are deprecated; only allowed for 'template' [TypelessParam]", warnTypelessParam: "'$1' has no type. Typeless parameters are deprecated; only allowed for 'template'",
warnDifferentHeaps: "possible inconsistency of thread local heaps [DifferentHeaps]", warnUseBase: "use {.base.} for base methods; baseless methods are deprecated",
warnWriteToForeignHeap: "write to foreign heap [WriteToForeignHeap]", warnWriteToForeignHeap: "write to foreign heap",
warnUnsafeCode: "unsafe code: '$1' [UnsafeCode]", warnUnsafeCode: "unsafe code: '$1'",
warnEachIdentIsTuple: "each identifier is a tuple [EachIdentIsTuple]", warnEachIdentIsTuple: "each identifier is a tuple",
warnShadowIdent: "shadowed identifier: '$1' [ShadowIdent]", warnShadowIdent: "shadowed identifier: '$1'",
warnProveInit: "Cannot prove that '$1' is initialized. This will become a compile time error in the future. [ProveInit]", warnProveInit: "Cannot prove that '$1' is initialized. This will become a compile time error in the future.",
warnProveField: "cannot prove that field '$1' is accessible [ProveField]", warnProveField: "cannot prove that field '$1' is accessible",
warnProveIndex: "cannot prove index '$1' is valid [ProveIndex]", warnProveIndex: "cannot prove index '$1' is valid",
warnGcUnsafe: "not GC-safe: '$1' [GcUnsafe]", warnGcUnsafe: "not GC-safe: '$1'",
warnGcUnsafe2: "$1", warnGcUnsafe2: "$1",
warnUninit: "'$1' might not have been initialized [Uninit]", warnUninit: "'$1' might not have been initialized",
warnGcMem: "'$1' uses GC'ed memory [GcMem]", warnGcMem: "'$1' uses GC'ed memory",
warnDestructor: "usage of a type with a destructor in a non destructible context. This will become a compile time error in the future. [Destructor]", warnDestructor: "usage of a type with a destructor in a non destructible context. This will become a compile time error in the future.",
warnLockLevel: "$1 [LockLevel]", warnLockLevel: "$1",
warnResultShadowed: "Special variable 'result' is shadowed. [ResultShadowed]", warnResultShadowed: "Special variable 'result' is shadowed.",
warnUser: "$1 [User]", warnUser: "$1",
hintSuccess: "operation successful [Success]", hintSuccess: "operation successful",
hintSuccessX: "operation successful ($# lines compiled; $# sec total; $#; $#) [SuccessX]", hintSuccessX: "operation successful ($# lines compiled; $# sec total; $#; $#)",
hintLineTooLong: "line too long [LineTooLong]", hintLineTooLong: "line too long",
hintXDeclaredButNotUsed: "\'$1\' is declared but not used [XDeclaredButNotUsed]", hintXDeclaredButNotUsed: "\'$1\' is declared but not used",
hintConvToBaseNotNeeded: "conversion to base object is not needed [ConvToBaseNotNeeded]", hintConvToBaseNotNeeded: "conversion to base object is not needed",
hintConvFromXtoItselfNotNeeded: "conversion from $1 to itself is pointless [ConvFromXtoItselfNotNeeded]", hintConvFromXtoItselfNotNeeded: "conversion from $1 to itself is pointless",
hintExprAlwaysX: "expression evaluates always to \'$1\' [ExprAlwaysX]", hintExprAlwaysX: "expression evaluates always to \'$1\'",
hintQuitCalled: "quit() called [QuitCalled]", hintQuitCalled: "quit() called",
hintProcessing: "$1 [Processing]", hintProcessing: "$1",
hintCodeBegin: "generated code listing: [CodeBegin]", hintCodeBegin: "generated code listing:",
hintCodeEnd: "end of listing [CodeEnd]", hintCodeEnd: "end of listing",
hintConf: "used config file \'$1\' [Conf]", hintConf: "used config file \'$1\'",
hintPath: "added path: '$1' [Path]", hintPath: "added path: '$1'",
hintConditionAlwaysTrue: "condition is always true: '$1' [CondTrue]", hintConditionAlwaysTrue: "condition is always true: '$1'",
hintName: "name should be: '$1' [Name]", hintName: "name should be: '$1'",
hintPattern: "$1 [Pattern]", hintPattern: "$1",
hintUser: "$1 [User]"] hintExecuting: "$1",
hintLinking: "",
hintDependency: "$1",
hintSource: "$1",
hintStackTrace: "$1",
hintGCStats: "$1",
hintUser: "$1"]
const const
WarningsToStr*: array[0..30, string] = ["CannotOpenFile", "OctalEscape", WarningsToStr*: array[0..30, string] = ["CannotOpenFile", "OctalEscape",
@ -420,15 +439,16 @@ const
"RedefinitionOfLabel", "UnknownSubstitutionX", "RedefinitionOfLabel", "UnknownSubstitutionX",
"LanguageXNotSupported", "FieldXNotSupported", "LanguageXNotSupported", "FieldXNotSupported",
"CommentXIgnored", "NilStmt", "CommentXIgnored", "NilStmt",
"TypelessParam", "DifferentHeaps", "WriteToForeignHeap", "TypelessParam", "UseBase", "WriteToForeignHeap",
"UnsafeCode", "EachIdentIsTuple", "ShadowIdent", "UnsafeCode", "EachIdentIsTuple", "ShadowIdent",
"ProveInit", "ProveField", "ProveIndex", "GcUnsafe", "GcUnsafe2", "Uninit", "ProveInit", "ProveField", "ProveIndex", "GcUnsafe", "GcUnsafe2", "Uninit",
"GcMem", "Destructor", "LockLevel", "ResultShadowed", "User"] "GcMem", "Destructor", "LockLevel", "ResultShadowed", "User"]
HintsToStr*: array[0..16, string] = ["Success", "SuccessX", "LineTooLong", HintsToStr*: array[0..22, string] = ["Success", "SuccessX", "LineTooLong",
"XDeclaredButNotUsed", "ConvToBaseNotNeeded", "ConvFromXtoItselfNotNeeded", "XDeclaredButNotUsed", "ConvToBaseNotNeeded", "ConvFromXtoItselfNotNeeded",
"ExprAlwaysX", "QuitCalled", "Processing", "CodeBegin", "CodeEnd", "Conf", "ExprAlwaysX", "QuitCalled", "Processing", "CodeBegin", "CodeEnd", "Conf",
"Path", "CondTrue", "Name", "Pattern", "Path", "CondTrue", "Name", "Pattern", "Exec", "Link", "Dependency",
"Source", "StackTrace", "GCStats",
"User"] "User"]
const const
@ -449,10 +469,10 @@ type
fullPath: string # This is a canonical full filesystem path fullPath: string # This is a canonical full filesystem path
projPath*: string # This is relative to the project's root projPath*: string # This is relative to the project's root
shortName*: string # short name of the module shortName*: string # short name of the module
quotedName*: Rope # cached quoted short name for codegen quotedName*: Rope # cached quoted short name for codegen
# purposes # purposes
lines*: seq[Rope] # the source code of the module lines*: seq[Rope] # the source code of the module
# used for better error messages and # used for better error messages and
# embedding the original source in the # embedding the original source in the
# generated code # generated code
@ -479,6 +499,30 @@ type
ERecoverableError* = object of ValueError ERecoverableError* = object of ValueError
ESuggestDone* = object of Exception ESuggestDone* = object of Exception
const
NotesVerbosity*: array[0..3, TNoteKinds] = [
{low(TNoteKind)..high(TNoteKind)} - {warnShadowIdent, warnUninit,
warnProveField, warnProveIndex,
warnGcUnsafe,
hintSuccessX, hintPath, hintConf,
hintProcessing,
hintDependency,
hintExecuting, hintLinking,
hintCodeBegin, hintCodeEnd,
hintSource, hintStackTrace,
hintGCStats},
{low(TNoteKind)..high(TNoteKind)} - {warnShadowIdent, warnUninit,
warnProveField, warnProveIndex,
warnGcUnsafe,
hintPath, hintConf,
hintDependency,
hintExecuting,
hintCodeBegin, hintCodeEnd,
hintSource, hintStackTrace,
hintGCStats},
{low(TNoteKind)..high(TNoteKind)} - {hintStackTrace},
{low(TNoteKind)..high(TNoteKind)}]
const const
InvalidFileIDX* = int32(-1) InvalidFileIDX* = int32(-1)
@ -567,9 +611,7 @@ proc raiseRecoverableError*(msg: string) {.noinline, noreturn.} =
proc sourceLine*(i: TLineInfo): Rope proc sourceLine*(i: TLineInfo): Rope
var var
gNotes*: TNoteKinds = {low(TNoteKind)..high(TNoteKind)} - gNotes*: TNoteKinds = NotesVerbosity[1] # defaults to verbosity of 1
{warnShadowIdent, warnUninit,
warnProveField, warnProveIndex, warnGcUnsafe}
gErrorCounter*: int = 0 # counts the number of errors gErrorCounter*: int = 0 # counts the number of errors
gHintCounter*: int = 0 gHintCounter*: int = 0
gWarnCounter*: int = 0 gWarnCounter*: int = 0
@ -589,8 +631,11 @@ var
proc suggestWriteln*(s: string) = proc suggestWriteln*(s: string) =
if eStdOut in errorOutputs: if eStdOut in errorOutputs:
if isNil(writelnHook): writeln(stdout, s) if isNil(writelnHook):
else: writelnHook(s) writeLine(stdout, s)
flushFile(stdout)
else:
writelnHook(s)
proc msgQuit*(x: int8) = quit x proc msgQuit*(x: int8) = quit x
proc msgQuit*(x: string) = quit x proc msgQuit*(x: string) = quit x
@ -601,13 +646,17 @@ proc suggestQuit*() =
# this format is understood by many text editors: it is the same that # this format is understood by many text editors: it is the same that
# Borland and Freepascal use # Borland and Freepascal use
const const
PosErrorFormat* = "$1($2, $3) Error: $4" PosFormat = "$1($2, $3) "
PosWarningFormat* = "$1($2, $3) Warning: $4" KindFormat = " [$1]"
PosHintFormat* = "$1($2, $3) Hint: $4" KindColor = fgCyan
PosContextFormat = "$1($2, $3) Info: $4" ErrorTitle = "Error: "
RawErrorFormat* = "Error: $1" ErrorColor = fgRed
RawWarningFormat* = "Warning: $1" WarningTitle = "Warning: "
RawHintFormat* = "Hint: $1" WarningColor = fgYellow
HintTitle = "Hint: "
HintColor = fgGreen
InfoTitle = "Info: "
InfoColor = fgCyan
proc getInfoContextLen*(): int = return msgContext.len proc getInfoContextLen*(): int = return msgContext.len
proc setInfoContextLen*(L: int) = setLen(msgContext, L) proc setInfoContextLen*(L: int) = setLen(msgContext, L)
@ -678,21 +727,79 @@ proc `??`* (info: TLineInfo, filename: string): bool =
var gTrackPos*: TLineInfo var gTrackPos*: TLineInfo
proc outWriteln*(s: string) = type
## Writes to stdout. Always. MsgFlag* = enum ## flags altering msgWriteln behavior
if eStdOut in errorOutputs: writeln(stdout, s) msgStdout, ## force writing to stdout, even stderr is default
msgSkipHook ## skip message hook even if it is present
MsgFlags* = set[MsgFlag]
proc msgWriteln*(s: string) = proc msgWriteln*(s: string, flags: MsgFlags = {}) =
## Writes to stdout. If --stdout option is given, writes to stderr instead. ## Writes given message string to stderr by default.
## If ``--stdout`` option is given, writes to stdout instead. If message hook
## is present, then it is used to output message rather than stderr/stdout.
## This behavior can be altered by given optional flags.
#if gCmd == cmdIdeTools and optCDebug notin gGlobalOptions: return #if gCmd == cmdIdeTools and optCDebug notin gGlobalOptions: return
if not isNil(writelnHook): if not isNil(writelnHook) and msgSkipHook notin flags:
writelnHook(s) writelnHook(s)
elif optStdout in gGlobalOptions: elif optStdout in gGlobalOptions or msgStdout in flags:
if eStdErr in errorOutputs: writeln(stderr, s) if eStdOut in errorOutputs:
writeLine(stdout, s)
flushFile(stdout)
else: else:
if eStdOut in errorOutputs: writeln(stdout, s) if eStdErr in errorOutputs:
writeLine(stderr, s)
# On Windows stderr is fully-buffered when piped, regardless of C std.
when defined(windows):
flushFile(stderr)
macro callIgnoringStyle(theProc: typed, first: typed,
args: varargs[expr]): stmt =
let typForegroundColor = bindSym"ForegroundColor".getType
let typBackgroundColor = bindSym"BackgroundColor".getType
let typStyle = bindSym"Style".getType
let typTerminalCmd = bindSym"TerminalCmd".getType
result = newCall(theProc)
if first.kind != nnkNilLit: result.add(first)
for arg in children(args[0][1]):
if arg.kind == nnkNilLit: continue
let typ = arg.getType
if typ.kind != nnkEnumTy or
typ != typForegroundColor and
typ != typBackgroundColor and
typ != typStyle and
typ != typTerminalCmd:
result.add(arg)
macro callStyledWriteLineStderr(args: varargs[expr]): stmt =
result = newCall(bindSym"styledWriteLine")
result.add(bindSym"stderr")
for arg in children(args[0][1]):
result.add(arg)
template callWritelnHook(args: varargs[string, `$`]) =
var s = ""
for arg in args:
s.add arg
writelnHook s
template styledMsgWriteln*(args: varargs[expr]) =
if not isNil(writelnHook):
callIgnoringStyle(callWritelnHook, nil, args)
elif optStdout in gGlobalOptions:
if eStdOut in errorOutputs:
callIgnoringStyle(writeLine, stdout, args)
flushFile(stdout)
else:
if eStdErr in errorOutputs:
if optUseColors in gGlobalOptions:
callStyledWriteLineStderr(args)
else:
callIgnoringStyle(writeLine, stderr, args)
# On Windows stderr is fully-buffered when piped, regardless of C std.
when defined(windows):
flushFile(stderr)
proc coordToStr(coord: int): string = proc coordToStr(coord: int): string =
if coord == -1: result = "???" if coord == -1: result = "???"
@ -710,11 +817,11 @@ type
proc handleError(msg: TMsgKind, eh: TErrorHandling, s: string) = proc handleError(msg: TMsgKind, eh: TErrorHandling, s: string) =
template quit = template quit =
if defined(debug) or gVerbosity >= 3 or msg == errInternal: if defined(debug) or msg == errInternal or hintStackTrace in gNotes:
if stackTraceAvailable() and isNil(writelnHook): if stackTraceAvailable() and isNil(writelnHook):
writeStackTrace() writeStackTrace()
else: else:
msgWriteln("No stack traceback available\nTo create a stacktrace, rerun compilation with ./koch temp " & options.command & " <file>") styledMsgWriteln(fgRed, "No stack traceback available\nTo create a stacktrace, rerun compilation with ./koch temp " & options.command & " <file>")
quit 1 quit 1
if msg >= fatalMin and msg <= fatalMax: if msg >= fatalMin and msg <= fatalMax:
@ -736,61 +843,85 @@ proc writeContext(lastinfo: TLineInfo) =
var info = lastinfo var info = lastinfo
for i in countup(0, len(msgContext) - 1): for i in countup(0, len(msgContext) - 1):
if msgContext[i] != lastinfo and msgContext[i] != info: if msgContext[i] != lastinfo and msgContext[i] != info:
msgWriteln(PosContextFormat % [toMsgFilename(msgContext[i]), styledMsgWriteln(styleBright,
coordToStr(msgContext[i].line), PosFormat % [toMsgFilename(msgContext[i]),
coordToStr(msgContext[i].col+1), coordToStr(msgContext[i].line),
getMessageStr(errInstantiationFrom, "")]) coordToStr(msgContext[i].col+1)],
resetStyle,
getMessageStr(errInstantiationFrom, ""))
info = msgContext[i] info = msgContext[i]
proc ignoreMsgBecauseOfIdeTools(msg: TMsgKind): bool = proc ignoreMsgBecauseOfIdeTools(msg: TMsgKind): bool =
msg >= errGenerated and gCmd == cmdIdeTools and optIdeDebug notin gGlobalOptions msg >= errGenerated and gCmd == cmdIdeTools and optIdeDebug notin gGlobalOptions
proc rawMessage*(msg: TMsgKind, args: openArray[string]) = proc rawMessage*(msg: TMsgKind, args: openArray[string]) =
var frmt: string var
title: string
color: ForegroundColor
kind: string
case msg case msg
of errMin..errMax: of errMin..errMax:
writeContext(unknownLineInfo()) writeContext(unknownLineInfo())
frmt = RawErrorFormat title = ErrorTitle
color = ErrorColor
of warnMin..warnMax: of warnMin..warnMax:
if optWarns notin gOptions: return if optWarns notin gOptions: return
if msg notin gNotes: return if msg notin gNotes: return
writeContext(unknownLineInfo()) writeContext(unknownLineInfo())
frmt = RawWarningFormat title = WarningTitle
color = WarningColor
kind = WarningsToStr[ord(msg) - ord(warnMin)]
inc(gWarnCounter) inc(gWarnCounter)
of hintMin..hintMax: of hintMin..hintMax:
if optHints notin gOptions: return if optHints notin gOptions: return
if msg notin gNotes: return if msg notin gNotes: return
frmt = RawHintFormat title = HintTitle
color = HintColor
kind = HintsToStr[ord(msg) - ord(hintMin)]
inc(gHintCounter) inc(gHintCounter)
let s = `%`(frmt, `%`(msgKindToString(msg), args)) let s = `%`(msgKindToString(msg), args)
if not ignoreMsgBecauseOfIdeTools(msg): if not ignoreMsgBecauseOfIdeTools(msg):
msgWriteln(s) if kind != nil:
styledMsgWriteln(color, title, resetStyle, s,
KindColor, `%`(KindFormat, kind))
else:
styledMsgWriteln(color, title, resetStyle, s)
handleError(msg, doAbort, s) handleError(msg, doAbort, s)
proc rawMessage*(msg: TMsgKind, arg: string) = proc rawMessage*(msg: TMsgKind, arg: string) =
rawMessage(msg, [arg]) rawMessage(msg, [arg])
proc resetAttributes* =
if {optUseColors, optStdout} * gGlobalOptions == {optUseColors}:
terminal.resetAttributes(stderr)
proc writeSurroundingSrc(info: TLineInfo) = proc writeSurroundingSrc(info: TLineInfo) =
const indent = " " const indent = " "
msgWriteln(indent & $info.sourceLine) msgWriteln(indent & $info.sourceLine)
msgWriteln(indent & spaces(info.col) & '^') msgWriteln(indent & spaces(info.col) & '^')
proc formatMsg*(info: TLineInfo, msg: TMsgKind, arg: string): string = proc formatMsg*(info: TLineInfo, msg: TMsgKind, arg: string): string =
let frmt = case msg let title = case msg
of warnMin..warnMax: PosWarningFormat of warnMin..warnMax: WarningTitle
of hintMin..hintMax: PosHintFormat of hintMin..hintMax: HintTitle
else: PosErrorFormat else: ErrorTitle
result = frmt % [toMsgFilename(info), coordToStr(info.line), result = PosFormat % [toMsgFilename(info), coordToStr(info.line),
coordToStr(info.col+1), getMessageStr(msg, arg)] coordToStr(info.col+1)] &
title &
getMessageStr(msg, arg)
proc liMessage(info: TLineInfo, msg: TMsgKind, arg: string, proc liMessage(info: TLineInfo, msg: TMsgKind, arg: string,
eh: TErrorHandling) = eh: TErrorHandling) =
var frmt: string var
var ignoreMsg = false title: string
color: ForegroundColor
kind: string
ignoreMsg = false
case msg case msg
of errMin..errMax: of errMin..errMax:
writeContext(info) writeContext(info)
frmt = PosErrorFormat title = ErrorTitle
color = ErrorColor
# we try to filter error messages so that not two error message # we try to filter error messages so that not two error message
# in the same file and line are produced: # in the same file and line are produced:
#ignoreMsg = lastError == info and eh != doAbort #ignoreMsg = lastError == info and eh != doAbort
@ -798,20 +929,29 @@ proc liMessage(info: TLineInfo, msg: TMsgKind, arg: string,
of warnMin..warnMax: of warnMin..warnMax:
ignoreMsg = optWarns notin gOptions or msg notin gNotes ignoreMsg = optWarns notin gOptions or msg notin gNotes
if not ignoreMsg: writeContext(info) if not ignoreMsg: writeContext(info)
frmt = PosWarningFormat title = WarningTitle
color = WarningColor
kind = WarningsToStr[ord(msg) - ord(warnMin)]
inc(gWarnCounter) inc(gWarnCounter)
of hintMin..hintMax: of hintMin..hintMax:
ignoreMsg = optHints notin gOptions or msg notin gNotes ignoreMsg = optHints notin gOptions or msg notin gNotes
frmt = PosHintFormat title = HintTitle
color = HintColor
kind = HintsToStr[ord(msg) - ord(hintMin)]
inc(gHintCounter) inc(gHintCounter)
# NOTE: currently line info line numbers start with 1, # NOTE: currently line info line numbers start with 1,
# but column numbers start with 0, however most editors expect # but column numbers start with 0, however most editors expect
# first column to be 1, so we need to +1 here # first column to be 1, so we need to +1 here
let s = frmt % [toMsgFilename(info), coordToStr(info.line), let x = PosFormat % [toMsgFilename(info), coordToStr(info.line),
coordToStr(info.col+1), getMessageStr(msg, arg)] coordToStr(info.col+1)]
let s = getMessageStr(msg, arg)
if not ignoreMsg and not ignoreMsgBecauseOfIdeTools(msg): if not ignoreMsg and not ignoreMsgBecauseOfIdeTools(msg):
msgWriteln(s) if kind != nil:
if optPrintSurroundingSrc and msg in errMin..errMax: styledMsgWriteln(styleBright, x, resetStyle, color, title, resetStyle, s,
KindColor, `%`(KindFormat, kind))
else:
styledMsgWriteln(styleBright, x, resetStyle, color, title, resetStyle, s)
if msg in errMin..errMax and hintSource in gNotes:
info.writeSurroundingSrc info.writeSurroundingSrc
handleError(msg, eh, s) handleError(msg, eh, s)
@ -882,3 +1022,22 @@ ropes.errorHandler = proc (err: RopesError, msg: string, useWarning: bool) =
of rCannotOpenFile: of rCannotOpenFile:
rawMessage(if useWarning: warnCannotOpenFile else: errCannotOpenFile, msg) rawMessage(if useWarning: warnCannotOpenFile else: errCannotOpenFile, msg)
proc listWarnings*() =
msgWriteln("Warnings:")
for warn in warnMin..warnMax:
msgWriteln(" [$1] $2" % [
if warn in gNotes: "x" else: " ",
msgs.WarningsToStr[ord(warn) - ord(warnMin)]
])
proc listHints*() =
msgWriteln("Hints:")
for hint in hintMin..hintMax:
msgWriteln(" [$1] $2" % [
if hint in gNotes: "x" else: " ",
msgs.HintsToStr[ord(hint) - ord(hintMin)]
])
# enable colors by default on terminals
if terminal.isatty(stderr):
incl(gGlobalOptions, optUseColors)

View file

@ -17,5 +17,4 @@ define:useStdoutAsStdmsg
cs:partial cs:partial
#define:useNodeIds #define:useNodeIds
symbol:nimfix
#gc:markAndSweep #gc:markAndSweep

View file

@ -1,7 +1,7 @@
# #
# #
# The Nim Compiler # The Nim Compiler
# (c) Copyright 2013 Andreas Rumpf # (c) Copyright 2015 Andreas Rumpf
# #
# See the file "copying.txt", included in this # See the file "copying.txt", included in this
# distribution, for details about the copyright. # distribution, for details about the copyright.
@ -16,7 +16,7 @@ when defined(gcc) and defined(windows):
import import
commands, lexer, condsyms, options, msgs, nversion, nimconf, ropes, commands, lexer, condsyms, options, msgs, nversion, nimconf, ropes,
extccomp, strutils, os, osproc, platform, main, parseopt, service, extccomp, strutils, os, osproc, platform, main, parseopt, service,
nodejs nodejs, scriptconfig
when hasTinyCBackend: when hasTinyCBackend:
import tccgen import tccgen
@ -38,31 +38,44 @@ proc handleCmdLine() =
else: else:
# Process command line arguments: # Process command line arguments:
processCmdLine(passCmd1, "") processCmdLine(passCmd1, "")
if gProjectName != "": if gProjectName == "-":
gProjectName = "stdinfile"
gProjectFull = "stdinfile"
gProjectPath = getCurrentDir()
gProjectIsStdin = true
elif gProjectName != "":
try: try:
gProjectFull = canonicalizePath(gProjectName) gProjectFull = canonicalizePath(gProjectName)
except OSError: except OSError:
gProjectFull = gProjectName gProjectFull = gProjectName
var p = splitFile(gProjectFull) let p = splitFile(gProjectFull)
gProjectPath = p.dir gProjectPath = p.dir
gProjectName = p.name gProjectName = p.name
else: else:
gProjectPath = getCurrentDir() gProjectPath = getCurrentDir()
loadConfigs(DefaultConfig) # load all config files loadConfigs(DefaultConfig) # load all config files
let scriptFile = gProjectFull.changeFileExt("nims")
if fileExists(scriptFile):
runNimScript(scriptFile)
# 'nim foo.nims' means to just run the NimScript file and do nothing more:
if scriptFile == gProjectFull: return
elif fileExists(gProjectPath / "config.nims"):
# directory wide NimScript file
runNimScript(gProjectPath / "config.nims")
# 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() extccomp.initVars()
processCmdLine(passCmd2, "") processCmdLine(passCmd2, "")
if options.command == "":
rawMessage(errNoCommand, command)
mainCommand() mainCommand()
if gVerbosity >= 2: echo(GC_getStatistics()) if optHints in gOptions and hintGCStats in gNotes: echo(GC_getStatistics())
#echo(GC_getStatistics()) #echo(GC_getStatistics())
if msgs.gErrorCounter == 0: if msgs.gErrorCounter == 0:
when hasTinyCBackend: when hasTinyCBackend:
if gCmd == cmdRun: if gCmd == cmdRun:
tccgen.run(commands.arguments) tccgen.run(commands.arguments)
if optRun in gGlobalOptions: if optRun in gGlobalOptions:
if gProjectName == "-":
gProjectFull = "stdinfile"
if gCmd == cmdCompileToJS: if gCmd == cmdCompileToJS:
var ex: string var ex: string
if options.outFile.len > 0: if options.outFile.len > 0:

View file

@ -11,8 +11,8 @@
import parseutils, strutils, strtabs, os, options, msgs, lists import parseutils, strutils, strtabs, os, options, msgs, lists
proc addPath*(path: string, info: TLineInfo) = proc addPath*(path: string, info: TLineInfo) =
if not contains(options.searchPaths, path): if not contains(options.searchPaths, path):
lists.prependStr(options.searchPaths, path) lists.prependStr(options.searchPaths, path)
proc versionSplitPos(s: string): int = proc versionSplitPos(s: string): int =
@ -23,7 +23,7 @@ proc versionSplitPos(s: string): int =
const const
latest = "head" latest = "head"
proc `<.`(a, b: string): bool = proc `<.`(a, b: string): bool =
# wether a has a smaller version than b: # wether a has a smaller version than b:
if a == latest: return false if a == latest: return false
var i = 0 var i = 0
@ -48,7 +48,7 @@ proc addPackage(packages: StringTableRef, p: string) =
let name = p.substr(0, x-1) let name = p.substr(0, x-1)
if x < p.len: if x < p.len:
let version = p.substr(x+1) let version = p.substr(x+1)
if packages[name] <. version: if packages.getOrDefault(name) <. version:
packages[name] = version packages[name] = version
else: else:
packages[name] = latest packages[name] = latest
@ -60,7 +60,7 @@ iterator chosen(packages: StringTableRef): string =
proc addNimblePath(p: string, info: TLineInfo) = proc addNimblePath(p: string, info: TLineInfo) =
if not contains(options.searchPaths, p): if not contains(options.searchPaths, p):
if gVerbosity >= 1: message(info, hintPath, p) message(info, hintPath, p)
lists.prependStr(options.lazyPaths, p) lists.prependStr(options.lazyPaths, p)
proc addPathWithNimFiles(p: string, info: TLineInfo) = proc addPathWithNimFiles(p: string, info: TLineInfo) =

View file

@ -9,106 +9,106 @@
# This module handles the reading of the config file. # This module handles the reading of the config file.
import import
llstream, nversion, commands, os, strutils, msgs, platform, condsyms, lexer, llstream, nversion, commands, os, strutils, msgs, platform, condsyms, lexer,
options, idents, wordrecg, strtabs options, idents, wordrecg, strtabs
# ---------------- configuration file parser ----------------------------- # ---------------- configuration file parser -----------------------------
# we use Nim's scanner here to save space and work # we use Nim's scanner here to save space and work
proc ppGetTok(L: var TLexer, tok: var TToken) = proc ppGetTok(L: var TLexer, tok: var TToken) =
# simple filter # simple filter
rawGetTok(L, tok) rawGetTok(L, tok)
while tok.tokType in {tkComment}: rawGetTok(L, tok) while tok.tokType in {tkComment}: rawGetTok(L, tok)
proc parseExpr(L: var TLexer, tok: var TToken): bool proc parseExpr(L: var TLexer, tok: var TToken): bool
proc parseAtom(L: var TLexer, tok: var TToken): bool = proc parseAtom(L: var TLexer, tok: var TToken): bool =
if tok.tokType == tkParLe: if tok.tokType == tkParLe:
ppGetTok(L, tok) ppGetTok(L, tok)
result = parseExpr(L, tok) result = parseExpr(L, tok)
if tok.tokType == tkParRi: ppGetTok(L, tok) if tok.tokType == tkParRi: ppGetTok(L, tok)
else: lexMessage(L, errTokenExpected, "\')\'") else: lexMessage(L, errTokenExpected, "\')\'")
elif tok.ident.id == ord(wNot): elif tok.ident.id == ord(wNot):
ppGetTok(L, tok) ppGetTok(L, tok)
result = not parseAtom(L, tok) result = not parseAtom(L, tok)
else: else:
result = isDefined(tok.ident) result = isDefined(tok.ident)
ppGetTok(L, tok) ppGetTok(L, tok)
proc parseAndExpr(L: var TLexer, tok: var TToken): bool = proc parseAndExpr(L: var TLexer, tok: var TToken): bool =
result = parseAtom(L, tok) result = parseAtom(L, tok)
while tok.ident.id == ord(wAnd): while tok.ident.id == ord(wAnd):
ppGetTok(L, tok) # skip "and" ppGetTok(L, tok) # skip "and"
var b = parseAtom(L, tok) var b = parseAtom(L, tok)
result = result and b result = result and b
proc parseExpr(L: var TLexer, tok: var TToken): bool = proc parseExpr(L: var TLexer, tok: var TToken): bool =
result = parseAndExpr(L, tok) result = parseAndExpr(L, tok)
while tok.ident.id == ord(wOr): while tok.ident.id == ord(wOr):
ppGetTok(L, tok) # skip "or" ppGetTok(L, tok) # skip "or"
var b = parseAndExpr(L, tok) var b = parseAndExpr(L, tok)
result = result or b result = result or b
proc evalppIf(L: var TLexer, tok: var TToken): bool = proc evalppIf(L: var TLexer, tok: var TToken): bool =
ppGetTok(L, tok) # skip 'if' or 'elif' ppGetTok(L, tok) # skip 'if' or 'elif'
result = parseExpr(L, tok) result = parseExpr(L, tok)
if tok.tokType == tkColon: ppGetTok(L, tok) if tok.tokType == tkColon: ppGetTok(L, tok)
else: lexMessage(L, errTokenExpected, "\':\'") else: lexMessage(L, errTokenExpected, "\':\'")
var condStack: seq[bool] = @[] var condStack: seq[bool] = @[]
proc doEnd(L: var TLexer, tok: var TToken) = proc doEnd(L: var TLexer, tok: var TToken) =
if high(condStack) < 0: lexMessage(L, errTokenExpected, "@if") if high(condStack) < 0: lexMessage(L, errTokenExpected, "@if")
ppGetTok(L, tok) # skip 'end' ppGetTok(L, tok) # skip 'end'
setLen(condStack, high(condStack)) setLen(condStack, high(condStack))
type type
TJumpDest = enum TJumpDest = enum
jdEndif, jdElseEndif jdEndif, jdElseEndif
proc jumpToDirective(L: var TLexer, tok: var TToken, dest: TJumpDest) proc jumpToDirective(L: var TLexer, tok: var TToken, dest: TJumpDest)
proc doElse(L: var TLexer, tok: var TToken) = proc doElse(L: var TLexer, tok: var TToken) =
if high(condStack) < 0: lexMessage(L, errTokenExpected, "@if") if high(condStack) < 0: lexMessage(L, errTokenExpected, "@if")
ppGetTok(L, tok) ppGetTok(L, tok)
if tok.tokType == tkColon: ppGetTok(L, tok) if tok.tokType == tkColon: ppGetTok(L, tok)
if condStack[high(condStack)]: jumpToDirective(L, tok, jdEndif) if condStack[high(condStack)]: jumpToDirective(L, tok, jdEndif)
proc doElif(L: var TLexer, tok: var TToken) = proc doElif(L: var TLexer, tok: var TToken) =
if high(condStack) < 0: lexMessage(L, errTokenExpected, "@if") if high(condStack) < 0: lexMessage(L, errTokenExpected, "@if")
var res = evalppIf(L, tok) var res = evalppIf(L, tok)
if condStack[high(condStack)] or not res: jumpToDirective(L, tok, jdElseEndif) if condStack[high(condStack)] or not res: jumpToDirective(L, tok, jdElseEndif)
else: condStack[high(condStack)] = true else: condStack[high(condStack)] = true
proc jumpToDirective(L: var TLexer, tok: var TToken, dest: TJumpDest) = proc jumpToDirective(L: var TLexer, tok: var TToken, dest: TJumpDest) =
var nestedIfs = 0 var nestedIfs = 0
while true: while true:
if tok.ident != nil and tok.ident.s == "@": if tok.ident != nil and tok.ident.s == "@":
ppGetTok(L, tok) ppGetTok(L, tok)
case whichKeyword(tok.ident) case whichKeyword(tok.ident)
of wIf: of wIf:
inc(nestedIfs) inc(nestedIfs)
of wElse: of wElse:
if dest == jdElseEndif and nestedIfs == 0: if dest == jdElseEndif and nestedIfs == 0:
doElse(L, tok) doElse(L, tok)
break break
of wElif: of wElif:
if dest == jdElseEndif and nestedIfs == 0: if dest == jdElseEndif and nestedIfs == 0:
doElif(L, tok) doElif(L, tok)
break break
of wEnd: of wEnd:
if nestedIfs == 0: if nestedIfs == 0:
doEnd(L, tok) doEnd(L, tok)
break break
if nestedIfs > 0: dec(nestedIfs) if nestedIfs > 0: dec(nestedIfs)
else: else:
discard discard
ppGetTok(L, tok) ppGetTok(L, tok)
elif tok.tokType == tkEof: elif tok.tokType == tkEof:
lexMessage(L, errTokenExpected, "@end") lexMessage(L, errTokenExpected, "@end")
else: else:
ppGetTok(L, tok) ppGetTok(L, tok)
proc parseDirective(L: var TLexer, tok: var TToken) = proc parseDirective(L: var TLexer, tok: var TToken) =
ppGetTok(L, tok) # skip @ ppGetTok(L, tok) # skip @
case whichKeyword(tok.ident) case whichKeyword(tok.ident)
of wIf: of wIf:
@ -126,13 +126,13 @@ proc parseDirective(L: var TLexer, tok: var TToken) =
ppGetTok(L, tok) ppGetTok(L, tok)
else: else:
case tok.ident.s.normalize case tok.ident.s.normalize
of "putenv": of "putenv":
ppGetTok(L, tok) ppGetTok(L, tok)
var key = tokToStr(tok) var key = tokToStr(tok)
ppGetTok(L, tok) ppGetTok(L, tok)
os.putEnv(key, tokToStr(tok)) os.putEnv(key, tokToStr(tok))
ppGetTok(L, tok) ppGetTok(L, tok)
of "prependenv": of "prependenv":
ppGetTok(L, tok) ppGetTok(L, tok)
var key = tokToStr(tok) var key = tokToStr(tok)
ppGetTok(L, tok) ppGetTok(L, tok)
@ -145,17 +145,17 @@ proc parseDirective(L: var TLexer, tok: var TToken) =
os.putEnv(key, os.getEnv(key) & tokToStr(tok)) os.putEnv(key, os.getEnv(key) & tokToStr(tok))
ppGetTok(L, tok) ppGetTok(L, tok)
else: lexMessage(L, errInvalidDirectiveX, tokToStr(tok)) else: lexMessage(L, errInvalidDirectiveX, tokToStr(tok))
proc confTok(L: var TLexer, tok: var TToken) = proc confTok(L: var TLexer, tok: var TToken) =
ppGetTok(L, tok) ppGetTok(L, tok)
while tok.ident != nil and tok.ident.s == "@": while tok.ident != nil and tok.ident.s == "@":
parseDirective(L, tok) # else: give the token to the parser parseDirective(L, tok) # else: give the token to the parser
proc checkSymbol(L: TLexer, tok: TToken) = proc checkSymbol(L: TLexer, tok: TToken) =
if tok.tokType notin {tkSymbol..pred(tkIntLit), tkStrLit..tkTripleStrLit}: if tok.tokType notin {tkSymbol..pred(tkIntLit), tkStrLit..tkTripleStrLit}:
lexMessage(L, errIdentifierExpected, tokToStr(tok)) lexMessage(L, errIdentifierExpected, tokToStr(tok))
proc parseAssignment(L: var TLexer, tok: var TToken) = proc parseAssignment(L: var TLexer, tok: var TToken) =
if tok.ident.id == getIdent("-").id or tok.ident.id == getIdent("--").id: if tok.ident.id == getIdent("-").id or tok.ident.id == getIdent("--").id:
confTok(L, tok) # skip unnecessary prefix confTok(L, tok) # skip unnecessary prefix
var info = getLineInfo(L, tok) # save for later in case of an error var info = getLineInfo(L, tok) # save for later in case of an error
@ -163,13 +163,13 @@ proc parseAssignment(L: var TLexer, tok: var TToken) =
var s = tokToStr(tok) var s = tokToStr(tok)
confTok(L, tok) # skip symbol confTok(L, tok) # skip symbol
var val = "" var val = ""
while tok.tokType == tkDot: while tok.tokType == tkDot:
add(s, '.') add(s, '.')
confTok(L, tok) confTok(L, tok)
checkSymbol(L, tok) checkSymbol(L, tok)
add(s, tokToStr(tok)) add(s, tokToStr(tok))
confTok(L, tok) confTok(L, tok)
if tok.tokType == tkBracketLe: if tok.tokType == tkBracketLe:
# BUGFIX: val, not s! # BUGFIX: val, not s!
# BUGFIX: do not copy '['! # BUGFIX: do not copy '['!
confTok(L, tok) confTok(L, tok)
@ -180,7 +180,7 @@ proc parseAssignment(L: var TLexer, tok: var TToken) =
else: lexMessage(L, errTokenExpected, "']'") else: lexMessage(L, errTokenExpected, "']'")
add(val, ']') add(val, ']')
let percent = tok.ident.id == getIdent("%=").id let percent = tok.ident.id == getIdent("%=").id
if tok.tokType in {tkColon, tkEquals} or percent: if tok.tokType in {tkColon, tkEquals} or percent:
if len(val) > 0: add(val, ':') if len(val) > 0: add(val, ':')
confTok(L, tok) # skip ':' or '=' or '%' confTok(L, tok) # skip ':' or '=' or '%'
checkSymbol(L, tok) checkSymbol(L, tok)
@ -211,7 +211,7 @@ proc readConfigFile(filename: string) =
while tok.tokType != tkEof: parseAssignment(L, tok) while tok.tokType != tkEof: parseAssignment(L, tok)
if len(condStack) > 0: lexMessage(L, errTokenExpected, "@end") if len(condStack) > 0: lexMessage(L, errTokenExpected, "@end")
closeLexer(L) closeLexer(L)
if gVerbosity >= 1: rawMessage(hintConf, filename) rawMessage(hintConf, filename)
proc getUserConfigPath(filename: string): string = proc getUserConfigPath(filename: string): string =
result = joinPath(getConfigDir(), filename) result = joinPath(getConfigDir(), filename)
@ -226,15 +226,7 @@ proc getSystemConfigPath(filename: string): string =
if not existsFile(result): result = "/etc/" & filename if not existsFile(result): result = "/etc/" & filename
proc loadConfigs*(cfg: string) = proc loadConfigs*(cfg: string) =
# set default value (can be overwritten): setDefaultLibpath()
if libpath == "":
# choose default libpath:
var prefix = getPrefixDir()
when defined(posix):
if prefix == "/usr": libpath = "/usr/lib/nim"
elif prefix == "/usr/local": libpath = "/usr/local/lib/nim"
else: libpath = joinPath(prefix, "lib")
else: libpath = joinPath(prefix, "lib")
if optSkipConfigFile notin gGlobalOptions: if optSkipConfigFile notin gGlobalOptions:
readConfigFile(getSystemConfigPath(cfg)) readConfigFile(getSystemConfigPath(cfg))
@ -246,10 +238,10 @@ proc loadConfigs*(cfg: string) =
if optSkipParentConfigFiles notin gGlobalOptions: if optSkipParentConfigFiles notin gGlobalOptions:
for dir in parentDirs(pd, fromRoot=true, inclusive=false): for dir in parentDirs(pd, fromRoot=true, inclusive=false):
readConfigFile(dir / cfg) readConfigFile(dir / cfg)
if optSkipProjConfigFile notin gGlobalOptions: if optSkipProjConfigFile notin gGlobalOptions:
readConfigFile(pd / cfg) readConfigFile(pd / cfg)
if gProjectName.len != 0: if gProjectName.len != 0:
# new project wide config file: # new project wide config file:
var projectConfig = changeFileExt(gProjectFull, "nimcfg") var projectConfig = changeFileExt(gProjectFull, "nimcfg")

View file

@ -10,7 +10,7 @@
## exposes the Nim VM to clients. ## exposes the Nim VM to clients.
import import
ast, modules, passes, passaux, condsyms, ast, modules, passes, passaux, condsyms,
options, nimconf, lists, sem, semdata, llstream, vm options, nimconf, lists, sem, semdata, llstream, vm
proc execute*(program: string) = proc execute*(program: string) =

View file

@ -10,8 +10,10 @@
## Nimfix is a tool that helps to convert old-style Nimrod code to Nim code. ## Nimfix is a tool that helps to convert old-style Nimrod code to Nim code.
import strutils, os, parseopt import strutils, os, parseopt
import options, commands, modules, sem, passes, passaux, pretty, msgs, nimconf, import compiler/options, compiler/commands, compiler/modules, compiler/sem,
extccomp, condsyms, lists compiler/passes, compiler/passaux, compiler/nimfix/pretty,
compiler/msgs, compiler/nimconf,
compiler/extccomp, compiler/condsyms, compiler/lists
const Usage = """ const Usage = """
Nimfix - Tool to patch Nim code Nimfix - Tool to patch Nim code
@ -24,7 +26,7 @@ Options:
--wholeProject overwrite every processed file. --wholeProject overwrite every processed file.
--checkExtern:on|off style check also extern names --checkExtern:on|off style check also extern names
--styleCheck:on|off|auto performs style checking for identifiers --styleCheck:on|off|auto performs style checking for identifiers
and suggests an alternative spelling; and suggests an alternative spelling;
'auto' corrects the spelling. 'auto' corrects the spelling.
--bestEffort try to fix the code even when there --bestEffort try to fix the code even when there
are errors. are errors.
@ -48,11 +50,11 @@ proc processCmdLine*(pass: TCmdLinePass, cmd: string) =
var p = parseopt.initOptParser(cmd) var p = parseopt.initOptParser(cmd)
var argsCount = 0 var argsCount = 0
gOnlyMainfile = true gOnlyMainfile = true
while true: while true:
parseopt.next(p) parseopt.next(p)
case p.kind case p.kind
of cmdEnd: break of cmdEnd: break
of cmdLongoption, cmdShortOption: of cmdLongoption, cmdShortOption:
case p.key.normalize case p.key.normalize
of "overwritefiles": of "overwritefiles":
case p.val.normalize case p.val.normalize
@ -80,7 +82,7 @@ proc processCmdLine*(pass: TCmdLinePass, cmd: string) =
proc handleCmdLine() = proc handleCmdLine() =
if paramCount() == 0: if paramCount() == 0:
stdout.writeln(Usage) stdout.writeLine(Usage)
else: else:
processCmdLine(passCmd1, "") processCmdLine(passCmd1, "")
if gProjectName != "": if gProjectName != "":

View file

@ -10,9 +10,11 @@
## This module implements the code "prettifier". This is part of the toolchain ## This module implements the code "prettifier". This is part of the toolchain
## to convert Nim code into a consistent style. ## to convert Nim code into a consistent style.
import import
strutils, os, options, ast, astalgo, msgs, ropes, idents, strutils, os, intsets, strtabs
intsets, strtabs, semdata, prettybase
import compiler/options, compiler/ast, compiler/astalgo, compiler/msgs,
compiler/semdata, compiler/nimfix/prettybase, compiler/ropes, compiler/idents
type type
StyleCheck* {.pure.} = enum None, Warn, Auto StyleCheck* {.pure.} = enum None, Warn, Auto
@ -92,7 +94,7 @@ proc beautifyName(s: string, k: TSymKind): string =
proc replaceInFile(info: TLineInfo; newName: string) = proc replaceInFile(info: TLineInfo; newName: string) =
loadFile(info) loadFile(info)
let line = gSourceFiles[info.fileIndex].lines[info.line-1] let line = gSourceFiles[info.fileIndex].lines[info.line-1]
var first = min(info.col.int, line.len) var first = min(info.col.int, line.len)
if first < 0: return if first < 0: return
@ -100,18 +102,18 @@ proc replaceInFile(info: TLineInfo; newName: string) =
while first > 0 and line[first-1] in prettybase.Letters: dec first while first > 0 and line[first-1] in prettybase.Letters: dec first
if first < 0: return if first < 0: return
if line[first] == '`': inc first if line[first] == '`': inc first
let last = first+identLen(line, first)-1 let last = first+identLen(line, first)-1
if differ(line, first, last, newName): if differ(line, first, last, newName):
# last-first+1 != newName.len or # last-first+1 != newName.len or
var x = line.substr(0, first-1) & newName & line.substr(last+1) var x = line.substr(0, first-1) & newName & line.substr(last+1)
system.shallowCopy(gSourceFiles[info.fileIndex].lines[info.line-1], x) system.shallowCopy(gSourceFiles[info.fileIndex].lines[info.line-1], x)
gSourceFiles[info.fileIndex].dirty = true gSourceFiles[info.fileIndex].dirty = true
proc checkStyle(info: TLineInfo, s: string, k: TSymKind; sym: PSym) = proc checkStyle(info: TLineInfo, s: string, k: TSymKind; sym: PSym) =
let beau = beautifyName(s, k) let beau = beautifyName(s, k)
if s != beau: if s != beau:
if gStyleCheck == StyleCheck.Auto: if gStyleCheck == StyleCheck.Auto:
sym.name = getIdent(beau) sym.name = getIdent(beau)
replaceInFile(info, beau) replaceInFile(info, beau)
else: else:
@ -137,7 +139,7 @@ proc styleCheckUseImpl(info: TLineInfo; s: PSym) =
if info.fileIndex < 0: return if info.fileIndex < 0: return
# we simply convert it to what it looks like in the definition # we simply convert it to what it looks like in the definition
# for consistency # for consistency
# operators stay as they are: # operators stay as they are:
if s.kind in {skResult, skTemp} or s.name.s[0] notin prettybase.Letters: if s.kind in {skResult, skTemp} or s.name.s[0] notin prettybase.Letters:
return return

View file

@ -7,7 +7,8 @@
# distribution, for details about the copyright. # distribution, for details about the copyright.
# #
import ast, msgs, strutils, idents, lexbase, streams import strutils, lexbase, streams
import compiler/ast, compiler/msgs, compiler/idents
from os import splitFile from os import splitFile
type type
@ -39,7 +40,7 @@ proc loadFile*(info: TLineInfo) =
var pos = lex.bufpos var pos = lex.bufpos
while true: while true:
case lex.buf[pos] case lex.buf[pos]
of '\c': of '\c':
gSourceFiles[i].newline = "\c\L" gSourceFiles[i].newline = "\c\L"
break break
of '\L', '\0': of '\L', '\0':
@ -70,7 +71,7 @@ proc replaceDeprecated*(info: TLineInfo; oldSym, newSym: PIdent) =
while first > 0 and line[first-1] in Letters: dec first while first > 0 and line[first-1] in Letters: dec first
if first < 0: return if first < 0: return
if line[first] == '`': inc first if line[first] == '`': inc first
let last = first+identLen(line, first)-1 let last = first+identLen(line, first)-1
if cmpIgnoreStyle(line[first..last], oldSym.s) == 0: if cmpIgnoreStyle(line[first..last], oldSym.s) == 0:
var x = line.substr(0, first-1) & newSym.s & line.substr(last+1) var x = line.substr(0, first-1) & newSym.s & line.substr(last+1)

View file

@ -12,10 +12,10 @@
# handling that exists! Only at line endings checks are necessary # handling that exists! Only at line endings checks are necessary
# if the buffer needs refilling. # if the buffer needs refilling.
import import
llstream, strutils llstream, strutils
const const
Lrz* = ' ' Lrz* = ' '
Apo* = '\'' Apo* = '\''
Tabulator* = '\x09' Tabulator* = '\x09'
@ -27,7 +27,7 @@ const
BACKSPACE* = '\x08' BACKSPACE* = '\x08'
VT* = '\x0B' VT* = '\x0B'
const const
EndOfFile* = '\0' # end of file marker EndOfFile* = '\0' # end of file marker
# A little picture makes everything clear :-) # A little picture makes everything clear :-)
# buf: # buf:
@ -36,7 +36,7 @@ const
# #
NewLines* = {CR, LF} NewLines* = {CR, LF}
type type
TBaseLexer* = object of RootObj TBaseLexer* = object of RootObj
bufpos*: int bufpos*: int
buf*: cstring buf*: cstring
@ -46,9 +46,9 @@ type
# private data: # private data:
sentinel*: int sentinel*: int
lineStart*: int # index of last line start in buffer lineStart*: int # index of last line start in buffer
proc openBaseLexer*(L: var TBaseLexer, inputstream: PLLStream,
proc openBaseLexer*(L: var TBaseLexer, inputstream: PLLStream,
bufLen: int = 8192) bufLen: int = 8192)
# 8K is a reasonable buffer size # 8K is a reasonable buffer size
proc closeBaseLexer*(L: var TBaseLexer) proc closeBaseLexer*(L: var TBaseLexer)
@ -64,15 +64,15 @@ proc handleLF*(L: var TBaseLexer, pos: int): int
# of the LF. # of the LF.
# implementation # implementation
const const
chrSize = sizeof(char) chrSize = sizeof(char)
proc closeBaseLexer(L: var TBaseLexer) = proc closeBaseLexer(L: var TBaseLexer) =
dealloc(L.buf) dealloc(L.buf)
llStreamClose(L.stream) llStreamClose(L.stream)
proc fillBuffer(L: var TBaseLexer) = proc fillBuffer(L: var TBaseLexer) =
var var
charsRead, toCopy, s: int # all are in characters, charsRead, toCopy, s: int # all are in characters,
# not bytes (in case this # not bytes (in case this
# is not the same) # is not the same)
@ -82,68 +82,68 @@ proc fillBuffer(L: var TBaseLexer) =
assert(L.sentinel < L.bufLen) assert(L.sentinel < L.bufLen)
toCopy = L.bufLen - L.sentinel - 1 toCopy = L.bufLen - L.sentinel - 1
assert(toCopy >= 0) assert(toCopy >= 0)
if toCopy > 0: if toCopy > 0:
moveMem(L.buf, addr(L.buf[L.sentinel + 1]), toCopy * chrSize) moveMem(L.buf, addr(L.buf[L.sentinel + 1]), toCopy * chrSize)
# "moveMem" handles overlapping regions # "moveMem" handles overlapping regions
charsRead = llStreamRead(L.stream, addr(L.buf[toCopy]), charsRead = llStreamRead(L.stream, addr(L.buf[toCopy]),
(L.sentinel + 1) * chrSize) div chrSize (L.sentinel + 1) * chrSize) div chrSize
s = toCopy + charsRead s = toCopy + charsRead
if charsRead < L.sentinel + 1: if charsRead < L.sentinel + 1:
L.buf[s] = EndOfFile # set end marker L.buf[s] = EndOfFile # set end marker
L.sentinel = s L.sentinel = s
else: else:
# compute sentinel: # compute sentinel:
dec(s) # BUGFIX (valgrind) dec(s) # BUGFIX (valgrind)
while true: while true:
assert(s < L.bufLen) assert(s < L.bufLen)
while (s >= 0) and not (L.buf[s] in NewLines): dec(s) while (s >= 0) and not (L.buf[s] in NewLines): dec(s)
if s >= 0: if s >= 0:
# we found an appropriate character for a sentinel: # we found an appropriate character for a sentinel:
L.sentinel = s L.sentinel = s
break break
else: else:
# rather than to give up here because the line is too long, # rather than to give up here because the line is too long,
# double the buffer's size and try again: # double the buffer's size and try again:
oldBufLen = L.bufLen oldBufLen = L.bufLen
L.bufLen = L.bufLen * 2 L.bufLen = L.bufLen * 2
L.buf = cast[cstring](realloc(L.buf, L.bufLen * chrSize)) L.buf = cast[cstring](realloc(L.buf, L.bufLen * chrSize))
assert(L.bufLen - oldBufLen == oldBufLen) assert(L.bufLen - oldBufLen == oldBufLen)
charsRead = llStreamRead(L.stream, addr(L.buf[oldBufLen]), charsRead = llStreamRead(L.stream, addr(L.buf[oldBufLen]),
oldBufLen * chrSize) div chrSize oldBufLen * chrSize) div chrSize
if charsRead < oldBufLen: if charsRead < oldBufLen:
L.buf[oldBufLen + charsRead] = EndOfFile L.buf[oldBufLen + charsRead] = EndOfFile
L.sentinel = oldBufLen + charsRead L.sentinel = oldBufLen + charsRead
break break
s = L.bufLen - 1 s = L.bufLen - 1
proc fillBaseLexer(L: var TBaseLexer, pos: int): int = proc fillBaseLexer(L: var TBaseLexer, pos: int): int =
assert(pos <= L.sentinel) assert(pos <= L.sentinel)
if pos < L.sentinel: if pos < L.sentinel:
result = pos + 1 # nothing to do result = pos + 1 # nothing to do
else: else:
fillBuffer(L) fillBuffer(L)
L.bufpos = 0 # XXX: is this really correct? L.bufpos = 0 # XXX: is this really correct?
result = 0 result = 0
L.lineStart = result L.lineStart = result
proc handleCR(L: var TBaseLexer, pos: int): int = proc handleCR(L: var TBaseLexer, pos: int): int =
assert(L.buf[pos] == CR) assert(L.buf[pos] == CR)
inc(L.lineNumber) inc(L.lineNumber)
result = fillBaseLexer(L, pos) result = fillBaseLexer(L, pos)
if L.buf[result] == LF: if L.buf[result] == LF:
result = fillBaseLexer(L, result) result = fillBaseLexer(L, result)
proc handleLF(L: var TBaseLexer, pos: int): int = proc handleLF(L: var TBaseLexer, pos: int): int =
assert(L.buf[pos] == LF) assert(L.buf[pos] == LF)
inc(L.lineNumber) inc(L.lineNumber)
result = fillBaseLexer(L, pos) #L.lastNL := result-1; // BUGFIX: was: result; result = fillBaseLexer(L, pos) #L.lastNL := result-1; // BUGFIX: was: result;
proc skipUTF8BOM(L: var TBaseLexer) = proc skipUTF8BOM(L: var TBaseLexer) =
if L.buf[0] == '\xEF' and L.buf[1] == '\xBB' and L.buf[2] == '\xBF': if L.buf[0] == '\xEF' and L.buf[1] == '\xBB' and L.buf[2] == '\xBF':
inc(L.bufpos, 3) inc(L.bufpos, 3)
inc(L.lineStart, 3) inc(L.lineStart, 3)
proc openBaseLexer(L: var TBaseLexer, inputstream: PLLStream, bufLen = 8192) = proc openBaseLexer(L: var TBaseLexer, inputstream: PLLStream, bufLen = 8192) =
assert(bufLen > 0) assert(bufLen > 0)
L.bufpos = 0 L.bufpos = 0
L.bufLen = bufLen L.bufLen = bufLen
@ -155,15 +155,15 @@ proc openBaseLexer(L: var TBaseLexer, inputstream: PLLStream, bufLen = 8192) =
fillBuffer(L) fillBuffer(L)
skipUTF8BOM(L) skipUTF8BOM(L)
proc getColNumber(L: TBaseLexer, pos: int): int = proc getColNumber(L: TBaseLexer, pos: int): int =
result = abs(pos - L.lineStart) result = abs(pos - L.lineStart)
proc getCurrentLine(L: TBaseLexer, marker: bool = true): string = proc getCurrentLine(L: TBaseLexer, marker: bool = true): string =
result = "" result = ""
var i = L.lineStart var i = L.lineStart
while not (L.buf[i] in {CR, LF, EndOfFile}): while not (L.buf[i] in {CR, LF, EndOfFile}):
add(result, L.buf[i]) add(result, L.buf[i])
inc(i) inc(i)
result.add("\n") result.add("\n")
if marker: if marker:
result.add(spaces(getColNumber(L, L.bufpos)) & '^' & "\n") result.add(spaces(getColNumber(L, L.bufpos)) & '^' & "\n")

View file

@ -9,7 +9,7 @@
# this unit handles Nim sets; it implements symbolic sets # this unit handles Nim sets; it implements symbolic sets
import import
ast, astalgo, trees, nversion, msgs, platform, bitsets, types, renderer ast, astalgo, trees, nversion, msgs, platform, bitsets, types, renderer
proc toBitSet*(s: PNode, b: var TBitSet) proc toBitSet*(s: PNode, b: var TBitSet)
@ -30,17 +30,17 @@ proc equalSets*(a, b: PNode): bool
proc cardSet*(s: PNode): BiggestInt proc cardSet*(s: PNode): BiggestInt
# implementation # implementation
proc inSet(s: PNode, elem: PNode): bool = proc inSet(s: PNode, elem: PNode): bool =
if s.kind != nkCurly: if s.kind != nkCurly:
internalError(s.info, "inSet") internalError(s.info, "inSet")
return false return false
for i in countup(0, sonsLen(s) - 1): for i in countup(0, sonsLen(s) - 1):
if s.sons[i].kind == nkRange: if s.sons[i].kind == nkRange:
if leValue(s.sons[i].sons[0], elem) and if leValue(s.sons[i].sons[0], elem) and
leValue(elem, s.sons[i].sons[1]): leValue(elem, s.sons[i].sons[1]):
return true return true
else: else:
if sameValue(s.sons[i], elem): if sameValue(s.sons[i], elem):
return true return true
result = false result = false
@ -58,37 +58,37 @@ proc overlap(a, b: PNode): bool =
else: else:
result = sameValue(a, b) result = sameValue(a, b)
proc someInSet(s: PNode, a, b: PNode): bool = proc someInSet(s: PNode, a, b: PNode): bool =
# checks if some element of a..b is in the set s # checks if some element of a..b is in the set s
if s.kind != nkCurly: if s.kind != nkCurly:
internalError(s.info, "SomeInSet") internalError(s.info, "SomeInSet")
return false return false
for i in countup(0, sonsLen(s) - 1): for i in countup(0, sonsLen(s) - 1):
if s.sons[i].kind == nkRange: if s.sons[i].kind == nkRange:
if leValue(s.sons[i].sons[0], b) and leValue(b, s.sons[i].sons[1]) or if leValue(s.sons[i].sons[0], b) and leValue(b, s.sons[i].sons[1]) or
leValue(s.sons[i].sons[0], a) and leValue(a, s.sons[i].sons[1]): leValue(s.sons[i].sons[0], a) and leValue(a, s.sons[i].sons[1]):
return true return true
else: else:
# a <= elem <= b # a <= elem <= b
if leValue(a, s.sons[i]) and leValue(s.sons[i], b): if leValue(a, s.sons[i]) and leValue(s.sons[i], b):
return true return true
result = false result = false
proc toBitSet(s: PNode, b: var TBitSet) = proc toBitSet(s: PNode, b: var TBitSet) =
var first, j: BiggestInt var first, j: BiggestInt
first = firstOrd(s.typ.sons[0]) first = firstOrd(s.typ.sons[0])
bitSetInit(b, int(getSize(s.typ))) bitSetInit(b, int(getSize(s.typ)))
for i in countup(0, sonsLen(s) - 1): for i in countup(0, sonsLen(s) - 1):
if s.sons[i].kind == nkRange: if s.sons[i].kind == nkRange:
j = getOrdValue(s.sons[i].sons[0]) j = getOrdValue(s.sons[i].sons[0])
while j <= getOrdValue(s.sons[i].sons[1]): while j <= getOrdValue(s.sons[i].sons[1]):
bitSetIncl(b, j - first) bitSetIncl(b, j - first)
inc(j) inc(j)
else: else:
bitSetIncl(b, getOrdValue(s.sons[i]) - first) bitSetIncl(b, getOrdValue(s.sons[i]) - first)
proc toTreeSet(s: TBitSet, settype: PType, info: TLineInfo): PNode = proc toTreeSet(s: TBitSet, settype: PType, info: TLineInfo): PNode =
var var
a, b, e, first: BiggestInt # a, b are interval borders a, b, e, first: BiggestInt # a, b are interval borders
elemType: PType elemType: PType
n: PNode n: PNode
@ -98,17 +98,17 @@ proc toTreeSet(s: TBitSet, settype: PType, info: TLineInfo): PNode =
result.typ = settype result.typ = settype
result.info = info result.info = info
e = 0 e = 0
while e < len(s) * ElemSize: while e < len(s) * ElemSize:
if bitSetIn(s, e): if bitSetIn(s, e):
a = e a = e
b = e b = e
while true: while true:
inc(b) inc(b)
if (b >= len(s) * ElemSize) or not bitSetIn(s, b): break if (b >= len(s) * ElemSize) or not bitSetIn(s, b): break
dec(b) dec(b)
if a == b: if a == b:
addSon(result, newIntTypeNode(nkIntLit, a + first, elemType)) addSon(result, newIntTypeNode(nkIntLit, a + first, elemType))
else: else:
n = newNodeI(nkRange, info) n = newNodeI(nkRange, info)
n.typ = elemType n.typ = elemType
addSon(n, newIntTypeNode(nkIntLit, a + first, elemType)) addSon(n, newIntTypeNode(nkIntLit, a + first, elemType))
@ -117,7 +117,7 @@ proc toTreeSet(s: TBitSet, settype: PType, info: TLineInfo): PNode =
e = b e = b
inc(e) inc(e)
template nodeSetOp(a, b: PNode, op: expr) {.dirty.} = template nodeSetOp(a, b: PNode, op: expr) {.dirty.} =
var x, y: TBitSet var x, y: TBitSet
toBitSet(a, x) toBitSet(a, x)
toBitSet(b, y) toBitSet(b, y)
@ -129,13 +129,13 @@ proc diffSets(a, b: PNode): PNode = nodeSetOp(a, b, bitSetDiff)
proc intersectSets(a, b: PNode): PNode = nodeSetOp(a, b, bitSetIntersect) proc intersectSets(a, b: PNode): PNode = nodeSetOp(a, b, bitSetIntersect)
proc symdiffSets(a, b: PNode): PNode = nodeSetOp(a, b, bitSetSymDiff) proc symdiffSets(a, b: PNode): PNode = nodeSetOp(a, b, bitSetSymDiff)
proc containsSets(a, b: PNode): bool = proc containsSets(a, b: PNode): bool =
var x, y: TBitSet var x, y: TBitSet
toBitSet(a, x) toBitSet(a, x)
toBitSet(b, y) toBitSet(b, y)
result = bitSetContains(x, y) result = bitSetContains(x, y)
proc equalSets(a, b: PNode): bool = proc equalSets(a, b: PNode): bool =
var x, y: TBitSet var x, y: TBitSet
toBitSet(a, x) toBitSet(a, x)
toBitSet(b, y) toBitSet(b, y)
@ -147,26 +147,26 @@ proc complement*(a: PNode): PNode =
for i in countup(0, high(x)): x[i] = not x[i] for i in countup(0, high(x)): x[i] = not x[i]
result = toTreeSet(x, a.typ, a.info) result = toTreeSet(x, a.typ, a.info)
proc cardSet(s: PNode): BiggestInt = proc cardSet(s: PNode): BiggestInt =
# here we can do better than converting it into a compact set # here we can do better than converting it into a compact set
# we just count the elements directly # we just count the elements directly
result = 0 result = 0
for i in countup(0, sonsLen(s) - 1): for i in countup(0, sonsLen(s) - 1):
if s.sons[i].kind == nkRange: if s.sons[i].kind == nkRange:
result = result + getOrdValue(s.sons[i].sons[1]) - result = result + getOrdValue(s.sons[i].sons[1]) -
getOrdValue(s.sons[i].sons[0]) + 1 getOrdValue(s.sons[i].sons[0]) + 1
else: else:
inc(result) inc(result)
proc setHasRange(s: PNode): bool = proc setHasRange(s: PNode): bool =
if s.kind != nkCurly: if s.kind != nkCurly:
internalError(s.info, "SetHasRange") internalError(s.info, "SetHasRange")
return false return false
for i in countup(0, sonsLen(s) - 1): for i in countup(0, sonsLen(s) - 1):
if s.sons[i].kind == nkRange: if s.sons[i].kind == nkRange:
return true return true
result = false result = false
proc emptyRange(a, b: PNode): bool = proc emptyRange(a, b: PNode): bool =
result = not leValue(a, b) # a > b iff not (a <= b) result = not leValue(a, b) # a > b iff not (a <= b)

View file

@ -7,192 +7,6 @@
# distribution, for details about the copyright. # distribution, for details about the copyright.
# #
## Nimsuggest is a tool that helps to give editors IDE like capabilities. ## Nimsuggest has been moved to https://github.com/nim-lang/nimsuggest
import strutils, os, parseopt, parseUtils {.error: "This project has moved to the following repo: https://github.com/nim-lang/nimsuggest".}
import options, commands, modules, sem, passes, passaux, msgs, nimconf,
extccomp, condsyms, lists, net, rdstdin
const Usage = """
Nimsuggest - Tool to give every editor IDE like capabilities for Nim
Usage:
nimsuggest [options] projectfile.nim
Options:
--port:PORT port, by default 6000
--address:HOST binds to that address, by default ""
--stdin read commands from stdin and write results to
stdout instead of using sockets
The server then listens to the connection and takes line-based commands.
In addition, all command line options of Nim that do not affect code generation
are supported.
"""
var
gPort = 6000.Port
gAddress = ""
gUseStdin: bool
const
seps = {':', ';', ' ', '\t'}
Help = "usage: sug|con|def|use file.nim[;dirtyfile.nim]:line:col\n"&
"type 'quit' to quit\n" &
"type 'debug' to toggle debug mode on/off\n" &
"type 'terse' to toggle terse mode on/off"
proc parseQuoted(cmd: string; outp: var string; start: int): int =
var i = start
i += skipWhitespace(cmd, i)
if cmd[i] == '"':
i += parseUntil(cmd, outp, '"', i+1)+2
else:
i += parseUntil(cmd, outp, seps, i)
result = i
proc action(cmd: string) =
template toggle(sw) =
if sw in gGlobalOptions:
excl(gGlobalOptions, sw)
else:
incl(gGlobalOptions, sw)
return
template err() =
echo Help
return
var opc = ""
var i = parseIdent(cmd, opc, 0)
case opc.normalize
of "sug": gIdeCmd = ideSug
of "con": gIdeCmd = ideCon
of "def": gIdeCmd = ideDef
of "use":
modules.resetAllModules()
gIdeCmd = ideUse
of "quit": quit()
of "debug": toggle optIdeDebug
of "terse": toggle optIdeTerse
else: err()
var dirtyfile = ""
var orig = ""
i = parseQuoted(cmd, orig, i)
if cmd[i] == ';':
i = parseQuoted(cmd, dirtyfile, i+1)
i += skipWhile(cmd, seps, i)
var line = -1
var col = 0
i += parseInt(cmd, line, i)
i += skipWhile(cmd, seps, i)
i += parseInt(cmd, col, i)
var isKnownFile = true
if orig.len == 0: err()
let dirtyIdx = orig.fileInfoIdx(isKnownFile)
if dirtyfile.len != 0: msgs.setDirtyFile(dirtyIdx, dirtyfile)
else: msgs.setDirtyFile(dirtyIdx, nil)
resetModule dirtyIdx
if dirtyIdx != gProjectMainIdx:
resetModule gProjectMainIdx
gTrackPos = newLineInfo(dirtyIdx, line, col-1)
#echo dirtyfile, gDirtyBufferIdx, " project ", gProjectMainIdx
gErrorCounter = 0
if not isKnownFile:
compileProject(dirtyIdx)
else:
compileProject()
proc serve() =
# do not stop after the first error:
msgs.gErrorMax = high(int)
if gUseStdin:
echo Help
var line = ""
while readLineFromStdin("> ", line):
action line
echo ""
flushFile(stdout)
else:
var server = newSocket()
server.bindAddr(gPort, gAddress)
var inp = "".TaintedString
server.listen()
while true:
var stdoutSocket = newSocket()
msgs.writelnHook = proc (line: string) =
stdoutSocket.send(line & "\c\L")
accept(server, stdoutSocket)
stdoutSocket.readLine(inp)
action inp.string
stdoutSocket.send("\c\L")
stdoutSocket.close()
proc mainCommand =
registerPass verbosePass
registerPass semPass
gCmd = cmdIdeTools
incl gGlobalOptions, optCaasEnabled
isServing = true
wantMainModule()
appendStr(searchPaths, options.libpath)
if gProjectFull.len != 0:
# current path is always looked first for modules
prependStr(searchPaths, gProjectPath)
serve()
proc processCmdLine*(pass: TCmdLinePass, cmd: string) =
var p = parseopt.initOptParser(cmd)
while true:
parseopt.next(p)
case p.kind
of cmdEnd: break
of cmdLongoption, cmdShortOption:
case p.key.normalize
of "port": gPort = parseInt(p.val).Port
of "address": gAddress = p.val
of "stdin": gUseStdin = true
else: processSwitch(pass, p)
of cmdArgument:
options.gProjectName = unixToNativePath(p.key)
# if processArgument(pass, p, argsCount): break
proc handleCmdLine() =
if paramCount() == 0:
stdout.writeln(Usage)
else:
processCmdLine(passCmd1, "")
if gProjectName != "":
try:
gProjectFull = canonicalizePath(gProjectName)
except OSError:
gProjectFull = gProjectName
var p = splitFile(gProjectFull)
gProjectPath = p.dir
gProjectName = p.name
else:
gProjectPath = getCurrentDir()
loadConfigs(DefaultConfig) # load all config files
# now process command line arguments again, because some options in the
# command line can overwite the config file's settings
extccomp.initVars()
processCmdLine(passCmd2, "")
mainCommand()
when false:
proc quitCalled() {.noconv.} =
writeStackTrace()
addQuitProc(quitCalled)
condsyms.initDefines()
defineSymbol "nimsuggest"
handleCmdline()

View file

@ -1,17 +0,0 @@
# Special configuration file for the Nim project
gc:markAndSweep
hint[XDeclaredButNotUsed]:off
path:"$projectPath/../.."
path:"$lib/packages/docutils"
path:"../../compiler"
define:useStdoutAsStdmsg
define:nimsuggest
cs:partial
#define:useNodeIds
define:booting
#define:noDocgen

View file

@ -4,3 +4,5 @@ proc findNodeJs*(): string =
result = findExe("nodejs") result = findExe("nodejs")
if result == "": if result == "":
result = findExe("node") result = findExe("node")
if result == "":
result = findExe("iojs")

View file

@ -10,7 +10,7 @@
# This module contains Nim's version. It is the only place where it needs # This module contains Nim's version. It is the only place where it needs
# to be changed. # to be changed.
const const
MaxSetElements* = 1 shl 16 # (2^16) to support unicode character sets? MaxSetElements* = 1 shl 16 # (2^16) to support unicode character sets?
VersionAsString* = system.NimVersion VersionAsString* = system.NimVersion
RodFileVersion* = "1215" # modify this if the rod-format changes! RodFileVersion* = "1215" # modify this if the rod-format changes!

View file

@ -13,6 +13,7 @@ import
const const
hasTinyCBackend* = defined(tinyc) hasTinyCBackend* = defined(tinyc)
useEffectSystem* = true useEffectSystem* = true
useWriteTracking* = false
hasFFI* = defined(useFFI) hasFFI* = defined(useFFI)
newScopeForIf* = true newScopeForIf* = true
useCaas* = not defined(noCaas) useCaas* = not defined(noCaas)
@ -39,7 +40,7 @@ type # please make sure we have under 32 options
TGlobalOption* = enum # **keep binary compatible** TGlobalOption* = enum # **keep binary compatible**
gloptNone, optForceFullMake, optDeadCodeElim, gloptNone, optForceFullMake, optDeadCodeElim,
optListCmd, optCompileOnly, optNoLinking, optListCmd, optCompileOnly, optNoLinking,
optSafeCode, # only allow safe code optReportConceptFailures, # report 'compiles' or 'concept' matching failures
optCDebug, # turn on debugging information optCDebug, # turn on debugging information
optGenDynLib, # generate a dynamic library optGenDynLib, # generate a dynamic library
optGenStaticLib, # generate a static library optGenStaticLib, # generate a static library
@ -54,6 +55,7 @@ type # please make sure we have under 32 options
optSkipUserConfigFile, # skip the users's config file optSkipUserConfigFile, # skip the users's config file
optSkipParentConfigFiles, # skip parent dir's config files optSkipParentConfigFiles, # skip parent dir's config files
optNoMain, # do not generate a "main" proc optNoMain, # do not generate a "main" proc
optUseColors, # use colors for hints, warnings, and errors
optThreads, # support for multi-threading optThreads, # support for multi-threading
optStdout, # output to stdout optStdout, # output to stdout
optThreadAnalysis, # thread analysis pass optThreadAnalysis, # thread analysis pass
@ -81,13 +83,13 @@ type # please make sure we have under 32 options
cmdRun # run the project via TCC backend cmdRun # run the project via TCC backend
TStringSeq* = seq[string] TStringSeq* = seq[string]
TGCMode* = enum # the selected GC TGCMode* = enum # the selected GC
gcNone, gcBoehm, gcMarkAndSweep, gcRefc, gcV2, gcGenerational gcNone, gcBoehm, gcGo, gcMarkAndSweep, gcRefc, gcV2, gcGenerational
TIdeCmd* = enum IdeCmd* = enum
ideNone, ideSug, ideCon, ideDef, ideUse ideNone, ideSug, ideCon, ideDef, ideUse, ideDus
var var
gIdeCmd*: TIdeCmd gIdeCmd*: IdeCmd
const const
ChecksOptions* = {optObjCheck, optFieldCheck, optRangeCheck, optNilCheck, ChecksOptions* = {optObjCheck, optFieldCheck, optRangeCheck, optNilCheck,
@ -126,9 +128,6 @@ template compilationCachePresent*: expr =
template optPreserveOrigSource*: expr = template optPreserveOrigSource*: expr =
optEmbedOrigSrc in gGlobalOptions optEmbedOrigSrc in gGlobalOptions
template optPrintSurroundingSrc*: expr =
gVerbosity >= 2
const const
genSubDir* = "nimcache" genSubDir* = "nimcache"
NimExt* = "nim" NimExt* = "nim"
@ -145,10 +144,12 @@ const
var var
gConfigVars* = newStringTable(modeStyleInsensitive) gConfigVars* = newStringTable(modeStyleInsensitive)
gDllOverrides = newStringTable(modeCaseInsensitive) gDllOverrides = newStringTable(modeCaseInsensitive)
gPrefixDir* = "" # Overrides the default prefix dir in getPrefixDir proc.
libpath* = "" libpath* = ""
gProjectName* = "" # holds a name like 'nimrod' gProjectName* = "" # holds a name like 'nimrod'
gProjectPath* = "" # holds a path like /home/alice/projects/nimrod/compiler/ gProjectPath* = "" # holds a path like /home/alice/projects/nimrod/compiler/
gProjectFull* = "" # projectPath/projectName gProjectFull* = "" # projectPath/projectName
gProjectIsStdin* = false # whether we're compiling from stdin
gProjectMainIdx*: int32 # the canonical path id of the main module gProjectMainIdx*: int32 # the canonical path id of the main module
nimcacheDir* = "" nimcacheDir* = ""
command* = "" # the main command (e.g. cc, check, scan, etc) command* = "" # the main command (e.g. cc, check, scan, etc)
@ -172,7 +173,7 @@ proc existsConfigVar*(key: string): bool =
result = hasKey(gConfigVars, key) result = hasKey(gConfigVars, key)
proc getConfigVar*(key: string): string = proc getConfigVar*(key: string): string =
result = gConfigVars[key] result = gConfigVars.getOrDefault key
proc setConfigVar*(key, val: string) = proc setConfigVar*(key, val: string) =
gConfigVars[key] = val gConfigVars[key] = val
@ -182,8 +183,24 @@ proc getOutFile*(filename, ext: string): string =
else: result = changeFileExt(filename, ext) else: result = changeFileExt(filename, ext)
proc getPrefixDir*(): string = proc getPrefixDir*(): string =
## gets the application directory ## Gets the prefix dir, usually the parent directory where the binary resides.
result = splitPath(getAppDir()).head ##
## This is overrided by some tools (namely nimsuggest) via the ``gPrefixDir``
## global.
if gPrefixDir != "": result = gPrefixDir
else:
result = splitPath(getAppDir()).head
proc setDefaultLibpath*() =
# set default value (can be overwritten):
if libpath == "":
# choose default libpath:
var prefix = getPrefixDir()
when defined(posix):
if prefix == "/usr": libpath = "/usr/lib/nim"
elif prefix == "/usr/local": libpath = "/usr/local/lib/nim"
else: libpath = joinPath(prefix, "lib")
else: libpath = joinPath(prefix, "lib")
proc canonicalizePath*(path: string): string = proc canonicalizePath*(path: string): string =
when not FileSystemCaseSensitive: result = path.expandFilename.toLower when not FileSystemCaseSensitive: result = path.expandFilename.toLower
@ -205,7 +222,7 @@ proc removeTrailingDirSep*(path: string): string =
else: else:
result = path result = path
proc getGeneratedPath: string = proc getNimcacheDir*: string =
result = if nimcacheDir.len > 0: nimcacheDir else: gProjectPath.shortenDir / result = if nimcacheDir.len > 0: nimcacheDir else: gProjectPath.shortenDir /
genSubDir genSubDir
@ -261,7 +278,7 @@ proc toGeneratedFile*(path, ext: string): string =
## converts "/home/a/mymodule.nim", "rod" to "/home/a/nimcache/mymodule.rod" ## converts "/home/a/mymodule.nim", "rod" to "/home/a/nimcache/mymodule.rod"
var (head, tail) = splitPath(path) var (head, tail) = splitPath(path)
#if len(head) > 0: head = shortenDir(head & dirSep) #if len(head) > 0: head = shortenDir(head & dirSep)
result = joinPath([getGeneratedPath(), changeFileExt(tail, ext)]) result = joinPath([getNimcacheDir(), changeFileExt(tail, ext)])
#echo "toGeneratedFile(", path, ", ", ext, ") = ", result #echo "toGeneratedFile(", path, ", ", ext, ") = ", result
when noTimeMachine: when noTimeMachine:
@ -289,14 +306,14 @@ when noTimeMachine:
proc completeGeneratedFilePath*(f: string, createSubDir: bool = true): string = proc completeGeneratedFilePath*(f: string, createSubDir: bool = true): string =
var (head, tail) = splitPath(f) var (head, tail) = splitPath(f)
#if len(head) > 0: head = removeTrailingDirSep(shortenDir(head & dirSep)) #if len(head) > 0: head = removeTrailingDirSep(shortenDir(head & dirSep))
var subdir = getGeneratedPath() # / head var subdir = getNimcacheDir() # / head
if createSubDir: if createSubDir:
try: try:
createDir(subdir) createDir(subdir)
when noTimeMachine: when noTimeMachine:
excludeDirFromTimeMachine(subdir) excludeDirFromTimeMachine(subdir)
except OSError: except OSError:
writeln(stdout, "cannot create directory: " & subdir) writeLine(stdout, "cannot create directory: " & subdir)
quit(1) quit(1)
result = joinPath(subdir, tail) result = joinPath(subdir, tail)
#echo "completeGeneratedFilePath(", f, ") = ", result #echo "completeGeneratedFilePath(", f, ") = ", result
@ -325,13 +342,16 @@ proc rawFindFile2(f: string): string =
result = "" result = ""
proc findFile*(f: string): string {.procvar.} = proc findFile*(f: string): string {.procvar.} =
result = f.rawFindFile if f.isAbsolute:
if result.len == 0: result = if f.existsFile: f else: ""
result = f.toLower.rawFindFile else:
result = f.rawFindFile
if result.len == 0: if result.len == 0:
result = f.rawFindFile2 result = f.toLower.rawFindFile
if result.len == 0: if result.len == 0:
result = f.toLower.rawFindFile2 result = f.rawFindFile2
if result.len == 0:
result = f.toLower.rawFindFile2
proc findModule*(modulename, currentModule: string): string = proc findModule*(modulename, currentModule: string): string =
# returns path to module # returns path to module
@ -395,3 +415,20 @@ template cnimdbg*: expr = p.module.module.fileIdx == gProjectMainIdx
template pnimdbg*: expr = p.lex.fileIdx == gProjectMainIdx template pnimdbg*: expr = p.lex.fileIdx == gProjectMainIdx
template lnimdbg*: expr = L.fileIdx == gProjectMainIdx template lnimdbg*: expr = L.fileIdx == gProjectMainIdx
proc parseIdeCmd*(s: string): IdeCmd =
case s:
of "sug": ideSug
of "con": ideCon
of "def": ideDef
of "use": ideUse
of "dus": ideDus
else: ideNone
proc `$`*(c: IdeCmd): string =
case c:
of ideSug: "sug"
of ideCon: "con"
of ideDef: "def"
of ideUse: "use"
of ideDus: "dus"
of ideNone: "none"

View file

@ -178,13 +178,14 @@ type
arDiscriminant, # is a discriminant arDiscriminant, # is a discriminant
arStrange # it is a strange beast like 'typedesc[var T]' arStrange # it is a strange beast like 'typedesc[var T]'
proc isAssignable*(owner: PSym, n: PNode): TAssignableResult = proc isAssignable*(owner: PSym, n: PNode; isUnsafeAddr=false): TAssignableResult =
## 'owner' can be nil! ## 'owner' can be nil!
result = arNone result = arNone
case n.kind case n.kind
of nkSym: of nkSym:
# don't list 'skLet' here: let kinds = if isUnsafeAddr: {skVar, skResult, skTemp, skParam, skLet}
if n.sym.kind in {skVar, skResult, skTemp}: else: {skVar, skResult, skTemp}
if n.sym.kind in kinds:
if owner != nil and owner.id == n.sym.owner.id and if owner != nil and owner.id == n.sym.owner.id and
sfGlobal notin n.sym.flags: sfGlobal notin n.sym.flags:
result = arLocalLValue result = arLocalLValue
@ -200,7 +201,7 @@ proc isAssignable*(owner: PSym, n: PNode): TAssignableResult =
{tyVar, tyPtr, tyRef}: {tyVar, tyPtr, tyRef}:
result = arLValue result = arLValue
else: else:
result = isAssignable(owner, n.sons[0]) result = isAssignable(owner, n.sons[0], isUnsafeAddr)
if result != arNone and sfDiscriminant in n.sons[1].sym.flags: if result != arNone and sfDiscriminant in n.sons[1].sym.flags:
result = arDiscriminant result = arDiscriminant
of nkBracketExpr: of nkBracketExpr:
@ -208,23 +209,27 @@ proc isAssignable*(owner: PSym, n: PNode): TAssignableResult =
{tyVar, tyPtr, tyRef}: {tyVar, tyPtr, tyRef}:
result = arLValue result = arLValue
else: else:
result = isAssignable(owner, n.sons[0]) result = isAssignable(owner, n.sons[0], isUnsafeAddr)
of nkHiddenStdConv, nkHiddenSubConv, nkConv: of nkHiddenStdConv, nkHiddenSubConv, nkConv:
# Object and tuple conversions are still addressable, so we skip them # Object and tuple conversions are still addressable, so we skip them
# XXX why is 'tyOpenArray' allowed here? # XXX why is 'tyOpenArray' allowed here?
if skipTypes(n.typ, abstractPtrs-{tyTypeDesc}).kind in if skipTypes(n.typ, abstractPtrs-{tyTypeDesc}).kind in
{tyOpenArray, tyTuple, tyObject}: {tyOpenArray, tyTuple, tyObject}:
result = isAssignable(owner, n.sons[1]) result = isAssignable(owner, n.sons[1], isUnsafeAddr)
elif compareTypes(n.typ, n.sons[1].typ, dcEqIgnoreDistinct): elif compareTypes(n.typ, n.sons[1].typ, dcEqIgnoreDistinct):
# types that are equal modulo distinction preserve l-value: # types that are equal modulo distinction preserve l-value:
result = isAssignable(owner, n.sons[1]) result = isAssignable(owner, n.sons[1], isUnsafeAddr)
of nkHiddenDeref, nkDerefExpr, nkHiddenAddr: of nkHiddenDeref, nkDerefExpr, nkHiddenAddr:
result = arLValue result = arLValue
of nkObjUpConv, nkObjDownConv, nkCheckedFieldExpr: of nkObjUpConv, nkObjDownConv, nkCheckedFieldExpr:
result = isAssignable(owner, n.sons[0]) result = isAssignable(owner, n.sons[0], isUnsafeAddr)
of nkCallKinds: of nkCallKinds:
# builtin slice keeps lvalue-ness: # builtin slice keeps lvalue-ness:
if getMagic(n) == mSlice: result = isAssignable(owner, n.sons[1]) if getMagic(n) in {mArrGet, mSlice}:
result = isAssignable(owner, n.sons[1], isUnsafeAddr)
of nkStmtList, nkStmtListExpr:
if n.typ != nil:
result = isAssignable(owner, n.lastSon, isUnsafeAddr)
else: else:
discard discard

View file

@ -64,6 +64,7 @@ proc setBaseFlags*(n: PNode, base: TNumericalBase)
proc parseSymbol*(p: var TParser, allowNil = false): PNode proc parseSymbol*(p: var TParser, allowNil = false): PNode
proc parseTry(p: var TParser; isExpr: bool): PNode proc parseTry(p: var TParser; isExpr: bool): PNode
proc parseCase(p: var TParser): PNode proc parseCase(p: var TParser): PNode
proc parseStmtPragma(p: var TParser): PNode
# implementation # implementation
proc getTok(p: var TParser) = proc getTok(p: var TParser) =
@ -124,6 +125,9 @@ proc rawSkipComment(p: var TParser, node: PNode) =
proc skipComment(p: var TParser, node: PNode) = proc skipComment(p: var TParser, node: PNode) =
if p.tok.indent < 0: rawSkipComment(p, node) if p.tok.indent < 0: rawSkipComment(p, node)
proc flexComment(p: var TParser, node: PNode) =
if p.tok.indent < 0 or realInd(p): rawSkipComment(p, node)
proc skipInd(p: var TParser) = proc skipInd(p: var TParser) =
if p.tok.indent >= 0: if p.tok.indent >= 0:
if not realInd(p): parMessage(p, errInvalidIndentation) if not realInd(p): parMessage(p, errInvalidIndentation)
@ -499,10 +503,13 @@ proc parsePar(p: var TParser): PNode =
#| parKeyw = 'discard' | 'include' | 'if' | 'while' | 'case' | 'try' #| parKeyw = 'discard' | 'include' | 'if' | 'while' | 'case' | 'try'
#| | 'finally' | 'except' | 'for' | 'block' | 'const' | 'let' #| | 'finally' | 'except' | 'for' | 'block' | 'const' | 'let'
#| | 'when' | 'var' | 'mixin' #| | 'when' | 'var' | 'mixin'
#| par = '(' optInd (&parKeyw complexOrSimpleStmt ^+ ';' #| par = '(' optInd
#| | simpleExpr ('=' expr (';' complexOrSimpleStmt ^+ ';' )? )? #| ( &parKeyw complexOrSimpleStmt ^+ ';'
#| | (':' expr)? (',' (exprColonEqExpr comma?)*)? )? #| | ';' complexOrSimpleStmt ^+ ';'
#| optPar ')' #| | pragmaStmt
#| | simpleExpr ( ('=' expr (';' complexOrSimpleStmt ^+ ';' )? )
#| | (':' expr (',' exprColonEqExpr ^+ ',' )? ) ) )
#| optPar ')'
# #
# unfortunately it's ambiguous: (expr: expr) vs (exprStmt); however a # unfortunately it's ambiguous: (expr: expr) vs (exprStmt); however a
# leading ';' could be used to enforce a 'stmt' context ... # leading ';' could be used to enforce a 'stmt' context ...
@ -521,6 +528,8 @@ proc parsePar(p: var TParser): PNode =
getTok(p) getTok(p)
optInd(p, result) optInd(p, result)
semiStmtList(p, result) semiStmtList(p, result)
elif p.tok.tokType == tkCurlyDotLe:
result.add(parseStmtPragma(p))
elif p.tok.tokType != tkParRi: elif p.tok.tokType != tkParRi:
var a = simpleExpr(p) var a = simpleExpr(p)
if p.tok.tokType == tkEquals: if p.tok.tokType == tkEquals:
@ -881,12 +890,13 @@ proc parseTuple(p: var TParser, indentAllowed = false): PNode =
skipComment(p, result) skipComment(p, result)
if realInd(p): if realInd(p):
withInd(p): withInd(p):
skipComment(p, result) rawSkipComment(p, result)
while true: while true:
case p.tok.tokType case p.tok.tokType
of tkSymbol, tkAccent: of tkSymbol, tkAccent:
var a = parseIdentColonEquals(p, {}) var a = parseIdentColonEquals(p, {})
skipComment(p, a) if p.tok.indent < 0 or p.tok.indent >= p.currInd:
rawSkipComment(p, a)
addSon(result, a) addSon(result, a)
of tkEof: break of tkEof: break
else: else:
@ -1602,6 +1612,7 @@ proc parseEnum(p: var TParser): PNode =
getTok(p) getTok(p)
addSon(result, ast.emptyNode) addSon(result, ast.emptyNode)
optInd(p, result) optInd(p, result)
flexComment(p, result)
while true: while true:
var a = parseSymbol(p) var a = parseSymbol(p)
if a.kind == nkEmpty: return if a.kind == nkEmpty: return
@ -1615,12 +1626,14 @@ proc parseEnum(p: var TParser): PNode =
a = newNodeP(nkEnumFieldDef, p) a = newNodeP(nkEnumFieldDef, p)
addSon(a, b) addSon(a, b)
addSon(a, parseExpr(p)) addSon(a, parseExpr(p))
skipComment(p, a) if p.tok.indent < 0 or p.tok.indent >= p.currInd:
rawSkipComment(p, a)
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, a)
else: else:
skipComment(p, a) if p.tok.indent < 0 or p.tok.indent >= p.currInd:
rawSkipComment(p, a)
addSon(result, a) addSon(result, a)
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:
@ -1641,7 +1654,7 @@ proc parseObjectWhen(p: var TParser): PNode =
addSon(branch, parseExpr(p)) addSon(branch, parseExpr(p))
colcom(p, branch) colcom(p, branch)
addSon(branch, parseObjectPart(p)) addSon(branch, parseObjectPart(p))
skipComment(p, branch) flexComment(p, branch)
addSon(result, branch) addSon(result, branch)
if p.tok.tokType != tkElif: break if p.tok.tokType != tkElif: break
if p.tok.tokType == tkElse and sameInd(p): if p.tok.tokType == tkElse and sameInd(p):
@ -1649,7 +1662,7 @@ proc parseObjectWhen(p: var TParser): PNode =
eat(p, tkElse) eat(p, tkElse)
colcom(p, branch) colcom(p, branch)
addSon(branch, parseObjectPart(p)) addSon(branch, parseObjectPart(p))
skipComment(p, branch) flexComment(p, branch)
addSon(result, branch) addSon(result, branch)
proc parseObjectCase(p: var TParser): PNode = proc parseObjectCase(p: var TParser): PNode =
@ -1669,7 +1682,7 @@ proc parseObjectCase(p: var TParser): PNode =
addSon(a, ast.emptyNode) addSon(a, ast.emptyNode)
addSon(result, a) addSon(result, a)
if p.tok.tokType == tkColon: getTok(p) if p.tok.tokType == tkColon: getTok(p)
skipComment(p, result) flexComment(p, result)
var wasIndented = false var wasIndented = false
let oldInd = p.currInd let oldInd = p.currInd
if realInd(p): if realInd(p):
@ -1718,7 +1731,8 @@ proc parseObjectPart(p: var TParser): PNode =
result = parseObjectCase(p) result = parseObjectCase(p)
of tkSymbol, tkAccent: of tkSymbol, tkAccent:
result = parseIdentColonEquals(p, {withPragma}) result = parseIdentColonEquals(p, {withPragma})
skipComment(p, result) if p.tok.indent < 0 or p.tok.indent >= p.currInd:
rawSkipComment(p, result)
of tkNil, tkDiscard: of tkNil, tkDiscard:
result = newNodeP(nkNilLit, p) result = newNodeP(nkNilLit, p)
getTok(p) getTok(p)

View file

@ -9,38 +9,38 @@
## implements some little helper passes ## implements some little helper passes
import import
strutils, ast, astalgo, passes, msgs, options, idgen strutils, ast, astalgo, passes, msgs, options, idgen
proc verboseOpen(s: PSym): PPassContext = proc verboseOpen(s: PSym): PPassContext =
#MessageOut('compiling ' + s.name.s); #MessageOut('compiling ' + s.name.s);
result = nil # we don't need a context result = nil # we don't need a context
if gVerbosity > 0: rawMessage(hintProcessing, s.name.s) rawMessage(hintProcessing, s.name.s)
proc verboseProcess(context: PPassContext, n: PNode): PNode = proc verboseProcess(context: PPassContext, n: PNode): PNode =
result = n result = n
if context != nil: internalError("logpass: context is not nil") if context != nil: internalError("logpass: context is not nil")
if gVerbosity == 3: if gVerbosity == 3:
# system.nim deactivates all hints, for verbosity:3 we want the processing # system.nim deactivates all hints, for verbosity:3 we want the processing
# messages nonetheless, so we activate them again unconditionally: # messages nonetheless, so we activate them again unconditionally:
incl(msgs.gNotes, hintProcessing) incl(msgs.gNotes, hintProcessing)
message(n.info, hintProcessing, $idgen.gBackendId) message(n.info, hintProcessing, $idgen.gBackendId)
const verbosePass* = makePass(open = verboseOpen, process = verboseProcess) const verbosePass* = makePass(open = verboseOpen, process = verboseProcess)
proc cleanUp(c: PPassContext, n: PNode): PNode = proc cleanUp(c: PPassContext, n: PNode): PNode =
result = n result = n
# we cannot clean up if dead code elimination is activated # we cannot clean up if dead code elimination is activated
if optDeadCodeElim in gGlobalOptions or n == nil: return if optDeadCodeElim in gGlobalOptions or n == nil: return
case n.kind case n.kind
of nkStmtList: of nkStmtList:
for i in countup(0, sonsLen(n) - 1): discard cleanUp(c, n.sons[i]) for i in countup(0, sonsLen(n) - 1): discard cleanUp(c, n.sons[i])
of nkProcDef, nkMethodDef: of nkProcDef, nkMethodDef:
if n.sons[namePos].kind == nkSym: if n.sons[namePos].kind == nkSym:
var s = n.sons[namePos].sym var s = n.sons[namePos].sym
if sfDeadCodeElim notin getModule(s).flags and not astNeeded(s): if sfDeadCodeElim notin getModule(s).flags and not astNeeded(s):
s.ast.sons[bodyPos] = ast.emptyNode # free the memory s.ast.sons[bodyPos] = ast.emptyNode # free the memory
else: else:
discard discard
const cleanupPass* = makePass(process = cleanUp, close = cleanUp) const cleanupPass* = makePass(process = cleanUp, close = cleanUp)

View file

@ -170,11 +170,7 @@ proc processModule(module: PSym, stream: PLLStream, rd: PRodReader) =
openPasses(a, module) openPasses(a, module)
if stream == nil: if stream == nil:
let filename = fileIdx.toFullPathConsiderDirty let filename = fileIdx.toFullPathConsiderDirty
if module.name.s == "-": s = llStreamOpen(filename, fmRead)
module.name.s = "stdinfile"
s = llStreamOpen(stdin)
else:
s = llStreamOpen(filename, fmRead)
if s == nil: if s == nil:
rawMessage(errCannotOpenFile, filename) rawMessage(errCannotOpenFile, filename)
return return
@ -194,8 +190,17 @@ proc processModule(module: PSym, stream: PLLStream, rd: PRodReader) =
while true: while true:
var n = parseTopLevelStmt(p) var n = parseTopLevelStmt(p)
if n.kind == nkEmpty: break if n.kind == nkEmpty: break
if not processTopLevelStmt(n, a): break if sfNoForward in module.flags:
# read everything, no streaming possible
var sl = newNodeI(nkStmtList, n.info)
sl.add n
while true:
var n = parseTopLevelStmt(p)
if n.kind == nkEmpty: break
sl.add n
discard processTopLevelStmt(sl, a)
break
elif not processTopLevelStmt(n, a): break
closeParsers(p) closeParsers(p)
if s.kind != llsStdIn: break if s.kind != llsStdIn: break
closePasses(a) closePasses(a)

View file

@ -68,7 +68,7 @@ proc inSymChoice(sc, x: PNode): bool =
elif sc.kind == nkOpenSymChoice: elif sc.kind == nkOpenSymChoice:
# same name suffices for open sym choices! # same name suffices for open sym choices!
result = sc.sons[0].sym.name.id == x.sym.name.id result = sc.sons[0].sym.name.id == x.sym.name.id
proc checkTypes(c: PPatternContext, p: PSym, n: PNode): bool = proc checkTypes(c: PPatternContext, p: PSym, n: PNode): bool =
# check param constraints first here as this is quite optimized: # check param constraints first here as this is quite optimized:
if p.constraint != nil: if p.constraint != nil:
@ -115,13 +115,13 @@ proc matchNested(c: PPatternContext, p, n: PNode, rpn: bool): bool =
if rpn: arglist.add(n.sons[0]) if rpn: arglist.add(n.sons[0])
elif n.kind == nkHiddenStdConv and n.sons[1].kind == nkBracket: elif n.kind == nkHiddenStdConv and n.sons[1].kind == nkBracket:
let n = n.sons[1] let n = n.sons[1]
for i in 0.. <n.len: for i in 0.. <n.len:
if not matchStarAux(c, op, n[i], arglist, rpn): return false if not matchStarAux(c, op, n[i], arglist, rpn): return false
elif checkTypes(c, p.sons[2].sym, n): elif checkTypes(c, p.sons[2].sym, n):
add(arglist, n) add(arglist, n)
else: else:
result = false result = false
if n.kind notin nkCallKinds: return false if n.kind notin nkCallKinds: return false
if matches(c, p.sons[1], n.sons[0]): if matches(c, p.sons[1], n.sons[0]):
var arglist = newNodeI(nkArgList, n.info) var arglist = newNodeI(nkArgList, n.info)
@ -130,7 +130,9 @@ proc matchNested(c: PPatternContext, p, n: PNode, rpn: bool): bool =
proc matches(c: PPatternContext, p, n: PNode): bool = proc matches(c: PPatternContext, p, n: PNode): bool =
# hidden conversions (?) # hidden conversions (?)
if isPatternParam(c, p): if nfNoRewrite in n.flags:
result = false
elif isPatternParam(c, p):
result = bindOrCheck(c, p.sym, n) result = bindOrCheck(c, p.sym, n)
elif n.kind == nkSym and p.kind == nkIdent: elif n.kind == nkSym and p.kind == nkIdent:
result = p.ident.id == n.sym.name.id result = p.ident.id == n.sym.name.id
@ -149,7 +151,7 @@ proc matches(c: PPatternContext, p, n: PNode): bool =
of "**": result = matchNested(c, p, n, rpn=true) of "**": result = matchNested(c, p, n, rpn=true)
of "~": result = not matches(c, p.sons[1], n) of "~": result = not matches(c, p.sons[1], n)
else: internalError(p.info, "invalid pattern") else: internalError(p.info, "invalid pattern")
# template {add(a, `&` * b)}(a: string{noalias}, b: varargs[string]) = # template {add(a, `&` * b)}(a: string{noalias}, b: varargs[string]) =
# add(a, b) # add(a, b)
elif p.kind == nkCurlyExpr: elif p.kind == nkCurlyExpr:
if p.sons[1].kind == nkPrefix: if p.sons[1].kind == nkPrefix:
@ -210,7 +212,7 @@ proc matchStmtList(c: PPatternContext, p, n: PNode): PNode =
if not isNil(c.mapping): c.mapping = nil if not isNil(c.mapping): c.mapping = nil
return false return false
result = true result = true
if p.kind == nkStmtList and n.kind == p.kind and p.len < n.len: if p.kind == nkStmtList and n.kind == p.kind and p.len < n.len:
let n = flattenStmts(n) let n = flattenStmts(n)
# no need to flatten 'p' here as that has already been done # no need to flatten 'p' here as that has already been done

View file

@ -7,12 +7,12 @@
# distribution, for details about the copyright. # distribution, for details about the copyright.
# #
import import
llstream, lexer, parser, idents, strutils, ast, msgs llstream, lexer, parser, idents, strutils, ast, msgs
proc parseAll*(p: var TParser): PNode = proc parseAll*(p: var TParser): PNode =
result = nil result = nil
proc parseTopLevelStmt*(p: var TParser): PNode = proc parseTopLevelStmt*(p: var TParser): PNode =
result = nil result = nil

View file

@ -158,8 +158,8 @@ type
TSystemCPU* = enum # Also add CPU for in initialization section and TSystemCPU* = enum # Also add CPU for in initialization section and
# alias conditionals to condsyms (end of module). # alias conditionals to condsyms (end of module).
cpuNone, cpuI386, cpuM68k, cpuAlpha, cpuPowerpc, cpuPowerpc64, cpuNone, cpuI386, cpuM68k, cpuAlpha, cpuPowerpc, cpuPowerpc64,
cpuSparc, cpuVm, cpuIa64, cpuAmd64, cpuMips, cpuArm, cpuPowerpc64el, cpuSparc, cpuVm, cpuIa64, cpuAmd64, cpuMips, cpuMipsel,
cpuJS, cpuNimrodVM, cpuAVR cpuArm, cpuArm64, cpuJS, cpuNimrodVM, cpuAVR, cpuMSP430
type type
TEndian* = enum TEndian* = enum
@ -175,15 +175,19 @@ const
(name: "alpha", intSize: 64, endian: littleEndian, floatSize: 64, bit: 64), (name: "alpha", intSize: 64, endian: littleEndian, floatSize: 64, bit: 64),
(name: "powerpc", intSize: 32, endian: bigEndian, floatSize: 64, bit: 32), (name: "powerpc", intSize: 32, endian: bigEndian, floatSize: 64, bit: 32),
(name: "powerpc64", intSize: 64, endian: bigEndian, floatSize: 64,bit: 64), (name: "powerpc64", intSize: 64, endian: bigEndian, floatSize: 64,bit: 64),
(name: "powerpc64el", intSize: 64, endian: littleEndian, floatSize: 64,bit: 64),
(name: "sparc", intSize: 32, endian: bigEndian, floatSize: 64, bit: 32), (name: "sparc", intSize: 32, endian: bigEndian, floatSize: 64, bit: 32),
(name: "vm", intSize: 32, endian: littleEndian, floatSize: 64, bit: 32), (name: "vm", intSize: 32, endian: littleEndian, floatSize: 64, bit: 32),
(name: "ia64", intSize: 64, endian: littleEndian, floatSize: 64, bit: 64), (name: "ia64", intSize: 64, endian: littleEndian, floatSize: 64, bit: 64),
(name: "amd64", intSize: 64, endian: littleEndian, floatSize: 64, bit: 64), (name: "amd64", intSize: 64, endian: littleEndian, floatSize: 64, bit: 64),
(name: "mips", intSize: 32, endian: bigEndian, floatSize: 64, bit: 32), (name: "mips", intSize: 32, endian: bigEndian, floatSize: 64, bit: 32),
(name: "mipsel", intSize: 32, endian: littleEndian, floatSize: 64, bit: 32),
(name: "arm", intSize: 32, endian: littleEndian, floatSize: 64, bit: 32), (name: "arm", intSize: 32, endian: littleEndian, floatSize: 64, bit: 32),
(name: "arm64", intSize: 64, endian: littleEndian, floatSize: 64, bit: 64),
(name: "js", intSize: 32, endian: bigEndian,floatSize: 64,bit: 32), (name: "js", intSize: 32, endian: bigEndian,floatSize: 64,bit: 32),
(name: "nimrodvm", intSize: 32, endian: bigEndian, floatSize: 64, bit: 32), (name: "nimrodvm", intSize: 32, endian: bigEndian, floatSize: 64, bit: 32),
(name: "avr", intSize: 16, endian: littleEndian, floatSize: 32, bit: 16)] (name: "avr", intSize: 16, endian: littleEndian, floatSize: 32, bit: 16),
(name: "msp430", intSize: 16, endian: littleEndian, floatSize: 32, bit: 16)]
var var
targetCPU*, hostCPU*: TSystemCPU targetCPU*, hostCPU*: TSystemCPU

View file

@ -9,7 +9,8 @@
## The builtin 'system.locals' implemented as a plugin. ## The builtin 'system.locals' implemented as a plugin.
import plugins, ast, astalgo, magicsys, lookups, semdata, lowerings import compiler/plugins, compiler/ast, compiler/astalgo, compiler/magicsys,
compiler/lookups, compiler/semdata, compiler/lowerings
proc semLocals(c: PContext, n: PNode): PNode = proc semLocals(c: PContext, n: PNode): PNode =
var counter = 0 var counter = 0

View file

@ -25,19 +25,20 @@ const
wBorrow, wExtern, wImportCompilerProc, wThread, wImportCpp, wImportObjC, wBorrow, wExtern, wImportCompilerProc, wThread, wImportCpp, wImportObjC,
wAsmNoStackFrame, wError, wDiscardable, wNoInit, wDestructor, wCodegenDecl, wAsmNoStackFrame, wError, wDiscardable, wNoInit, wDestructor, wCodegenDecl,
wGensym, wInject, wRaises, wTags, wLocks, wDelegator, wGcSafe, wGensym, wInject, wRaises, wTags, wLocks, wDelegator, wGcSafe,
wOverride, wConstructor} wOverride, wConstructor, wExportNims}
converterPragmas* = procPragmas converterPragmas* = procPragmas
methodPragmas* = procPragmas methodPragmas* = procPragmas+{wBase}
templatePragmas* = {wImmediate, wDeprecated, wError, wGensym, wInject, wDirty, templatePragmas* = {wImmediate, wDeprecated, wError, wGensym, wInject, wDirty,
wDelegator} wDelegator, wExportNims}
macroPragmas* = {FirstCallConv..LastCallConv, wImmediate, wImportc, wExportc, macroPragmas* = {FirstCallConv..LastCallConv, wImmediate, wImportc, wExportc,
wNodecl, wMagic, wNosideeffect, wCompilerproc, wDeprecated, wExtern, wNodecl, wMagic, wNosideeffect, wCompilerproc, wDeprecated, wExtern,
wImportCpp, wImportObjC, wError, wDiscardable, wGensym, wInject, wDelegator} wImportCpp, wImportObjC, wError, wDiscardable, wGensym, wInject, wDelegator,
wExportNims}
iteratorPragmas* = {FirstCallConv..LastCallConv, wNosideeffect, wSideeffect, iteratorPragmas* = {FirstCallConv..LastCallConv, wNosideeffect, wSideeffect,
wImportc, wExportc, wNodecl, wMagic, wDeprecated, wBorrow, wExtern, wImportc, wExportc, wNodecl, wMagic, wDeprecated, wBorrow, wExtern,
wImportCpp, wImportObjC, wError, wDiscardable, wGensym, wInject, wRaises, wImportCpp, wImportObjC, wError, wDiscardable, wGensym, wInject, wRaises,
wTags, wLocks, wGcSafe} wTags, wLocks, wGcSafe, wExportNims}
exprPragmas* = {wLine, wLocks} exprPragmas* = {wLine, wLocks, wNoRewrite}
stmtPragmas* = {wChecks, wObjChecks, wFieldChecks, wRangechecks, stmtPragmas* = {wChecks, wObjChecks, wFieldChecks, wRangechecks,
wBoundchecks, wOverflowchecks, wNilchecks, wAssertions, wWarnings, wHints, wBoundchecks, wOverflowchecks, wNilchecks, wAssertions, wWarnings, wHints,
wLinedir, wStacktrace, wLinetrace, wOptimization, wHint, wWarning, wError, wLinedir, wStacktrace, wLinetrace, wOptimization, wHint, wWarning, wError,
@ -54,15 +55,15 @@ const
wPure, wHeader, wCompilerproc, wFinal, wSize, wExtern, wShallow, wPure, wHeader, wCompilerproc, wFinal, wSize, wExtern, wShallow,
wImportCpp, wImportObjC, wError, wIncompleteStruct, wByCopy, wByRef, wImportCpp, wImportObjC, wError, wIncompleteStruct, wByCopy, wByRef,
wInheritable, wGensym, wInject, wRequiresInit, wUnchecked, wUnion, wPacked, wInheritable, wGensym, wInject, wRequiresInit, wUnchecked, wUnion, wPacked,
wBorrow, wGcSafe} wBorrow, wGcSafe, wExportNims}
fieldPragmas* = {wImportc, wExportc, wDeprecated, wExtern, fieldPragmas* = {wImportc, wExportc, wDeprecated, wExtern,
wImportCpp, wImportObjC, wError, wGuard} wImportCpp, wImportObjC, wError, wGuard, wBitsize}
varPragmas* = {wImportc, wExportc, wVolatile, wRegister, wThreadVar, wNodecl, varPragmas* = {wImportc, wExportc, wVolatile, wRegister, wThreadVar, wNodecl,
wMagic, wHeader, wDeprecated, wCompilerproc, wDynlib, wExtern, wMagic, wHeader, wDeprecated, wCompilerproc, wDynlib, wExtern,
wImportCpp, wImportObjC, wError, wNoInit, wCompileTime, wGlobal, wImportCpp, wImportObjC, wError, wNoInit, wCompileTime, wGlobal,
wGensym, wInject, wCodegenDecl, wGuard, wGoto} wGensym, wInject, wCodegenDecl, wGuard, wGoto, wExportNims}
constPragmas* = {wImportc, wExportc, wHeader, wDeprecated, wMagic, wNodecl, constPragmas* = {wImportc, wExportc, wHeader, wDeprecated, wMagic, wNodecl,
wExtern, wImportCpp, wImportObjC, wError, wGensym, wInject} wExtern, wImportCpp, wImportObjC, wError, wGensym, wInject, wExportNims}
letPragmas* = varPragmas letPragmas* = varPragmas
procTypePragmas* = {FirstCallConv..LastCallConv, wVarargs, wNosideeffect, procTypePragmas* = {FirstCallConv..LastCallConv, wVarargs, wNosideeffect,
wThread, wRaises, wLocks, wTags, wGcSafe} wThread, wRaises, wLocks, wTags, wGcSafe}
@ -276,7 +277,8 @@ proc processNote(c: PContext, n: PNode) =
if (n.kind == nkExprColonExpr) and (sonsLen(n) == 2) and if (n.kind == nkExprColonExpr) and (sonsLen(n) == 2) and
(n.sons[0].kind == nkBracketExpr) and (n.sons[0].kind == nkBracketExpr) and
(n.sons[0].sons[1].kind == nkIdent) and (n.sons[0].sons[1].kind == nkIdent) and
(n.sons[0].sons[0].kind == nkIdent) and (n.sons[1].kind == nkIdent): (n.sons[0].sons[0].kind == nkIdent):
#and (n.sons[1].kind == nkIdent):
var nk: TNoteKind var nk: TNoteKind
case whichKeyword(n.sons[0].sons[0].ident) case whichKeyword(n.sons[0].sons[0].ident)
of wHint: of wHint:
@ -394,6 +396,8 @@ proc processCompile(c: PContext, n: PNode) =
var found = findFile(s) var found = findFile(s)
if found == "": found = s if found == "": found = s
var trunc = changeFileExt(found, "") var trunc = changeFileExt(found, "")
if not isAbsolute(found):
found = parentDir(n.info.toFullPath) / found
extccomp.addExternalFileToCompile(found) extccomp.addExternalFileToCompile(found)
extccomp.addFileToLink(completeCFilePath(trunc, false)) extccomp.addFileToLink(completeCFilePath(trunc, false))
@ -590,278 +594,293 @@ proc singlePragma(c: PContext, sym: PSym, n: PNode, i: int,
validPragmas: TSpecialWords): bool = validPragmas: TSpecialWords): bool =
var it = n.sons[i] var it = n.sons[i]
var key = if it.kind == nkExprColonExpr: it.sons[0] else: it var key = if it.kind == nkExprColonExpr: it.sons[0] else: it
if key.kind == nkIdent: if key.kind == nkBracketExpr:
var userPragma = strTableGet(c.userPragmas, key.ident) processNote(c, it)
if userPragma != nil: return
inc c.instCounter let ident = considerQuotedIdent(key)
if c.instCounter > 100: var userPragma = strTableGet(c.userPragmas, ident)
globalError(it.info, errRecursiveDependencyX, userPragma.name.s) if userPragma != nil:
pragma(c, sym, userPragma.ast, validPragmas) inc c.instCounter
# ensure the pragma is also remember for generic instantiations in other if c.instCounter > 100:
# modules: globalError(it.info, errRecursiveDependencyX, userPragma.name.s)
n.sons[i] = userPragma.ast pragma(c, sym, userPragma.ast, validPragmas)
dec c.instCounter # ensure the pragma is also remember for generic instantiations in other
else: # modules:
var k = whichKeyword(key.ident) n.sons[i] = userPragma.ast
if k in validPragmas: dec c.instCounter
case k else:
of wExportc: var k = whichKeyword(ident)
makeExternExport(sym, getOptionalStr(c, it, "$1"), it.info) if k in validPragmas:
incl(sym.flags, sfUsed) # avoid wrong hints case k
of wImportc: makeExternImport(sym, getOptionalStr(c, it, "$1")) of wExportc:
of wImportCompilerProc: makeExternExport(sym, getOptionalStr(c, it, "$1"), it.info)
processImportCompilerProc(sym, getOptionalStr(c, it, "$1")) incl(sym.flags, sfUsed) # avoid wrong hints
of wExtern: setExternName(sym, expectStrLit(c, it)) of wImportc: makeExternImport(sym, getOptionalStr(c, it, "$1"))
of wImmediate: of wImportCompilerProc:
if sym.kind in {skTemplate, skMacro}: incl(sym.flags, sfImmediate) processImportCompilerProc(sym, getOptionalStr(c, it, "$1"))
else: invalidPragma(it) of wExtern: setExternName(sym, expectStrLit(c, it))
of wDirty: of wImmediate:
if sym.kind == skTemplate: incl(sym.flags, sfDirty) if sym.kind in {skTemplate, skMacro}: incl(sym.flags, sfImmediate)
else: invalidPragma(it)
of wImportCpp:
processImportCpp(sym, getOptionalStr(c, it, "$1"))
of wImportObjC:
processImportObjC(sym, getOptionalStr(c, it, "$1"))
of wAlign:
if sym.typ == nil: invalidPragma(it)
var align = expectIntLit(c, it)
if (not isPowerOfTwo(align) and align != 0) or align >% high(int16):
localError(it.info, errPowerOfTwoExpected)
else:
sym.typ.align = align.int16
of wSize:
if sym.typ == nil: invalidPragma(it)
var size = expectIntLit(c, it)
if not isPowerOfTwo(size) or size <= 0 or size > 8:
localError(it.info, errPowerOfTwoExpected)
else:
sym.typ.size = size
of wNodecl:
noVal(it)
incl(sym.loc.flags, lfNoDecl)
of wPure, wAsmNoStackFrame:
noVal(it)
if sym != nil:
if k == wPure and sym.kind in routineKinds: invalidPragma(it)
else: incl(sym.flags, sfPure)
of wVolatile:
noVal(it)
incl(sym.flags, sfVolatile)
of wRegister:
noVal(it)
incl(sym.flags, sfRegister)
of wThreadVar:
noVal(it)
incl(sym.flags, sfThread)
of wDeadCodeElim: pragmaDeadCodeElim(c, it)
of wNoForward: pragmaNoForward(c, it)
of wMagic: processMagic(c, it, sym)
of wCompileTime:
noVal(it)
incl(sym.flags, sfCompileTime)
incl(sym.loc.flags, lfNoDecl)
of wGlobal:
noVal(it)
incl(sym.flags, sfGlobal)
incl(sym.flags, sfPure)
of wMerge:
# only supported for backwards compat, doesn't do anything anymore
noVal(it)
of wConstructor:
noVal(it)
incl(sym.flags, sfConstructor)
of wHeader:
var lib = getLib(c, libHeader, getStrLitNode(c, it))
addToLib(lib, sym)
incl(sym.flags, sfImportc)
incl(sym.loc.flags, lfHeader)
incl(sym.loc.flags, lfNoDecl)
# implies nodecl, because otherwise header would not make sense
if sym.loc.r == nil: sym.loc.r = rope(sym.name.s)
of wDestructor:
sym.flags.incl sfOverriden
if sym.name.s.normalize != "destroy":
localError(n.info, errGenerated, "destructor has to be named 'destroy'")
of wOverride:
sym.flags.incl sfOverriden
of wNosideeffect:
noVal(it)
incl(sym.flags, sfNoSideEffect)
if sym.typ != nil: incl(sym.typ.flags, tfNoSideEffect)
of wSideeffect:
noVal(it)
incl(sym.flags, sfSideEffect)
of wNoreturn:
noVal(it)
incl(sym.flags, sfNoReturn)
of wDynlib:
processDynLib(c, it, sym)
of wCompilerproc:
noVal(it) # compilerproc may not get a string!
if sfFromGeneric notin sym.flags: markCompilerProc(sym)
of wProcVar:
noVal(it)
incl(sym.flags, sfProcvar)
of wDeprecated:
if it.kind == nkExprColonExpr: deprecatedStmt(c, it)
elif sym != nil: incl(sym.flags, sfDeprecated)
else: incl(c.module.flags, sfDeprecated)
of wVarargs:
noVal(it)
if sym.typ == nil: invalidPragma(it)
else: incl(sym.typ.flags, tfVarargs)
of wBorrow:
if sym.kind == skType:
typeBorrow(sym, it)
else:
noVal(it)
incl(sym.flags, sfBorrow)
of wFinal:
noVal(it)
if sym.typ == nil: invalidPragma(it)
else: incl(sym.typ.flags, tfFinal)
of wInheritable:
noVal(it)
if sym.typ == nil or tfFinal in sym.typ.flags: invalidPragma(it)
else: incl(sym.typ.flags, tfInheritable)
of wAcyclic:
noVal(it)
if sym.typ == nil: invalidPragma(it)
else: incl(sym.typ.flags, tfAcyclic)
of wShallow:
noVal(it)
if sym.typ == nil: invalidPragma(it)
else: incl(sym.typ.flags, tfShallow)
of wThread:
noVal(it)
incl(sym.flags, sfThread)
incl(sym.flags, sfProcvar)
if sym.typ != nil: incl(sym.typ.flags, tfThread)
of wGcSafe:
noVal(it)
if sym.kind != skType: incl(sym.flags, sfThread)
if sym.typ != nil: incl(sym.typ.flags, tfGcSafe)
else: invalidPragma(it)
of wPacked:
noVal(it)
if sym.typ == nil: invalidPragma(it)
else: incl(sym.typ.flags, tfPacked)
of wHint: message(it.info, hintUser, expectStrLit(c, it))
of wWarning: message(it.info, warnUser, expectStrLit(c, it))
of wError:
if sym != nil and sym.isRoutine:
# This is subtle but correct: the error *statement* is only
# allowed for top level statements. Seems to be easier than
# distinguishing properly between
# ``proc p() {.error}`` and ``proc p() = {.error: "msg".}``
noVal(it)
incl(sym.flags, sfError)
else:
localError(it.info, errUser, expectStrLit(c, it))
of wFatal: fatal(it.info, errUser, expectStrLit(c, it))
of wDefine: processDefine(c, it)
of wUndef: processUndef(c, it)
of wCompile: processCompile(c, it)
of wLink: processCommonLink(c, it, linkNormal)
of wLinksys: processCommonLink(c, it, linkSys)
of wPassl: extccomp.addLinkOption(expectStrLit(c, it))
of wPassc: extccomp.addCompileOption(expectStrLit(c, it))
of wBreakpoint: pragmaBreakpoint(c, it)
of wWatchPoint: pragmaWatchpoint(c, it)
of wPush:
processPush(c, n, i + 1)
result = true
of wPop: processPop(c, it)
of wPragma:
processPragma(c, n, i)
result = true
of wDiscardable:
noVal(it)
if sym != nil: incl(sym.flags, sfDiscardable)
of wNoInit:
noVal(it)
if sym != nil: incl(sym.flags, sfNoInit)
of wCodegenDecl: processCodegenDecl(c, it, sym)
of wChecks, wObjChecks, wFieldChecks, wRangechecks, wBoundchecks,
wOverflowchecks, wNilchecks, wAssertions, wWarnings, wHints,
wLinedir, wStacktrace, wLinetrace, wOptimization,
wCallconv,
wDebugger, wProfiler, wFloatchecks, wNanChecks, wInfChecks,
wPatterns:
if processOption(c, it):
# calling conventions (boring...):
localError(it.info, errOptionExpected)
of FirstCallConv..LastCallConv:
assert(sym != nil)
if sym.typ == nil: invalidPragma(it)
else: sym.typ.callConv = wordToCallConv(k)
of wEmit: pragmaEmit(c, it)
of wUnroll: pragmaUnroll(c, it)
of wLinearScanEnd, wComputedGoto: noVal(it)
of wEffects:
# is later processed in effect analysis:
noVal(it)
of wIncompleteStruct:
noVal(it)
if sym.typ == nil: invalidPragma(it)
else: incl(sym.typ.flags, tfIncompleteStruct)
of wUnchecked:
noVal(it)
if sym.typ == nil: invalidPragma(it)
else: incl(sym.typ.flags, tfUncheckedArray)
of wUnion:
noVal(it)
if sym.typ == nil: invalidPragma(it)
else: incl(sym.typ.flags, tfUnion)
of wRequiresInit:
noVal(it)
if sym.typ == nil: invalidPragma(it)
else: incl(sym.typ.flags, tfNeedsInit)
of wByRef:
noVal(it)
if sym == nil or sym.typ == nil:
if processOption(c, it): localError(it.info, errOptionExpected)
else:
incl(sym.typ.flags, tfByRef)
of wByCopy:
noVal(it)
if sym.kind != skType or sym.typ == nil: invalidPragma(it)
else: incl(sym.typ.flags, tfByCopy)
of wInject, wGensym:
# We check for errors, but do nothing with these pragmas otherwise
# as they are handled directly in 'evalTemplate'.
noVal(it)
if sym == nil: invalidPragma(it)
of wLine: pragmaLine(c, it)
of wRaises, wTags: pragmaRaisesOrTags(c, it)
of wLocks:
if sym == nil: pragmaLockStmt(c, it)
elif sym.typ == nil: invalidPragma(it)
else: sym.typ.lockLevel = pragmaLocks(c, it)
of wGuard:
if sym == nil or sym.kind notin {skVar, skLet, skField}:
invalidPragma(it)
else:
sym.guard = pragmaGuard(c, it, sym.kind)
of wGoto:
if sym == nil or sym.kind notin {skVar, skLet}:
invalidPragma(it)
else:
sym.flags.incl sfGoto
of wInjectStmt:
if it.kind != nkExprColonExpr:
localError(it.info, errExprExpected)
else:
it.sons[1] = c.semExpr(c, it.sons[1])
of wExperimental:
noVal(it)
if isTopLevel(c):
c.module.flags.incl sfExperimental
else:
localError(it.info, "'experimental' pragma only valid as toplevel statement")
else: invalidPragma(it) else: invalidPragma(it)
of wDirty:
if sym.kind == skTemplate: incl(sym.flags, sfDirty)
else: invalidPragma(it)
of wImportCpp:
processImportCpp(sym, getOptionalStr(c, it, "$1"))
of wImportObjC:
processImportObjC(sym, getOptionalStr(c, it, "$1"))
of wAlign:
if sym.typ == nil: invalidPragma(it)
var align = expectIntLit(c, it)
if (not isPowerOfTwo(align) and align != 0) or align >% high(int16):
localError(it.info, errPowerOfTwoExpected)
else:
sym.typ.align = align.int16
of wSize:
if sym.typ == nil: invalidPragma(it)
var size = expectIntLit(c, it)
if not isPowerOfTwo(size) or size <= 0 or size > 8:
localError(it.info, errPowerOfTwoExpected)
else:
sym.typ.size = size
of wNodecl:
noVal(it)
incl(sym.loc.flags, lfNoDecl)
of wPure, wAsmNoStackFrame:
noVal(it)
if sym != nil:
if k == wPure and sym.kind in routineKinds: invalidPragma(it)
else: incl(sym.flags, sfPure)
of wVolatile:
noVal(it)
incl(sym.flags, sfVolatile)
of wRegister:
noVal(it)
incl(sym.flags, sfRegister)
of wThreadVar:
noVal(it)
incl(sym.flags, sfThread)
of wDeadCodeElim: pragmaDeadCodeElim(c, it)
of wNoForward: pragmaNoForward(c, it)
of wMagic: processMagic(c, it, sym)
of wCompileTime:
noVal(it)
incl(sym.flags, sfCompileTime)
incl(sym.loc.flags, lfNoDecl)
of wGlobal:
noVal(it)
incl(sym.flags, sfGlobal)
incl(sym.flags, sfPure)
of wMerge:
# only supported for backwards compat, doesn't do anything anymore
noVal(it)
of wConstructor:
noVal(it)
incl(sym.flags, sfConstructor)
of wHeader:
var lib = getLib(c, libHeader, getStrLitNode(c, it))
addToLib(lib, sym)
incl(sym.flags, sfImportc)
incl(sym.loc.flags, lfHeader)
incl(sym.loc.flags, lfNoDecl)
# implies nodecl, because otherwise header would not make sense
if sym.loc.r == nil: sym.loc.r = rope(sym.name.s)
of wDestructor:
sym.flags.incl sfOverriden
if sym.name.s.normalize != "destroy":
localError(n.info, errGenerated, "destructor has to be named 'destroy'")
of wOverride:
sym.flags.incl sfOverriden
of wNosideeffect:
noVal(it)
incl(sym.flags, sfNoSideEffect)
if sym.typ != nil: incl(sym.typ.flags, tfNoSideEffect)
of wSideeffect:
noVal(it)
incl(sym.flags, sfSideEffect)
of wNoreturn:
noVal(it)
incl(sym.flags, sfNoReturn)
of wDynlib:
processDynLib(c, it, sym)
of wCompilerproc:
noVal(it) # compilerproc may not get a string!
if sfFromGeneric notin sym.flags: markCompilerProc(sym)
of wProcVar:
noVal(it)
incl(sym.flags, sfProcvar)
of wDeprecated:
if it.kind == nkExprColonExpr: deprecatedStmt(c, it)
elif sym != nil: incl(sym.flags, sfDeprecated)
else: incl(c.module.flags, sfDeprecated)
of wVarargs:
noVal(it)
if sym.typ == nil: invalidPragma(it)
else: incl(sym.typ.flags, tfVarargs)
of wBorrow:
if sym.kind == skType:
typeBorrow(sym, it)
else:
noVal(it)
incl(sym.flags, sfBorrow)
of wFinal:
noVal(it)
if sym.typ == nil: invalidPragma(it)
else: incl(sym.typ.flags, tfFinal)
of wInheritable:
noVal(it)
if sym.typ == nil or tfFinal in sym.typ.flags: invalidPragma(it)
else: incl(sym.typ.flags, tfInheritable)
of wAcyclic:
noVal(it)
if sym.typ == nil: invalidPragma(it)
else: incl(sym.typ.flags, tfAcyclic)
of wShallow:
noVal(it)
if sym.typ == nil: invalidPragma(it)
else: incl(sym.typ.flags, tfShallow)
of wThread:
noVal(it)
incl(sym.flags, sfThread)
incl(sym.flags, sfProcvar)
if sym.typ != nil: incl(sym.typ.flags, tfThread)
of wGcSafe:
noVal(it)
if sym.kind != skType: incl(sym.flags, sfThread)
if sym.typ != nil: incl(sym.typ.flags, tfGcSafe)
else: invalidPragma(it)
of wPacked:
noVal(it)
if sym.typ == nil: invalidPragma(it)
else: incl(sym.typ.flags, tfPacked)
of wHint: message(it.info, hintUser, expectStrLit(c, it))
of wWarning: message(it.info, warnUser, expectStrLit(c, it))
of wError:
if sym != nil and sym.isRoutine:
# This is subtle but correct: the error *statement* is only
# allowed for top level statements. Seems to be easier than
# distinguishing properly between
# ``proc p() {.error}`` and ``proc p() = {.error: "msg".}``
noVal(it)
incl(sym.flags, sfError)
else:
localError(it.info, errUser, expectStrLit(c, it))
of wFatal: fatal(it.info, errUser, expectStrLit(c, it))
of wDefine: processDefine(c, it)
of wUndef: processUndef(c, it)
of wCompile: processCompile(c, it)
of wLink: processCommonLink(c, it, linkNormal)
of wLinksys: processCommonLink(c, it, linkSys)
of wPassl: extccomp.addLinkOption(expectStrLit(c, it))
of wPassc: extccomp.addCompileOption(expectStrLit(c, it))
of wBreakpoint: pragmaBreakpoint(c, it)
of wWatchPoint: pragmaWatchpoint(c, it)
of wPush:
processPush(c, n, i + 1)
result = true
of wPop: processPop(c, it)
of wPragma:
processPragma(c, n, i)
result = true
of wDiscardable:
noVal(it)
if sym != nil: incl(sym.flags, sfDiscardable)
of wNoInit:
noVal(it)
if sym != nil: incl(sym.flags, sfNoInit)
of wCodegenDecl: processCodegenDecl(c, it, sym)
of wChecks, wObjChecks, wFieldChecks, wRangechecks, wBoundchecks,
wOverflowchecks, wNilchecks, wAssertions, wWarnings, wHints,
wLinedir, wStacktrace, wLinetrace, wOptimization,
wCallconv,
wDebugger, wProfiler, wFloatchecks, wNanChecks, wInfChecks,
wPatterns:
if processOption(c, it):
# calling conventions (boring...):
localError(it.info, errOptionExpected)
of FirstCallConv..LastCallConv:
assert(sym != nil)
if sym.typ == nil: invalidPragma(it)
else: sym.typ.callConv = wordToCallConv(k)
of wEmit: pragmaEmit(c, it)
of wUnroll: pragmaUnroll(c, it)
of wLinearScanEnd, wComputedGoto: noVal(it)
of wEffects:
# is later processed in effect analysis:
noVal(it)
of wIncompleteStruct:
noVal(it)
if sym.typ == nil: invalidPragma(it)
else: incl(sym.typ.flags, tfIncompleteStruct)
of wUnchecked:
noVal(it)
if sym.typ == nil: invalidPragma(it)
else: incl(sym.typ.flags, tfUncheckedArray)
of wUnion:
noVal(it)
if sym.typ == nil: invalidPragma(it)
else: incl(sym.typ.flags, tfUnion)
of wRequiresInit:
noVal(it)
if sym.typ == nil: invalidPragma(it)
else: incl(sym.typ.flags, tfNeedsInit)
of wByRef:
noVal(it)
if sym == nil or sym.typ == nil:
if processOption(c, it): localError(it.info, errOptionExpected)
else:
incl(sym.typ.flags, tfByRef)
of wByCopy:
noVal(it)
if sym.kind != skType or sym.typ == nil: invalidPragma(it)
else: incl(sym.typ.flags, tfByCopy)
of wInject, wGensym:
# We check for errors, but do nothing with these pragmas otherwise
# as they are handled directly in 'evalTemplate'.
noVal(it)
if sym == nil: invalidPragma(it)
of wLine: pragmaLine(c, it)
of wRaises, wTags: pragmaRaisesOrTags(c, it)
of wLocks:
if sym == nil: pragmaLockStmt(c, it)
elif sym.typ == nil: invalidPragma(it)
else: sym.typ.lockLevel = pragmaLocks(c, it)
of wBitsize:
if sym == nil or sym.kind != skField or it.kind != nkExprColonExpr:
invalidPragma(it)
else:
sym.bitsize = expectIntLit(c, it)
of wGuard:
if sym == nil or sym.kind notin {skVar, skLet, skField}:
invalidPragma(it)
else:
sym.guard = pragmaGuard(c, it, sym.kind)
of wGoto:
if sym == nil or sym.kind notin {skVar, skLet}:
invalidPragma(it)
else:
sym.flags.incl sfGoto
of wExportNims:
if sym == nil: invalidPragma(it)
else: magicsys.registerNimScriptSymbol(sym)
of wInjectStmt:
if it.kind != nkExprColonExpr:
localError(it.info, errExprExpected)
else:
it.sons[1] = c.semExpr(c, it.sons[1])
of wExperimental:
noVal(it)
if isTopLevel(c):
c.module.flags.incl sfExperimental
else:
localError(it.info, "'experimental' pragma only valid as toplevel statement")
of wNoRewrite:
noVal(it)
of wBase:
noVal(it)
sym.flags.incl sfBase
else: invalidPragma(it) else: invalidPragma(it)
else: processNote(c, it) else: invalidPragma(it)
proc implicitPragmas*(c: PContext, sym: PSym, n: PNode, proc implicitPragmas*(c: PContext, sym: PSym, n: PNode,
validPragmas: TSpecialWords) = validPragmas: TSpecialWords) =

View file

@ -87,7 +87,7 @@ proc searchForProcNew(c: PContext, scope: PScope, fn: PSym): PSym =
discard discard
result = nextIdentIter(it, scope.symbols) result = nextIdentIter(it, scope.symbols)
return nil return nil
proc searchForProc*(c: PContext, scope: PScope, fn: PSym): PSym = proc searchForProc*(c: PContext, scope: PScope, fn: PSym): PSym =
@ -99,17 +99,17 @@ proc searchForProc*(c: PContext, scope: PScope, fn: PSym): PSym =
debug fn.typ debug fn.typ
debug if result != nil: result.typ else: nil debug if result != nil: result.typ else: nil
debug if old != nil: old.typ else: nil debug if old != nil: old.typ else: nil
when false: when false:
proc paramsFitBorrow(child, parent: PNode): bool = proc paramsFitBorrow(child, parent: PNode): bool =
var length = sonsLen(child) var length = sonsLen(child)
result = false result = false
if length == sonsLen(parent): if length == sonsLen(parent):
for i in countup(1, length - 1): for i in countup(1, length - 1):
var m = child.sons[i].sym var m = child.sons[i].sym
var n = parent.sons[i].sym var n = parent.sons[i].sym
assert((m.kind == skParam) and (n.kind == skParam)) assert((m.kind == skParam) and (n.kind == skParam))
if not compareTypes(m.typ, n.typ, dcEqOrDistinctOf): return if not compareTypes(m.typ, n.typ, dcEqOrDistinctOf): return
if not compareTypes(child.sons[0].typ, parent.sons[0].typ, if not compareTypes(child.sons[0].typ, parent.sons[0].typ,
dcEqOrDistinctOf): return dcEqOrDistinctOf): return
result = true result = true
@ -120,10 +120,10 @@ when false:
var it: TIdentIter var it: TIdentIter
for scope in walkScopes(startScope): for scope in walkScopes(startScope):
result = initIdentIter(it, scope.symbols, fn.Name) result = initIdentIter(it, scope.symbols, fn.Name)
while result != nil: while result != nil:
# watchout! result must not be the same as fn! # watchout! result must not be the same as fn!
if (result.Kind == fn.kind) and (result.id != fn.id): if (result.Kind == fn.kind) and (result.id != fn.id):
if equalGenericParams(result.ast.sons[genericParamsPos], if equalGenericParams(result.ast.sons[genericParamsPos],
fn.ast.sons[genericParamsPos]): fn.ast.sons[genericParamsPos]):
if paramsFitBorrow(fn.typ.n, result.typ.n): return if paramsFitBorrow(fn.typ.n, result.typ.n): return
result = NextIdentIter(it, scope.symbols) result = NextIdentIter(it, scope.symbols)

View file

@ -31,6 +31,7 @@ type
buf*: string buf*: string
pendingNL*: int # negative if not active; else contains the pendingNL*: int # negative if not active; else contains the
# indentation value # indentation value
pendingWhitespace: int
comStack*: seq[PNode] # comment stack comStack*: seq[PNode] # comment stack
flags*: TRenderFlags flags*: TRenderFlags
checkAnon: bool # we're in a context that can contain sfAnon checkAnon: bool # we're in a context that can contain sfAnon
@ -83,6 +84,7 @@ proc initSrcGen(g: var TSrcGen, renderFlags: TRenderFlags) =
g.buf = "" g.buf = ""
g.flags = renderFlags g.flags = renderFlags
g.pendingNL = -1 g.pendingNL = -1
g.pendingWhitespace = -1
g.checkAnon = false g.checkAnon = false
proc addTok(g: var TSrcGen, kind: TTokType, s: string) = proc addTok(g: var TSrcGen, kind: TTokType, s: string) =
@ -97,12 +99,21 @@ proc addPendingNL(g: var TSrcGen) =
addTok(g, tkSpaces, "\n" & spaces(g.pendingNL)) addTok(g, tkSpaces, "\n" & spaces(g.pendingNL))
g.lineLen = g.pendingNL g.lineLen = g.pendingNL
g.pendingNL = - 1 g.pendingNL = - 1
g.pendingWhitespace = -1
elif g.pendingWhitespace >= 0:
addTok(g, tkSpaces, spaces(g.pendingWhitespace))
g.pendingWhitespace = -1
proc putNL(g: var TSrcGen, indent: int) = proc putNL(g: var TSrcGen, indent: int) =
if g.pendingNL >= 0: addPendingNL(g) if g.pendingNL >= 0: addPendingNL(g)
else: addTok(g, tkSpaces, "\n") else: addTok(g, tkSpaces, "\n")
g.pendingNL = indent g.pendingNL = indent
g.lineLen = indent g.lineLen = indent
g.pendingWhitespace = -1
proc previousNL(g: TSrcGen): bool =
result = g.pendingNL >= 0 or (g.tokens.len > 0 and
g.tokens[^1].kind == tkSpaces)
proc putNL(g: var TSrcGen) = proc putNL(g: var TSrcGen) =
putNL(g, g.indent) putNL(g, g.indent)
@ -127,10 +138,13 @@ proc dedent(g: var TSrcGen) =
dec(g.lineLen, IndentWidth) dec(g.lineLen, IndentWidth)
proc put(g: var TSrcGen, kind: TTokType, s: string) = proc put(g: var TSrcGen, kind: TTokType, s: string) =
addPendingNL(g) if kind != tkSpaces:
if len(s) > 0: addPendingNL(g)
addTok(g, kind, s) if len(s) > 0:
inc(g.lineLen, len(s)) addTok(g, kind, s)
inc(g.lineLen, len(s))
else:
g.pendingWhitespace = s.len
proc putLong(g: var TSrcGen, kind: TTokType, s: string, lineLen: int) = proc putLong(g: var TSrcGen, kind: TTokType, s: string, lineLen: int) =
# use this for tokens over multiple lines. # use this for tokens over multiple lines.
@ -767,7 +781,8 @@ proc gasm(g: var TSrcGen, n: PNode) =
putWithSpace(g, tkAsm, "asm") putWithSpace(g, tkAsm, "asm")
gsub(g, n.sons[0]) gsub(g, n.sons[0])
gcoms(g) gcoms(g)
gsub(g, n.sons[1]) if n.sons.len > 1:
gsub(g, n.sons[1])
proc gident(g: var TSrcGen, n: PNode) = proc gident(g: var TSrcGen, n: PNode) =
if g.checkAnon and n.kind == nkSym and sfAnon in n.sym.flags: return if g.checkAnon and n.kind == nkSym and sfAnon in n.sym.flags: return
@ -1195,6 +1210,7 @@ proc gsub(g: var TSrcGen, n: PNode, c: TContext) =
if renderNoPragmas notin g.flags: if renderNoPragmas notin g.flags:
if g.inPragma <= 0: if g.inPragma <= 0:
inc g.inPragma inc g.inPragma
#if not previousNL(g):
put(g, tkSpaces, Space) put(g, tkSpaces, Space)
put(g, tkCurlyDotLe, "{.") put(g, tkCurlyDotLe, "{.")
gcomma(g, n, emptyContext) gcomma(g, n, emptyContext)

File diff suppressed because it is too large Load diff

View file

@ -12,7 +12,7 @@ import strutils
proc c_sprintf(buf, frmt: cstring) {.importc: "sprintf", header: "<stdio.h>", nodecl, varargs.} proc c_sprintf(buf, frmt: cstring) {.importc: "sprintf", header: "<stdio.h>", nodecl, varargs.}
proc toStrMaxPrecision*(f: BiggestFloat): string = proc toStrMaxPrecision*(f: BiggestFloat): string =
if f != f: if f != f:
result = "NAN" result = "NAN"
elif f == 0.0: elif f == 0.0:
@ -21,17 +21,17 @@ proc toStrMaxPrecision*(f: BiggestFloat): string =
if f > 0.0: result = "INF" if f > 0.0: result = "INF"
else: result = "-INF" else: result = "-INF"
else: else:
var buf: array [0..80, char] var buf: array [0..80, char]
c_sprintf(buf, "%#.16e", f) c_sprintf(buf, "%#.16e", f)
result = $buf result = $buf
proc encodeStr*(s: string, result: var string) = proc encodeStr*(s: string, result: var string) =
for i in countup(0, len(s) - 1): for i in countup(0, len(s) - 1):
case s[i] case s[i]
of 'a'..'z', 'A'..'Z', '0'..'9', '_': add(result, s[i]) of 'a'..'z', 'A'..'Z', '0'..'9', '_': add(result, s[i])
else: add(result, '\\' & toHex(ord(s[i]), 2)) else: add(result, '\\' & toHex(ord(s[i]), 2))
proc hexChar(c: char, xi: var int) = proc hexChar(c: char, xi: var int) =
case c case c
of '0'..'9': xi = (xi shl 4) or (ord(c) - ord('0')) of '0'..'9': xi = (xi shl 4) or (ord(c) - ord('0'))
of 'a'..'f': xi = (xi shl 4) or (ord(c) - ord('a') + 10) of 'a'..'f': xi = (xi shl 4) or (ord(c) - ord('a') + 10)
@ -41,18 +41,18 @@ proc hexChar(c: char, xi: var int) =
proc decodeStr*(s: cstring, pos: var int): string = proc decodeStr*(s: cstring, pos: var int): string =
var i = pos var i = pos
result = "" result = ""
while true: while true:
case s[i] case s[i]
of '\\': of '\\':
inc(i, 3) inc(i, 3)
var xi = 0 var xi = 0
hexChar(s[i-2], xi) hexChar(s[i-2], xi)
hexChar(s[i-1], xi) hexChar(s[i-1], xi)
add(result, chr(xi)) add(result, chr(xi))
of 'a'..'z', 'A'..'Z', '0'..'9', '_': of 'a'..'z', 'A'..'Z', '0'..'9', '_':
add(result, s[i]) add(result, s[i])
inc(i) inc(i)
else: break else: break
pos = i pos = i
const const
@ -68,11 +68,11 @@ template encodeIntImpl(self: expr) =
var d: char var d: char
var v = x var v = x
var rem = v mod 190 var rem = v mod 190
if rem < 0: if rem < 0:
add(result, '-') add(result, '-')
v = - (v div 190) v = - (v div 190)
rem = - rem rem = - rem
else: else:
v = v div 190 v = v div 190
var idx = int(rem) var idx = int(rem)
if idx < 62: d = chars[idx] if idx < 62: d = chars[idx]
@ -89,11 +89,11 @@ proc encodeVBiggestInt*(x: BiggestInt, result: var string) =
encodeVBiggestIntAux(x +% vintDelta, result) encodeVBiggestIntAux(x +% vintDelta, result)
# encodeIntImpl(encodeVBiggestInt) # encodeIntImpl(encodeVBiggestInt)
proc encodeVIntAux(x: int, result: var string) = proc encodeVIntAux(x: int, result: var string) =
## encode an int as a variable length base 190 int. ## encode an int as a variable length base 190 int.
encodeIntImpl(encodeVIntAux) encodeIntImpl(encodeVIntAux)
proc encodeVInt*(x: int, result: var string) = proc encodeVInt*(x: int, result: var string) =
## encode an int as a variable length base 190 int. ## encode an int as a variable length base 190 int.
encodeVIntAux(x +% vintDelta, result) encodeVIntAux(x +% vintDelta, result)
@ -101,11 +101,11 @@ template decodeIntImpl() =
var i = pos var i = pos
var sign = - 1 var sign = - 1
assert(s[i] in {'a'..'z', 'A'..'Z', '0'..'9', '-', '\x80'..'\xFF'}) assert(s[i] in {'a'..'z', 'A'..'Z', '0'..'9', '-', '\x80'..'\xFF'})
if s[i] == '-': if s[i] == '-':
inc(i) inc(i)
sign = 1 sign = 1
result = 0 result = 0
while true: while true:
case s[i] case s[i]
of '0'..'9': result = result * 190 - (ord(s[i]) - ord('0')) of '0'..'9': result = result * 190 - (ord(s[i]) - ord('0'))
of 'a'..'z': result = result * 190 - (ord(s[i]) - ord('a') + 10) of 'a'..'z': result = result * 190 - (ord(s[i]) - ord('a') + 10)
@ -116,7 +116,7 @@ template decodeIntImpl() =
result = result * sign -% vintDelta result = result * sign -% vintDelta
pos = i pos = i
proc decodeVInt*(s: cstring, pos: var int): int = proc decodeVInt*(s: cstring, pos: var int): int =
decodeIntImpl() decodeIntImpl()
proc decodeVBiggestInt*(s: cstring, pos: var int): BiggestInt = proc decodeVBiggestInt*(s: cstring, pos: var int): BiggestInt =

View file

@ -11,16 +11,17 @@
# rod files is a pass, reading of rod files is not! This is why reading and # rod files is a pass, reading of rod files is not! This is why reading and
# writing of rod files is split into two different modules. # writing of rod files is split into two different modules.
import import
intsets, os, options, strutils, nversion, ast, astalgo, msgs, platform, intsets, os, options, strutils, nversion, ast, astalgo, msgs, platform,
condsyms, ropes, idents, crc, rodread, passes, importer, idgen, rodutils condsyms, ropes, idents, securehash, rodread, passes, importer, idgen,
rodutils
# implementation # implementation
type type
TRodWriter = object of TPassContext TRodWriter = object of TPassContext
module: PSym module: PSym
crc: TCrc32 hash: SecureHash
options: TOptions options: TOptions
defines: string defines: string
inclDeps: string inclDeps: string
@ -38,22 +39,22 @@ type
PRodWriter = ref TRodWriter PRodWriter = ref TRodWriter
proc newRodWriter(crc: TCrc32, module: PSym): PRodWriter proc newRodWriter(hash: SecureHash, module: PSym): PRodWriter
proc addModDep(w: PRodWriter, dep: string) proc addModDep(w: PRodWriter, dep: string)
proc addInclDep(w: PRodWriter, dep: string) proc addInclDep(w: PRodWriter, dep: string)
proc addInterfaceSym(w: PRodWriter, s: PSym) proc addInterfaceSym(w: PRodWriter, s: PSym)
proc addStmt(w: PRodWriter, n: PNode) proc addStmt(w: PRodWriter, n: PNode)
proc writeRod(w: PRodWriter) proc writeRod(w: PRodWriter)
proc getDefines(): string = proc getDefines(): string =
result = "" result = ""
for d in definedSymbolNames(): for d in definedSymbolNames():
if result.len != 0: add(result, " ") if result.len != 0: add(result, " ")
add(result, d) add(result, d)
proc fileIdx(w: PRodWriter, filename: string): int = proc fileIdx(w: PRodWriter, filename: string): int =
for i in countup(0, high(w.files)): for i in countup(0, high(w.files)):
if w.files[i] == filename: if w.files[i] == filename:
return i return i
result = len(w.files) result = len(w.files)
setLen(w.files, result + 1) setLen(w.files, result + 1)
@ -62,7 +63,7 @@ proc fileIdx(w: PRodWriter, filename: string): int =
template filename*(w: PRodWriter): string = template filename*(w: PRodWriter): string =
w.module.filename w.module.filename
proc newRodWriter(crc: TCrc32, module: PSym): PRodWriter = proc newRodWriter(hash: SecureHash, module: PSym): PRodWriter =
new(result) new(result)
result.sstack = @[] result.sstack = @[]
result.tstack = @[] result.tstack = @[]
@ -70,7 +71,7 @@ proc newRodWriter(crc: TCrc32, module: PSym): PRodWriter =
initIiTable(result.imports.tab) initIiTable(result.imports.tab)
result.index.r = "" result.index.r = ""
result.imports.r = "" result.imports.r = ""
result.crc = crc result.hash = hash
result.module = module result.module = module
result.defines = getDefines() result.defines = getDefines()
result.options = options.gOptions result.options = options.gOptions
@ -84,19 +85,19 @@ proc newRodWriter(crc: TCrc32, module: PSym): PRodWriter =
result.init = "" result.init = ""
result.origFile = module.info.toFilename result.origFile = module.info.toFilename
result.data = newStringOfCap(12_000) result.data = newStringOfCap(12_000)
proc addModDep(w: PRodWriter, dep: string) = proc addModDep(w: PRodWriter, dep: string) =
if w.modDeps.len != 0: add(w.modDeps, ' ') if w.modDeps.len != 0: add(w.modDeps, ' ')
encodeVInt(fileIdx(w, dep), w.modDeps) encodeVInt(fileIdx(w, dep), w.modDeps)
const const
rodNL = "\x0A" rodNL = "\x0A"
proc addInclDep(w: PRodWriter, dep: string) = proc addInclDep(w: PRodWriter, dep: string) =
var resolved = dep.findModule(w.module.info.toFullPath) var resolved = dep.findModule(w.module.info.toFullPath)
encodeVInt(fileIdx(w, dep), w.inclDeps) encodeVInt(fileIdx(w, dep), w.inclDeps)
add(w.inclDeps, " ") add(w.inclDeps, " ")
encodeVInt(crcFromFile(resolved), w.inclDeps) encodeStr($secureHashFile(resolved), w.inclDeps)
add(w.inclDeps, rodNL) add(w.inclDeps, rodNL)
proc pushType(w: PRodWriter, t: PType) = proc pushType(w: PRodWriter, t: PType) =
@ -109,18 +110,18 @@ proc pushSym(w: PRodWriter, s: PSym) =
if iiTableGet(w.index.tab, s.id) == InvalidKey: if iiTableGet(w.index.tab, s.id) == InvalidKey:
w.sstack.add(s) w.sstack.add(s)
proc encodeNode(w: PRodWriter, fInfo: TLineInfo, n: PNode, proc encodeNode(w: PRodWriter, fInfo: TLineInfo, n: PNode,
result: var string) = result: var string) =
if n == nil: if n == nil:
# nil nodes have to be stored too: # nil nodes have to be stored too:
result.add("()") result.add("()")
return return
result.add('(') result.add('(')
encodeVInt(ord(n.kind), result) encodeVInt(ord(n.kind), result)
# we do not write comments for now # we do not write comments for now
# Line information takes easily 20% or more of the filesize! Therefore we # Line information takes easily 20% or more of the filesize! Therefore we
# omit line information if it is the same as the father's line information: # omit line information if it is the same as the father's line information:
if fInfo.fileIndex != n.info.fileIndex: if fInfo.fileIndex != n.info.fileIndex:
result.add('?') result.add('?')
encodeVInt(n.info.col, result) encodeVInt(n.info.col, result)
result.add(',') result.add(',')
@ -138,7 +139,7 @@ proc encodeNode(w: PRodWriter, fInfo: TLineInfo, n: PNode,
# No need to output the file index, as this is the serialization of one # No need to output the file index, as this is the serialization of one
# file. # file.
var f = n.flags * PersistentNodeFlags var f = n.flags * PersistentNodeFlags
if f != {}: if f != {}:
result.add('$') result.add('$')
encodeVInt(cast[int32](f), result) encodeVInt(cast[int32](f), result)
if n.typ != nil: if n.typ != nil:
@ -146,16 +147,16 @@ proc encodeNode(w: PRodWriter, fInfo: TLineInfo, n: PNode,
encodeVInt(n.typ.id, result) encodeVInt(n.typ.id, result)
pushType(w, n.typ) pushType(w, n.typ)
case n.kind case n.kind
of nkCharLit..nkInt64Lit: of nkCharLit..nkInt64Lit:
if n.intVal != 0: if n.intVal != 0:
result.add('!') result.add('!')
encodeVBiggestInt(n.intVal, result) encodeVBiggestInt(n.intVal, result)
of nkFloatLit..nkFloat64Lit: of nkFloatLit..nkFloat64Lit:
if n.floatVal != 0.0: if n.floatVal != 0.0:
result.add('!') result.add('!')
encodeStr($n.floatVal, result) encodeStr($n.floatVal, result)
of nkStrLit..nkTripleStrLit: of nkStrLit..nkTripleStrLit:
if n.strVal != "": if n.strVal != "":
result.add('!') result.add('!')
encodeStr(n.strVal, result) encodeStr(n.strVal, result)
of nkIdent: of nkIdent:
@ -166,25 +167,25 @@ proc encodeNode(w: PRodWriter, fInfo: TLineInfo, n: PNode,
encodeVInt(n.sym.id, result) encodeVInt(n.sym.id, result)
pushSym(w, n.sym) pushSym(w, n.sym)
else: else:
for i in countup(0, sonsLen(n) - 1): for i in countup(0, sonsLen(n) - 1):
encodeNode(w, n.info, n.sons[i], result) encodeNode(w, n.info, n.sons[i], result)
add(result, ')') add(result, ')')
proc encodeLoc(w: PRodWriter, loc: TLoc, result: var string) = proc encodeLoc(w: PRodWriter, loc: TLoc, result: var string) =
var oldLen = result.len var oldLen = result.len
result.add('<') result.add('<')
if loc.k != low(loc.k): encodeVInt(ord(loc.k), result) if loc.k != low(loc.k): encodeVInt(ord(loc.k), result)
if loc.s != low(loc.s): if loc.s != low(loc.s):
add(result, '*') add(result, '*')
encodeVInt(ord(loc.s), result) encodeVInt(ord(loc.s), result)
if loc.flags != {}: if loc.flags != {}:
add(result, '$') add(result, '$')
encodeVInt(cast[int32](loc.flags), result) encodeVInt(cast[int32](loc.flags), result)
if loc.t != nil: if loc.t != nil:
add(result, '^') add(result, '^')
encodeVInt(cast[int32](loc.t.id), result) encodeVInt(cast[int32](loc.t.id), result)
pushType(w, loc.t) pushType(w, loc.t)
if loc.r != nil: if loc.r != nil:
add(result, '!') add(result, '!')
encodeStr($loc.r, result) encodeStr($loc.r, result)
if oldLen + 1 == result.len: if oldLen + 1 == result.len:
@ -192,9 +193,9 @@ proc encodeLoc(w: PRodWriter, loc: TLoc, result: var string) =
setLen(result, oldLen) setLen(result, oldLen)
else: else:
add(result, '>') add(result, '>')
proc encodeType(w: PRodWriter, t: PType, result: var string) = proc encodeType(w: PRodWriter, t: PType, result: var string) =
if t == nil: if t == nil:
# nil nodes have to be stored too: # nil nodes have to be stored too:
result.add("[]") result.add("[]")
return return
@ -206,38 +207,38 @@ proc encodeType(w: PRodWriter, t: PType, result: var string) =
encodeVInt(ord(t.kind), result) encodeVInt(ord(t.kind), result)
add(result, '+') add(result, '+')
encodeVInt(t.id, result) encodeVInt(t.id, result)
if t.n != nil: if t.n != nil:
encodeNode(w, unknownLineInfo(), t.n, result) encodeNode(w, unknownLineInfo(), t.n, result)
if t.flags != {}: if t.flags != {}:
add(result, '$') add(result, '$')
encodeVInt(cast[int32](t.flags), result) encodeVInt(cast[int32](t.flags), result)
if t.callConv != low(t.callConv): if t.callConv != low(t.callConv):
add(result, '?') add(result, '?')
encodeVInt(ord(t.callConv), result) encodeVInt(ord(t.callConv), result)
if t.owner != nil: if t.owner != nil:
add(result, '*') add(result, '*')
encodeVInt(t.owner.id, result) encodeVInt(t.owner.id, result)
pushSym(w, t.owner) pushSym(w, t.owner)
if t.sym != nil: if t.sym != nil:
add(result, '&') add(result, '&')
encodeVInt(t.sym.id, result) encodeVInt(t.sym.id, result)
pushSym(w, t.sym) pushSym(w, t.sym)
if t.size != - 1: if t.size != - 1:
add(result, '/') add(result, '/')
encodeVBiggestInt(t.size, result) encodeVBiggestInt(t.size, result)
if t.align != 2: if t.align != 2:
add(result, '=') add(result, '=')
encodeVInt(t.align, result) encodeVInt(t.align, result)
encodeLoc(w, t.loc, result) encodeLoc(w, t.loc, result)
for i in countup(0, sonsLen(t) - 1): for i in countup(0, sonsLen(t) - 1):
if t.sons[i] == nil: if t.sons[i] == nil:
add(result, "^()") add(result, "^()")
else: else:
add(result, '^') add(result, '^')
encodeVInt(t.sons[i].id, result) encodeVInt(t.sons[i].id, result)
pushType(w, t.sons[i]) pushType(w, t.sons[i])
proc encodeLib(w: PRodWriter, lib: PLib, info: TLineInfo, result: var string) = proc encodeLib(w: PRodWriter, lib: PLib, info: TLineInfo, result: var string) =
add(result, '|') add(result, '|')
encodeVInt(ord(lib.kind), result) encodeVInt(ord(lib.kind), result)
add(result, '|') add(result, '|')
@ -276,10 +277,10 @@ proc encodeSym(w: PRodWriter, s: PSym, result: var string) =
if s.magic != mNone: if s.magic != mNone:
result.add('@') result.add('@')
encodeVInt(ord(s.magic), result) encodeVInt(ord(s.magic), result)
if s.options != w.options: if s.options != w.options:
result.add('!') result.add('!')
encodeVInt(cast[int32](s.options), result) encodeVInt(cast[int32](s.options), result)
if s.position != 0: if s.position != 0:
result.add('%') result.add('%')
encodeVInt(s.position, result) encodeVInt(s.position, result)
if s.offset != - 1: if s.offset != - 1:
@ -307,7 +308,7 @@ proc encodeSym(w: PRodWriter, s: PSym, result: var string) =
if codeAst != nil: if codeAst != nil:
# resore the AST: # resore the AST:
s.ast.sons[codePos] = codeAst s.ast.sons[codePos] = codeAst
proc addToIndex(w: var TIndex, key, val: int) = proc addToIndex(w: var TIndex, key, val: int) =
if key - w.lastIdxKey == 1: if key - w.lastIdxKey == 1:
# we do not store a key-diff of 1 to safe space # we do not store a key-diff of 1 to safe space
@ -328,24 +329,25 @@ when debugWrittenIds:
proc symStack(w: PRodWriter): int = proc symStack(w: PRodWriter): int =
var i = 0 var i = 0
while i < len(w.sstack): while i < len(w.sstack):
var s = w.sstack[i] var s = w.sstack[i]
if sfForward in s.flags: if sfForward in s.flags:
w.sstack[result] = s w.sstack[result] = s
inc result inc result
elif iiTableGet(w.index.tab, s.id) == InvalidKey: elif iiTableGet(w.index.tab, s.id) == InvalidKey:
var m = getModule(s) var m = getModule(s)
if m == nil: internalError("symStack: module nil: " & s.name.s) if m == nil and s.kind != skPackage:
if (m.id == w.module.id) or (sfFromGeneric in s.flags): internalError("symStack: module nil: " & s.name.s)
if s.kind == skPackage or m.id == w.module.id or sfFromGeneric in s.flags:
# put definition in here # put definition in here
var L = w.data.len var L = w.data.len
addToIndex(w.index, s.id, L) addToIndex(w.index, s.id, L)
when debugWrittenIds: incl(debugWritten, s.id) when debugWrittenIds: incl(debugWritten, s.id)
encodeSym(w, s, w.data) encodeSym(w, s, w.data)
add(w.data, rodNL) add(w.data, rodNL)
# put into interface section if appropriate: # put into interface section if appropriate:
if {sfExported, sfFromGeneric} * s.flags == {sfExported} and if {sfExported, sfFromGeneric} * s.flags == {sfExported} and
s.kind in ExportableSymKinds: s.kind in ExportableSymKinds:
encodeStr(s.name.s, w.interf) encodeStr(s.name.s, w.interf)
add(w.interf, ' ') add(w.interf, ' ')
encodeVInt(s.id, w.interf) encodeVInt(s.id, w.interf)
@ -361,26 +363,26 @@ proc symStack(w: PRodWriter): int =
if s.kind == skMethod and sfDispatcher notin s.flags: if s.kind == skMethod and sfDispatcher notin s.flags:
if w.methods.len != 0: add(w.methods, ' ') if w.methods.len != 0: add(w.methods, ' ')
encodeVInt(s.id, w.methods) encodeVInt(s.id, w.methods)
elif iiTableGet(w.imports.tab, s.id) == InvalidKey: elif iiTableGet(w.imports.tab, s.id) == InvalidKey:
addToIndex(w.imports, s.id, m.id) addToIndex(w.imports, s.id, m.id)
when debugWrittenIds: when debugWrittenIds:
if not Contains(debugWritten, s.id): if not contains(debugWritten, s.id):
echo(w.filename) echo(w.filename)
debug(s) debug(s)
debug(s.owner) debug(s.owner)
debug(m) debug(m)
InternalError("Symbol referred to but never written") internalError("Symbol referred to but never written")
inc(i) inc(i)
setLen(w.sstack, result) setLen(w.sstack, result)
proc typeStack(w: PRodWriter): int = proc typeStack(w: PRodWriter): int =
var i = 0 var i = 0
while i < len(w.tstack): while i < len(w.tstack):
var t = w.tstack[i] var t = w.tstack[i]
if t.kind == tyForward: if t.kind == tyForward:
w.tstack[result] = t w.tstack[result] = t
inc result inc result
elif iiTableGet(w.index.tab, t.id) == InvalidKey: elif iiTableGet(w.index.tab, t.id) == InvalidKey:
var L = w.data.len var L = w.data.len
addToIndex(w.index, t.id, L) addToIndex(w.index, t.id, L)
encodeType(w, t, w.data) encodeType(w, t, w.data)
@ -400,24 +402,24 @@ proc processStacks(w: PRodWriter, finalPass: bool) =
if finalPass and (oldS != 0 or oldT != 0): if finalPass and (oldS != 0 or oldT != 0):
internalError("could not serialize some forwarded symbols/types") internalError("could not serialize some forwarded symbols/types")
proc rawAddInterfaceSym(w: PRodWriter, s: PSym) = proc rawAddInterfaceSym(w: PRodWriter, s: PSym) =
pushSym(w, s) pushSym(w, s)
processStacks(w, false) processStacks(w, false)
proc addInterfaceSym(w: PRodWriter, s: PSym) = proc addInterfaceSym(w: PRodWriter, s: PSym) =
if w == nil: return if w == nil: return
if s.kind in ExportableSymKinds and if s.kind in ExportableSymKinds and
{sfExported, sfCompilerProc} * s.flags != {}: {sfExported, sfCompilerProc} * s.flags != {}:
rawAddInterfaceSym(w, s) rawAddInterfaceSym(w, s)
proc addStmt(w: PRodWriter, n: PNode) = proc addStmt(w: PRodWriter, n: PNode) =
encodeVInt(w.data.len, w.init) encodeVInt(w.data.len, w.init)
add(w.init, rodNL) add(w.init, rodNL)
encodeNode(w, unknownLineInfo(), n, w.data) encodeNode(w, unknownLineInfo(), n, w.data)
add(w.data, rodNL) add(w.data, rodNL)
processStacks(w, false) processStacks(w, false)
proc writeRod(w: PRodWriter) = proc writeRod(w: PRodWriter) =
processStacks(w, true) processStacks(w, true)
var f: File var f: File
if not open(f, completeGeneratedFilePath(changeFileExt( if not open(f, completeGeneratedFilePath(changeFileExt(
@ -438,12 +440,12 @@ proc writeRod(w: PRodWriter) =
encodeStr(w.origFile, orig) encodeStr(w.origFile, orig)
f.write(orig) f.write(orig)
f.write(rodNL) f.write(rodNL)
var crc = "CRC:" var hash = "HASH:"
encodeVInt(w.crc, crc) encodeStr($w.hash, hash)
f.write(crc) f.write(hash)
f.write(rodNL) f.write(rodNL)
var options = "OPTIONS:" var options = "OPTIONS:"
encodeVInt(cast[int32](w.options), options) encodeVInt(cast[int32](w.options), options)
f.write(options) f.write(options)
@ -457,31 +459,31 @@ proc writeRod(w: PRodWriter) =
var cmd = "CMD:" var cmd = "CMD:"
encodeVInt(cast[int32](gCmd), cmd) encodeVInt(cast[int32](gCmd), cmd)
f.write(cmd) f.write(cmd)
f.write(rodNL) f.write(rodNL)
f.write("DEFINES:") f.write("DEFINES:")
f.write(w.defines) f.write(w.defines)
f.write(rodNL) f.write(rodNL)
var files = "FILES(" & rodNL var files = "FILES(" & rodNL
for i in countup(0, high(w.files)): for i in countup(0, high(w.files)):
encodeStr(w.files[i], files) encodeStr(w.files[i], files)
files.add(rodNL) files.add(rodNL)
f.write(files) f.write(files)
f.write(')' & rodNL) f.write(')' & rodNL)
f.write("INCLUDES(" & rodNL) f.write("INCLUDES(" & rodNL)
f.write(w.inclDeps) f.write(w.inclDeps)
f.write(')' & rodNL) f.write(')' & rodNL)
f.write("DEPS:") f.write("DEPS:")
f.write(w.modDeps) f.write(w.modDeps)
f.write(rodNL) f.write(rodNL)
f.write("INTERF(" & rodNL) f.write("INTERF(" & rodNL)
f.write(w.interf) f.write(w.interf)
f.write(')' & rodNL) f.write(')' & rodNL)
f.write("COMPILERPROCS(" & rodNL) f.write("COMPILERPROCS(" & rodNL)
f.write(w.compilerProcs) f.write(w.compilerProcs)
f.write(')' & rodNL) f.write(')' & rodNL)
@ -489,11 +491,11 @@ proc writeRod(w: PRodWriter) =
f.write("INDEX(" & rodNL) f.write("INDEX(" & rodNL)
f.write(w.index.r) f.write(w.index.r)
f.write(')' & rodNL) f.write(')' & rodNL)
f.write("IMPORTS(" & rodNL) f.write("IMPORTS(" & rodNL)
f.write(w.imports.r) f.write(w.imports.r)
f.write(')' & rodNL) f.write(')' & rodNL)
f.write("CONVERTERS:") f.write("CONVERTERS:")
f.write(w.converters) f.write(w.converters)
f.write(rodNL) f.write(rodNL)
@ -501,11 +503,11 @@ proc writeRod(w: PRodWriter) =
f.write("METHODS:") f.write("METHODS:")
f.write(w.methods) f.write(w.methods)
f.write(rodNL) f.write(rodNL)
f.write("INIT(" & rodNL) f.write("INIT(" & rodNL)
f.write(w.init) f.write(w.init)
f.write(')' & rodNL) f.write(')' & rodNL)
f.write("DATA(" & rodNL) f.write("DATA(" & rodNL)
f.write(w.data) f.write(w.data)
f.write(')' & rodNL) f.write(')' & rodNL)
@ -513,23 +515,23 @@ proc writeRod(w: PRodWriter) =
# for reading: # for reading:
f.write("\0") f.write("\0")
f.close() f.close()
#echo "interf: ", w.interf.len #echo "interf: ", w.interf.len
#echo "index: ", w.index.r.len #echo "index: ", w.index.r.len
#echo "init: ", w.init.len #echo "init: ", w.init.len
#echo "data: ", w.data.len #echo "data: ", w.data.len
proc process(c: PPassContext, n: PNode): PNode = proc process(c: PPassContext, n: PNode): PNode =
result = n result = n
if c == nil: return if c == nil: return
var w = PRodWriter(c) var w = PRodWriter(c)
case n.kind case n.kind
of nkStmtList: of nkStmtList:
for i in countup(0, sonsLen(n) - 1): discard process(c, n.sons[i]) for i in countup(0, sonsLen(n) - 1): discard process(c, n.sons[i])
#var s = n.sons[namePos].sym #var s = n.sons[namePos].sym
#addInterfaceSym(w, s) #addInterfaceSym(w, s)
of nkProcDef, nkMethodDef, nkIteratorDef, nkConverterDef, of nkProcDef, nkMethodDef, nkIteratorDef, nkConverterDef,
nkTemplateDef, nkMacroDef: nkTemplateDef, nkMacroDef:
var s = n.sons[namePos].sym var s = n.sons[namePos].sym
if s == nil: internalError(n.info, "rodwrite.process") if s == nil: internalError(n.info, "rodwrite.process")
if n.sons[bodyPos] == nil: if n.sons[bodyPos] == nil:
@ -538,17 +540,17 @@ proc process(c: PPassContext, n: PNode): PNode =
sfForward notin s.flags: sfForward notin s.flags:
addInterfaceSym(w, s) addInterfaceSym(w, s)
of nkVarSection, nkLetSection, nkConstSection: of nkVarSection, nkLetSection, nkConstSection:
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 a.kind == nkCommentStmt: continue if a.kind == nkCommentStmt: continue
addInterfaceSym(w, a.sons[0].sym) addInterfaceSym(w, a.sons[0].sym)
of nkTypeSection: of nkTypeSection:
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 a.kind == nkCommentStmt: continue if a.kind == nkCommentStmt: continue
if a.sons[0].kind != nkSym: internalError(a.info, "rodwrite.process") if a.sons[0].kind != nkSym: internalError(a.info, "rodwrite.process")
var s = a.sons[0].sym var s = a.sons[0].sym
addInterfaceSym(w, s) addInterfaceSym(w, s)
# this takes care of enum fields too # this takes care of enum fields too
# Note: The check for ``s.typ.kind = tyEnum`` is wrong for enum # Note: The check for ``s.typ.kind = tyEnum`` is wrong for enum
# type aliasing! Otherwise the same enum symbol would be included # type aliasing! Otherwise the same enum symbol would be included
@ -556,29 +558,29 @@ proc process(c: PPassContext, n: PNode): PNode =
# #
# if (a.sons[2] <> nil) and (a.sons[2].kind = nkEnumTy) then begin # if (a.sons[2] <> nil) and (a.sons[2].kind = nkEnumTy) then begin
# a := s.typ.n; # a := s.typ.n;
# for j := 0 to sonsLen(a)-1 do # for j := 0 to sonsLen(a)-1 do
# addInterfaceSym(w, a.sons[j].sym); # addInterfaceSym(w, a.sons[j].sym);
# end # end
of nkImportStmt: of nkImportStmt:
for i in countup(0, sonsLen(n) - 1): addModDep(w, getModuleName(n.sons[i])) for i in countup(0, sonsLen(n) - 1): addModDep(w, getModuleName(n.sons[i]))
addStmt(w, n) addStmt(w, n)
of nkFromStmt: of nkFromStmt:
addModDep(w, getModuleName(n.sons[0])) addModDep(w, getModuleName(n.sons[0]))
addStmt(w, n) addStmt(w, n)
of nkIncludeStmt: of nkIncludeStmt:
for i in countup(0, sonsLen(n) - 1): addInclDep(w, getModuleName(n.sons[i])) for i in countup(0, sonsLen(n) - 1): addInclDep(w, getModuleName(n.sons[i]))
of nkPragma: of nkPragma:
addStmt(w, n) addStmt(w, n)
else: else:
discard discard
proc myOpen(module: PSym): PPassContext = proc myOpen(module: PSym): PPassContext =
if module.id < 0: internalError("rodwrite: module ID not set") if module.id < 0: internalError("rodwrite: module ID not set")
var w = newRodWriter(module.fileIdx.getCRC, module) var w = newRodWriter(module.fileIdx.getHash, module)
rawAddInterfaceSym(w, module) rawAddInterfaceSym(w, module)
result = w result = w
proc myClose(c: PPassContext, n: PNode): PNode = proc myClose(c: PPassContext, n: PNode): PNode =
result = process(c, n) result = process(c, n)
var w = PRodWriter(c) var w = PRodWriter(c)
writeRod(w) writeRod(w)

View file

@ -306,7 +306,7 @@ const
proc equalsFile*(r: Rope, f: File): bool = proc equalsFile*(r: Rope, f: File): bool =
## returns true if the contents of the file `f` equal `r`. ## returns true if the contents of the file `f` equal `r`.
var var
buf: array[bufSize, char] buf: array[bufSize, char]
bpos = buf.len bpos = buf.len
blen = buf.len blen = buf.len

View file

@ -72,7 +72,7 @@ proc `|*|`*(a, b: BiggestInt): BiggestInt =
# 32 * abs(diff) <= abs(prod) -- 5 good bits is "close enough" # 32 * abs(diff) <= abs(prod) -- 5 good bits is "close enough"
if 32.0 * abs(resAsFloat - floatProd) <= abs(floatProd): if 32.0 * abs(resAsFloat - floatProd) <= abs(floatProd):
return result return result
if floatProd >= 0.0: if floatProd >= 0.0:
result = high(result) result = high(result)
else: else:

146
compiler/scriptconfig.nim Normal file
View file

@ -0,0 +1,146 @@
#
#
# The Nim Compiler
# (c) Copyright 2015 Andreas Rumpf
#
# See the file "copying.txt", included in this
# distribution, for details about the copyright.
#
## Implements the new configuration system for Nim. Uses Nim as a scripting
## language.
import
ast, modules, passes, passaux, condsyms,
options, nimconf, lists, sem, semdata, llstream, vm, vmdef, commands, msgs,
os, times, osproc
# we support 'cmpIgnoreStyle' natively for efficiency:
from strutils import cmpIgnoreStyle
proc listDirs(a: VmArgs, filter: set[PathComponent]) =
let dir = getString(a, 0)
var result: seq[string] = @[]
for kind, path in walkDir(dir):
if kind in filter: result.add path
setResult(a, result)
proc setupVM*(module: PSym; scriptName: string): PEvalContext =
# For Nimble we need to export 'setupVM'.
result = newCtx(module)
result.mode = emRepl
registerAdditionalOps(result)
# captured vars:
var errorMsg: string
var vthisDir = scriptName.splitFile.dir
template cbconf(name, body) {.dirty.} =
result.registerCallback "stdlib.system." & astToStr(name),
proc (a: VmArgs) =
body
template cbos(name, body) {.dirty.} =
result.registerCallback "stdlib.system." & astToStr(name),
proc (a: VmArgs) =
try:
body
except OSError:
errorMsg = getCurrentExceptionMsg()
# Idea: Treat link to file as a file, but ignore link to directory to prevent
# endless recursions out of the box.
cbos listFiles:
listDirs(a, {pcFile, pcLinkToFile})
cbos listDirs:
listDirs(a, {pcDir})
cbos removeDir:
os.removeDir getString(a, 0)
cbos removeFile:
os.removeFile getString(a, 0)
cbos createDir:
os.createDir getString(a, 0)
cbos getOsError:
setResult(a, errorMsg)
cbos setCurrentDir:
os.setCurrentDir getString(a, 0)
cbos getCurrentDir:
setResult(a, os.getCurrentDir())
cbos moveFile:
os.moveFile(getString(a, 0), getString(a, 1))
cbos copyFile:
os.copyFile(getString(a, 0), getString(a, 1))
cbos getLastModificationTime:
setResult(a, toSeconds(getLastModificationTime(getString(a, 0))))
cbos rawExec:
setResult(a, osproc.execCmd getString(a, 0))
cbconf getEnv:
setResult(a, os.getEnv(a.getString 0))
cbconf existsEnv:
setResult(a, os.existsEnv(a.getString 0))
cbconf dirExists:
setResult(a, os.dirExists(a.getString 0))
cbconf fileExists:
setResult(a, os.fileExists(a.getString 0))
cbconf thisDir:
setResult(a, vthisDir)
cbconf put:
options.setConfigVar(getString(a, 0), getString(a, 1))
cbconf get:
setResult(a, options.getConfigVar(a.getString 0))
cbconf exists:
setResult(a, options.existsConfigVar(a.getString 0))
cbconf nimcacheDir:
setResult(a, options.getNimcacheDir())
cbconf paramStr:
setResult(a, os.paramStr(int a.getInt 0))
cbconf paramCount:
setResult(a, os.paramCount())
cbconf cmpIgnoreStyle:
setResult(a, strutils.cmpIgnoreStyle(a.getString 0, a.getString 1))
cbconf cmpIgnoreCase:
setResult(a, strutils.cmpIgnoreCase(a.getString 0, a.getString 1))
cbconf setCommand:
options.command = a.getString 0
let arg = a.getString 1
if arg.len > 0:
gProjectName = arg
try:
gProjectFull = canonicalizePath(gProjectPath / gProjectName)
except OSError:
gProjectFull = gProjectName
cbconf getCommand:
setResult(a, options.command)
cbconf switch:
processSwitch(a.getString 0, a.getString 1, passPP, unknownLineInfo())
proc runNimScript*(scriptName: string) =
passes.gIncludeFile = includeModule
passes.gImportModule = importModule
initDefines()
defineSymbol("nimscript")
defineSymbol("nimconfig")
registerPass(semPass)
registerPass(evalPass)
appendStr(searchPaths, options.libpath)
var m = makeModule(scriptName)
incl(m.flags, sfMainModule)
vm.globalCtx = setupVM(m, scriptName)
compileSystemModule()
processModule(m, llStreamOpen(scriptName, fmRead), nil)
# ensure we load 'system.nim' again for the real non-config stuff!
resetAllModulesHard()
vm.globalCtx = nil
# do not remove the defined symbols
#initDefines()
undefSymbol("nimscript")
undefSymbol("nimconfig")

View file

@ -171,11 +171,15 @@ proc newSymS(kind: TSymKind, n: PNode, c: PContext): PSym =
result = newSym(kind, considerQuotedIdent(n), getCurrOwner(), n.info) result = newSym(kind, considerQuotedIdent(n), getCurrOwner(), n.info)
proc newSymG*(kind: TSymKind, n: PNode, c: PContext): PSym = proc newSymG*(kind: TSymKind, n: PNode, c: PContext): PSym =
proc `$`(kind: TSymKind): string = substr(system.`$`(kind), 2).toLower
# like newSymS, but considers gensym'ed symbols # like newSymS, but considers gensym'ed symbols
if n.kind == nkSym: if n.kind == nkSym:
# and sfGenSym in n.sym.flags: # and sfGenSym in n.sym.flags:
result = n.sym result = n.sym
internalAssert result.kind == kind if result.kind != kind:
localError(n.info, "cannot use symbol of kind '" &
$result.kind & "' as a '" & $kind & "'")
# when there is a nested proc inside a template, semtmpl # when there is a nested proc inside a template, semtmpl
# will assign a wrong owner during the first pass over the # will assign a wrong owner during the first pass over the
# template; we must fix it here: see #909 # template; we must fix it here: see #909
@ -421,7 +425,11 @@ proc myOpenCached(module: PSym, rd: PRodReader): PPassContext =
for m in items(rd.methods): methodDef(m, true) for m in items(rd.methods): methodDef(m, true)
proc semStmtAndGenerateGenerics(c: PContext, n: PNode): PNode = proc semStmtAndGenerateGenerics(c: PContext, n: PNode): PNode =
result = semStmt(c, n) if sfNoForward in c.module.flags:
result = semAllTypeSections(c, n)
else:
result = n
result = semStmt(c, result)
# BUGFIX: process newly generated generics here, not at the end! # BUGFIX: process newly generated generics here, not at the end!
if c.lastGenericIdx < c.generics.len: if c.lastGenericIdx < c.generics.len:
var a = newNodeI(nkStmtList, n.info) var a = newNodeI(nkStmtList, n.info)

View file

@ -95,7 +95,7 @@ proc notFoundError*(c: PContext, n: PNode, errors: CandidateErrors) =
# Gives a detailed error message; this is separated from semOverloadedCall, # Gives a detailed error message; this is separated from semOverloadedCall,
# as semOverlodedCall is already pretty slow (and we need this information # as semOverlodedCall is already pretty slow (and we need this information
# only in case of an error). # only in case of an error).
if c.inCompilesContext > 0: if c.compilesContextId > 0 and optReportConceptFailures notin gGlobalOptions:
# fail fast: # fail fast:
globalError(n.info, errTypeMismatch, "") globalError(n.info, errTypeMismatch, "")
if errors.isNil or errors.len == 0: if errors.isNil or errors.len == 0:
@ -133,7 +133,10 @@ proc notFoundError*(c: PContext, n: PNode, errors: CandidateErrors) =
add(candidates, "\n") add(candidates, "\n")
if candidates != "": if candidates != "":
add(result, "\n" & msgKindToString(errButExpected) & "\n" & candidates) add(result, "\n" & msgKindToString(errButExpected) & "\n" & candidates)
localError(n.info, errGenerated, result) if c.compilesContextId > 0 and optReportConceptFailures in gGlobalOptions:
globalError(n.info, errGenerated, result)
else:
localError(n.info, errGenerated, result)
proc gatherUsedSyms(c: PContext, usedSyms: var seq[PNode]) = proc gatherUsedSyms(c: PContext, usedSyms: var seq[PNode]) =
for scope in walkScopes(c.currentScope): for scope in walkScopes(c.currentScope):
@ -209,7 +212,10 @@ proc resolveOverloads(c: PContext, n, orig: PNode,
pickBest(callOp) pickBest(callOp)
if overloadsState == csEmpty and result.state == csEmpty: if overloadsState == csEmpty and result.state == csEmpty:
localError(n.info, errUndeclaredIdentifier, considerQuotedIdent(f).s) if nfDotField in n.flags and nfExplicitCall notin n.flags:
localError(n.info, errUndeclaredField, considerQuotedIdent(f).s)
else:
localError(n.info, errUndeclaredRoutine, considerQuotedIdent(f).s)
return return
elif result.state != csMatch: elif result.state != csMatch:
if nfExprCall in n.flags: if nfExprCall in n.flags:
@ -232,7 +238,7 @@ proc resolveOverloads(c: PContext, n, orig: PNode,
internalAssert result.state == csMatch internalAssert result.state == csMatch
#writeMatches(result) #writeMatches(result)
#writeMatches(alt) #writeMatches(alt)
if c.inCompilesContext > 0: if c.compilesContextId > 0:
# quick error message for performance of 'compiles' built-in: # quick error message for performance of 'compiles' built-in:
globalError(n.info, errGenerated, "ambiguous call") globalError(n.info, errGenerated, "ambiguous call")
elif gErrorCounter == 0: elif gErrorCounter == 0:
@ -302,8 +308,25 @@ proc semResolvedCall(c: PContext, n: PNode, x: TCandidate): PNode =
if containsGenericType(result.typ) or x.fauxMatch == tyUnknown: if containsGenericType(result.typ) or x.fauxMatch == tyUnknown:
result.typ = newTypeS(x.fauxMatch, c) result.typ = newTypeS(x.fauxMatch, c)
return return
if finalCallee.ast.sons[genericParamsPos].kind != nkEmpty: let gp = finalCallee.ast.sons[genericParamsPos]
finalCallee = generateInstance(c, x.calleeSym, x.bindings, n.info) if gp.kind != nkEmpty:
if x.calleeSym.kind notin {skMacro, skTemplate}:
if x.calleeSym.magic in {mArrGet, mArrPut}:
finalCallee = x.calleeSym
else:
finalCallee = generateInstance(c, x.calleeSym, x.bindings, n.info)
else:
# For macros and templates, the resolved generic params
# are added as normal params.
for s in instantiateGenericParamList(c, gp, x.bindings):
case s.kind
of skConst:
x.call.add s.ast
of skType:
x.call.add newSymNode(s, n.info)
else:
internalAssert false
result = x.call result = x.call
instGenericConvertersSons(c, result, x) instGenericConvertersSons(c, result, x)
result.sons[0] = newSymNode(finalCallee, result.sons[0].info) result.sons[0] = newSymNode(finalCallee, result.sons[0].info)

View file

@ -45,7 +45,8 @@ type
TExprFlag* = enum TExprFlag* = enum
efLValue, efWantIterator, efInTypeof, efLValue, efWantIterator, efInTypeof,
efWantStmt, efAllowStmt, efDetermineType, efWantStmt, efAllowStmt, efDetermineType,
efAllowDestructor, efWantValue, efOperand, efNoSemCheck efAllowDestructor, efWantValue, efOperand, efNoSemCheck,
efNoProcvarCheck
TExprFlags* = set[TExprFlag] TExprFlags* = set[TExprFlag]
TTypeAttachedOp* = enum TTypeAttachedOp* = enum
@ -70,7 +71,8 @@ type
inTypeClass*: int # > 0 if we are in a user-defined type class inTypeClass*: int # > 0 if we are in a user-defined type class
inGenericContext*: int # > 0 if we are in a generic type inGenericContext*: int # > 0 if we are in a generic type
inUnrolledContext*: int # > 0 if we are unrolling a loop inUnrolledContext*: int # > 0 if we are unrolling a loop
inCompilesContext*: int # > 0 if we are in a ``compiles`` magic compilesContextId*: int # > 0 if we are in a ``compiles`` magic
compilesContextIdGenerator*: int
inGenericInst*: int # > 0 if we are instantiating a generic inGenericInst*: int # > 0 if we are instantiating a generic
converters*: TSymSeq # sequence of converters converters*: TSymSeq # sequence of converters
patterns*: TSymSeq # sequence of pattern matchers patterns*: TSymSeq # sequence of pattern matchers

View file

@ -24,7 +24,6 @@ var
destructorName = getIdent"destroy_" destructorName = getIdent"destroy_"
destructorParam = getIdent"this_" destructorParam = getIdent"this_"
destructorPragma = newIdentNode(getIdent"destructor", unknownLineInfo()) destructorPragma = newIdentNode(getIdent"destructor", unknownLineInfo())
rangeDestructorProc*: PSym
proc instantiateDestructor(c: PContext, typ: PType): PType proc instantiateDestructor(c: PContext, typ: PType): PType
@ -141,9 +140,7 @@ proc instantiateDestructor(c: PContext, typ: PType): PType =
case t.kind case t.kind
of tySequence, tyArray, tyArrayConstr, tyOpenArray, tyVarargs: of tySequence, tyArray, tyArrayConstr, tyOpenArray, tyVarargs:
if instantiateDestructor(c, t.sons[0]) != nil: if instantiateDestructor(c, t.sons[0]) != nil:
if rangeDestructorProc == nil: t.destructor = getCompilerProc"nimDestroyRange"
rangeDestructorProc = searchInScopes(c, getIdent"nimDestroyRange")
t.destructor = rangeDestructorProc
return t return t
else: else:
return nil return nil
@ -177,6 +174,15 @@ proc instantiateDestructor(c: PContext, typ: PType): PType =
else: else:
return nil return nil
proc createDestructorCall(c: PContext, s: PSym): PNode =
let varTyp = s.typ
if varTyp == nil or sfGlobal in s.flags: return
let destructableT = instantiateDestructor(c, varTyp)
if destructableT != nil:
let call = semStmt(c, newNode(nkCall, s.info, @[
useSym(destructableT.destructor), useSym(s)]))
result = newNode(nkDefer, s.info, @[call])
proc insertDestructors(c: PContext, proc insertDestructors(c: PContext,
varSection: PNode): tuple[outer, inner: PNode] = varSection: PNode): tuple[outer, inner: PNode] =
# Accepts a var or let section. # Accepts a var or let section.

View file

@ -33,6 +33,7 @@ proc semOperand(c: PContext, n: PNode, flags: TExprFlags = {}): PNode =
if result.typ.kind == tyVar: result = newDeref(result) if result.typ.kind == tyVar: result = newDeref(result)
elif {efWantStmt, efAllowStmt} * flags != {}: elif {efWantStmt, efAllowStmt} * flags != {}:
result.typ = newTypeS(tyEmpty, c) result.typ = newTypeS(tyEmpty, c)
result.typ.flags.incl tfVoid
else: else:
localError(n.info, errExprXHasNoType, localError(n.info, errExprXHasNoType,
renderTree(result, {renderNoComments})) renderTree(result, {renderNoComments}))
@ -51,7 +52,7 @@ proc semExprWithType(c: PContext, n: PNode, flags: TExprFlags = {}): PNode =
result.typ = errorType(c) result.typ = errorType(c)
else: else:
# XXX tyGenericInst here? # XXX tyGenericInst here?
semProcvarCheck(c, result) if efNoProcvarCheck notin flags: semProcvarCheck(c, result)
if result.typ.kind == tyVar: result = newDeref(result) if result.typ.kind == tyVar: result = newDeref(result)
semDestructorCheck(c, result, flags) semDestructorCheck(c, result, flags)
@ -123,6 +124,9 @@ proc semSym(c: PContext, n: PNode, s: PSym, flags: TExprFlags): PNode =
return newSymNode(u, n.info) return newSymNode(u, n.info)
result = newSymNode(s, n.info) result = newSymNode(s, n.info)
of skVar, skLet, skResult, skForVar: of skVar, skLet, skResult, skForVar:
if s.magic == mNimvm:
localError(n.info, "illegal context for 'nimvm' magic")
markUsed(n.info, s) markUsed(n.info, s)
styleCheckUse(n.info, s) styleCheckUse(n.info, s)
# if a proc accesses a global variable, it is not side effect free: # if a proc accesses a global variable, it is not side effect free:
@ -288,8 +292,6 @@ proc semConv(c: PContext, n: PNode): PNode =
proc semCast(c: PContext, n: PNode): PNode = proc semCast(c: PContext, n: PNode): PNode =
## Semantically analyze a casting ("cast[type](param)") ## Semantically analyze a casting ("cast[type](param)")
if optSafeCode in gGlobalOptions: localError(n.info, errCastNotInSafeMode)
#incl(c.p.owner.flags, sfSideEffect)
checkSonsLen(n, 2) checkSonsLen(n, 2)
result = newNodeI(nkCast, n.info) result = newNodeI(nkCast, n.info)
result.typ = semTypeNode(c, n.sons[0], nil) result.typ = semTypeNode(c, n.sons[0], nil)
@ -448,18 +450,18 @@ proc changeType(n: PNode, newType: PType, check: bool) =
let tup = newType.skipTypes({tyGenericInst}) let tup = newType.skipTypes({tyGenericInst})
if tup.kind != tyTuple: if tup.kind != tyTuple:
if tup.kind == tyObject: return if tup.kind == tyObject: return
internalError(n.info, "changeType: no tuple type for constructor") globalError(n.info, "no tuple type for constructor")
elif sonsLen(n) > 0 and n.sons[0].kind == nkExprColonExpr: elif sonsLen(n) > 0 and n.sons[0].kind == nkExprColonExpr:
# named tuple? # named tuple?
for i in countup(0, sonsLen(n) - 1): for i in countup(0, sonsLen(n) - 1):
var m = n.sons[i].sons[0] var m = n.sons[i].sons[0]
if m.kind != nkSym: if m.kind != nkSym:
internalError(m.info, "changeType(): invalid tuple constr") globalError(m.info, "invalid tuple constructor")
return return
if tup.n != nil: if tup.n != nil:
var f = getSymFromList(tup.n, m.sym.name) var f = getSymFromList(tup.n, m.sym.name)
if f == nil: if f == nil:
internalError(m.info, "changeType(): invalid identifier") globalError(m.info, "unknown identifier: " & m.sym.name.s)
return return
changeType(n.sons[i].sons[1], f.typ, check) changeType(n.sons[i].sons[1], f.typ, check)
else: else:
@ -474,7 +476,7 @@ proc changeType(n: PNode, newType: PType, check: bool) =
addSon(a, m) addSon(a, m)
changeType(m, tup.sons[i], check) changeType(m, tup.sons[i], check)
of nkCharLit..nkUInt64Lit: of nkCharLit..nkUInt64Lit:
if check: if check and n.kind != nkUInt64Lit:
let value = n.intVal let value = n.intVal
if value < firstOrd(newType) or value > lastOrd(newType): if value < firstOrd(newType) or value > lastOrd(newType):
localError(n.info, errGenerated, "cannot convert " & $value & localError(n.info, errGenerated, "cannot convert " & $value &
@ -596,8 +598,8 @@ proc skipObjConv(n: PNode): PNode =
of nkObjUpConv, nkObjDownConv: result = n.sons[0] of nkObjUpConv, nkObjDownConv: result = n.sons[0]
else: result = n else: result = n
proc isAssignable(c: PContext, n: PNode): TAssignableResult = proc isAssignable(c: PContext, n: PNode; isUnsafeAddr=false): TAssignableResult =
result = parampatterns.isAssignable(c.p.owner, n) result = parampatterns.isAssignable(c.p.owner, n, isUnsafeAddr)
proc newHiddenAddrTaken(c: PContext, n: PNode): PNode = proc newHiddenAddrTaken(c: PContext, n: PNode): PNode =
if n.kind == nkHiddenDeref and not (gCmd == cmdCompileToCpp or if n.kind == nkHiddenDeref and not (gCmd == cmdCompileToCpp or
@ -805,6 +807,9 @@ proc semIndirectOp(c: PContext, n: PNode, flags: TExprFlags): PNode =
return semExpr(c, result, flags) return semExpr(c, result, flags)
else: else:
n.sons[0] = semExpr(c, n.sons[0]) n.sons[0] = semExpr(c, n.sons[0])
let t = n.sons[0].typ
if t != nil and t.kind == tyVar:
n.sons[0] = newDeref(n.sons[0])
let nOrig = n.copyTree let nOrig = n.copyTree
semOpAux(c, n) semOpAux(c, n)
var t: PType = nil var t: PType = nil
@ -814,7 +819,7 @@ proc semIndirectOp(c: PContext, n: PNode, flags: TExprFlags): PNode =
# This is a proc variable, apply normal overload resolution # This is a proc variable, apply normal overload resolution
let m = resolveIndirectCall(c, n, nOrig, t) let m = resolveIndirectCall(c, n, nOrig, t)
if m.state != csMatch: if m.state != csMatch:
if c.inCompilesContext > 0: if c.compilesContextId > 0:
# speed up error generation: # speed up error generation:
globalError(n.info, errTypeMismatch, "") globalError(n.info, errTypeMismatch, "")
return emptyNode return emptyNode
@ -1152,7 +1157,7 @@ proc semSubscript(c: PContext, n: PNode, flags: TExprFlags): PNode =
result.add(x[0]) result.add(x[0])
return return
checkMinSonsLen(n, 2) checkMinSonsLen(n, 2)
n.sons[0] = semExprWithType(c, n.sons[0]) n.sons[0] = semExprWithType(c, n.sons[0], {efNoProcvarCheck})
let arr = skipTypes(n.sons[0].typ, {tyGenericInst, tyVar, tyPtr, tyRef}) let arr = skipTypes(n.sons[0].typ, {tyGenericInst, tyVar, tyPtr, tyRef})
case arr.kind case arr.kind
of tyArray, tyOpenArray, tyVarargs, tyArrayConstr, tySequence, tyString, of tyArray, tyOpenArray, tyVarargs, tyArrayConstr, tySequence, tyString,
@ -1192,7 +1197,17 @@ proc semSubscript(c: PContext, n: PNode, flags: TExprFlags): PNode =
localError(n.info, errIndexTypesDoNotMatch) localError(n.info, errIndexTypesDoNotMatch)
result = n result = n
else: else:
c.p.bracketExpr = n.sons[0] let s = if n.sons[0].kind == nkSym: n.sons[0].sym
elif n[0].kind in nkSymChoices: n.sons[0][0].sym
else: nil
if s != nil and s.kind in {skProc, skMethod, skConverter}+skIterators:
# type parameters: partial generic specialization
n.sons[0] = semSymGenericInstantiation(c, n.sons[0], s)
result = explicitGenericInstantiation(c, n, s)
elif s != nil and s.kind == skType:
result = symNodeFromType(c, semTypeNode(c, n, nil), n.info)
else:
c.p.bracketExpr = n.sons[0]
proc semArrayAccess(c: PContext, n: PNode, flags: TExprFlags): PNode = proc semArrayAccess(c: PContext, n: PNode, flags: TExprFlags): PNode =
let oldBracketExpr = c.p.bracketExpr let oldBracketExpr = c.p.bracketExpr
@ -1246,7 +1261,7 @@ proc asgnToResultVar(c: PContext, n, le, ri: PNode) {.inline.} =
template resultTypeIsInferrable(typ: PType): expr = template resultTypeIsInferrable(typ: PType): expr =
typ.isMetaType and typ.kind != tyTypeDesc typ.isMetaType and typ.kind != tyTypeDesc
proc semAsgn(c: PContext, n: PNode): PNode = proc semAsgn(c: PContext, n: PNode; mode=asgnNormal): PNode =
checkSonsLen(n, 2) checkSonsLen(n, 2)
var a = n.sons[0] var a = n.sons[0]
case a.kind case a.kind
@ -1269,18 +1284,29 @@ proc semAsgn(c: PContext, n: PNode): PNode =
# --> `[]=`(a, i, x) # --> `[]=`(a, i, x)
let oldBracketExpr = c.p.bracketExpr let oldBracketExpr = c.p.bracketExpr
a = semSubscript(c, a, {efLValue}) a = semSubscript(c, a, {efLValue})
if a == nil: if a == nil and mode != noOverloadedSubscript:
result = buildOverloadedSubscripts(n.sons[0], getIdent"[]=") result = buildOverloadedSubscripts(n.sons[0], getIdent"[]=")
add(result, n[1]) add(result, n[1])
result = semExprNoType(c, result) result = semExprNoType(c, result)
c.p.bracketExpr = oldBracketExpr c.p.bracketExpr = oldBracketExpr
return result return result
elif a == nil:
localError(n.info, "could not resolve: " & $n[0])
return n
c.p.bracketExpr = oldBracketExpr c.p.bracketExpr = oldBracketExpr
of nkCurlyExpr: of nkCurlyExpr:
# a{i} = x --> `{}=`(a, i, x) # a{i} = x --> `{}=`(a, i, x)
result = buildOverloadedSubscripts(n.sons[0], getIdent"{}=") result = buildOverloadedSubscripts(n.sons[0], getIdent"{}=")
add(result, n[1]) add(result, n[1])
return semExprNoType(c, result) return semExprNoType(c, result)
of nkPar:
if a.len >= 2:
# unfortunately we need to rewrite ``(x, y) = foo()`` already here so
# that overloading of the assignment operator still works. Usually we
# prefer to do these rewritings in transf.nim:
return semStmt(c, lowerTupleUnpackingForAsgn(n, c.p.owner))
else:
a = semExprWithType(c, a, {efLValue})
else: else:
a = semExprWithType(c, a, {efLValue}) a = semExprWithType(c, a, {efLValue})
n.sons[0] = a n.sons[0] = a
@ -1311,7 +1337,8 @@ proc semAsgn(c: PContext, n: PNode): PNode =
typeMismatch(n, lhs.typ, rhs.typ) typeMismatch(n, lhs.typ, rhs.typ)
n.sons[1] = fitNode(c, le, rhs) n.sons[1] = fitNode(c, le, rhs)
if tfHasAsgn in lhs.typ.flags and not lhsIsResult: if tfHasAsgn in lhs.typ.flags and not lhsIsResult and
mode != noOverloadedAsgn:
return overloadedAsgn(c, lhs, n.sons[1]) return overloadedAsgn(c, lhs, n.sons[1])
fixAbstractType(c, n) fixAbstractType(c, n)
@ -1369,7 +1396,7 @@ proc semProcBody(c: PContext, n: PNode): PNode =
c.p.resultSym != nil and c.p.resultSym.typ.isMetaType: c.p.resultSym != nil and c.p.resultSym.typ.isMetaType:
if isEmptyType(result.typ): if isEmptyType(result.typ):
# we inferred a 'void' return type: # we inferred a 'void' return type:
c.p.resultSym.typ = nil c.p.resultSym.typ = errorType(c)
c.p.owner.typ.sons[0] = nil c.p.owner.typ.sons[0] = nil
else: else:
localError(c.p.resultSym.info, errCannotInferReturnType) localError(c.p.resultSym.info, errCannotInferReturnType)
@ -1610,7 +1637,9 @@ proc tryExpr(c: PContext, n: PNode, flags: TExprFlags = {}): PNode =
# watch out, hacks ahead: # watch out, hacks ahead:
let oldErrorCount = msgs.gErrorCounter let oldErrorCount = msgs.gErrorCounter
let oldErrorMax = msgs.gErrorMax let oldErrorMax = msgs.gErrorMax
inc c.inCompilesContext let oldCompilesId = c.compilesContextId
inc c.compilesContextIdGenerator
c.compilesContextId = c.compilesContextIdGenerator
# do not halt after first error: # do not halt after first error:
msgs.gErrorMax = high(int) msgs.gErrorMax = high(int)
@ -1628,12 +1657,15 @@ proc tryExpr(c: PContext, n: PNode, flags: TExprFlags = {}): PNode =
let oldInGenericInst = c.inGenericInst let oldInGenericInst = c.inGenericInst
let oldProcCon = c.p let oldProcCon = c.p
c.generics = @[] c.generics = @[]
var err: string
try: try:
result = semExpr(c, n, flags) result = semExpr(c, n, flags)
if msgs.gErrorCounter != oldErrorCount: result = nil if msgs.gErrorCounter != oldErrorCount: result = nil
except ERecoverableError: except ERecoverableError:
discard if optReportConceptFailures in gGlobalOptions:
err = getCurrentExceptionMsg()
# undo symbol table changes (as far as it's possible): # undo symbol table changes (as far as it's possible):
c.compilesContextId = oldCompilesId
c.generics = oldGenerics c.generics = oldGenerics
c.inGenericContext = oldInGenericContext c.inGenericContext = oldInGenericContext
c.inUnrolledContext = oldInUnrolledContext c.inUnrolledContext = oldInUnrolledContext
@ -1642,10 +1674,11 @@ proc tryExpr(c: PContext, n: PNode, flags: TExprFlags = {}): PNode =
msgs.setInfoContextLen(oldContextLen) msgs.setInfoContextLen(oldContextLen)
setLen(gOwners, oldOwnerLen) setLen(gOwners, oldOwnerLen)
c.currentScope = oldScope c.currentScope = oldScope
dec c.inCompilesContext
errorOutputs = oldErrorOutputs errorOutputs = oldErrorOutputs
msgs.gErrorCounter = oldErrorCount msgs.gErrorCounter = oldErrorCount
msgs.gErrorMax = oldErrorMax msgs.gErrorMax = oldErrorMax
if optReportConceptFailures in gGlobalOptions and not err.isNil:
localError(n.info, err)
proc semCompiles(c: PContext, n: PNode, flags: TExprFlags): PNode = proc semCompiles(c: PContext, n: PNode, flags: TExprFlags): PNode =
# we replace this node by a 'true' or 'false' node: # we replace this node by a 'true' or 'false' node:
@ -1699,10 +1732,13 @@ proc semMagic(c: PContext, n: PNode, s: PSym, flags: TExprFlags): PNode =
case s.magic # magics that need special treatment case s.magic # magics that need special treatment
of mAddr: of mAddr:
checkSonsLen(n, 2) checkSonsLen(n, 2)
result = semAddr(c, n.sons[1]) result = semAddr(c, n.sons[1], s.name.s == "unsafeAddr")
of mTypeOf: of mTypeOf:
checkSonsLen(n, 2) checkSonsLen(n, 2)
result = semTypeOf(c, n.sons[1]) result = semTypeOf(c, n.sons[1])
#of mArrGet: result = semArrGet(c, n, flags)
#of mArrPut: result = semArrPut(c, n, flags)
#of mAsgn: result = semAsgnOpr(c, n)
of mDefined: result = semDefined(c, setMs(n, s), false) of mDefined: result = semDefined(c, setMs(n, s), false)
of mDefinedInScope: result = semDefined(c, setMs(n, s), true) of mDefinedInScope: result = semDefined(c, setMs(n, s), true)
of mCompiles: result = semCompiles(c, setMs(n, s), flags) of mCompiles: result = semCompiles(c, setMs(n, s), flags)
@ -1719,6 +1755,8 @@ proc semMagic(c: PContext, n: PNode, s: PSym, flags: TExprFlags): PNode =
result = newStrNodeT(renderTree(n[1], {renderNoComments}), n) result = newStrNodeT(renderTree(n[1], {renderNoComments}), n)
result.typ = getSysType(tyString) result.typ = getSysType(tyString)
of mParallel: of mParallel:
if not experimentalMode(c):
localError(n.info, "use the {.experimental.} pragma to enable 'parallel'")
result = setMs(n, s) result = setMs(n, s)
var x = n.lastSon var x = n.lastSon
if x.kind == nkDo: x = x.sons[bodyPos] if x.kind == nkDo: x = x.sons[bodyPos]
@ -1727,13 +1765,17 @@ proc semMagic(c: PContext, n: PNode, s: PSym, flags: TExprFlags): PNode =
dec c.inParallelStmt dec c.inParallelStmt
of mSpawn: of mSpawn:
result = setMs(n, s) result = setMs(n, s)
result.sons[1] = semExpr(c, n.sons[1]) for i in 1 .. <n.len:
if not result[1].typ.isEmptyType: result.sons[i] = semExpr(c, n.sons[i])
if spawnResult(result[1].typ, c.inParallelStmt > 0) == srFlowVar: let typ = result[^1].typ
result.typ = createFlowVar(c, result[1].typ, n.info) if not typ.isEmptyType:
if spawnResult(typ, c.inParallelStmt > 0) == srFlowVar:
result.typ = createFlowVar(c, typ, n.info)
else: else:
result.typ = result[1].typ result.typ = typ
result.add instantiateCreateFlowVarCall(c, result[1].typ, n.info).newSymNode result.add instantiateCreateFlowVarCall(c, typ, n.info).newSymNode
else:
result.add emptyNode
of mProcCall: of mProcCall:
result = setMs(n, s) result = setMs(n, s)
result.sons[1] = semExpr(c, n.sons[1]) result.sons[1] = semExpr(c, n.sons[1])
@ -1749,22 +1791,44 @@ proc semWhen(c: PContext, n: PNode, semCheck = true): PNode =
if semCheck: result = semStmt(c, e) # do not open a new scope! if semCheck: result = semStmt(c, e) # do not open a new scope!
else: result = e else: result = e
# Check if the node is "when nimvm"
# when nimvm:
# ...
# else:
# ...
var whenNimvm = false
if n.sons.len == 2 and n.sons[0].kind == nkElifBranch and
n.sons[1].kind == nkElse:
let exprNode = n.sons[0].sons[0]
if exprNode.kind == nkIdent:
whenNimvm = lookUp(c, exprNode).magic == mNimvm
elif exprNode.kind == nkSym:
whenNimvm = exprNode.sym.magic == mNimvm
for i in countup(0, sonsLen(n) - 1): for i in countup(0, sonsLen(n) - 1):
var it = n.sons[i] var it = n.sons[i]
case it.kind case it.kind
of nkElifBranch, nkElifExpr: of nkElifBranch, nkElifExpr:
checkSonsLen(it, 2) checkSonsLen(it, 2)
var e = semConstExpr(c, it.sons[0]) if whenNimvm:
if e.kind != nkIntLit: if semCheck:
# can happen for cascading errors, assume false it.sons[1] = semStmt(c, it.sons[1])
# InternalError(n.info, "semWhen") result = n # when nimvm is not elimited until codegen
discard else:
elif e.intVal != 0 and result == nil: var e = semConstExpr(c, it.sons[0])
setResult(it.sons[1]) if e.kind != nkIntLit:
# can happen for cascading errors, assume false
# InternalError(n.info, "semWhen")
discard
elif e.intVal != 0 and result == nil:
setResult(it.sons[1])
of nkElse, nkElseExpr: of nkElse, nkElseExpr:
checkSonsLen(it, 1) checkSonsLen(it, 1)
if result == nil: if result == nil or whenNimvm:
setResult(it.sons[0]) if semCheck:
it.sons[0] = semStmt(c, it.sons[0])
if result == nil:
result = it.sons[0]
else: illFormedAst(n) else: illFormedAst(n)
if result == nil: if result == nil:
result = newNodeI(nkEmpty, n.info) result = newNodeI(nkEmpty, n.info)
@ -2026,6 +2090,19 @@ proc semExport(c: PContext, n: PNode): PNode =
c.module.ast.add x c.module.ast.add x
result = n result = n
proc shouldBeBracketExpr(n: PNode): bool =
assert n.kind in nkCallKinds
let a = n.sons[0]
if a.kind in nkCallKinds:
let b = a[0]
if b.kind in nkSymChoices:
for i in 0..<b.len:
if b[i].sym.magic == mArrGet:
let be = newNodeI(nkBracketExpr, n.info)
for i in 1..<a.len: be.add(a[i])
n.sons[0] = be
return true
proc setGenericParams(c: PContext, n: PNode) = proc setGenericParams(c: PContext, n: PNode) =
for i in 1 .. <n.len: for i in 1 .. <n.len:
n[i].typ = semTypeNode(c, n[i], nil) n[i].typ = semTypeNode(c, n[i], nil)
@ -2133,7 +2210,8 @@ proc semExpr(c: PContext, n: PNode, flags: TExprFlags = {}): PNode =
else: else:
#liMessage(n.info, warnUser, renderTree(n)); #liMessage(n.info, warnUser, renderTree(n));
result = semIndirectOp(c, n, flags) result = semIndirectOp(c, n, flags)
elif n[0].kind == nkBracketExpr and isSymChoice(n[0][0]): elif (n[0].kind == nkBracketExpr or shouldBeBracketExpr(n)) and
isSymChoice(n[0][0]):
# indirectOp can deal with explicit instantiations; the fixes # indirectOp can deal with explicit instantiations; the fixes
# the 'newSeq[T](x)' bug # the 'newSeq[T](x)' bug
setGenericParams(c, n.sons[0]) setGenericParams(c, n.sons[0])
@ -2147,19 +2225,14 @@ proc semExpr(c: PContext, n: PNode, flags: TExprFlags = {}): PNode =
result = semWhen(c, n, true) result = semWhen(c, n, true)
else: else:
result = semWhen(c, n, false) result = semWhen(c, n, false)
result = semExpr(c, result, flags) if result == n:
# This is a "when nimvm" stmt.
result = semWhen(c, n, true)
else:
result = semExpr(c, result, flags)
of nkBracketExpr: of nkBracketExpr:
checkMinSonsLen(n, 1) checkMinSonsLen(n, 1)
var s = qualifiedLookUp(c, n.sons[0], {checkUndeclared}) result = semArrayAccess(c, n, flags)
if (s != nil and s.kind in {skProc, skMethod, skConverter}+skIterators) or
n[0].kind in nkSymChoices:
# type parameters: partial generic specialization
n.sons[0] = semSymGenericInstantiation(c, n.sons[0], s)
result = explicitGenericInstantiation(c, n, s)
elif s != nil and s.kind in {skType}:
result = symNodeFromType(c, semTypeNode(c, n, nil), n.info)
else:
result = semArrayAccess(c, n, flags)
of nkCurlyExpr: of nkCurlyExpr:
result = semExpr(c, buildOverloadedSubscripts(n, getIdent"{}"), flags) result = semExpr(c, buildOverloadedSubscripts(n, getIdent"{}"), flags)
of nkPragmaExpr: of nkPragmaExpr:
@ -2254,7 +2327,10 @@ 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:
localError(n.info, errGenerated, "'defer' not allowed in this context") n.sons[0] = semExpr(c, n.sons[0])
if not n.sons[0].typ.isEmptyType and not implicitlyDiscardable(n.sons[0]):
localError(n.info, errGenerated, "'defer' takes a 'void' expression")
#localError(n.info, errGenerated, "'defer' not allowed in this context")
else: else:
localError(n.info, errInvalidExpressionX, localError(n.info, errInvalidExpressionX,
renderTree(n, {renderNoComments})) renderTree(n, {renderNoComments}))

View file

@ -76,7 +76,7 @@ proc semForObjectFields(c: TFieldsCtx, typ, forLoop, father: PNode) =
let L = forLoop.len let L = forLoop.len
let call = forLoop.sons[L-2] let call = forLoop.sons[L-2]
if call.len > 2: if call.len > 2:
localError(forLoop.info, errGenerated, localError(forLoop.info, errGenerated,
"parallel 'fields' iterator does not work for 'case' objects") "parallel 'fields' iterator does not work for 'case' objects")
return return
# iterate over the selector: # iterate over the selector:
@ -106,7 +106,7 @@ proc semForFields(c: PContext, n: PNode, m: TMagic): PNode =
# a 'while true: stmt; break' loop ... # a 'while true: stmt; break' loop ...
result = newNodeI(nkWhileStmt, n.info, 2) result = newNodeI(nkWhileStmt, n.info, 2)
var trueSymbol = strTableGet(magicsys.systemModule.tab, getIdent"true") var trueSymbol = strTableGet(magicsys.systemModule.tab, getIdent"true")
if trueSymbol == nil: if trueSymbol == nil:
localError(n.info, errSystemNeeds, "true") localError(n.info, errSystemNeeds, "true")
trueSymbol = newSym(skUnknown, getIdent"true", getCurrOwner(), n.info) trueSymbol = newSym(skUnknown, getIdent"true", getCurrOwner(), n.info)
trueSymbol.typ = getSysType(tyBool) trueSymbol.typ = getSysType(tyBool)
@ -114,13 +114,13 @@ proc semForFields(c: PContext, n: PNode, m: TMagic): PNode =
result.sons[0] = newSymNode(trueSymbol, n.info) result.sons[0] = newSymNode(trueSymbol, n.info)
var stmts = newNodeI(nkStmtList, n.info) var stmts = newNodeI(nkStmtList, n.info)
result.sons[1] = stmts result.sons[1] = stmts
var length = sonsLen(n) var length = sonsLen(n)
var call = n.sons[length-2] var call = n.sons[length-2]
if length-2 != sonsLen(call)-1 + ord(m==mFieldPairs): if length-2 != sonsLen(call)-1 + ord(m==mFieldPairs):
localError(n.info, errWrongNumberOfVariables) localError(n.info, errWrongNumberOfVariables)
return result return result
var tupleTypeA = skipTypes(call.sons[1].typ, abstractVar-{tyTypeDesc}) var tupleTypeA = skipTypes(call.sons[1].typ, abstractVar-{tyTypeDesc})
if tupleTypeA.kind notin {tyTuple, tyObject}: if tupleTypeA.kind notin {tyTuple, tyObject}:
localError(n.info, errGenerated, "no object or tuple type") localError(n.info, errGenerated, "no object or tuple type")
@ -129,7 +129,7 @@ proc semForFields(c: PContext, n: PNode, m: TMagic): PNode =
var tupleTypeB = skipTypes(call.sons[i].typ, abstractVar-{tyTypeDesc}) var tupleTypeB = skipTypes(call.sons[i].typ, abstractVar-{tyTypeDesc})
if not sameType(tupleTypeA, tupleTypeB): if not sameType(tupleTypeA, tupleTypeB):
typeMismatch(call.sons[i], tupleTypeA, tupleTypeB) typeMismatch(call.sons[i], tupleTypeA, tupleTypeB)
inc(c.p.nestedLoopCounter) inc(c.p.nestedLoopCounter)
if tupleTypeA.kind == tyTuple: if tupleTypeA.kind == tyTuple:
var loopBody = n.sons[length-1] var loopBody = n.sons[length-1]

View file

@ -179,7 +179,7 @@ proc getIntervalType*(m: TMagic, n: PNode): PType =
else: else:
result = makeRangeF(a, abs(getFloat(a.n.sons[1])), result = makeRangeF(a, abs(getFloat(a.n.sons[1])),
abs(getFloat(a.n.sons[0]))) abs(getFloat(a.n.sons[0])))
of mAbsI, mAbsI64: of mAbsI:
let a = n.sons[1].typ let a = n.sons[1].typ
if isIntRange(a): if isIntRange(a):
if a.n[0].intVal <= 0: if a.n[0].intVal <= 0:
@ -200,13 +200,13 @@ proc getIntervalType*(m: TMagic, n: PNode): PType =
if isIntRange(a) and isIntLit(b): if isIntRange(a) and isIntLit(b):
result = makeRange(a, pickMinInt(n.sons[1]) |-| pickMinInt(n.sons[2]), result = makeRange(a, pickMinInt(n.sons[1]) |-| pickMinInt(n.sons[2]),
pickMaxInt(n.sons[1]) |-| pickMaxInt(n.sons[2])) pickMaxInt(n.sons[1]) |-| pickMaxInt(n.sons[2]))
of mAddI, mAddI64, mAddU: of mAddI, mAddU:
commutativeOp(`|+|`) commutativeOp(`|+|`)
of mMulI, mMulI64, mMulU: of mMulI, mMulU:
commutativeOp(`|*|`) commutativeOp(`|*|`)
of mSubI, mSubI64, mSubU: of mSubI, mSubU:
binaryOp(`|-|`) binaryOp(`|-|`)
of mBitandI, mBitandI64: of mBitandI:
# since uint64 is still not even valid for 'range' (since it's no ordinal # since uint64 is still not even valid for 'range' (since it's no ordinal
# yet), we exclude it from the list (see bug #1638) for now: # yet), we exclude it from the list (see bug #1638) for now:
var a = n.sons[1] var a = n.sons[1]
@ -225,7 +225,7 @@ proc getIntervalType*(m: TMagic, n: PNode): PType =
result = makeRange(a.typ, 0, b.intVal-1) result = makeRange(a.typ, 0, b.intVal-1)
else: else:
result = makeRange(a.typ, b.intVal+1, 0) result = makeRange(a.typ, b.intVal+1, 0)
of mModI, mModI64: of mModI:
# so ... if you ever wondered about modulo's signedness; this defines it: # so ... if you ever wondered about modulo's signedness; this defines it:
let a = n.sons[1] let a = n.sons[1]
let b = n.sons[2] let b = n.sons[2]
@ -234,7 +234,7 @@ proc getIntervalType*(m: TMagic, n: PNode): PType =
result = makeRange(a.typ, -(b.intVal-1), b.intVal-1) result = makeRange(a.typ, -(b.intVal-1), b.intVal-1)
else: else:
result = makeRange(a.typ, b.intVal+1, -(b.intVal+1)) result = makeRange(a.typ, b.intVal+1, -(b.intVal+1))
of mDivI, mDivI64, mDivU: of mDivI, mDivU:
binaryOp(`|div|`) binaryOp(`|div|`)
of mMinI: of mMinI:
commutativeOp(min) commutativeOp(min)
@ -243,8 +243,8 @@ proc getIntervalType*(m: TMagic, n: PNode): PType =
else: discard else: discard
discard """ discard """
mShlI, mShlI64, mShlI,
mShrI, mShrI64, mAddF64, mSubF64, mMulF64, mDivF64, mMaxF64, mMinF64 mShrI, mAddF64, mSubF64, mMulF64, mDivF64, mMaxF64, mMinF64
""" """
proc evalIs(n, a: PNode): PNode = proc evalIs(n, a: PNode): PNode =
@ -285,7 +285,7 @@ proc evalOp(m: TMagic, n, a, b, c: PNode): PNode =
of mUnaryMinusF64: result = newFloatNodeT(- getFloat(a), n) of mUnaryMinusF64: result = newFloatNodeT(- getFloat(a), n)
of mNot: result = newIntNodeT(1 - getInt(a), n) of mNot: result = newIntNodeT(1 - getInt(a), n)
of mCard: result = newIntNodeT(nimsets.cardSet(a), n) of mCard: result = newIntNodeT(nimsets.cardSet(a), n)
of mBitnotI, mBitnotI64: result = newIntNodeT(not getInt(a), n) of mBitnotI: result = newIntNodeT(not getInt(a), n)
of mLengthStr, mXLenStr: of mLengthStr, mXLenStr:
if a.kind == nkNilLit: result = newIntNodeT(0, n) if a.kind == nkNilLit: result = newIntNodeT(0, n)
else: result = newIntNodeT(len(getStr(a)), n) else: result = newIntNodeT(len(getStr(a)), n)
@ -298,7 +298,7 @@ proc evalOp(m: TMagic, n, a, b, c: PNode): PNode =
result = newFloatNodeT(toFloat(int(getInt(a))), n) result = newFloatNodeT(toFloat(int(getInt(a))), n)
of mToInt, mToBiggestInt: result = newIntNodeT(system.toInt(getFloat(a)), n) of mToInt, mToBiggestInt: result = newIntNodeT(system.toInt(getFloat(a)), n)
of mAbsF64: result = newFloatNodeT(abs(getFloat(a)), n) of mAbsF64: result = newFloatNodeT(abs(getFloat(a)), n)
of mAbsI, mAbsI64: of mAbsI:
if getInt(a) >= 0: result = a if getInt(a) >= 0: result = a
else: result = newIntNodeT(- getInt(a), n) else: result = newIntNodeT(- getInt(a), n)
of mZe8ToI, mZe8ToI64, mZe16ToI, mZe16ToI64, mZe32ToI64, mZeIToI64: of mZe8ToI, mZe8ToI64, mZe16ToI, mZe16ToI64, mZe32ToI64, mZeIToI64:
@ -307,19 +307,19 @@ proc evalOp(m: TMagic, n, a, b, c: PNode): PNode =
of mToU8: result = newIntNodeT(getInt(a) and 0x000000FF, n) of mToU8: result = newIntNodeT(getInt(a) and 0x000000FF, n)
of mToU16: result = newIntNodeT(getInt(a) and 0x0000FFFF, n) of mToU16: result = newIntNodeT(getInt(a) and 0x0000FFFF, n)
of mToU32: result = newIntNodeT(getInt(a) and 0x00000000FFFFFFFF'i64, n) of mToU32: result = newIntNodeT(getInt(a) and 0x00000000FFFFFFFF'i64, n)
of mUnaryLt: result = newIntNodeT(getOrdValue(a) - 1, n) of mUnaryLt: result = newIntNodeT(getOrdValue(a) |-| 1, n)
of mSucc: result = newIntNodeT(getOrdValue(a) + getInt(b), n) of mSucc: result = newIntNodeT(getOrdValue(a) |+| getInt(b), n)
of mPred: result = newIntNodeT(getOrdValue(a) - getInt(b), n) of mPred: result = newIntNodeT(getOrdValue(a) |-| getInt(b), n)
of mAddI, mAddI64: result = newIntNodeT(getInt(a) + getInt(b), n) of mAddI: result = newIntNodeT(getInt(a) |+| getInt(b), n)
of mSubI, mSubI64: result = newIntNodeT(getInt(a) - getInt(b), n) of mSubI: result = newIntNodeT(getInt(a) |-| getInt(b), n)
of mMulI, mMulI64: result = newIntNodeT(getInt(a) * getInt(b), n) of mMulI: result = newIntNodeT(getInt(a) |*| getInt(b), n)
of mMinI: of mMinI:
if getInt(a) > getInt(b): result = newIntNodeT(getInt(b), n) if getInt(a) > getInt(b): result = newIntNodeT(getInt(b), n)
else: result = newIntNodeT(getInt(a), n) else: result = newIntNodeT(getInt(a), n)
of mMaxI: of mMaxI:
if getInt(a) > getInt(b): result = newIntNodeT(getInt(a), n) if getInt(a) > getInt(b): result = newIntNodeT(getInt(a), n)
else: result = newIntNodeT(getInt(b), n) else: result = newIntNodeT(getInt(b), n)
of mShlI, mShlI64: of mShlI:
case skipTypes(n.typ, abstractRange).kind case skipTypes(n.typ, abstractRange).kind
of tyInt8: result = newIntNodeT(int8(getInt(a)) shl int8(getInt(b)), n) of tyInt8: result = newIntNodeT(int8(getInt(a)) shl int8(getInt(b)), n)
of tyInt16: result = newIntNodeT(int16(getInt(a)) shl int16(getInt(b)), n) of tyInt16: result = newIntNodeT(int16(getInt(a)) shl int16(getInt(b)), n)
@ -327,7 +327,7 @@ proc evalOp(m: TMagic, n, a, b, c: PNode): PNode =
of tyInt64, tyInt, tyUInt..tyUInt64: of tyInt64, tyInt, tyUInt..tyUInt64:
result = newIntNodeT(`shl`(getInt(a), getInt(b)), n) result = newIntNodeT(`shl`(getInt(a), getInt(b)), n)
else: internalError(n.info, "constant folding for shl") else: internalError(n.info, "constant folding for shl")
of mShrI, mShrI64: of mShrI:
case skipTypes(n.typ, abstractRange).kind case skipTypes(n.typ, abstractRange).kind
of tyInt8: result = newIntNodeT(int8(getInt(a)) shr int8(getInt(b)), n) of tyInt8: result = newIntNodeT(int8(getInt(a)) shr int8(getInt(b)), n)
of tyInt16: result = newIntNodeT(int16(getInt(a)) shr int16(getInt(b)), n) of tyInt16: result = newIntNodeT(int16(getInt(a)) shr int16(getInt(b)), n)
@ -335,14 +335,14 @@ proc evalOp(m: TMagic, n, a, b, c: PNode): PNode =
of tyInt64, tyInt, tyUInt..tyUInt64: of tyInt64, tyInt, tyUInt..tyUInt64:
result = newIntNodeT(`shr`(getInt(a), getInt(b)), n) result = newIntNodeT(`shr`(getInt(a), getInt(b)), n)
else: internalError(n.info, "constant folding for shr") else: internalError(n.info, "constant folding for shr")
of mDivI, mDivI64: of mDivI:
let y = getInt(b) let y = getInt(b)
if y != 0: if y != 0:
result = newIntNodeT(getInt(a) div y, n) result = newIntNodeT(`|div|`(getInt(a), y), n)
of mModI, mModI64: of mModI:
let y = getInt(b) let y = getInt(b)
if y != 0: if y != 0:
result = newIntNodeT(getInt(a) mod y, n) result = newIntNodeT(`|mod|`(getInt(a), y), n)
of mAddF64: result = newFloatNodeT(getFloat(a) + getFloat(b), n) of mAddF64: result = newFloatNodeT(getFloat(a) + getFloat(b), n)
of mSubF64: result = newFloatNodeT(getFloat(a) - getFloat(b), n) of mSubF64: result = newFloatNodeT(getFloat(a) - getFloat(b), n)
of mMulF64: result = newFloatNodeT(getFloat(a) * getFloat(b), n) of mMulF64: result = newFloatNodeT(getFloat(a) * getFloat(b), n)
@ -359,11 +359,11 @@ proc evalOp(m: TMagic, n, a, b, c: PNode): PNode =
if getFloat(a) > getFloat(b): result = newFloatNodeT(getFloat(b), n) if getFloat(a) > getFloat(b): result = newFloatNodeT(getFloat(b), n)
else: result = newFloatNodeT(getFloat(a), n) else: result = newFloatNodeT(getFloat(a), n)
of mIsNil: result = newIntNodeT(ord(a.kind == nkNilLit), n) of mIsNil: result = newIntNodeT(ord(a.kind == nkNilLit), n)
of mLtI, mLtI64, mLtB, mLtEnum, mLtCh: of mLtI, mLtB, mLtEnum, mLtCh:
result = newIntNodeT(ord(getOrdValue(a) < getOrdValue(b)), n) result = newIntNodeT(ord(getOrdValue(a) < getOrdValue(b)), n)
of mLeI, mLeI64, mLeB, mLeEnum, mLeCh: of mLeI, mLeB, mLeEnum, mLeCh:
result = newIntNodeT(ord(getOrdValue(a) <= getOrdValue(b)), n) result = newIntNodeT(ord(getOrdValue(a) <= getOrdValue(b)), n)
of mEqI, mEqI64, mEqB, mEqEnum, mEqCh: of mEqI, mEqB, mEqEnum, mEqCh:
result = newIntNodeT(ord(getOrdValue(a) == getOrdValue(b)), n) result = newIntNodeT(ord(getOrdValue(a) == getOrdValue(b)), n)
of mLtF64: result = newIntNodeT(ord(getFloat(a) < getFloat(b)), n) of mLtF64: result = newIntNodeT(ord(getFloat(a) < getFloat(b)), n)
of mLeF64: result = newIntNodeT(ord(getFloat(a) <= getFloat(b)), n) of mLeF64: result = newIntNodeT(ord(getFloat(a) <= getFloat(b)), n)
@ -375,9 +375,9 @@ proc evalOp(m: TMagic, n, a, b, c: PNode): PNode =
result = newIntNodeT(ord(`<%`(getOrdValue(a), getOrdValue(b))), n) result = newIntNodeT(ord(`<%`(getOrdValue(a), getOrdValue(b))), n)
of mLeU, mLeU64: of mLeU, mLeU64:
result = newIntNodeT(ord(`<=%`(getOrdValue(a), getOrdValue(b))), n) result = newIntNodeT(ord(`<=%`(getOrdValue(a), getOrdValue(b))), n)
of mBitandI, mBitandI64, mAnd: result = newIntNodeT(a.getInt and b.getInt, n) of mBitandI, mAnd: result = newIntNodeT(a.getInt and b.getInt, n)
of mBitorI, mBitorI64, mOr: result = newIntNodeT(getInt(a) or getInt(b), n) of mBitorI, mOr: result = newIntNodeT(getInt(a) or getInt(b), n)
of mBitxorI, mBitxorI64, mXor: result = newIntNodeT(a.getInt xor b.getInt, n) of mBitxorI, mXor: result = newIntNodeT(a.getInt xor b.getInt, n)
of mAddU: result = newIntNodeT(`+%`(getInt(a), getInt(b)), n) of mAddU: result = newIntNodeT(`+%`(getInt(a), getInt(b)), n)
of mSubU: result = newIntNodeT(`-%`(getInt(a), getInt(b)), n) of mSubU: result = newIntNodeT(`-%`(getInt(a), getInt(b)), n)
of mMulU: result = newIntNodeT(`*%`(getInt(a), getInt(b)), n) of mMulU: result = newIntNodeT(`*%`(getInt(a), getInt(b)), n)
@ -430,14 +430,10 @@ proc evalOp(m: TMagic, n, a, b, c: PNode): PNode =
of mCompileOptionArg: of mCompileOptionArg:
result = newIntNodeT(ord( result = newIntNodeT(ord(
testCompileOptionArg(getStr(a), getStr(b), n.info)), n) testCompileOptionArg(getStr(a), getStr(b), n.info)), n)
of mNewString, mNewStringOfCap, of mEqProc:
mExit, mInc, ast.mDec, mEcho, mSwap, mAppendStrCh, result = newIntNodeT(ord(
mAppendStrStr, mAppendSeqElem, mSetLengthStr, mSetLengthSeq, exprStructuralEquivalent(a, b, strictSymEquality=true)), n)
mParseExprToAst, mParseStmtToAst, mExpandToAst, mTypeTrait, mDotDot, else: discard
mNLen..mNError, mEqRef, mSlurp, mStaticExec, mNGenSym, mSpawn,
mParallel, mPlugin:
discard
else: internalError(a.info, "evalOp(" & $m & ')')
proc getConstIfExpr(c: PSym, n: PNode): PNode = proc getConstIfExpr(c: PSym, n: PNode): PNode =
result = nil result = nil
@ -540,7 +536,7 @@ proc foldConv*(n, a: PNode; check = false): PNode =
of tyFloat..tyFloat64: of tyFloat..tyFloat64:
case skipTypes(a.typ, abstractRange).kind case skipTypes(a.typ, abstractRange).kind
of tyInt..tyInt64, tyEnum, tyBool, tyChar: of tyInt..tyInt64, tyEnum, tyBool, tyChar:
result = newFloatNodeT(toFloat(int(getOrdValue(a))), n) result = newFloatNodeT(toBiggestFloat(getOrdValue(a)), n)
else: else:
result = a result = a
result.typ = n.typ result.typ = n.typ

View file

@ -30,6 +30,7 @@ type
GenericCtx = object GenericCtx = object
toMixin: IntSet toMixin: IntSet
cursorInBody: bool # only for nimsuggest cursorInBody: bool # only for nimsuggest
bracketExpr: PNode
type type
TSemGenericFlag = enum TSemGenericFlag = enum
@ -39,9 +40,9 @@ type
proc semGenericStmt(c: PContext, n: PNode, proc semGenericStmt(c: PContext, n: PNode,
flags: TSemGenericFlags, ctx: var GenericCtx): PNode flags: TSemGenericFlags, ctx: var GenericCtx): PNode
proc semGenericStmtScope(c: PContext, n: PNode, proc semGenericStmtScope(c: PContext, n: PNode,
flags: TSemGenericFlags, flags: TSemGenericFlags,
ctx: var GenericCtx): PNode = ctx: var GenericCtx): PNode =
openScope(c) openScope(c)
result = semGenericStmt(c, n, flags, ctx) result = semGenericStmt(c, n, flags, ctx)
closeScope(c) closeScope(c)
@ -57,7 +58,7 @@ proc semGenericStmtSymbol(c: PContext, n: PNode, s: PSym,
of skUnknown: of skUnknown:
# Introduced in this pass! Leave it as an identifier. # Introduced in this pass! Leave it as an identifier.
result = n result = n
of skProc, skMethod, skIterators, skConverter: of skProc, skMethod, skIterators, skConverter, skModule:
result = symChoice(c, n, s, scOpen) result = symChoice(c, n, s, scOpen)
of skTemplate: of skTemplate:
if macroToExpand(s): if macroToExpand(s):
@ -73,7 +74,7 @@ proc semGenericStmtSymbol(c: PContext, n: PNode, s: PSym,
result = semGenericStmt(c, result, {}, ctx) result = semGenericStmt(c, result, {}, ctx)
else: else:
result = symChoice(c, n, s, scOpen) result = symChoice(c, n, s, scOpen)
of skGenericParam: of skGenericParam:
if s.typ != nil and s.typ.kind == tyStatic: if s.typ != nil and s.typ.kind == tyStatic:
if s.typ.n != nil: if s.typ.n != nil:
result = s.typ.n result = s.typ.n
@ -85,18 +86,18 @@ proc semGenericStmtSymbol(c: PContext, n: PNode, s: PSym,
of skParam: of skParam:
result = n result = n
styleCheckUse(n.info, s) styleCheckUse(n.info, s)
of skType: of skType:
if (s.typ != nil) and if (s.typ != nil) and
(s.typ.flags * {tfGenericTypeParam, tfImplicitTypeParam} == {}): (s.typ.flags * {tfGenericTypeParam, tfImplicitTypeParam} == {}):
result = newSymNodeTypeDesc(s, n.info) result = newSymNodeTypeDesc(s, n.info)
else: else:
result = n result = n
styleCheckUse(n.info, s) styleCheckUse(n.info, s)
else: else:
result = newSymNode(s, n.info) result = newSymNode(s, n.info)
styleCheckUse(n.info, s) styleCheckUse(n.info, s)
proc lookup(c: PContext, n: PNode, flags: TSemGenericFlags, proc lookup(c: PContext, n: PNode, flags: TSemGenericFlags,
ctx: var GenericCtx): PNode = ctx: var GenericCtx): PNode =
result = n result = n
let ident = considerQuotedIdent(n) let ident = considerQuotedIdent(n)
@ -118,13 +119,13 @@ proc newDot(n, b: PNode): PNode =
result.add(n.sons[0]) result.add(n.sons[0])
result.add(b) result.add(b)
proc fuzzyLookup(c: PContext, n: PNode, flags: TSemGenericFlags, proc fuzzyLookup(c: PContext, n: PNode, flags: TSemGenericFlags,
ctx: var GenericCtx; isMacro: var bool): PNode = ctx: var GenericCtx; isMacro: var bool): PNode =
assert n.kind == nkDotExpr assert n.kind == nkDotExpr
semIdeForTemplateOrGenericCheck(n, ctx.cursorInBody) semIdeForTemplateOrGenericCheck(n, ctx.cursorInBody)
let luf = if withinMixin notin flags: {checkUndeclared} else: {} let luf = if withinMixin notin flags: {checkUndeclared} else: {}
var s = qualifiedLookUp(c, n, luf) var s = qualifiedLookUp(c, n, luf)
if s != nil: if s != nil:
result = semGenericStmtSymbol(c, n, s, ctx) result = semGenericStmtSymbol(c, n, s, ctx)
@ -141,18 +142,20 @@ proc fuzzyLookup(c: PContext, n: PNode, flags: TSemGenericFlags,
elif s.name.id in ctx.toMixin: elif s.name.id in ctx.toMixin:
result = newDot(result, symChoice(c, n, s, scForceOpen)) result = newDot(result, symChoice(c, n, s, scForceOpen))
else: else:
let sym = semGenericStmtSymbol(c, n, s, ctx) let syms = semGenericStmtSymbol(c, n, s, ctx)
if sym.kind == nkSym: if syms.kind == nkSym:
result = newDot(result, symChoice(c, n, s, scForceOpen)) let choice = symChoice(c, n, s, scForceOpen)
choice.kind = nkClosedSymChoice
result = newDot(result, choice)
else: else:
result = newDot(result, sym) result = newDot(result, syms)
proc addTempDecl(c: PContext; n: PNode; kind: TSymKind) = proc addTempDecl(c: PContext; n: PNode; kind: TSymKind) =
let s = newSymS(skUnknown, getIdentNode(n), c) let s = newSymS(skUnknown, getIdentNode(n), c)
addPrelimDecl(c, s) addPrelimDecl(c, s)
styleCheckDef(n.info, s, kind) styleCheckDef(n.info, s, kind)
proc semGenericStmt(c: PContext, n: PNode, proc semGenericStmt(c: PContext, n: PNode,
flags: TSemGenericFlags, ctx: var GenericCtx): PNode = flags: TSemGenericFlags, ctx: var GenericCtx): PNode =
result = n result = n
#if gCmd == cmdIdeTools: suggestStmt(c, n) #if gCmd == cmdIdeTools: suggestStmt(c, n)
@ -181,16 +184,16 @@ proc semGenericStmt(c: PContext, n: PNode,
result = semGenericStmt(c, n.sons[0], flags+{withinBind}, ctx) result = semGenericStmt(c, n.sons[0], flags+{withinBind}, ctx)
of nkMixinStmt: of nkMixinStmt:
result = semMixinStmt(c, n, ctx.toMixin) result = semMixinStmt(c, n, ctx.toMixin)
of nkCall, nkHiddenCallConv, nkInfix, nkPrefix, nkCommand, nkCallStrLit: of nkCall, nkHiddenCallConv, nkInfix, nkPrefix, nkCommand, nkCallStrLit:
# check if it is an expression macro: # check if it is an expression macro:
checkMinSonsLen(n, 1) checkMinSonsLen(n, 1)
let fn = n.sons[0] let fn = n.sons[0]
var s = qualifiedLookUp(c, fn, {}) var s = qualifiedLookUp(c, fn, {})
if s == nil and withinMixin notin flags and if s == nil and withinMixin notin flags and
fn.kind in {nkIdent, nkAccQuoted} and fn.kind in {nkIdent, nkAccQuoted} and
considerQuotedIdent(fn).id notin ctx.toMixin: considerQuotedIdent(fn).id notin ctx.toMixin:
localError(n.info, errUndeclaredIdentifier, fn.renderTree) localError(n.info, errUndeclaredIdentifier, fn.renderTree)
var first = 0 var first = 0
var mixinContext = false var mixinContext = false
if s != nil: if s != nil:
@ -220,19 +223,23 @@ proc semGenericStmt(c: PContext, n: PNode,
# we need to put the ``c`` in ``t(c)`` in a mixin context to prevent # we need to put the ``c`` in ``t(c)`` in a mixin context to prevent
# the famous "undeclared identifier: it" bug: # the famous "undeclared identifier: it" bug:
mixinContext = true mixinContext = true
of skUnknown, skParam: of skUnknown, skParam:
# Leave it as an identifier. # Leave it as an identifier.
discard discard
of skProc, skMethod, skIterators, skConverter: of skProc, skMethod, skIterators, skConverter, skModule:
result.sons[0] = symChoice(c, fn, s, scOption) result.sons[0] = symChoice(c, fn, s, scOption)
# do not check of 's.magic==mRoof' here because it might be some
# other '^' but after overload resolution the proper one:
if ctx.bracketExpr != nil and n.len == 2 and s.name.s == "^":
result.add ctx.bracketExpr
first = 1 first = 1
of skGenericParam: of skGenericParam:
result.sons[0] = newSymNodeTypeDesc(s, fn.info) result.sons[0] = newSymNodeTypeDesc(s, fn.info)
styleCheckUse(fn.info, s) styleCheckUse(fn.info, s)
first = 1 first = 1
of skType: of skType:
# bad hack for generics: # bad hack for generics:
if (s.typ != nil) and (s.typ.kind != tyGenericParam): if (s.typ != nil) and (s.typ.kind != tyGenericParam):
result.sons[0] = newSymNodeTypeDesc(s, fn.info) result.sons[0] = newSymNodeTypeDesc(s, fn.info)
styleCheckUse(fn.info, s) styleCheckUse(fn.info, s)
first = 1 first = 1
@ -244,34 +251,68 @@ proc semGenericStmt(c: PContext, n: PNode,
result.sons[0] = fuzzyLookup(c, fn, flags, ctx, mixinContext) result.sons[0] = fuzzyLookup(c, fn, flags, ctx, mixinContext)
first = 1 first = 1
# Consider 'when declared(globalsSlot): ThreadVarSetValue(globalsSlot, ...)' # Consider 'when declared(globalsSlot): ThreadVarSetValue(globalsSlot, ...)'
# in threads.nim: the subtle preprocessing here binds 'globalsSlot' which # in threads.nim: the subtle preprocessing here binds 'globalsSlot' which
# is not exported and yet the generic 'threadProcWrapper' works correctly. # is not exported and yet the generic 'threadProcWrapper' works correctly.
let flags = if mixinContext: flags+{withinMixin} else: flags let flags = if mixinContext: flags+{withinMixin} else: flags
for i in countup(first, sonsLen(result) - 1): for i in countup(first, sonsLen(result) - 1):
result.sons[i] = semGenericStmt(c, result.sons[i], flags, ctx) result.sons[i] = semGenericStmt(c, result.sons[i], flags, ctx)
of nkIfStmt: of nkCurlyExpr:
for i in countup(0, sonsLen(n)-1): result = newNodeI(nkCall, n.info)
result.add newIdentNode(getIdent("{}"), n.info)
for i in 0 ..< n.len: result.add(n[i])
result = semGenericStmt(c, result, flags, ctx)
of nkBracketExpr:
result = newNodeI(nkCall, n.info)
result.add newIdentNode(getIdent("[]"), n.info)
for i in 0 ..< n.len: result.add(n[i])
withBracketExpr ctx, n.sons[0]:
result = semGenericStmt(c, result, flags, ctx)
of nkAsgn, nkFastAsgn:
checkSonsLen(n, 2)
let a = n.sons[0]
let b = n.sons[1]
let k = a.kind
case k
of nkCurlyExpr:
result = newNodeI(nkCall, n.info)
result.add newIdentNode(getIdent("{}="), n.info)
for i in 0 ..< a.len: result.add(a[i])
result.add(b)
result = semGenericStmt(c, result, flags, ctx)
of nkBracketExpr:
result = newNodeI(nkCall, n.info)
result.add newIdentNode(getIdent("[]="), n.info)
for i in 0 ..< a.len: result.add(a[i])
result.add(b)
withBracketExpr ctx, a.sons[0]:
result = semGenericStmt(c, result, flags, ctx)
else:
for i in countup(0, sonsLen(n) - 1):
result.sons[i] = semGenericStmt(c, n.sons[i], flags, ctx)
of nkIfStmt:
for i in countup(0, sonsLen(n)-1):
n.sons[i] = semGenericStmtScope(c, n.sons[i], flags, ctx) n.sons[i] = semGenericStmtScope(c, n.sons[i], flags, ctx)
of nkWhenStmt: of nkWhenStmt:
for i in countup(0, sonsLen(n)-1): for i in countup(0, sonsLen(n)-1):
n.sons[i] = semGenericStmt(c, n.sons[i], flags+{withinMixin}, ctx) n.sons[i] = semGenericStmt(c, n.sons[i], flags+{withinMixin}, ctx)
of nkWhileStmt: of nkWhileStmt:
openScope(c) openScope(c)
for i in countup(0, sonsLen(n)-1): for i in countup(0, sonsLen(n)-1):
n.sons[i] = semGenericStmt(c, n.sons[i], flags, ctx) n.sons[i] = semGenericStmt(c, n.sons[i], flags, ctx)
closeScope(c) closeScope(c)
of nkCaseStmt: of nkCaseStmt:
openScope(c) openScope(c)
n.sons[0] = semGenericStmt(c, n.sons[0], flags, ctx) n.sons[0] = semGenericStmt(c, n.sons[0], flags, ctx)
for i in countup(1, sonsLen(n)-1): for i in countup(1, sonsLen(n)-1):
var a = n.sons[i] var a = n.sons[i]
checkMinSonsLen(a, 1) checkMinSonsLen(a, 1)
var L = sonsLen(a) var L = sonsLen(a)
for j in countup(0, L-2): for j in countup(0, L-2):
a.sons[j] = semGenericStmt(c, a.sons[j], flags, ctx) a.sons[j] = semGenericStmt(c, a.sons[j], flags, ctx)
a.sons[L - 1] = semGenericStmtScope(c, a.sons[L-1], flags, ctx) a.sons[L - 1] = semGenericStmtScope(c, a.sons[L-1], flags, ctx)
closeScope(c) closeScope(c)
of nkForStmt, nkParForStmt: of nkForStmt, nkParForStmt:
var L = sonsLen(n) var L = sonsLen(n)
openScope(c) openScope(c)
n.sons[L - 2] = semGenericStmt(c, n.sons[L-2], flags, ctx) n.sons[L - 2] = semGenericStmt(c, n.sons[L-2], flags, ctx)
@ -279,27 +320,27 @@ proc semGenericStmt(c: PContext, n: PNode,
addTempDecl(c, n.sons[i], skForVar) addTempDecl(c, n.sons[i], skForVar)
n.sons[L - 1] = semGenericStmt(c, n.sons[L-1], flags, ctx) n.sons[L - 1] = semGenericStmt(c, n.sons[L-1], flags, ctx)
closeScope(c) closeScope(c)
of nkBlockStmt, nkBlockExpr, nkBlockType: of nkBlockStmt, nkBlockExpr, nkBlockType:
checkSonsLen(n, 2) checkSonsLen(n, 2)
openScope(c) openScope(c)
if n.sons[0].kind != nkEmpty: if n.sons[0].kind != nkEmpty:
addTempDecl(c, n.sons[0], skLabel) addTempDecl(c, n.sons[0], skLabel)
n.sons[1] = semGenericStmt(c, n.sons[1], flags, ctx) n.sons[1] = semGenericStmt(c, n.sons[1], flags, ctx)
closeScope(c) closeScope(c)
of nkTryStmt: of nkTryStmt:
checkMinSonsLen(n, 2) checkMinSonsLen(n, 2)
n.sons[0] = semGenericStmtScope(c, n.sons[0], flags, ctx) n.sons[0] = semGenericStmtScope(c, n.sons[0], flags, ctx)
for i in countup(1, sonsLen(n)-1): for i in countup(1, sonsLen(n)-1):
var a = n.sons[i] var a = n.sons[i]
checkMinSonsLen(a, 1) checkMinSonsLen(a, 1)
var L = sonsLen(a) var L = sonsLen(a)
for j in countup(0, L-2): for j in countup(0, L-2):
a.sons[j] = semGenericStmt(c, a.sons[j], flags+{withinTypeDesc}, ctx) a.sons[j] = semGenericStmt(c, a.sons[j], flags+{withinTypeDesc}, ctx)
a.sons[L-1] = semGenericStmtScope(c, a.sons[L-1], flags, ctx) a.sons[L-1] = semGenericStmtScope(c, a.sons[L-1], flags, ctx)
of nkVarSection, nkLetSection: of nkVarSection, nkLetSection:
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 a.kind == nkCommentStmt: continue if a.kind == nkCommentStmt: continue
if (a.kind != nkIdentDefs) and (a.kind != nkVarTuple): illFormedAst(a) if (a.kind != nkIdentDefs) and (a.kind != nkVarTuple): illFormedAst(a)
checkMinSonsLen(a, 3) checkMinSonsLen(a, 3)
var L = sonsLen(a) var L = sonsLen(a)
@ -307,79 +348,79 @@ proc semGenericStmt(c: PContext, n: PNode,
a.sons[L-1] = semGenericStmt(c, a.sons[L-1], flags, ctx) a.sons[L-1] = semGenericStmt(c, a.sons[L-1], flags, ctx)
for j in countup(0, L-3): for j in countup(0, L-3):
addTempDecl(c, getIdentNode(a.sons[j]), skVar) addTempDecl(c, getIdentNode(a.sons[j]), skVar)
of nkGenericParams: of nkGenericParams:
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 (a.kind != nkIdentDefs): illFormedAst(a) if (a.kind != nkIdentDefs): illFormedAst(a)
checkMinSonsLen(a, 3) checkMinSonsLen(a, 3)
var L = sonsLen(a) var L = sonsLen(a)
a.sons[L-2] = semGenericStmt(c, a.sons[L-2], flags+{withinTypeDesc}, ctx) a.sons[L-2] = semGenericStmt(c, a.sons[L-2], flags+{withinTypeDesc}, ctx)
# do not perform symbol lookup for default expressions # do not perform symbol lookup for default expressions
for j in countup(0, L-3): for j in countup(0, L-3):
addTempDecl(c, getIdentNode(a.sons[j]), skType) addTempDecl(c, getIdentNode(a.sons[j]), skType)
of nkConstSection: of nkConstSection:
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 a.kind == nkCommentStmt: continue if a.kind == nkCommentStmt: continue
if (a.kind != nkConstDef): illFormedAst(a) if (a.kind != nkConstDef): illFormedAst(a)
checkSonsLen(a, 3) checkSonsLen(a, 3)
addTempDecl(c, getIdentNode(a.sons[0]), skConst) addTempDecl(c, getIdentNode(a.sons[0]), skConst)
a.sons[1] = semGenericStmt(c, a.sons[1], flags+{withinTypeDesc}, ctx) a.sons[1] = semGenericStmt(c, a.sons[1], flags+{withinTypeDesc}, ctx)
a.sons[2] = semGenericStmt(c, a.sons[2], flags, ctx) a.sons[2] = semGenericStmt(c, a.sons[2], flags, ctx)
of nkTypeSection: of nkTypeSection:
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 a.kind == nkCommentStmt: continue if a.kind == nkCommentStmt: continue
if (a.kind != nkTypeDef): illFormedAst(a) if (a.kind != nkTypeDef): illFormedAst(a)
checkSonsLen(a, 3) checkSonsLen(a, 3)
addTempDecl(c, getIdentNode(a.sons[0]), skType) addTempDecl(c, getIdentNode(a.sons[0]), skType)
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 a.kind == nkCommentStmt: continue if a.kind == nkCommentStmt: continue
if (a.kind != nkTypeDef): illFormedAst(a) if (a.kind != nkTypeDef): illFormedAst(a)
checkSonsLen(a, 3) checkSonsLen(a, 3)
if a.sons[1].kind != nkEmpty: if a.sons[1].kind != nkEmpty:
openScope(c) openScope(c)
a.sons[1] = semGenericStmt(c, a.sons[1], flags, ctx) a.sons[1] = semGenericStmt(c, a.sons[1], flags, ctx)
a.sons[2] = semGenericStmt(c, a.sons[2], flags+{withinTypeDesc}, ctx) a.sons[2] = semGenericStmt(c, a.sons[2], flags+{withinTypeDesc}, ctx)
closeScope(c) closeScope(c)
else: else:
a.sons[2] = semGenericStmt(c, a.sons[2], flags+{withinTypeDesc}, ctx) a.sons[2] = semGenericStmt(c, a.sons[2], flags+{withinTypeDesc}, ctx)
of nkEnumTy: of nkEnumTy:
if n.sonsLen > 0: if n.sonsLen > 0:
if n.sons[0].kind != nkEmpty: if n.sons[0].kind != nkEmpty:
n.sons[0] = semGenericStmt(c, n.sons[0], flags+{withinTypeDesc}, ctx) n.sons[0] = semGenericStmt(c, n.sons[0], flags+{withinTypeDesc}, ctx)
for i in countup(1, sonsLen(n) - 1): for i in countup(1, sonsLen(n) - 1):
var a: PNode var a: PNode
case n.sons[i].kind case n.sons[i].kind
of nkEnumFieldDef: a = n.sons[i].sons[0] of nkEnumFieldDef: a = n.sons[i].sons[0]
of nkIdent: a = n.sons[i] of nkIdent: a = n.sons[i]
else: illFormedAst(n) else: illFormedAst(n)
addDecl(c, newSymS(skUnknown, getIdentNode(a.sons[i]), c)) addDecl(c, newSymS(skUnknown, getIdentNode(a), c))
of nkObjectTy, nkTupleTy, nkTupleClassTy: of nkObjectTy, nkTupleTy, nkTupleClassTy:
discard discard
of nkFormalParams: of nkFormalParams:
checkMinSonsLen(n, 1) checkMinSonsLen(n, 1)
if n.sons[0].kind != nkEmpty: if n.sons[0].kind != nkEmpty:
n.sons[0] = semGenericStmt(c, n.sons[0], flags+{withinTypeDesc}, ctx) n.sons[0] = semGenericStmt(c, n.sons[0], flags+{withinTypeDesc}, ctx)
for i in countup(1, sonsLen(n) - 1): for i in countup(1, sonsLen(n) - 1):
var a = n.sons[i] var a = n.sons[i]
if (a.kind != nkIdentDefs): illFormedAst(a) if (a.kind != nkIdentDefs): illFormedAst(a)
checkMinSonsLen(a, 3) checkMinSonsLen(a, 3)
var L = sonsLen(a) var L = sonsLen(a)
a.sons[L-2] = semGenericStmt(c, a.sons[L-2], flags+{withinTypeDesc}, ctx) a.sons[L-2] = semGenericStmt(c, a.sons[L-2], flags+{withinTypeDesc}, ctx)
a.sons[L-1] = semGenericStmt(c, a.sons[L-1], flags, ctx) a.sons[L-1] = semGenericStmt(c, a.sons[L-1], flags, ctx)
for j in countup(0, L-3): for j in countup(0, L-3):
addTempDecl(c, getIdentNode(a.sons[j]), skParam) addTempDecl(c, getIdentNode(a.sons[j]), skParam)
of nkProcDef, nkMethodDef, nkConverterDef, nkMacroDef, nkTemplateDef, of nkProcDef, nkMethodDef, nkConverterDef, nkMacroDef, nkTemplateDef,
nkIteratorDef, nkLambdaKinds: nkIteratorDef, nkLambdaKinds:
checkSonsLen(n, bodyPos + 1) checkSonsLen(n, bodyPos + 1)
if n.sons[namePos].kind != nkEmpty: if n.sons[namePos].kind != nkEmpty:
addTempDecl(c, getIdentNode(n.sons[0]), skProc) addTempDecl(c, getIdentNode(n.sons[0]), skProc)
openScope(c) openScope(c)
n.sons[genericParamsPos] = semGenericStmt(c, n.sons[genericParamsPos], n.sons[genericParamsPos] = semGenericStmt(c, n.sons[genericParamsPos],
flags, ctx) flags, ctx)
if n.sons[paramsPos].kind != nkEmpty: if n.sons[paramsPos].kind != nkEmpty:
if n.sons[paramsPos].sons[0].kind != nkEmpty: if n.sons[paramsPos].sons[0].kind != nkEmpty:
addPrelimDecl(c, newSym(skUnknown, getIdent("result"), nil, n.info)) addPrelimDecl(c, newSym(skUnknown, getIdent("result"), nil, n.info))
n.sons[paramsPos] = semGenericStmt(c, n.sons[paramsPos], flags, ctx) n.sons[paramsPos] = semGenericStmt(c, n.sons[paramsPos], flags, ctx)
@ -394,7 +435,7 @@ proc semGenericStmt(c: PContext, n: PNode,
checkMinSonsLen(n, 2) checkMinSonsLen(n, 2)
result.sons[1] = semGenericStmt(c, n.sons[1], flags, ctx) result.sons[1] = semGenericStmt(c, n.sons[1], flags, ctx)
else: else:
for i in countup(0, sonsLen(n) - 1): for i in countup(0, sonsLen(n) - 1):
result.sons[i] = semGenericStmt(c, n.sons[i], flags, ctx) result.sons[i] = semGenericStmt(c, n.sons[i], flags, ctx)
proc semGenericStmt(c: PContext, n: PNode): PNode = proc semGenericStmt(c: PContext, n: PNode): PNode =

View file

@ -10,14 +10,10 @@
# This module implements the instantiation of generic procs. # This module implements the instantiation of generic procs.
# included from sem.nim # included from sem.nim
proc instantiateGenericParamList(c: PContext, n: PNode, pt: TIdTable, iterator instantiateGenericParamList(c: PContext, n: PNode, pt: TIdTable): PSym =
entry: var TInstantiation) = internalAssert n.kind == nkGenericParams
if n.kind != nkGenericParams:
internalError(n.info, "instantiateGenericParamList; no generic params")
newSeq(entry.concreteTypes, n.len)
for i, a in n.pairs: for i, a in n.pairs:
if a.kind != nkSym: internalAssert a.kind == nkSym
internalError(a.info, "instantiateGenericParamList; no symbol")
var q = a.sym var q = a.sym
if q.typ.kind notin {tyTypeDesc, tyGenericParam, tyStatic, tyIter}+tyTypeClasses: if q.typ.kind notin {tyTypeDesc, tyGenericParam, tyStatic, tyIter}+tyTypeClasses:
continue continue
@ -42,8 +38,7 @@ proc instantiateGenericParamList(c: PContext, n: PNode, pt: TIdTable,
#t = ReplaceTypeVarsT(cl, t) #t = ReplaceTypeVarsT(cl, t)
s.typ = t s.typ = t
if t.kind == tyStatic: s.ast = t.n if t.kind == tyStatic: s.ast = t.n
addDecl(c, s) yield s
entry.concreteTypes[i] = t
proc sameInstantiation(a, b: TInstantiation): bool = proc sameInstantiation(a, b: TInstantiation): bool =
if a.concreteTypes.len == b.concreteTypes.len: if a.concreteTypes.len == b.concreteTypes.len:
@ -52,10 +47,11 @@ proc sameInstantiation(a, b: TInstantiation): bool =
flags = {ExactTypeDescValues}): return flags = {ExactTypeDescValues}): return
result = true result = true
proc genericCacheGet(genericSym: PSym, entry: TInstantiation): PSym = proc genericCacheGet(genericSym: PSym, entry: TInstantiation;
id: CompilesId): PSym =
if genericSym.procInstCache != nil: if genericSym.procInstCache != nil:
for inst in genericSym.procInstCache: for inst in genericSym.procInstCache:
if sameInstantiation(entry, inst[]): if inst.compilesId == id and sameInstantiation(entry, inst[]):
return inst.sym return inst.sym
proc removeDefaultParamValues(n: PNode) = proc removeDefaultParamValues(n: PNode) =
@ -169,7 +165,7 @@ proc instantiateProcType(c: PContext, pt: TIdTable,
addDecl(c, prc) addDecl(c, prc)
pushInfoContext(info) pushInfoContext(info)
var cl = initTypeVars(c, pt, info) var cl = initTypeVars(c, pt, info, nil)
var result = instCopyType(cl, prc.typ) var result = instCopyType(cl, prc.typ)
let originalParams = result.n let originalParams = result.n
result.n = originalParams.shallowCopy result.n = originalParams.shallowCopy
@ -217,7 +213,7 @@ proc generateInstance(c: PContext, fn: PSym, pt: TIdTable,
## The `pt` parameter is a type-unsafe mapping table used to link generic ## The `pt` parameter is a type-unsafe mapping table used to link generic
## parameters to their concrete types within the generic instance. ## parameters to their concrete types within the generic instance.
# no need to instantiate generic templates/macros: # no need to instantiate generic templates/macros:
if fn.kind in {skTemplate, skMacro}: return fn internalAssert fn.kind notin {skMacro, skTemplate}
# generates an instantiated proc # generates an instantiated proc
if c.instCounter > 1000: internalError(fn.ast.info, "nesting too deep") if c.instCounter > 1000: internalError(fn.ast.info, "nesting too deep")
inc(c.instCounter) inc(c.instCounter)
@ -226,30 +222,49 @@ proc generateInstance(c: PContext, fn: PSym, pt: TIdTable,
# NOTE: for access of private fields within generics from a different module # NOTE: for access of private fields within generics from a different module
# we set the friend module: # we set the friend module:
c.friendModules.add(getModule(fn)) c.friendModules.add(getModule(fn))
#let oldScope = c.currentScope let oldInTypeClass = c.inTypeClass
#c.currentScope = fn.scope c.inTypeClass = 0
let oldScope = c.currentScope
while not isTopLevel(c): c.currentScope = c.currentScope.parent
result = copySym(fn, false) result = copySym(fn, false)
incl(result.flags, sfFromGeneric) incl(result.flags, sfFromGeneric)
result.owner = fn result.owner = fn
result.ast = n result.ast = n
pushOwner(result) pushOwner(result)
openScope(c) openScope(c)
internalAssert n.sons[genericParamsPos].kind != nkEmpty let gp = n.sons[genericParamsPos]
internalAssert gp.kind != nkEmpty
n.sons[namePos] = newSymNode(result) n.sons[namePos] = newSymNode(result)
pushInfoContext(info) pushInfoContext(info)
var entry = TInstantiation.new var entry = TInstantiation.new
entry.sym = result entry.sym = result
instantiateGenericParamList(c, n.sons[genericParamsPos], pt, entry[]) # we need to compare both the generic types and the concrete types:
# generic[void](), generic[int]()
# see ttypeor.nim test.
var i = 0
newSeq(entry.concreteTypes, fn.typ.len+gp.len-1)
for s in instantiateGenericParamList(c, gp, pt):
addDecl(c, s)
entry.concreteTypes[i] = s.typ
inc i
pushProcCon(c, result) pushProcCon(c, result)
instantiateProcType(c, pt, result, info) instantiateProcType(c, pt, result, info)
for j in 1 .. result.typ.len-1:
entry.concreteTypes[i] = result.typ.sons[j]
inc i
if tfTriggersCompileTime in result.typ.flags:
incl(result.flags, sfCompileTime)
n.sons[genericParamsPos] = ast.emptyNode n.sons[genericParamsPos] = ast.emptyNode
var oldPrc = genericCacheGet(fn, entry[]) var oldPrc = genericCacheGet(fn, entry[], c.compilesContextId)
if oldPrc == nil: if oldPrc == nil:
# we MUST not add potentially wrong instantiations to the caching mechanism. # we MUST not add potentially wrong instantiations to the caching mechanism.
# This means recursive instantiations behave differently when in # This means recursive instantiations behave differently when in
# a ``compiles`` context but this is the lesser evil. See # a ``compiles`` context but this is the lesser evil. See
# bug #1055 (tevilcompiles). # bug #1055 (tevilcompiles).
if c.inCompilesContext == 0: fn.procInstCache.safeAdd(entry) #if c.compilesContextId == 0:
entry.compilesId = c.compilesContextId
fn.procInstCache.safeAdd(entry)
c.generics.add(makeInstPair(fn, entry)) c.generics.add(makeInstPair(fn, entry))
if n.sons[pragmasPos].kind != nkEmpty: if n.sons[pragmasPos].kind != nkEmpty:
pragma(c, result, n.sons[pragmasPos], allRoutinePragmas) pragma(c, result, n.sons[pragmasPos], allRoutinePragmas)
@ -264,7 +279,8 @@ proc generateInstance(c: PContext, fn: PSym, pt: TIdTable,
popInfoContext() popInfoContext()
closeScope(c) # close scope for parameters closeScope(c) # close scope for parameters
popOwner() popOwner()
#c.currentScope = oldScope c.currentScope = oldScope
discard c.friendModules.pop() discard c.friendModules.pop()
dec(c.instCounter) dec(c.instCounter)
c.inTypeClass = oldInTypeClass
if result.kind == skMethod: finishMethod(c, result) if result.kind == skMethod: finishMethod(c, result)

View file

@ -38,14 +38,16 @@ proc annotateType*(n: PNode, t: PType) =
# Note: x can be unequal to t and we need to be careful to use 't' # Note: x can be unequal to t and we need to be careful to use 't'
# to not to skip tyGenericInst # to not to skip tyGenericInst
case n.kind case n.kind
of nkObjConstr:
n.typ = t
for i in 1 .. <n.len:
let field = x.n.ithField(i - 1)
if field.isNil: globalError n.info, "invalid field at index " & $i
else:
internalAssert(n.sons[i].kind == nkExprColonExpr)
annotateType(n.sons[i].sons[1], field.typ)
of nkPar: of nkPar:
if x.kind == tyObject: if x.kind == tyTuple:
n.typ = t
for i in 0 .. <n.len:
let field = x.n.ithField(i)
if field.isNil: globalError n.info, "invalid field at index " & $i
else: annotateType(n.sons[i], field.typ)
elif x.kind == tyTuple:
n.typ = t n.typ = t
for i in 0 .. <n.len: for i in 0 .. <n.len:
if i >= x.len: globalError n.info, "invalid field at index " & $i if i >= x.len: globalError n.info, "invalid field at index " & $i
@ -53,7 +55,7 @@ proc annotateType*(n: PNode, t: PType) =
elif x.kind == tyProc and x.callConv == ccClosure: elif x.kind == tyProc and x.callConv == ccClosure:
n.typ = t n.typ = t
else: else:
globalError(n.info, "() must have an object or tuple type") globalError(n.info, "() must have a tuple type")
of nkBracket: of nkBracket:
if x.kind in {tyArrayConstr, tyArray, tySequence, tyOpenArray}: if x.kind in {tyArrayConstr, tyArray, tySequence, tyOpenArray}:
n.typ = t n.typ = t

View file

@ -10,10 +10,12 @@
# This include file implements the semantic checking for magics. # This include file implements the semantic checking for magics.
# included from sem.nim # included from sem.nim
proc semAddr(c: PContext; n: PNode): PNode = proc semAddr(c: PContext; n: PNode; isUnsafeAddr=false): PNode =
result = newNodeI(nkAddr, n.info) result = newNodeI(nkAddr, n.info)
let x = semExprWithType(c, n) let x = semExprWithType(c, n)
if isAssignable(c, x) notin {arLValue, arLocalLValue}: if x.kind == nkSym:
x.sym.flags.incl(sfAddrTaken)
if isAssignable(c, x, isUnsafeAddr) notin {arLValue, arLocalLValue}:
localError(n.info, errExprHasNoAddress) localError(n.info, errExprHasNoAddress)
result.add x result.add x
result.typ = makePtrType(c, x.typ) result.typ = makePtrType(c, x.typ)
@ -24,6 +26,41 @@ proc semTypeOf(c: PContext; n: PNode): PNode =
result.add typExpr result.add typExpr
result.typ = makeTypeDesc(c, typExpr.typ.skipTypes({tyTypeDesc, tyIter})) result.typ = makeTypeDesc(c, typExpr.typ.skipTypes({tyTypeDesc, tyIter}))
type
SemAsgnMode = enum asgnNormal, noOverloadedSubscript, noOverloadedAsgn
proc semAsgn(c: PContext, n: PNode; mode=asgnNormal): PNode
proc semSubscript(c: PContext, n: PNode, flags: TExprFlags): PNode
proc skipAddr(n: PNode): PNode {.inline.} =
(if n.kind == nkHiddenAddr: n.sons[0] else: n)
proc semArrGet(c: PContext; n: PNode; flags: TExprFlags): PNode =
result = newNodeI(nkBracketExpr, n.info)
for i in 1..<n.len: result.add(n[i])
let oldBracketExpr = c.p.bracketExpr
result = semSubscript(c, result, flags)
c.p.bracketExpr = oldBracketExpr
if result.isNil:
localError(n.info, "could not resolve: " & $n)
result = n
proc semArrPut(c: PContext; n: PNode; flags: TExprFlags): PNode =
# rewrite `[]=`(a, i, x) back to ``a[i] = x``.
let b = newNodeI(nkBracketExpr, n.info)
b.add(n[1].skipAddr)
for i in 2..n.len-2: b.add(n[i])
result = newNodeI(nkAsgn, n.info, 2)
result.sons[0] = b
result.sons[1] = n.lastSon
result = semAsgn(c, result, noOverloadedSubscript)
proc semAsgnOpr(c: PContext; n: PNode): PNode =
result = newNodeI(nkAsgn, n.info, 2)
result.sons[0] = n[1]
result.sons[1] = n[2]
result = semAsgn(c, result, noOverloadedAsgn)
proc semIsPartOf(c: PContext, n: PNode, flags: TExprFlags): PNode = proc semIsPartOf(c: PContext, n: PNode, flags: TExprFlags): PNode =
var r = isPartOf(n[1], n[2]) var r = isPartOf(n[1], n[2])
result = newIntNodeT(ord(r), n) result = newIntNodeT(ord(r), n)
@ -119,10 +156,13 @@ proc magicsAfterOverloadResolution(c: PContext, n: PNode,
case n[0].sym.magic case n[0].sym.magic
of mAddr: of mAddr:
checkSonsLen(n, 2) checkSonsLen(n, 2)
result = semAddr(c, n.sons[1]) result = semAddr(c, n.sons[1], n[0].sym.name.s == "unsafeAddr")
of mTypeOf: of mTypeOf:
checkSonsLen(n, 2) checkSonsLen(n, 2)
result = semTypeOf(c, n.sons[1]) result = semTypeOf(c, n.sons[1])
of mArrGet: result = semArrGet(c, n, flags)
of mArrPut: result = semArrPut(c, n, flags)
of mAsgn: result = semAsgnOpr(c, n)
of mIsPartOf: result = semIsPartOf(c, n, flags) of mIsPartOf: result = semIsPartOf(c, n, flags)
of mTypeTrait: result = semTypeTraits(c, n) of mTypeTrait: result = semTypeTraits(c, n)
of mAstToStr: of mAstToStr:
@ -143,25 +183,28 @@ proc magicsAfterOverloadResolution(c: PContext, n: PNode,
if isNegative(n.sons[1]) or (n.len > 2 and isNegative(n.sons[2])): if isNegative(n.sons[1]) or (n.len > 2 and isNegative(n.sons[2])):
localError(n.info, "use '^' instead of '-'; negative indexing is obsolete") localError(n.info, "use '^' instead of '-'; negative indexing is obsolete")
of mRoof: of mRoof:
# error correction: let bracketExpr = if n.len == 3: n.sons[2] else: c.p.bracketExpr
result = n.sons[1] if bracketExpr.isNil:
if c.p.bracketExpr.isNil:
localError(n.info, "no surrounding array access context for '^'") localError(n.info, "no surrounding array access context for '^'")
elif c.p.bracketExpr.checkForSideEffects != seNoSideEffect: result = n.sons[1]
elif bracketExpr.checkForSideEffects != seNoSideEffect:
localError(n.info, "invalid context for '^' as '$#' has side effects" % localError(n.info, "invalid context for '^' as '$#' has side effects" %
renderTree(c.p.bracketExpr)) renderTree(bracketExpr))
elif c.p.bracketExpr.typ.isStrangeArray: result = n.sons[1]
elif bracketExpr.typ.isStrangeArray:
localError(n.info, "invalid context for '^' as len!=high+1 for '$#'" % localError(n.info, "invalid context for '^' as len!=high+1 for '$#'" %
renderTree(c.p.bracketExpr)) renderTree(bracketExpr))
result = n.sons[1]
else: else:
# ^x is rewritten to: len(a)-x # ^x is rewritten to: len(a)-x
let lenExpr = newNodeI(nkCall, n.info) let lenExpr = newNodeI(nkCall, n.info)
lenExpr.add newIdentNode(getIdent"len", n.info) lenExpr.add newIdentNode(getIdent"len", n.info)
lenExpr.add c.p.bracketExpr lenExpr.add bracketExpr
let lenExprB = semExprWithType(c, lenExpr) let lenExprB = semExprWithType(c, lenExpr)
if lenExprB.typ.isNil or not isOrdinalType(lenExprB.typ): if lenExprB.typ.isNil or not isOrdinalType(lenExprB.typ):
localError(n.info, "'$#' has to be of an ordinal type for '^'" % localError(n.info, "'$#' has to be of an ordinal type for '^'" %
renderTree(lenExpr)) renderTree(lenExpr))
result = n.sons[1]
else: else:
result = newNodeIT(nkCall, n.info, getSysType(tyInt)) result = newNodeIT(nkCall, n.info, getSysType(tyInt))
result.add newSymNode(createMagic("-", mSubI), n.info) result.add newSymNode(createMagic("-", mSubI), n.info)

View file

@ -128,10 +128,10 @@ template `?`(x): expr = x.renderTree
proc checkLe(c: AnalysisCtx; a, b: PNode) = proc checkLe(c: AnalysisCtx; a, b: PNode) =
case proveLe(c.guards, a, b) case proveLe(c.guards, a, b)
of impUnknown: of impUnknown:
localError(a.info, "cannot prove: " & ?a & " <= " & ?b) localError(a.info, "cannot prove: " & ?a & " <= " & ?b & " (bounds check)")
of impYes: discard of impYes: discard
of impNo: of impNo:
localError(a.info, "can prove: " & ?a & " > " & ?b) localError(a.info, "can prove: " & ?a & " > " & ?b & " (bounds check)")
proc checkBounds(c: AnalysisCtx; arr, idx: PNode) = proc checkBounds(c: AnalysisCtx; arr, idx: PNode) =
checkLe(c, arr.lowBound, idx) checkLe(c, arr.lowBound, idx)
@ -156,19 +156,23 @@ proc addSlice(c: var AnalysisCtx; n: PNode; x, le, ri: PNode) =
proc overlap(m: TModel; x,y,c,d: PNode) = proc overlap(m: TModel; x,y,c,d: PNode) =
# X..Y and C..D overlap iff (X <= D and C <= Y) # X..Y and C..D overlap iff (X <= D and C <= Y)
case proveLe(m, x, d) case proveLe(m, c, y)
of impUnknown: of impUnknown:
localError(x.info, case proveLe(m, x, d)
"cannot prove: $# > $#; required for ($#)..($#) disjoint from ($#)..($#)" % of impNo: discard
[?x, ?d, ?x, ?y, ?c, ?d]) of impUnknown, impYes:
localError(x.info,
"cannot prove: $# > $#; required for ($#)..($#) disjoint from ($#)..($#)" %
[?c, ?y, ?x, ?y, ?c, ?d])
of impYes: of impYes:
case proveLe(m, c, y) case proveLe(m, x, d)
of impUnknown: of impUnknown:
localError(x.info, localError(x.info,
"cannot prove: $# > $#; required for ($#)..($#) disjoint from ($#)..($#)" % "cannot prove: $# > $#; required for ($#)..($#) disjoint from ($#)..($#)" %
[?c, ?y, ?x, ?y, ?c, ?d]) [?x, ?d, ?x, ?y, ?c, ?d])
of impYes: of impYes:
localError(x.info, "($#)..($#) not disjoint from ($#)..($#)" % [?x, ?y, ?c, ?d]) localError(x.info, "($#)..($#) not disjoint from ($#)..($#)" %
[?c, ?y, ?x, ?y, ?c, ?d])
of impNo: discard of impNo: discard
of impNo: discard of impNo: discard
@ -278,10 +282,12 @@ proc analyseCall(c: var AnalysisCtx; n: PNode; op: PSym) =
slot.stride = min(slot.stride, incr) slot.stride = min(slot.stride, incr)
analyseSons(c, n) analyseSons(c, n)
elif op.name.s == "[]" and op.fromSystem: elif op.name.s == "[]" and op.fromSystem:
c.addSlice(n, n[1], n[2][1], n[2][2]) let slice = n[2].skipStmtList
c.addSlice(n, n[1], slice[1], slice[2])
analyseSons(c, n) analyseSons(c, n)
elif op.name.s == "[]=" and op.fromSystem: elif op.name.s == "[]=" and op.fromSystem:
c.addSlice(n, n[1], n[2][1], n[2][2]) let slice = n[2].skipStmtList
c.addSlice(n, n[1], slice[1], slice[2])
analyseSons(c, n) analyseSons(c, n)
else: else:
analyseSons(c, n) analyseSons(c, n)
@ -363,7 +369,7 @@ proc analyse(c: var AnalysisCtx; n: PNode) =
else: internalError(it.info, "slot already has a lower bound") else: internalError(it.info, "slot already has a lower bound")
if not isSpawned: analyse(c, value) if not isSpawned: analyse(c, value)
of nkCaseStmt: analyseCase(c, n) of nkCaseStmt: analyseCase(c, n)
of nkIfStmt, nkIfExpr: analyseIf(c, n) of nkWhen, nkIfStmt, nkIfExpr: analyseIf(c, n)
of nkWhileStmt: of nkWhileStmt:
analyse(c, n.sons[0]) analyse(c, n.sons[0])
# 'while true' loop? # 'while true' loop?
@ -395,8 +401,9 @@ proc transformSlices(n: PNode): PNode =
result = copyNode(n) result = copyNode(n)
result.add opSlice.newSymNode result.add opSlice.newSymNode
result.add n[1] result.add n[1]
result.add n[2][1] let slice = n[2].skipStmtList
result.add n[2][2] result.add slice[1]
result.add slice[2]
return result return result
if n.safeLen > 0: if n.safeLen > 0:
result = shallowCopy(n) result = shallowCopy(n)

View file

@ -9,7 +9,7 @@
import import
intsets, ast, astalgo, msgs, renderer, magicsys, types, idents, trees, intsets, ast, astalgo, msgs, renderer, magicsys, types, idents, trees,
wordrecg, strutils, options, guards wordrecg, strutils, options, guards, writetracking
# Second semantic checking pass over the AST. Necessary because the old # Second semantic checking pass over the AST. Necessary because the old
# way had some inherent problems. Performs: # way had some inherent problems. Performs:
@ -17,7 +17,7 @@ import
# * effect+exception tracking # * effect+exception tracking
# * "usage before definition" checking # * "usage before definition" checking
# * checks for invalid usages of compiletime magics (not implemented) # * checks for invalid usages of compiletime magics (not implemented)
# * checks for invalid usages of PNimNode (not implemented) # * checks for invalid usages of NimNode (not implemented)
# * later: will do an escape analysis for closures at least # * later: will do an escape analysis for closures at least
# Predefined effects: # Predefined effects:
@ -29,21 +29,6 @@ import
# --> a TR macro can annotate the proc with user defined annotations # --> a TR macro can annotate the proc with user defined annotations
# --> the effect system can access these # --> the effect system can access these
# Load&Store analysis is performed on *paths*. A path is an access like
# obj.x.y[i].z; splitting paths up causes some problems:
#
# var x = obj.x
# var z = x.y[i].z
#
# Alias analysis is affected by this too! A good solution is *type splitting*:
# T becomes T1 and T2 if it's known that T1 and T2 can't alias.
#
# An aliasing problem and a race condition are effectively the same problem.
# Type based alias analysis is nice but not sufficient; especially splitting
# an array and filling it in parallel should be supported but is not easily
# done: It essentially requires a built-in 'indexSplit' operation and dependent
# typing.
# ------------------------ exception and tag tracking ------------------------- # ------------------------ exception and tag tracking -------------------------
discard """ discard """
@ -207,9 +192,9 @@ proc markGcUnsafe(a: PEffects; reason: PNode) =
a.owner.gcUnsafetyReason = newSym(skUnknown, getIdent("<unknown>"), a.owner.gcUnsafetyReason = newSym(skUnknown, getIdent("<unknown>"),
a.owner, reason.info) a.owner, reason.info)
proc listGcUnsafety(s: PSym; onlyWarning: bool) = proc listGcUnsafety(s: PSym; onlyWarning: bool; cycleCheck: var IntSet) =
let u = s.gcUnsafetyReason let u = s.gcUnsafetyReason
if u != nil: if u != nil and not cycleCheck.containsOrIncl(u.id):
let msgKind = if onlyWarning: warnGcUnsafe2 else: errGenerated let msgKind = if onlyWarning: warnGcUnsafe2 else: errGenerated
if u.kind in {skLet, skVar}: if u.kind in {skLet, skVar}:
message(s.info, msgKind, message(s.info, msgKind,
@ -218,7 +203,7 @@ proc listGcUnsafety(s: PSym; onlyWarning: bool) =
elif u.kind in routineKinds: elif u.kind in routineKinds:
# recursive call *always* produces only a warning so the full error # recursive call *always* produces only a warning so the full error
# message is printed: # message is printed:
listGcUnsafety(u, true) listGcUnsafety(u, true, cycleCheck)
message(s.info, msgKind, message(s.info, msgKind,
"'$#' is not GC-safe as it calls '$#'" % "'$#' is not GC-safe as it calls '$#'" %
[s.name.s, u.name.s]) [s.name.s, u.name.s])
@ -227,6 +212,10 @@ proc listGcUnsafety(s: PSym; onlyWarning: bool) =
message(u.info, msgKind, message(u.info, msgKind,
"'$#' is not GC-safe as it performs an indirect call here" % s.name.s) "'$#' is not GC-safe as it performs an indirect call here" % s.name.s)
proc listGcUnsafety(s: PSym; onlyWarning: bool) =
var cycleCheck = initIntSet()
listGcUnsafety(s, onlyWarning, cycleCheck)
proc useVar(a: PEffects, n: PNode) = proc useVar(a: PEffects, n: PNode) =
let s = n.sym let s = n.sym
if isLocalVar(a, s): if isLocalVar(a, s):
@ -237,9 +226,10 @@ proc useVar(a: PEffects, n: PNode) =
message(n.info, warnUninit, s.name.s) message(n.info, warnUninit, s.name.s)
# prevent superfluous warnings about the same variable: # prevent superfluous warnings about the same variable:
a.init.add s.id a.init.add s.id
if {sfGlobal, sfThread} * s.flags == {sfGlobal} and s.kind in {skVar, skLet}: if {sfGlobal, sfThread} * s.flags != {} and s.kind in {skVar, skLet}:
if s.guard != nil: guardGlobal(a, n, s.guard) if s.guard != nil: guardGlobal(a, n, s.guard)
if (tfHasGCedMem in s.typ.flags or s.typ.isGCedMem): if {sfGlobal, sfThread} * s.flags == {sfGlobal} and
(tfHasGCedMem in s.typ.flags or s.typ.isGCedMem):
#if warnGcUnsafe in gNotes: warnAboutGcUnsafe(n) #if warnGcUnsafe in gNotes: warnAboutGcUnsafe(n)
markGcUnsafe(a, s) markGcUnsafe(a, s)
@ -433,17 +423,41 @@ proc documentEffect(n, x: PNode, effectType: TSpecialWord, idx: int): PNode =
result = newNode(nkExprColonExpr, n.info, @[ result = newNode(nkExprColonExpr, n.info, @[
newIdentNode(getIdent(specialWords[effectType]), n.info), effects]) newIdentNode(getIdent(specialWords[effectType]), n.info), effects])
proc documentWriteEffect(n: PNode; flag: TSymFlag; pragmaName: string): PNode =
let s = n.sons[namePos].sym
let params = s.typ.n
var effects = newNodeI(nkBracket, n.info)
for i in 1 ..< params.len:
if params[i].kind == nkSym and flag in params[i].sym.flags:
effects.add params[i]
if effects.len > 0:
result = newNode(nkExprColonExpr, n.info, @[
newIdentNode(getIdent(pragmaName), n.info), effects])
proc documentNewEffect(n: PNode): PNode =
let s = n.sons[namePos].sym
if tfReturnsNew in s.typ.flags:
result = newIdentNode(getIdent("new"), n.info)
proc documentRaises*(n: PNode) = proc documentRaises*(n: PNode) =
if n.sons[namePos].kind != nkSym: return if n.sons[namePos].kind != nkSym: return
let pragmas = n.sons[pragmasPos] let pragmas = n.sons[pragmasPos]
let p1 = documentEffect(n, pragmas, wRaises, exceptionEffects) let p1 = documentEffect(n, pragmas, wRaises, exceptionEffects)
let p2 = documentEffect(n, pragmas, wTags, tagEffects) let p2 = documentEffect(n, pragmas, wTags, tagEffects)
let p3 = documentWriteEffect(n, sfWrittenTo, "writes")
let p4 = documentNewEffect(n)
let p5 = documentWriteEffect(n, sfEscapes, "escapes")
if p1 != nil or p2 != nil: if p1 != nil or p2 != nil or p3 != nil or p4 != nil or p5 != nil:
if pragmas.kind == nkEmpty: if pragmas.kind == nkEmpty:
n.sons[pragmasPos] = newNodeI(nkPragma, n.info) n.sons[pragmasPos] = newNodeI(nkPragma, n.info)
if p1 != nil: n.sons[pragmasPos].add p1 if p1 != nil: n.sons[pragmasPos].add p1
if p2 != nil: n.sons[pragmasPos].add p2 if p2 != nil: n.sons[pragmasPos].add p2
if p3 != nil: n.sons[pragmasPos].add p3
if p4 != nil: n.sons[pragmasPos].add p4
if p5 != nil: n.sons[pragmasPos].add p5
template notGcSafe(t): expr = {tfGcSafe, tfNoSideEffect} * t.flags == {} template notGcSafe(t): expr = {tfGcSafe, tfNoSideEffect} * t.flags == {}
@ -721,7 +735,7 @@ proc track(tracked: PEffects, n: PNode) =
# since 'var (a, b): T = ()' is not even allowed, there is always type # since 'var (a, b): T = ()' is not even allowed, there is always type
# inference for (a, b) and thus no nil checking is necessary. # inference for (a, b) and thus no nil checking is necessary.
of nkCaseStmt: trackCase(tracked, n) of nkCaseStmt: trackCase(tracked, n)
of nkIfStmt, nkIfExpr: trackIf(tracked, n) of nkWhen, nkIfStmt, nkIfExpr: trackIf(tracked, n)
of nkBlockStmt, nkBlockExpr: trackBlock(tracked, n.sons[1]) of nkBlockStmt, nkBlockExpr: trackBlock(tracked, n.sons[1])
of nkWhileStmt: of nkWhileStmt:
track(tracked, n.sons[0]) track(tracked, n.sons[0])
@ -748,7 +762,7 @@ proc track(tracked: PEffects, n: PNode) =
for i in 1 .. <len(n): for i in 1 .. <len(n):
let x = n.sons[i] let x = n.sons[i]
track(tracked, x) track(tracked, x)
if sfDiscriminant in x.sons[0].sym.flags: if x.sons[0].kind == nkSym and sfDiscriminant in x.sons[0].sym.flags:
addDiscriminantFact(tracked.guards, x) addDiscriminantFact(tracked.guards, x)
setLen(tracked.guards, oldFacts) setLen(tracked.guards, oldFacts)
of nkPragmaBlock: of nkPragmaBlock:
@ -841,6 +855,8 @@ proc initEffects(effects: PNode; s: PSym; t: var TEffects) =
newSeq(effects.sons, effectListLen) newSeq(effects.sons, effectListLen)
effects.sons[exceptionEffects] = newNodeI(nkArgList, s.info) effects.sons[exceptionEffects] = newNodeI(nkArgList, s.info)
effects.sons[tagEffects] = newNodeI(nkArgList, s.info) effects.sons[tagEffects] = newNodeI(nkArgList, s.info)
effects.sons[usesEffects] = ast.emptyNode
effects.sons[writeEffects] = ast.emptyNode
t.exc = effects.sons[exceptionEffects] t.exc = effects.sons[exceptionEffects]
t.tags = effects.sons[tagEffects] t.tags = effects.sons[tagEffects]
@ -895,6 +911,7 @@ proc trackProc*(s: PSym, body: PNode) =
message(s.info, warnLockLevel, message(s.info, warnLockLevel,
"declared lock level is $1, but real lock level is $2" % "declared lock level is $1, but real lock level is $2" %
[$s.typ.lockLevel, $t.maxLockLevel]) [$s.typ.lockLevel, $t.maxLockLevel])
when useWriteTracking: trackWrites(s, body)
proc trackTopLevelStmt*(module: PSym; n: PNode) = proc trackTopLevelStmt*(module: PSym; n: PNode) =
if n.kind in {nkPragma, nkMacroDef, nkTemplateDef, nkProcDef, if n.kind in {nkPragma, nkMacroDef, nkTemplateDef, nkProcDef,

View file

@ -369,6 +369,15 @@ proc addToVarSection(c: PContext; result: var PNode; orig, identDefs: PNode) =
else: else:
result.add identDefs result.add identDefs
proc addDefer(c: PContext; result: var PNode; s: PSym) =
let deferDestructorCall = createDestructorCall(c, s)
if deferDestructorCall != nil:
if result.kind != nkStmtList:
let oldResult = result
result = newNodeI(nkStmtList, result.info)
result.add oldResult
result.add deferDestructorCall
proc isDiscardUnderscore(v: PSym): bool = proc isDiscardUnderscore(v: PSym): bool =
if v.name.s == "_": if v.name.s == "_":
v.flags.incl(sfGenSym) v.flags.incl(sfGenSym)
@ -469,6 +478,7 @@ proc semVarOrLet(c: PContext, n: PNode, symkind: TSymKind): PNode =
if def.kind == nkPar: v.ast = def[j] if def.kind == nkPar: v.ast = def[j]
v.typ = tup.sons[j] v.typ = tup.sons[j]
b.sons[j] = newSymNode(v) b.sons[j] = newSymNode(v)
addDefer(c, result, v)
checkNilable(v) checkNilable(v)
if sfCompileTime in v.flags: hasCompileTime = true if sfCompileTime in v.flags: hasCompileTime = true
if hasCompileTime: vm.setupCompileTimeVar(c.module, result) if hasCompileTime: vm.setupCompileTimeVar(c.module, result)
@ -744,13 +754,62 @@ proc typeSectionFinalPass(c: PContext, n: PNode) =
if s.typ.kind in {tyObject, tyTuple} and not s.typ.n.isNil: if s.typ.kind in {tyObject, tyTuple} and not s.typ.n.isNil:
checkForMetaFields(s.typ.n) checkForMetaFields(s.typ.n)
proc semAllTypeSections(c: PContext; n: PNode): PNode =
proc gatherStmts(c: PContext; n: PNode; result: PNode) {.nimcall.} =
case n.kind
of nkIncludeStmt:
for i in 0..<n.len:
var f = checkModuleName(n.sons[i])
if f != InvalidFileIDX:
if containsOrIncl(c.includedFiles, f):
localError(n.info, errRecursiveDependencyX, f.toFilename)
else:
let code = gIncludeFile(c.module, f)
gatherStmts c, code, result
excl(c.includedFiles, f)
of nkStmtList:
for i in 0 ..< n.len:
gatherStmts(c, n.sons[i], result)
of nkTypeSection:
incl n.flags, nfSem
typeSectionLeftSidePass(c, n)
result.add n
else:
result.add n
result = newNodeI(nkStmtList, n.info)
gatherStmts(c, n, result)
template rec(name) =
for i in 0 ..< result.len:
if result[i].kind == nkTypeSection:
name(c, result[i])
rec typeSectionRightSidePass
rec typeSectionFinalPass
when false:
# too beautiful to delete:
template rec(name; setbit=false) =
proc `name rec`(c: PContext; n: PNode) {.nimcall.} =
if n.kind == nkTypeSection:
when setbit: incl n.flags, nfSem
name(c, n)
elif n.kind == nkStmtList:
for i in 0 ..< n.len:
`name rec`(c, n.sons[i])
`name rec`(c, n)
rec typeSectionLeftSidePass, true
rec typeSectionRightSidePass
rec typeSectionFinalPass
proc semTypeSection(c: PContext, n: PNode): PNode = proc semTypeSection(c: PContext, n: PNode): PNode =
## Processes a type section. This must be done in separate passes, in order ## Processes a type section. This must be done in separate passes, in order
## to allow the type definitions in the section to reference each other ## to allow the type definitions in the section to reference each other
## without regard for the order of their definitions. ## without regard for the order of their definitions.
typeSectionLeftSidePass(c, n) if sfNoForward notin c.module.flags or nfSem notin n.flags:
typeSectionRightSidePass(c, n) typeSectionLeftSidePass(c, n)
typeSectionFinalPass(c, n) typeSectionRightSidePass(c, n)
typeSectionFinalPass(c, n)
result = n result = n
proc semParamList(c: PContext, n, genericParams: PNode, s: PSym) = proc semParamList(c: PContext, n, genericParams: PNode, s: PSym) =
@ -898,27 +957,35 @@ proc semDo(c: PContext, n: PNode, flags: TExprFlags): PNode =
proc semInferredLambda(c: PContext, pt: TIdTable, n: PNode): PNode = proc semInferredLambda(c: PContext, pt: TIdTable, n: PNode): PNode =
var n = n var n = n
n = replaceTypesInBody(c, pt, n) let original = n.sons[namePos].sym
let s = copySym(original, false)
incl(s.flags, sfFromGeneric)
n = replaceTypesInBody(c, pt, n, original)
result = n result = n
s.ast = result
n.sons[namePos].sym = s
n.sons[genericParamsPos] = emptyNode n.sons[genericParamsPos] = emptyNode
n.sons[paramsPos] = n.typ.n let params = n.typ.n
n.sons[paramsPos] = params
s.typ = n.typ
for i in 1..<params.len:
if params[i].typ.kind in {tyTypeDesc, tyGenericParam,
tyFromExpr, tyFieldAccessor}+tyTypeClasses:
localError(params[i].info, "cannot infer type of parameter: " &
params[i].sym.name.s)
openScope(c) openScope(c)
var s = n.sons[namePos].sym
pushOwner(s) pushOwner(s)
addParams(c, n.typ.n, skProc) addParams(c, params, skProc)
pushProcCon(c, s) pushProcCon(c, s)
addResult(c, n.typ.sons[0], n.info, skProc) addResult(c, n.typ.sons[0], n.info, skProc)
addResultNode(c, n) addResultNode(c, n)
let semBody = hloBody(c, semProcBody(c, n.sons[bodyPos])) let semBody = hloBody(c, semProcBody(c, n.sons[bodyPos]))
n.sons[bodyPos] = transformBody(c.module, semBody, n.sons[namePos].sym) n.sons[bodyPos] = transformBody(c.module, semBody, s)
popProcCon(c) popProcCon(c)
popOwner() popOwner()
closeScope(c) closeScope(c)
s.ast = result
# alternative variant (not quite working): # alternative variant (not quite working):
# var prc = arg[0].sym # var prc = arg[0].sym
# let inferred = c.semGenerateInstance(c, prc, m.bindings, arg.info) # let inferred = c.semGenerateInstance(c, prc, m.bindings, arg.info)
@ -974,6 +1041,7 @@ proc semOverride(c: PContext, s: PSym, n: PNode) =
"signature for 'deepCopy' must be proc[T: ptr|ref](x: T): T") "signature for 'deepCopy' must be proc[T: ptr|ref](x: T): T")
incl(s.flags, sfUsed) incl(s.flags, sfUsed)
of "=": of "=":
if s.magic == mAsgn: return
incl(s.flags, sfUsed) incl(s.flags, sfUsed)
let t = s.typ let t = s.typ
if t.len == 3 and t.sons[0] == nil and t.sons[1].kind == tyVar: if t.len == 3 and t.sons[0] == nil and t.sons[1].kind == tyVar:
@ -1033,12 +1101,13 @@ proc semProcAux(c: PContext, n: PNode, kind: TSymKind,
n.sons[namePos] = newSymNode(s) n.sons[namePos] = newSymNode(s)
s.ast = n s.ast = n
#s.scope = c.currentScope #s.scope = c.currentScope
when false:
if sfNoForward in c.module.flags and # disable for now
sfSystemModule notin c.module.flags: if sfNoForward in c.module.flags and
addInterfaceOverloadableSymAt(c, c.currentScope, s) sfSystemModule notin c.module.flags:
s.flags.incl sfForward addInterfaceOverloadableSymAt(c, c.currentScope, s)
return s.flags.incl sfForward
return
else: else:
s = n[namePos].sym s = n[namePos].sym
s.owner = getCurrOwner() s.owner = getCurrOwner()
@ -1071,6 +1140,7 @@ proc semProcAux(c: PContext, n: PNode, kind: TSymKind,
# semParamList(c, n.sons[ParamsPos], nil, s) # semParamList(c, n.sons[ParamsPos], nil, s)
else: else:
s.typ = newProcType(c, n.info) s.typ = newProcType(c, n.info)
if tfTriggersCompileTime in s.typ.flags: incl(s.flags, sfCompileTime)
if n.sons[patternPos].kind != nkEmpty: if n.sons[patternPos].kind != nkEmpty:
n.sons[patternPos] = semPattern(c, n.sons[patternPos]) n.sons[patternPos] = semPattern(c, n.sons[patternPos])
if s.kind in skIterators: if s.kind in skIterators:
@ -1160,6 +1230,9 @@ proc semProcAux(c: PContext, n: PNode, kind: TSymKind,
if n.sons[patternPos].kind != nkEmpty: if n.sons[patternPos].kind != nkEmpty:
c.patterns.add(s) c.patterns.add(s)
if isAnon: result.typ = s.typ if isAnon: result.typ = s.typ
if isTopLevel(c) and s.kind != skClosureIterator and
s.typ.callConv == ccClosure:
message(s.info, warnDeprecated, "top level '.closure' calling convention")
proc determineType(c: PContext, s: PSym) = proc determineType(c: PContext, s: PSym) =
if s.typ != nil: return if s.typ != nil: return
@ -1268,6 +1341,8 @@ proc semPragmaBlock(c: PContext, n: PNode): PNode =
of wLocks: of wLocks:
result = n result = n
result.typ = n.sons[1].typ result.typ = n.sons[1].typ
of wNoRewrite:
incl(result.flags, nfNoRewrite)
else: discard else: discard
proc semStaticStmt(c: PContext, n: PNode): PNode = proc semStaticStmt(c: PContext, n: PNode): PNode =
@ -1319,7 +1394,7 @@ proc semStmtList(c: PContext, n: PNode, flags: TExprFlags): PNode =
for i in countup(0, length - 1): for i in countup(0, length - 1):
let k = n.sons[i].kind let k = n.sons[i].kind
case k case k
of nkFinally, nkExceptBranch, nkDefer: of nkFinally, nkExceptBranch:
# stand-alone finally and except blocks are # stand-alone finally and except blocks are
# transformed into regular try blocks: # transformed into regular try blocks:
# #
@ -1360,7 +1435,7 @@ proc semStmtList(c: PContext, n: PNode, flags: TExprFlags): PNode =
localError(result.info, "type class predicate failed") localError(result.info, "type class predicate failed")
of tyUnknown: continue of tyUnknown: continue
else: discard else: discard
if n.sons[i].typ == enforceVoidContext or usesResult(n.sons[i]): if n.sons[i].typ == enforceVoidContext: #or usesResult(n.sons[i]):
voidContext = true voidContext = true
n.typ = enforceVoidContext n.typ = enforceVoidContext
if i == last and (length == 1 or efWantValue in flags): if i == last and (length == 1 or efWantValue in flags):
@ -1372,21 +1447,13 @@ 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
case n.sons[i].kind case n.sons[i].kind
of nkVarSection, nkLetSection:
let (outer, inner) = insertDestructors(c, n.sons[i])
if outer != nil:
n.sons[i] = outer
var rest = newNode(nkStmtList, n.info, n.sons[i+1 .. length-1])
inner.addSon(semStmtList(c, rest, flags))
n.sons.setLen(i+1)
return
of LastBlockStmts: of LastBlockStmts:
for j in countup(i + 1, length - 1): for j in countup(i + 1, length - 1):
case n.sons[j].kind case n.sons[j].kind
of nkPragma, nkCommentStmt, nkNilLit, nkEmpty: discard of nkPragma, nkCommentStmt, nkNilLit, nkEmpty: discard
else: localError(n.sons[j].info, errStmtInvalidAfterReturn) else: localError(n.sons[j].info, errStmtInvalidAfterReturn)
else: discard else: discard
if result.len == 1: if result.len == 1 and result.sons[0].kind != nkDefer:
result = result.sons[0] result = result.sons[0]
when defined(nimfix): when defined(nimfix):
if result.kind == nkCommentStmt and not result.comment.isNil and if result.kind == nkCommentStmt and not result.comment.isNil and

View file

@ -110,6 +110,13 @@ type
toBind, toMixin, toInject: IntSet toBind, toMixin, toInject: IntSet
owner: PSym owner: PSym
cursorInBody: bool # only for nimsuggest cursorInBody: bool # only for nimsuggest
bracketExpr: PNode
template withBracketExpr(ctx, x, body: untyped) =
let old = ctx.bracketExpr
ctx.bracketExpr = x
body
ctx.bracketExpr = old
proc getIdentNode(c: var TemplCtx, n: PNode): PNode = proc getIdentNode(c: var TemplCtx, n: PNode): PNode =
case n.kind case n.kind
@ -184,10 +191,25 @@ proc addLocalDecl(c: var TemplCtx, n: var PNode, k: TSymKind) =
else: else:
let ident = getIdentNode(c, n) let ident = getIdentNode(c, n)
if not isTemplParam(c, ident): if not isTemplParam(c, ident):
let local = newGenSym(k, ident, c) # fix #2670, consider:
addPrelimDecl(c.c, local) #
styleCheckDef(n.info, local) # when b:
replaceIdentBySym(n, newSymNode(local, n.info)) # var a = "hi"
# else:
# var a = 5
# echo a
#
# We need to ensure that both 'a' produce the same gensym'ed symbol.
# So we need only check the *current* scope.
let s = localSearchInScope(c.c, considerQuotedIdent ident)
if s != nil and s.owner == c.owner and sfGenSym in s.flags:
styleCheckUse(n.info, s)
replaceIdentBySym(n, newSymNode(s, n.info))
else:
let local = newGenSym(k, ident, c)
addPrelimDecl(c.c, local)
styleCheckDef(n.info, local)
replaceIdentBySym(n, newSymNode(local, n.info))
else: else:
replaceIdentBySym(n, ident) replaceIdentBySym(n, ident)
@ -260,6 +282,53 @@ proc semTemplSomeDecl(c: var TemplCtx, n: PNode, symKind: TSymKind; start=0) =
addLocalDecl(c, a.sons[j], symKind) addLocalDecl(c, a.sons[j], symKind)
proc semPattern(c: PContext, n: PNode): PNode proc semPattern(c: PContext, n: PNode): PNode
proc semTemplBodySons(c: var TemplCtx, n: PNode): PNode =
result = n
for i in 0.. < n.len:
result.sons[i] = semTemplBody(c, n.sons[i])
proc wrapInBind(c: var TemplCtx; n: PNode; opr: string): PNode =
let ident = getIdent(opr)
if ident.id in c.toInject: return n
let s = searchInScopes(c.c, ident)
if s != nil:
var callee: PNode
if contains(c.toBind, s.id):
callee = symChoice(c.c, n, s, scClosed)
elif contains(c.toMixin, s.name.id):
callee = symChoice(c.c, n, s, scForceOpen)
elif s.owner == c.owner and sfGenSym in s.flags:
# template tmp[T](x: var seq[T]) =
# var yz: T
incl(s.flags, sfUsed)
callee = newSymNode(s, n.info)
styleCheckUse(n.info, s)
else:
callee = semTemplSymbol(c.c, n, s)
let call = newNodeI(nkCall, n.info)
call.add(callee)
for i in 0 .. n.len-1: call.add(n[i])
result = newNodeI(nkBind, n.info, 2)
result.sons[0] = n
result.sons[1] = call
else:
result = n
proc oprIsRoof(n: PNode): bool =
const roof = "^"
case n.kind
of nkIdent: result = n.ident.s == roof
of nkSym: result = n.sym.name.s == roof
of nkAccQuoted:
if n.len == 1:
result = oprIsRoof(n.sons[0])
of nkOpenSymChoice, nkClosedSymChoice:
result = oprIsRoof(n.sons[0])
else: discard
proc semTemplBody(c: var TemplCtx, n: PNode): PNode = proc semTemplBody(c: var TemplCtx, n: PNode): PNode =
result = n result = n
semIdeForTemplateOrGenericCheck(n, c.cursorInBody) semIdeForTemplateOrGenericCheck(n, c.cursorInBody)
@ -402,27 +471,65 @@ proc semTemplBody(c: var TemplCtx, n: PNode): PNode =
result.sons[1] = semTemplBody(c, n.sons[1]) result.sons[1] = semTemplBody(c, n.sons[1])
of nkPragma: of nkPragma:
result = onlyReplaceParams(c, n) result = onlyReplaceParams(c, n)
else: of nkBracketExpr:
result = newNodeI(nkCall, n.info)
result.add newIdentNode(getIdent("[]"), n.info)
for i in 0 ..< n.len: result.add(n[i])
let n0 = semTemplBody(c, n.sons[0])
withBracketExpr c, n0:
result = semTemplBodySons(c, result)
of nkCurlyExpr:
result = newNodeI(nkCall, n.info)
result.add newIdentNode(getIdent("{}"), n.info)
for i in 0 ..< n.len: result.add(n[i])
result = semTemplBodySons(c, result)
of nkAsgn, nkFastAsgn:
checkSonsLen(n, 2)
let a = n.sons[0]
let b = n.sons[1]
let k = a.kind
case k
of nkBracketExpr:
result = newNodeI(nkCall, n.info)
result.add newIdentNode(getIdent("[]="), n.info)
for i in 0 ..< a.len: result.add(a[i])
result.add(b)
let a0 = semTemplBody(c, a.sons[0])
withBracketExpr c, a0:
result = semTemplBodySons(c, result)
of nkCurlyExpr:
result = newNodeI(nkCall, n.info)
result.add newIdentNode(getIdent("{}="), n.info)
for i in 0 ..< a.len: result.add(a[i])
result.add(b)
result = semTemplBodySons(c, result)
else:
result = semTemplBodySons(c, n)
of nkCallKinds-{nkPostfix}:
result = semTemplBodySons(c, n)
if c.bracketExpr != nil and n.len == 2 and oprIsRoof(n.sons[0]):
result.add c.bracketExpr
of nkDotExpr, nkAccQuoted:
# dotExpr is ambiguous: note that we explicitly allow 'x.TemplateParam', # dotExpr is ambiguous: note that we explicitly allow 'x.TemplateParam',
# so we use the generic code for nkDotExpr too # so we use the generic code for nkDotExpr too
if n.kind == nkDotExpr or n.kind == nkAccQuoted: let s = qualifiedLookUp(c.c, n, {})
let s = qualifiedLookUp(c.c, n, {}) if s != nil:
if s != nil: # do not symchoice a quoted template parameter (bug #2390):
# do not symchoice a quoted template parameter (bug #2390): if s.owner == c.owner and s.kind == skParam and
if s.owner == c.owner and s.kind == skParam and n.kind == nkAccQuoted and n.len == 1:
n.kind == nkAccQuoted and n.len == 1: incl(s.flags, sfUsed)
incl(s.flags, sfUsed) styleCheckUse(n.info, s)
styleCheckUse(n.info, s) return newSymNode(s, n.info)
return newSymNode(s, n.info) elif contains(c.toBind, s.id):
elif contains(c.toBind, s.id): return symChoice(c.c, n, s, scClosed)
return symChoice(c.c, n, s, scClosed) elif contains(c.toMixin, s.name.id):
elif contains(c.toMixin, s.name.id): return symChoice(c.c, n, s, scForceOpen)
return symChoice(c.c, n, s, scForceOpen) else:
else: return symChoice(c.c, n, s, scOpen)
return symChoice(c.c, n, s, scOpen) result = semTemplBodySons(c, n)
result = n else:
for i in countup(0, sonsLen(n) - 1): result = semTemplBodySons(c, n)
result.sons[i] = semTemplBody(c, n.sons[i])
proc semTemplBodyDirty(c: var TemplCtx, n: PNode): PNode = proc semTemplBodyDirty(c: var TemplCtx, n: PNode): PNode =
result = n result = n
@ -452,30 +559,9 @@ proc semTemplBodyDirty(c: var TemplCtx, n: PNode): PNode =
for i in countup(0, sonsLen(n) - 1): for i in countup(0, sonsLen(n) - 1):
result.sons[i] = semTemplBodyDirty(c, n.sons[i]) result.sons[i] = semTemplBodyDirty(c, n.sons[i])
proc transformToExpr(n: PNode): PNode =
var realStmt: int
result = n
case n.kind
of nkStmtList:
realStmt = - 1
for i in countup(0, sonsLen(n) - 1):
case n.sons[i].kind
of nkCommentStmt, nkEmpty, nkNilLit:
discard
else:
if realStmt == - 1: realStmt = i
else: realStmt = - 2
if realStmt >= 0: result = transformToExpr(n.sons[realStmt])
else: n.kind = nkStmtListExpr
of nkBlockStmt:
n.kind = nkBlockExpr
#nkIfStmt: n.kind = nkIfExpr // this is not correct!
else:
discard
proc semTemplateDef(c: PContext, n: PNode): PNode = proc semTemplateDef(c: PContext, n: PNode): PNode =
var s: PSym var s: PSym
if c.p.owner.kind == skModule: if isTopLevel(c):
s = semIdentVis(c, skTemplate, n.sons[0], {sfExported}) s = semIdentVis(c, skTemplate, n.sons[0], {sfExported})
incl(s.flags, sfGlobal) incl(s.flags, sfGlobal)
else: else:
@ -530,9 +616,7 @@ proc semTemplateDef(c: PContext, n: PNode): PNode =
n.sons[bodyPos] = semTemplBodyDirty(ctx, n.sons[bodyPos]) n.sons[bodyPos] = semTemplBodyDirty(ctx, n.sons[bodyPos])
else: else:
n.sons[bodyPos] = semTemplBody(ctx, n.sons[bodyPos]) n.sons[bodyPos] = semTemplBody(ctx, n.sons[bodyPos])
if s.typ.sons[0].kind notin {tyStmt, tyTypeDesc}: # only parameters are resolved, no type checking is performed
n.sons[bodyPos] = transformToExpr(n.sons[bodyPos])
# only parameters are resolved, no type checking is performed
semIdeForTemplateOrGeneric(c, n.sons[bodyPos], ctx.cursorInBody) semIdeForTemplateOrGeneric(c, n.sons[bodyPos], ctx.cursorInBody)
closeScope(c) closeScope(c)
popOwner() popOwner()
@ -585,6 +669,11 @@ proc semPatternBody(c: var TemplCtx, n: PNode): PNode =
localError(n.info, errInvalidExpression) localError(n.info, errInvalidExpression)
result = n result = n
proc stupidStmtListExpr(n: PNode): bool =
for i in 0 .. n.len-2:
if n[i].kind notin {nkEmpty, nkCommentStmt}: return false
result = true
result = n result = n
case n.kind case n.kind
of nkIdent: of nkIdent:
@ -610,6 +699,12 @@ proc semPatternBody(c: var TemplCtx, n: PNode): PNode =
localError(n.info, errInvalidExpression) localError(n.info, errInvalidExpression)
else: else:
localError(n.info, errInvalidExpression) localError(n.info, errInvalidExpression)
of nkStmtList, nkStmtListExpr:
if stupidStmtListExpr(n):
result = semPatternBody(c, n.lastSon)
else:
for i in countup(0, sonsLen(n) - 1):
result.sons[i] = semPatternBody(c, n.sons[i])
of nkCallKinds: of nkCallKinds:
let s = qualifiedLookUp(c.c, n.sons[0], {}) let s = qualifiedLookUp(c.c, n.sons[0], {})
if s != nil: if s != nil:

View file

@ -23,6 +23,9 @@ proc newConstraint(c: PContext, k: TTypeKind): PType =
proc semEnum(c: PContext, n: PNode, prev: PType): PType = proc semEnum(c: PContext, n: PNode, prev: PType): PType =
if n.sonsLen == 0: return newConstraint(c, tyEnum) if n.sonsLen == 0: return newConstraint(c, tyEnum)
elif n.sonsLen == 1:
# don't create an empty tyEnum; fixes #3052
return errorType(c)
var var
counter, x: BiggestInt counter, x: BiggestInt
e: PSym e: PSym
@ -130,6 +133,7 @@ proc semAnyRef(c: PContext; n: PNode; kind: TTypeKind; prev: PType): PType =
let isCall = ord(n.kind in nkCallKinds) let isCall = ord(n.kind in nkCallKinds)
let n = if n[0].kind == nkBracket: n[0] else: n let n = if n[0].kind == nkBracket: n[0] else: n
checkMinSonsLen(n, 1) checkMinSonsLen(n, 1)
var base = semTypeNode(c, n.lastSon, nil)
result = newOrPrevType(kind, prev, c) result = newOrPrevType(kind, prev, c)
# check every except the last is an object: # check every except the last is an object:
for i in isCall .. n.len-2: for i in isCall .. n.len-2:
@ -137,7 +141,6 @@ proc semAnyRef(c: PContext; n: PNode; kind: TTypeKind; prev: PType): PType =
if region.skipTypes({tyGenericInst}).kind notin {tyError, tyObject}: if region.skipTypes({tyGenericInst}).kind notin {tyError, tyObject}:
message n[i].info, errGenerated, "region needs to be an object type" message n[i].info, errGenerated, "region needs to be an object type"
addSonSkipIntLit(result, region) addSonSkipIntLit(result, region)
var base = semTypeNode(c, n.lastSon, nil)
addSonSkipIntLit(result, base) addSonSkipIntLit(result, base)
proc semVarType(c: PContext, n: PNode, prev: PType): PType = proc semVarType(c: PContext, n: PNode, prev: PType): PType =
@ -251,19 +254,21 @@ proc semArrayIndex(c: PContext, n: PNode): PType =
proc semArray(c: PContext, n: PNode, prev: PType): PType = proc semArray(c: PContext, n: PNode, prev: PType): PType =
var base: PType var base: PType
result = newOrPrevType(tyArray, prev, c)
if sonsLen(n) == 3: if sonsLen(n) == 3:
# 3 = length(array indx base) # 3 = length(array indx base)
var indx = semArrayIndex(c, n[1]) let indx = semArrayIndex(c, n[1])
addSonSkipIntLit(result, indx) var indxB = indx
if indx.kind == tyGenericInst: indx = lastSon(indx) if indxB.kind == tyGenericInst: indxB = lastSon(indxB)
if indx.kind notin {tyGenericParam, tyStatic, tyFromExpr}: if indxB.kind notin {tyGenericParam, tyStatic, tyFromExpr}:
if not isOrdinalType(indx): if not isOrdinalType(indxB):
localError(n.sons[1].info, errOrdinalTypeExpected) localError(n.sons[1].info, errOrdinalTypeExpected)
elif enumHasHoles(indx): elif enumHasHoles(indxB):
localError(n.sons[1].info, errEnumXHasHoles, localError(n.sons[1].info, errEnumXHasHoles,
typeToString(indx.skipTypes({tyRange}))) typeToString(indxB.skipTypes({tyRange})))
base = semTypeNode(c, n.sons[2], nil) base = semTypeNode(c, n.sons[2], nil)
# ensure we only construct a tyArray when there was no error (bug #3048):
result = newOrPrevType(tyArray, prev, c)
addSonSkipIntLit(result, indx)
addSonSkipIntLit(result, base) addSonSkipIntLit(result, base)
else: else:
localError(n.info, errArrayExpectsTwoTypeParams) localError(n.info, errArrayExpectsTwoTypeParams)
@ -503,8 +508,9 @@ proc semRecordCase(c: PContext, n: PNode, check: var IntSet, pos: var int,
var typ = skipTypes(a.sons[0].typ, abstractVar-{tyTypeDesc}) var typ = skipTypes(a.sons[0].typ, abstractVar-{tyTypeDesc})
if not isOrdinalType(typ): if not isOrdinalType(typ):
localError(n.info, errSelectorMustBeOrdinal) localError(n.info, errSelectorMustBeOrdinal)
elif firstOrd(typ) < 0: elif firstOrd(typ) != 0:
localError(n.info, errOrdXMustNotBeNegative, a.sons[0].sym.name.s) localError(n.info, errGenerated, "low(" & $a.sons[0].sym.name.s &
") must be 0 for discriminant")
elif lengthOrd(typ) > 0x00007FFF: elif lengthOrd(typ) > 0x00007FFF:
localError(n.info, errLenXinvalid, a.sons[0].sym.name.s) localError(n.info, errLenXinvalid, a.sons[0].sym.name.s)
var chckCovered = true var chckCovered = true
@ -629,7 +635,7 @@ proc skipGenericInvocation(t: PType): PType {.inline.} =
result = t result = t
if result.kind == tyGenericInvocation: if result.kind == tyGenericInvocation:
result = result.sons[0] result = result.sons[0]
while result.kind in {tyGenericInst, tyGenericBody}: while result.kind in {tyGenericInst, tyGenericBody, tyRef, tyPtr}:
result = lastSon(result) result = lastSon(result)
proc addInheritedFields(c: PContext, check: var IntSet, pos: var int, proc addInheritedFields(c: PContext, check: var IntSet, pos: var int,
@ -651,7 +657,7 @@ proc semObjectNode(c: PContext, n: PNode, prev: PType): PType =
if base.isNil: if base.isNil:
localError(n.info, errIllegalRecursionInTypeX, "object") localError(n.info, errIllegalRecursionInTypeX, "object")
else: else:
var concreteBase = skipGenericInvocation(base).skipTypes(skipPtrs) var concreteBase = skipGenericInvocation(base)
if concreteBase.kind == tyObject and tfFinal notin concreteBase.flags: if concreteBase.kind == tyObject and tfFinal notin concreteBase.flags:
addInheritedFields(c, check, pos, concreteBase) addInheritedFields(c, check, pos, concreteBase)
else: else:
@ -712,12 +718,12 @@ proc liftParamType(c: PContext, procKind: TSymKind, genericParams: PNode,
if paramType == nil: return # (e.g. proc return type) if paramType == nil: return # (e.g. proc return type)
proc addImplicitGenericImpl(typeClass: PType, typId: PIdent): PType = proc addImplicitGenericImpl(typeClass: PType, typId: PIdent): PType =
let finalTypId = if typId != nil: typId
else: getIdent(paramName & ":type")
if genericParams == nil: if genericParams == nil:
# This happens with anonymous proc types appearing in signatures # This happens with anonymous proc types appearing in signatures
# XXX: we need to lift these earlier # XXX: we need to lift these earlier
return return
let finalTypId = if typId != nil: typId
else: getIdent(paramName & ":type")
# is this a bindOnce type class already present in the param list? # is this a bindOnce type class already present in the param list?
for i in countup(0, genericParams.len - 1): for i in countup(0, genericParams.len - 1):
if genericParams.sons[i].sym.name.id == finalTypId.id: if genericParams.sons[i].sym.name.id == finalTypId.id:
@ -751,7 +757,7 @@ proc liftParamType(c: PContext, procKind: TSymKind, genericParams: PNode,
case paramType.kind: case paramType.kind:
of tyAnything: of tyAnything:
result = addImplicitGeneric(newTypeS(tyGenericParam, c)) result = addImplicitGenericImpl(newTypeS(tyGenericParam, c), nil)
of tyStatic: of tyStatic:
# proc(a: expr{string}, b: expr{nkLambda}) # proc(a: expr{string}, b: expr{nkLambda})
@ -862,6 +868,7 @@ proc liftParamType(c: PContext, procKind: TSymKind, genericParams: PNode,
of tyExpr: of tyExpr:
if procKind notin {skMacro, skTemplate}: if procKind notin {skMacro, skTemplate}:
result = addImplicitGeneric(newTypeS(tyAnything, c)) result = addImplicitGeneric(newTypeS(tyAnything, c))
#result = addImplicitGenericImpl(newTypeS(tyGenericParam, c), nil)
of tyGenericParam: of tyGenericParam:
markUsed(info, paramType.sym) markUsed(info, paramType.sym)
@ -964,18 +971,26 @@ proc semProcTypeNode(c: PContext, n, genericParams: PNode,
elif kind == skIterator: elif kind == skIterator:
# XXX This is special magic we should likely get rid of # XXX This is special magic we should likely get rid of
r = newTypeS(tyExpr, c) r = newTypeS(tyExpr, c)
message(n.info, warnDeprecated, "implicit return type for 'iterator'")
if r != nil: if r != nil:
# 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}).kind != tyEmpty: if skipTypes(r, {tyGenericInst}).kind != tyEmpty:
# 'auto' as a return type does not imply a generic: # 'auto' as a return type does not imply a generic:
if r.kind != tyExpr: if r.kind == tyAnything:
# 'p(): auto' and 'p(): expr' are equivalent, but the rest of the
# compiler is hardly aware of 'auto':
r = newTypeS(tyExpr, c)
elif r.kind != tyExpr:
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)
if lifted != nil: r = lifted if lifted != nil:
r.flags.incl tfRetType r = lifted
#if r.kind != tyGenericParam:
#echo "came here for ", typeToString(r)
r.flags.incl tfRetType
r = skipIntLit(r) r = skipIntLit(r)
if kind == skIterator: if kind == skIterator:
# see tchainediterators # see tchainediterators
@ -1049,6 +1064,7 @@ proc semGeneric(c: PContext, n: PNode, s: PSym, prev: PType): PType =
return newOrPrevType(tyError, prev, c) return newOrPrevType(tyError, prev, c)
else: else:
var m = newCandidate(c, t) var m = newCandidate(c, t)
m.isNoCall = true
matches(c, n, copyTree(n), m) matches(c, n, copyTree(n), m)
if m.state != csMatch and not m.typedescMatched: if m.state != csMatch and not m.typedescMatched:
@ -1085,6 +1101,7 @@ proc semTypeExpr(c: PContext, n: PNode): PType =
result = n.typ.base result = n.typ.base
else: else:
localError(n.info, errTypeExpected, n.renderTree) localError(n.info, errTypeExpected, n.renderTree)
result = errorType(c)
proc freshType(res, prev: PType): PType {.inline.} = proc freshType(res, prev: PType): PType {.inline.} =
if prev.isNil: if prev.isNil:
@ -1140,7 +1157,17 @@ proc semTypeNode(c: PContext, n: PNode, prev: PType): PType =
else: else:
result = semAnonTuple(c, n, prev) result = semAnonTuple(c, n, prev)
of nkCallKinds: of nkCallKinds:
if isRange(n): let x = n[0]
let ident = case x.kind
of nkIdent: x.ident
of nkSym: x.sym.name
of nkClosedSymChoice, nkOpenSymChoice: x[0].sym.name
else: nil
if ident != nil and ident.s == "[]":
let b = newNodeI(nkBracketExpr, n.info)
for i in 1..<n.len: b.add(n[i])
result = semTypeNode(c, b, prev)
elif ident != nil and ident.id == ord(wDotDot):
result = semRangeAux(c, n, prev) result = semRangeAux(c, n, prev)
elif n[0].kind notin nkIdentKinds: elif n[0].kind notin nkIdentKinds:
result = semTypeExpr(c, n) result = semTypeExpr(c, n)
@ -1326,12 +1353,23 @@ proc processMagicType(c: PContext, m: PSym) =
rawAddSon(m.typ, newTypeS(tyEmpty, c)) rawAddSon(m.typ, newTypeS(tyEmpty, c))
of mIntSetBaseType: setMagicType(m, tyRange, intSize) of mIntSetBaseType: setMagicType(m, tyRange, intSize)
of mNil: setMagicType(m, tyNil, ptrSize) of mNil: setMagicType(m, tyNil, ptrSize)
of mExpr: setMagicType(m, tyExpr, 0) of mExpr:
of mStmt: setMagicType(m, tyStmt, 0) if m.name.s == "auto":
setMagicType(m, tyAnything, 0)
else:
setMagicType(m, tyExpr, 0)
if m.name.s == "expr": m.typ.flags.incl tfOldSchoolExprStmt
of mStmt:
setMagicType(m, tyStmt, 0)
if m.name.s == "stmt": m.typ.flags.incl tfOldSchoolExprStmt
of mTypeDesc: of mTypeDesc:
setMagicType(m, tyTypeDesc, 0) setMagicType(m, tyTypeDesc, 0)
rawAddSon(m.typ, newTypeS(tyNone, c)) rawAddSon(m.typ, newTypeS(tyNone, c))
of mVoidType: setMagicType(m, tyEmpty, 0) of mVoidType:
setMagicType(m, tyEmpty, 0)
# for historical reasons we conflate 'void' with 'empty' so that '@[]'
# has the type 'seq[void]'.
m.typ.flags.incl tfVoid
of mArray: of mArray:
setMagicType(m, tyArray, 0) setMagicType(m, tyArray, 0)
of mOpenArray: of mOpenArray:
@ -1348,7 +1386,8 @@ proc processMagicType(c: PContext, m: PSym) =
of mOrdinal: of mOrdinal:
setMagicType(m, tyOrdinal, 0) setMagicType(m, tyOrdinal, 0)
rawAddSon(m.typ, newTypeS(tyNone, c)) rawAddSon(m.typ, newTypeS(tyNone, c))
of mPNimrodNode: discard of mPNimrodNode:
incl m.typ.flags, tfTriggersCompileTime
of mShared: of mShared:
setMagicType(m, tyObject, 0) setMagicType(m, tyObject, 0)
m.typ.n = newNodeI(nkRecList, m.info) m.typ.n = newNodeI(nkRecList, m.info)

View file

@ -75,8 +75,12 @@ proc searchInstTypes*(key: PType): PType =
proc cacheTypeInst*(inst: PType) = proc cacheTypeInst*(inst: PType) =
# XXX: add to module's generics # XXX: add to module's generics
# update the refcount # update the refcount
let genericTyp = inst.sons[0] let gt = inst.sons[0]
genericTyp.sym.typeInstCache.safeAdd(inst) let t = if gt.kind == tyGenericBody: gt.lastSon else: gt
if t.kind in {tyStatic, tyGenericParam, tyIter} + tyTypeClasses:
return
gt.sym.typeInstCache.safeAdd(inst)
type type
TReplTypeVars* {.final.} = object TReplTypeVars* {.final.} = object
@ -90,6 +94,8 @@ type
allowMetaTypes*: bool # allow types such as seq[Number] allowMetaTypes*: bool # allow types such as seq[Number]
# i.e. the result contains unresolved generics # i.e. the result contains unresolved generics
skipTypedesc*: bool # wether we should skip typeDescs skipTypedesc*: bool # wether we should skip typeDescs
owner*: PSym # where this instantiation comes from
recursionLimit: int
proc replaceTypeVarsTAux(cl: var TReplTypeVars, t: PType): PType proc replaceTypeVarsTAux(cl: var TReplTypeVars, t: PType): PType
proc replaceTypeVarsS(cl: var TReplTypeVars, s: PSym): PSym proc replaceTypeVarsS(cl: var TReplTypeVars, s: PSym): PSym
@ -207,6 +213,9 @@ proc replaceTypeVarsN(cl: var TReplTypeVars, n: PNode): PNode =
proc replaceTypeVarsS(cl: var TReplTypeVars, s: PSym): PSym = proc replaceTypeVarsS(cl: var TReplTypeVars, s: PSym): PSym =
if s == nil: return nil if s == nil: return nil
# symbol is not our business:
if cl.owner != nil and s.owner != cl.owner:
return s
result = PSym(idTableGet(cl.symMap, s)) result = PSym(idTableGet(cl.symMap, s))
if result == nil: if result == nil:
result = copySym(s, false) result = copySym(s, false)
@ -365,6 +374,19 @@ proc propagateFieldFlags(t: PType, n: PNode) =
else: discard else: discard
proc replaceTypeVarsTAux(cl: var TReplTypeVars, t: PType): PType = proc replaceTypeVarsTAux(cl: var TReplTypeVars, t: PType): PType =
template bailout =
if cl.recursionLimit > 100:
# bail out, see bug #2509. But note this caching is in general wrong,
# look at this example where TwoVectors should not share the generic
# instantiations (bug #3112):
# type
# Vector[N: static[int]] = array[N, float64]
# TwoVectors[Na, Nb: static[int]] = (Vector[Na], Vector[Nb])
result = PType(idTableGet(cl.localCache, t))
if result != nil: return result
inc cl.recursionLimit
result = t result = t
if t == nil: return if t == nil: return
@ -420,8 +442,7 @@ proc replaceTypeVarsTAux(cl: var TReplTypeVars, t: PType): PType =
result = t result = t
of tyGenericInst: of tyGenericInst:
result = PType(idTableGet(cl.localCache, t)) bailout()
if result != nil: return result
result = instCopyType(cl, t) result = instCopyType(cl, t)
idTablePut(cl.localCache, t, result) idTablePut(cl.localCache, t, result)
for i in 1 .. <result.sonsLen: for i in 1 .. <result.sonsLen:
@ -431,8 +452,7 @@ proc replaceTypeVarsTAux(cl: var TReplTypeVars, t: PType): PType =
else: else:
if containsGenericType(t): if containsGenericType(t):
#if not cl.allowMetaTypes: #if not cl.allowMetaTypes:
result = PType(idTableGet(cl.localCache, t)) bailout()
if result != nil: return result
result = instCopyType(cl, t) result = instCopyType(cl, t)
result.size = -1 # needs to be recomputed result.size = -1 # needs to be recomputed
#if not cl.allowMetaTypes: #if not cl.allowMetaTypes:
@ -440,8 +460,14 @@ proc replaceTypeVarsTAux(cl: var TReplTypeVars, t: PType): PType =
for i in countup(0, sonsLen(result) - 1): for i in countup(0, sonsLen(result) - 1):
if result.sons[i] != nil: if result.sons[i] != nil:
result.sons[i] = replaceTypeVarsT(cl, result.sons[i]) var r = replaceTypeVarsT(cl, result.sons[i])
propagateToOwner(result, result.sons[i]) if result.kind == tyObject:
# carefully coded to not skip the precious tyGenericInst:
let r2 = r.skipTypes({tyGenericInst})
if r2.kind in {tyPtr, tyRef}:
r = skipTypes(r2, {tyPtr, tyRef})
result.sons[i] = r
propagateToOwner(result, r)
result.n = replaceTypeVarsN(cl, result.n) result.n = replaceTypeVarsN(cl, result.n)
@ -459,22 +485,33 @@ proc replaceTypeVarsTAux(cl: var TReplTypeVars, t: PType): PType =
else: discard else: discard
proc initTypeVars*(p: PContext, pt: TIdTable, info: TLineInfo): TReplTypeVars = proc initTypeVars*(p: PContext, pt: TIdTable, info: TLineInfo;
owner: PSym): TReplTypeVars =
initIdTable(result.symMap) initIdTable(result.symMap)
copyIdTable(result.typeMap, pt) copyIdTable(result.typeMap, pt)
initIdTable(result.localCache) initIdTable(result.localCache)
result.info = info result.info = info
result.c = p result.c = p
result.owner = owner
proc replaceTypesInBody*(p: PContext, pt: TIdTable, n: PNode): PNode = proc replaceTypesInBody*(p: PContext, pt: TIdTable, n: PNode;
var cl = initTypeVars(p, pt, n.info) owner: PSym): PNode =
var cl = initTypeVars(p, pt, n.info, owner)
pushInfoContext(n.info)
result = replaceTypeVarsN(cl, n)
popInfoContext()
proc replaceTypesForLambda*(p: PContext, pt: TIdTable, n: PNode;
original, new: PSym): PNode =
var cl = initTypeVars(p, pt, n.info, original)
idTablePut(cl.symMap, original, new)
pushInfoContext(n.info) pushInfoContext(n.info)
result = replaceTypeVarsN(cl, n) result = replaceTypeVarsN(cl, n)
popInfoContext() popInfoContext()
proc generateTypeInstance*(p: PContext, pt: TIdTable, info: TLineInfo, proc generateTypeInstance*(p: PContext, pt: TIdTable, info: TLineInfo,
t: PType): PType = t: PType): PType =
var cl = initTypeVars(p, pt, info) var cl = initTypeVars(p, pt, info, nil)
pushInfoContext(info) pushInfoContext(info)
result = replaceTypeVarsT(cl, t) result = replaceTypeVarsT(cl, t)
popInfoContext() popInfoContext()

View file

@ -18,7 +18,7 @@ when useCaas:
# We cache modules and the dependency graph. However, we don't check for # We cache modules and the dependency graph. However, we don't check for
# file changes but expect the client to tell us about them, otherwise the # file changes but expect the client to tell us about them, otherwise the
# repeated CRC calculations may turn out to be too slow. # repeated hash calculations may turn out to be too slow.
var var
curCaasCmd* = "" curCaasCmd* = ""

View file

@ -47,6 +47,7 @@ type
coerceDistincts*: bool # this is an explicit coercion that can strip away coerceDistincts*: bool # this is an explicit coercion that can strip away
# a distrinct type # a distrinct type
typedescMatched*: bool typedescMatched*: bool
isNoCall*: bool # misused for generic type instantiations C[T]
inheritancePenalty: int # to prefer closest father object type inheritancePenalty: int # to prefer closest father object type
errors*: CandidateErrors # additional clarifications to be displayed to the errors*: CandidateErrors # additional clarifications to be displayed to the
# user if overload resolution fails # user if overload resolution fails
@ -57,6 +58,10 @@ type
isSubtype, isSubtype,
isSubrange, # subrange of the wanted type; no type conversion isSubrange, # subrange of the wanted type; no type conversion
# but apart from that counts as ``isSubtype`` # but apart from that counts as ``isSubtype``
isBothMetaConvertible # generic proc parameter was matched against
# generic type, e.g., map(mySeq, x=>x+1),
# maybe recoverable by rerun if the parameter is
# the proc's return value
isInferred, # generic proc was matched against a concrete type isInferred, # generic proc was matched against a concrete type
isInferredConvertible, # same as above, but requiring proc CC conversion isInferredConvertible, # same as above, but requiring proc CC conversion
isGeneric, isGeneric,
@ -158,11 +163,15 @@ proc sumGeneric(t: PType): int =
t = t.sons[0] t = t.sons[0]
inc result inc result
inc isvar inc isvar
of tyTypeDesc:
t = t.lastSon
if t.kind == tyEmpty: break
inc result
of tyGenericInvocation, tyTuple: of tyGenericInvocation, tyTuple:
result += ord(t.kind == tyGenericInvocation) result += ord(t.kind == tyGenericInvocation)
for i in 0 .. <t.len: result += t.sons[i].sumGeneric for i in 0 .. <t.len: result += t.sons[i].sumGeneric
break break
of tyGenericParam, tyExpr, tyStatic, tyStmt, tyTypeDesc: break of tyGenericParam, tyExpr, tyStatic, tyStmt: break
of tyBool, tyChar, tyEnum, tyObject, tyProc, tyPointer, of tyBool, tyChar, tyEnum, tyObject, tyProc, tyPointer,
tyString, tyCString, tyInt..tyInt64, tyFloat..tyFloat128, tyString, tyCString, tyInt..tyInt64, tyFloat..tyFloat128,
tyUInt..tyUInt64: tyUInt..tyUInt64:
@ -218,12 +227,12 @@ proc cmpCandidates*(a, b: TCandidate): int =
result = complexDisambiguation(a.callee, b.callee) result = complexDisambiguation(a.callee, b.callee)
proc writeMatches*(c: TCandidate) = proc writeMatches*(c: TCandidate) =
writeln(stdout, "exact matches: " & $c.exactMatches) writeLine(stdout, "exact matches: " & $c.exactMatches)
writeln(stdout, "generic matches: " & $c.genericMatches) writeLine(stdout, "generic matches: " & $c.genericMatches)
writeln(stdout, "subtype matches: " & $c.subtypeMatches) writeLine(stdout, "subtype matches: " & $c.subtypeMatches)
writeln(stdout, "intconv matches: " & $c.intConvMatches) writeLine(stdout, "intconv matches: " & $c.intConvMatches)
writeln(stdout, "conv matches: " & $c.convMatches) writeLine(stdout, "conv matches: " & $c.convMatches)
writeln(stdout, "inheritance: " & $c.inheritancePenalty) writeLine(stdout, "inheritance: " & $c.inheritancePenalty)
proc argTypeToString(arg: PNode; prefer: TPreferedDesc): string = proc argTypeToString(arg: PNode; prefer: TPreferedDesc): string =
if arg.kind in nkSymChoices: if arg.kind in nkSymChoices:
@ -264,6 +273,9 @@ proc concreteType(c: TCandidate, t: PType): PType =
addSonSkipIntLit(result, t.sons[1]) # XXX: semantic checking for the type? addSonSkipIntLit(result, t.sons[1]) # XXX: semantic checking for the type?
of tyNil: of tyNil:
result = nil # what should it be? result = nil # what should it be?
of tyTypeDesc:
if c.isNoCall: result = t
else: result = nil
of tySequence, tySet: of tySequence, tySet:
if t.sons[0].kind == tyEmpty: result = nil if t.sons[0].kind == tyEmpty: result = nil
else: result = t else: result = t
@ -381,8 +393,29 @@ proc inconsistentVarTypes(f, a: PType): bool {.inline.} =
result = f.kind != a.kind and (f.kind == tyVar or a.kind == tyVar) result = f.kind != a.kind and (f.kind == tyVar or a.kind == tyVar)
proc procParamTypeRel(c: var TCandidate, f, a: PType): TTypeRelation = proc procParamTypeRel(c: var TCandidate, f, a: PType): TTypeRelation =
var f = f ## For example we have:
## .. code-block:: nim
## proc myMap[T,S](sIn: seq[T], f: proc(x: T): S): seq[S] = ...
## proc innerProc[Q,W](q: Q): W = ...
## And we want to match: myMap(@[1,2,3], innerProc)
## This proc (procParamTypeRel) will do the following steps in
## three different calls:
## - matches f=T to a=Q. Since f is metatype, we resolve it
## to int (which is already known at this point). So in this case
## Q=int mapping will be saved to c.bindings.
## - matches f=S to a=W. Both of these metatypes are unknown, so we
## return with isBothMetaConvertible to ask for rerun.
## - matches f=S to a=W. At this point the return type of innerProc
## is known (we get it from c.bindings). We can use that value
## to match with f, and save back to c.bindings.
var
f = f
a = a
if a.isMetaType:
let aResolved = PType(idTableGet(c.bindings, a))
if aResolved != nil:
a = aResolved
if a.isMetaType: if a.isMetaType:
if f.isMetaType: if f.isMetaType:
# We are matching a generic proc (as proc param) # We are matching a generic proc (as proc param)
@ -393,12 +426,15 @@ proc procParamTypeRel(c: var TCandidate, f, a: PType): TTypeRelation =
f = generateTypeInstance(c.c, c.bindings, c.call.info, f) f = generateTypeInstance(c.c, c.bindings, c.call.info, f)
if f == nil or f.isMetaType: if f == nil or f.isMetaType:
# no luck resolving the type, so the inference fails # no luck resolving the type, so the inference fails
return isNone return isBothMetaConvertible
# Note that this typeRel call will save a's resolved type into c.bindings
let reverseRel = typeRel(c, a, f) let reverseRel = typeRel(c, a, f)
if reverseRel >= isGeneric: if reverseRel >= isGeneric:
result = isInferred result = isInferred
#inc c.genericMatches #inc c.genericMatches
else: else:
# Note that this typeRel call will save f's resolved type into c.bindings
# if f is metatype.
result = typeRel(c, f, a) result = typeRel(c, f, a)
if result <= isSubtype or inconsistentVarTypes(f, a): if result <= isSubtype or inconsistentVarTypes(f, a):
@ -442,8 +478,9 @@ proc procTypeRel(c: var TCandidate, f, a: PType): TTypeRelation =
elif f.callConv != a.callConv: elif f.callConv != a.callConv:
# valid to pass a 'nimcall' thingie to 'closure': # valid to pass a 'nimcall' thingie to 'closure':
if f.callConv == ccClosure and a.callConv == ccDefault: if f.callConv == ccClosure and a.callConv == ccDefault:
result = if result != isInferred: isConvertible result = if result == isInferred: isInferredConvertible
else: isInferredConvertible elif result == isBothMetaConvertible: isBothMetaConvertible
else: isConvertible
else: else:
return isNone return isNone
when useEffectSystem: when useEffectSystem:
@ -474,6 +511,9 @@ proc typeRangeRel(f, a: PType): TTypeRelation {.noinline.} =
proc matchUserTypeClass*(c: PContext, m: var TCandidate, proc matchUserTypeClass*(c: PContext, m: var TCandidate,
ff, a: PType): TTypeRelation = ff, a: PType): TTypeRelation =
var body = ff.skipTypes({tyUserTypeClassInst}) var body = ff.skipTypes({tyUserTypeClassInst})
if c.inTypeClass > 20:
localError(body.n[3].info, $body.n[3] & " too nested for type matching")
return isNone
openScope(c) openScope(c)
inc c.inTypeClass inc c.inTypeClass
@ -553,7 +593,7 @@ proc tryResolvingStaticExpr(c: var TCandidate, n: PNode): PNode =
# Here, N-1 will be initially nkStaticExpr that can be evaluated only after # Here, N-1 will be initially nkStaticExpr that can be evaluated only after
# N is bound to a concrete value during the matching of the first param. # N is bound to a concrete value during the matching of the first param.
# This proc is used to evaluate such static expressions. # This proc is used to evaluate such static expressions.
let instantiated = replaceTypesInBody(c.c, c.bindings, n) let instantiated = replaceTypesInBody(c.c, c.bindings, n, nil)
result = c.c.semExpr(c.c, instantiated) result = c.c.semExpr(c.c, instantiated)
proc typeRel(c: var TCandidate, f, aOrig: PType, doBind = true): TTypeRelation = proc typeRel(c: var TCandidate, f, aOrig: PType, doBind = true): TTypeRelation =
@ -644,6 +684,13 @@ proc typeRel(c: var TCandidate, f, aOrig: PType, doBind = true): TTypeRelation =
of tyAnything: of tyAnything:
return if f.kind == tyAnything: isGeneric return if f.kind == tyAnything: isGeneric
else: isNone else: isNone
of tyUserTypeClass, tyUserTypeClassInst:
# consider this: 'var g: Node' *within* a concept where 'Node'
# is a concept too (tgraph)
let x = typeRel(c, a, f, false)
if x >= isGeneric:
return isGeneric
else: discard else: discard
case f.kind case f.kind
@ -727,8 +774,12 @@ proc typeRel(c: var TCandidate, f, aOrig: PType, doBind = true): TTypeRelation =
result = isNone result = isNone
else: discard else: discard
of tyOpenArray, tyVarargs: of tyOpenArray, tyVarargs:
# varargs[expr] is special # varargs[expr] is special too but handled earlier. So we only need to
if f.kind == tyVarargs and f.sons[0].kind == tyExpr: return # handle varargs[stmt] which is the same as varargs[typed]:
if f.kind == tyVarargs:
if tfOldSchoolExprStmt in f.sons[0].flags:
if f.sons[0].kind == tyExpr: return
elif f.sons[0].kind == tyStmt: return
case a.kind case a.kind
of tyOpenArray, tyVarargs: of tyOpenArray, tyVarargs:
result = typeRel(c, base(f), base(a)) result = typeRel(c, base(f), base(a))
@ -748,6 +799,13 @@ proc typeRel(c: var TCandidate, f, aOrig: PType, doBind = true): TTypeRelation =
result = isConvertible result = isConvertible
elif typeRel(c, base(f), a.sons[0]) >= isGeneric: elif typeRel(c, base(f), a.sons[0]) >= isGeneric:
result = isConvertible result = isConvertible
of tyString:
if f.kind == tyOpenArray:
if f.sons[0].kind == tyChar:
result = isConvertible
elif f.sons[0].kind == tyGenericParam and a.len > 0 and
typeRel(c, base(f), base(a)) >= isGeneric:
result = isConvertible
else: discard else: discard
of tySequence: of tySequence:
case a.kind case a.kind
@ -1091,6 +1149,8 @@ proc typeRel(c: var TCandidate, f, aOrig: PType, doBind = true): TTypeRelation =
result = isNone result = isNone
of tyStmt: of tyStmt:
if aOrig != nil and tfOldSchoolExprStmt notin f.flags:
put(c.bindings, f, aOrig)
result = isGeneric result = isGeneric
of tyProxy: of tyProxy:
@ -1204,7 +1264,7 @@ proc incMatches(m: var TCandidate; r: TTypeRelation; convMatch = 1) =
case r case r
of isConvertible, isIntConv: inc(m.convMatches, convMatch) of isConvertible, isIntConv: inc(m.convMatches, convMatch)
of isSubtype, isSubrange: inc(m.subtypeMatches) of isSubtype, isSubrange: inc(m.subtypeMatches)
of isGeneric, isInferred: inc(m.genericMatches) of isGeneric, isInferred, isBothMetaConvertible: inc(m.genericMatches)
of isFromIntLit: inc(m.intConvMatches, 256) of isFromIntLit: inc(m.intConvMatches, 256)
of isInferredConvertible: of isInferredConvertible:
inc(m.convMatches) inc(m.convMatches)
@ -1270,6 +1330,29 @@ proc paramTypesMatchAux(m: var TCandidate, f, argType: PType,
put(m.bindings, f, inlined) put(m.bindings, f, inlined)
return argSemantized return argSemantized
# If r == isBothMetaConvertible then we rerun typeRel.
# bothMetaCounter is for safety to avoid any infinite loop,
# I don't have any example when it is needed.
# lastBindingsLenth is used to check whether m.bindings remains the same,
# because in that case there is no point in continuing.
var bothMetaCounter = 0
var lastBindingsLength = -1
while r == isBothMetaConvertible and
lastBindingsLength != m.bindings.counter and
bothMetaCounter < 100:
lastBindingsLength = m.bindings.counter
inc(bothMetaCounter)
if arg.kind in {nkProcDef, nkIteratorDef} + nkLambdaKinds:
result = c.semInferredLambda(c, m.bindings, arg)
elif arg.kind != nkSym:
return nil
else:
let inferred = c.semGenerateInstance(c, arg.sym, m.bindings, arg.info)
result = newSymNode(inferred, arg.info)
inc(m.convMatches)
arg = result
r = typeRel(m, f, arg.typ)
case r case r
of isConvertible: of isConvertible:
inc(m.convMatches) inc(m.convMatches)
@ -1281,7 +1364,10 @@ proc paramTypesMatchAux(m: var TCandidate, f, argType: PType,
result = implicitConv(nkHiddenStdConv, f, arg, m, c) result = implicitConv(nkHiddenStdConv, f, arg, m, c)
of isSubtype: of isSubtype:
inc(m.subtypeMatches) inc(m.subtypeMatches)
result = implicitConv(nkHiddenSubConv, f, arg, m, c) if f.kind == tyTypeDesc:
result = arg
else:
result = implicitConv(nkHiddenSubConv, f, arg, m, c)
of isSubrange: of isSubrange:
inc(m.subtypeMatches) inc(m.subtypeMatches)
if f.kind == tyVar: if f.kind == tyVar:
@ -1291,6 +1377,8 @@ proc paramTypesMatchAux(m: var TCandidate, f, argType: PType,
of isInferred, isInferredConvertible: of isInferred, isInferredConvertible:
if arg.kind in {nkProcDef, nkIteratorDef} + nkLambdaKinds: if arg.kind in {nkProcDef, nkIteratorDef} + nkLambdaKinds:
result = c.semInferredLambda(c, m.bindings, arg) result = c.semInferredLambda(c, m.bindings, arg)
elif arg.kind != nkSym:
return nil
else: else:
let inferred = c.semGenerateInstance(c, arg.sym, m.bindings, arg.info) let inferred = c.semGenerateInstance(c, arg.sym, m.bindings, arg.info)
result = newSymNode(inferred, arg.info) result = newSymNode(inferred, arg.info)
@ -1310,6 +1398,9 @@ proc paramTypesMatchAux(m: var TCandidate, f, argType: PType,
result.typ = getInstantiatedType(c, arg, m, f) result.typ = getInstantiatedType(c, arg, m, f)
else: else:
result = arg result = arg
of isBothMetaConvertible:
# This is the result for the 101th time.
result = nil
of isFromIntLit: of isFromIntLit:
# too lazy to introduce another ``*matches`` field, so we conflate # too lazy to introduce another ``*matches`` field, so we conflate
# ``isIntConv`` and ``isIntLit`` here: # ``isIntConv`` and ``isIntLit`` here:
@ -1454,6 +1545,10 @@ proc incrIndexType(t: PType) =
assert t.kind == tyArrayConstr assert t.kind == tyArrayConstr
inc t.sons[0].n.sons[1].intVal inc t.sons[0].n.sons[1].intVal
template isVarargsUntyped(x): expr =
x.kind == tyVarargs and x.sons[0].kind == tyExpr and
tfOldSchoolExprStmt notin x.sons[0].flags
proc matchesAux(c: PContext, n, nOrig: PNode, proc matchesAux(c: PContext, n, nOrig: PNode,
m: var TCandidate, marker: var IntSet) = m: var TCandidate, marker: var IntSet) =
template checkConstraint(n: expr) {.immediate, dirty.} = template checkConstraint(n: expr) {.immediate, dirty.} =
@ -1482,10 +1577,17 @@ proc matchesAux(c: PContext, n, nOrig: PNode,
var formalLen = m.callee.n.len var formalLen = m.callee.n.len
addSon(m.call, copyTree(n.sons[0])) addSon(m.call, copyTree(n.sons[0]))
var container: PNode = nil # constructed container var container: PNode = nil # constructed container
var formal: PSym = nil var formal: PSym = if formalLen > 1: m.callee.n.sons[1].sym else: nil
while a < n.len: while a < n.len:
if n.sons[a].kind == nkExprEqExpr: if a >= formalLen-1 and formal != nil and formal.typ.isVarargsUntyped:
if container.isNil:
container = newNodeIT(nkBracket, n.sons[a].info, arrayConstr(c, n.info))
setSon(m.call, formal.position + 1, container)
else:
incrIndexType(container.typ)
addSon(container, n.sons[a])
elif n.sons[a].kind == nkExprEqExpr:
# named param # named param
# check if m.callee has such a param: # check if m.callee has such a param:
prepareNamedParam(n.sons[a]) prepareNamedParam(n.sons[a])
@ -1507,7 +1609,7 @@ proc matchesAux(c: PContext, n, nOrig: PNode,
n.sons[a].sons[1] = prepareOperand(c, formal.typ, n.sons[a].sons[1]) n.sons[a].sons[1] = prepareOperand(c, formal.typ, n.sons[a].sons[1])
n.sons[a].typ = n.sons[a].sons[1].typ n.sons[a].typ = n.sons[a].sons[1].typ
var arg = paramTypesMatch(m, formal.typ, n.sons[a].typ, var arg = paramTypesMatch(m, formal.typ, n.sons[a].typ,
n.sons[a].sons[1], nOrig.sons[a].sons[1]) n.sons[a].sons[1], n.sons[a].sons[1])
if arg == nil: if arg == nil:
m.state = csNoMatch m.state = csNoMatch
return return
@ -1536,7 +1638,7 @@ proc matchesAux(c: PContext, n, nOrig: PNode,
addSon(m.call, copyTree(n.sons[a])) addSon(m.call, copyTree(n.sons[a]))
elif formal != nil and formal.typ.kind == tyVarargs: elif formal != nil and formal.typ.kind == tyVarargs:
# beware of the side-effects in 'prepareOperand'! So only do it for # beware of the side-effects in 'prepareOperand'! So only do it for
# varags matching. See tests/metatype/tstatic_overloading. # varargs matching. See tests/metatype/tstatic_overloading.
m.baseTypeMatch = false m.baseTypeMatch = false
n.sons[a] = prepareOperand(c, formal.typ, n.sons[a]) n.sons[a] = prepareOperand(c, formal.typ, n.sons[a])
var arg = paramTypesMatch(m, formal.typ, n.sons[a].typ, var arg = paramTypesMatch(m, formal.typ, n.sons[a].typ,
@ -1599,6 +1701,10 @@ proc partialMatch*(c: PContext, n, nOrig: PNode, m: var TCandidate) =
matchesAux(c, n, nOrig, m, marker) matchesAux(c, n, nOrig, m, marker)
proc matches*(c: PContext, n, nOrig: PNode, m: var TCandidate) = proc matches*(c: PContext, n, nOrig: PNode, m: var TCandidate) =
if m.calleeSym != nil and m.calleeSym.magic in {mArrGet, mArrPut}:
m.state = csMatch
m.call = n
return
var marker = initIntSet() var marker = initIntSet()
matchesAux(c, n, nOrig, m, marker) matchesAux(c, n, nOrig, m, marker)
if m.state == csNoMatch: return if m.state == csNoMatch: return

View file

@ -15,57 +15,79 @@ import algorithm, sequtils
const const
sep = '\t' sep = '\t'
sectionSuggest = "sug"
sectionDef = "def" type
sectionContext = "con" Suggest* = object
sectionUsage = "use" section*: IdeCmd
qualifiedPath*: seq[string]
filePath*: string
line*: int # Starts at 1
column*: int # Starts at 0
doc*: string # Not escaped (yet)
symkind*: TSymKind
forth*: string # XXX TODO object on symkind
var
suggestionResultHook*: proc (result: Suggest) {.closure.}
#template sectionSuggest(): expr = "##begin\n" & getStackTrace() & "##end\n" #template sectionSuggest(): expr = "##begin\n" & getStackTrace() & "##end\n"
template origModuleName(m: PSym): string = m.name.s template origModuleName(m: PSym): string = m.name.s
proc symToStr(s: PSym, isLocal: bool, section: string, li: TLineInfo): string = proc symToSuggest(s: PSym, isLocal: bool, section: string, li: TLineInfo): Suggest =
result = section result.section = parseIdeCmd(section)
result.add(sep)
if optIdeTerse in gGlobalOptions: if optIdeTerse in gGlobalOptions:
if s.kind in routineKinds: result.symkind = s.kind
result.add renderTree(s.ast, {renderNoBody, renderNoComments, result.filePath = toFullPath(li)
renderDocComments, renderNoPragmas}) result.line = toLinenumber(li)
else: result.column = toColumn(li)
result.add s.name.s
result.add(sep)
result.add(toFullPath(li))
result.add(sep)
result.add($toLinenumber(li))
result.add(sep)
result.add($toColumn(li))
else: else:
result.add($s.kind) result.symkind = s.kind
result.add(sep) result.qualifiedPath = @[]
if not isLocal and s.kind != skModule: if not isLocal and s.kind != skModule:
let ow = s.owner let ow = s.owner
if ow.kind != skModule and ow.owner != nil: if ow.kind != skModule and ow.owner != nil:
let ow2 = ow.owner let ow2 = ow.owner
result.add(ow2.origModuleName) result.qualifiedPath.add(ow2.origModuleName)
result.add('.') result.qualifiedPath.add(ow.origModuleName)
result.add(ow.origModuleName) result.qualifiedPath.add(s.name.s)
result.add('.')
result.add(s.name.s)
result.add(sep)
if s.typ != nil:
result.add(typeToString(s.typ))
result.add(sep)
result.add(toFullPath(li))
result.add(sep)
result.add($toLinenumber(li))
result.add(sep)
result.add($toColumn(li))
result.add(sep)
when not defined(noDocgen):
result.add(s.extractDocComment.escape)
proc symToStr(s: PSym, isLocal: bool, section: string): string = if s.typ != nil:
result = symToStr(s, isLocal, section, s.info) result.forth = typeToString(s.typ)
else:
result.forth = ""
result.filePath = toFullPath(li)
result.line = toLinenumber(li)
result.column = toColumn(li)
when not defined(noDocgen):
result.doc = s.extractDocComment
proc `$`(suggest: Suggest): string =
result = $suggest.section
result.add(sep)
result.add($suggest.symkind)
result.add(sep)
result.add(suggest.qualifiedPath.join("."))
result.add(sep)
result.add(suggest.forth)
result.add(sep)
result.add(suggest.filePath)
result.add(sep)
result.add($suggest.line)
result.add(sep)
result.add($suggest.column)
result.add(sep)
when not defined(noDocgen):
result.add(suggest.doc.escape)
proc symToSuggest(s: PSym, isLocal: bool, section: string): Suggest =
result = symToSuggest(s, isLocal, section, s.info)
proc suggestResult(s: Suggest) =
if not isNil(suggestionResultHook):
suggestionResultHook(s)
else:
suggestWriteln($(s))
proc filterSym(s: PSym): bool {.inline.} = proc filterSym(s: PSym): bool {.inline.} =
result = s.kind != skModule result = s.kind != skModule
@ -82,9 +104,9 @@ proc fieldVisible*(c: PContext, f: PSym): bool {.inline.} =
result = true result = true
break break
proc suggestField(c: PContext, s: PSym, outputs: var int) = proc suggestField(c: PContext, s: PSym, outputs: var int) =
if filterSym(s) and fieldVisible(c, s): if filterSym(s) and fieldVisible(c, s):
suggestWriteln(symToStr(s, isLocal=true, sectionSuggest)) suggestResult(symToSuggest(s, isLocal=true, $ideSug))
inc outputs inc outputs
template wholeSymTab(cond, section: expr) {.immediate.} = template wholeSymTab(cond, section: expr) {.immediate.} =
@ -97,20 +119,20 @@ template wholeSymTab(cond, section: expr) {.immediate.} =
for item in entries: for item in entries:
let it {.inject.} = item let it {.inject.} = item
if cond: if cond:
suggestWriteln(symToStr(it, isLocal = isLocal, section)) suggestResult(symToSuggest(it, isLocal = isLocal, section))
inc outputs inc outputs
proc suggestSymList(c: PContext, list: PNode, outputs: var int) = proc suggestSymList(c: PContext, list: PNode, outputs: var int) =
for i in countup(0, sonsLen(list) - 1): for i in countup(0, sonsLen(list) - 1):
if list.sons[i].kind == nkSym: if list.sons[i].kind == nkSym:
suggestField(c, list.sons[i].sym, outputs) suggestField(c, list.sons[i].sym, outputs)
#else: InternalError(list.info, "getSymFromList") #else: InternalError(list.info, "getSymFromList")
proc suggestObject(c: PContext, n: PNode, outputs: var int) = proc suggestObject(c: PContext, n: PNode, outputs: var int) =
case n.kind case n.kind
of nkRecList: of nkRecList:
for i in countup(0, sonsLen(n)-1): suggestObject(c, n.sons[i], outputs) for i in countup(0, sonsLen(n)-1): suggestObject(c, n.sons[i], outputs)
of nkRecCase: of nkRecCase:
var L = sonsLen(n) var L = sonsLen(n)
if L > 0: if L > 0:
suggestObject(c, n.sons[0], outputs) suggestObject(c, n.sons[0], outputs)
@ -118,7 +140,7 @@ proc suggestObject(c: PContext, n: PNode, outputs: var int) =
of nkSym: suggestField(c, n.sym, outputs) of nkSym: suggestField(c, n.sym, outputs)
else: discard else: discard
proc nameFits(c: PContext, s: PSym, n: PNode): bool = proc nameFits(c: PContext, s: PSym, n: PNode): bool =
var op = n.sons[0] var op = n.sons[0]
if op.kind in {nkOpenSymChoice, nkClosedSymChoice}: op = op.sons[0] if op.kind in {nkOpenSymChoice, nkClosedSymChoice}: op = op.sons[0]
var opr: PIdent var opr: PIdent
@ -128,8 +150,8 @@ proc nameFits(c: PContext, s: PSym, n: PNode): bool =
else: return false else: return false
result = opr.id == s.name.id result = opr.id == s.name.id
proc argsFit(c: PContext, candidate: PSym, n, nOrig: PNode): bool = proc argsFit(c: PContext, candidate: PSym, n, nOrig: PNode): bool =
case candidate.kind case candidate.kind
of OverloadableSyms: of OverloadableSyms:
var m: TCandidate var m: TCandidate
initCandidate(c, m, candidate, nil) initCandidate(c, m, candidate, nil)
@ -138,11 +160,11 @@ proc argsFit(c: PContext, candidate: PSym, n, nOrig: PNode): bool =
else: else:
result = false result = false
proc suggestCall(c: PContext, n, nOrig: PNode, outputs: var int) = proc suggestCall(c: PContext, n, nOrig: PNode, outputs: var int) =
wholeSymTab(filterSym(it) and nameFits(c, it, n) and argsFit(c, it, n, nOrig), wholeSymTab(filterSym(it) and nameFits(c, it, n) and argsFit(c, it, n, nOrig),
sectionContext) $ideCon)
proc typeFits(c: PContext, s: PSym, firstArg: PType): bool {.inline.} = proc typeFits(c: PContext, s: PSym, firstArg: PType): bool {.inline.} =
if s.typ != nil and sonsLen(s.typ) > 1 and s.typ.sons[1] != nil: if s.typ != nil and sonsLen(s.typ) > 1 and s.typ.sons[1] != nil:
# special rule: if system and some weird generic match via 'tyExpr' # special rule: if system and some weird generic match via 'tyExpr'
# or 'tyGenericParam' we won't list it either to reduce the noise (nobody # or 'tyGenericParam' we won't list it either to reduce the noise (nobody
@ -157,7 +179,7 @@ proc typeFits(c: PContext, s: PSym, firstArg: PType): bool {.inline.} =
proc suggestOperations(c: PContext, n: PNode, typ: PType, outputs: var int) = proc suggestOperations(c: PContext, n: PNode, typ: PType, outputs: var int) =
assert typ != nil assert typ != nil
wholeSymTab(filterSymNoOpr(it) and typeFits(c, it, typ), sectionSuggest) wholeSymTab(filterSymNoOpr(it) and typeFits(c, it, typ), $ideSug)
proc suggestEverything(c: PContext, n: PNode, outputs: var int) = proc suggestEverything(c: PContext, n: PNode, outputs: var int) =
# do not produce too many symbols: # do not produce too many symbols:
@ -166,7 +188,7 @@ proc suggestEverything(c: PContext, n: PNode, outputs: var int) =
if scope == c.topLevelScope: isLocal = false if scope == c.topLevelScope: isLocal = false
for it in items(scope.symbols): for it in items(scope.symbols):
if filterSym(it): if filterSym(it):
suggestWriteln(symToStr(it, isLocal = isLocal, sectionSuggest)) suggestResult(symToSuggest(it, isLocal = isLocal, $ideSug))
inc outputs inc outputs
if scope == c.topLevelScope: break if scope == c.topLevelScope: break
@ -176,48 +198,48 @@ proc suggestFieldAccess(c: PContext, n: PNode, outputs: var int) =
var typ = n.typ var typ = n.typ
if typ == nil: if typ == nil:
# a module symbol has no type for example: # a module symbol has no type for example:
if n.kind == nkSym and n.sym.kind == skModule: if n.kind == nkSym and n.sym.kind == skModule:
if n.sym == c.module: if n.sym == c.module:
# all symbols accessible, because we are in the current module: # all symbols accessible, because we are in the current module:
for it in items(c.topLevelScope.symbols): for it in items(c.topLevelScope.symbols):
if filterSym(it): if filterSym(it):
suggestWriteln(symToStr(it, isLocal=false, sectionSuggest)) suggestResult(symToSuggest(it, isLocal=false, $ideSug))
inc outputs inc outputs
else: else:
for it in items(n.sym.tab): for it in items(n.sym.tab):
if filterSym(it): if filterSym(it):
suggestWriteln(symToStr(it, isLocal=false, sectionSuggest)) suggestResult(symToSuggest(it, isLocal=false, $ideSug))
inc outputs inc outputs
else: else:
# fallback: # fallback:
suggestEverything(c, n, outputs) suggestEverything(c, n, outputs)
elif typ.kind == tyEnum and n.kind == nkSym and n.sym.kind == skType: elif typ.kind == tyEnum and n.kind == nkSym and n.sym.kind == skType:
# look up if the identifier belongs to the enum: # look up if the identifier belongs to the enum:
var t = typ var t = typ
while t != nil: while t != nil:
suggestSymList(c, t.n, outputs) suggestSymList(c, t.n, outputs)
t = t.sons[0] t = t.sons[0]
suggestOperations(c, n, typ, outputs) suggestOperations(c, n, typ, outputs)
else: else:
typ = skipTypes(typ, {tyGenericInst, tyVar, tyPtr, tyRef}) typ = skipTypes(typ, {tyGenericInst, tyVar, tyPtr, tyRef})
if typ.kind == tyObject: if typ.kind == tyObject:
var t = typ var t = typ
while true: while true:
suggestObject(c, t.n, outputs) suggestObject(c, t.n, outputs)
if t.sons[0] == nil: break if t.sons[0] == nil: break
t = skipTypes(t.sons[0], {tyGenericInst}) t = skipTypes(t.sons[0], {tyGenericInst})
suggestOperations(c, n, typ, outputs) suggestOperations(c, n, typ, outputs)
elif typ.kind == tyTuple and typ.n != nil: elif typ.kind == tyTuple and typ.n != nil:
suggestSymList(c, typ.n, outputs) suggestSymList(c, typ.n, outputs)
suggestOperations(c, n, typ, outputs) suggestOperations(c, n, typ, outputs)
else: else:
suggestOperations(c, n, typ, outputs) suggestOperations(c, n, typ, outputs)
type type
TCheckPointResult = enum TCheckPointResult = enum
cpNone, cpFuzzy, cpExact cpNone, cpFuzzy, cpExact
proc inCheckpoint(current: TLineInfo): TCheckPointResult = proc inCheckpoint(current: TLineInfo): TCheckPointResult =
if current.fileIndex == gTrackPos.fileIndex: if current.fileIndex == gTrackPos.fileIndex:
if current.line == gTrackPos.line and if current.line == gTrackPos.line and
abs(current.col-gTrackPos.col) < 4: abs(current.col-gTrackPos.col) < 4:
@ -233,8 +255,8 @@ proc findClosestDot(n: PNode): PNode =
result = findClosestDot(n.sons[i]) result = findClosestDot(n.sons[i])
if result != nil: return if result != nil: return
proc findClosestCall(n: PNode): PNode = proc findClosestCall(n: PNode): PNode =
if n.kind in nkCallKinds and inCheckpoint(n.info) == cpExact: if n.kind in nkCallKinds and inCheckpoint(n.info) == cpExact:
result = n result = n
else: else:
for i in 0.. <safeLen(n): for i in 0.. <safeLen(n):
@ -248,8 +270,8 @@ proc isTracked(current: TLineInfo, tokenLen: int): bool =
if col >= current.col and col <= current.col+tokenLen-1: if col >= current.col and col <= current.col+tokenLen-1:
return true return true
proc findClosestSym(n: PNode): PNode = proc findClosestSym(n: PNode): PNode =
if n.kind == nkSym and inCheckpoint(n.info) == cpExact: if n.kind == nkSym and inCheckpoint(n.info) == cpExact:
result = n result = n
elif n.kind notin {nkNone..nkNilLit}: elif n.kind notin {nkNone..nkNilLit}:
for i in 0.. <sonsLen(n): for i in 0.. <sonsLen(n):
@ -263,16 +285,16 @@ var
proc findUsages(info: TLineInfo; s: PSym) = proc findUsages(info: TLineInfo; s: PSym) =
if usageSym == nil and isTracked(info, s.name.s.len): if usageSym == nil and isTracked(info, s.name.s.len):
usageSym = s usageSym = s
suggestWriteln(symToStr(s, isLocal=false, sectionUsage)) suggestResult(symToSuggest(s, isLocal=false, $ideUse))
elif s == usageSym: elif s == usageSym:
if lastLineInfo != info: if lastLineInfo != info:
suggestWriteln(symToStr(s, isLocal=false, sectionUsage, info)) suggestResult(symToSuggest(s, isLocal=false, $ideUse, info))
lastLineInfo = info lastLineInfo = info
proc findDefinition(info: TLineInfo; s: PSym) = proc findDefinition(info: TLineInfo; s: PSym) =
if s.isNil: return if s.isNil: return
if isTracked(info, s.name.s.len): if isTracked(info, s.name.s.len):
suggestWriteln(symToStr(s, isLocal=false, sectionDef)) suggestResult(symToSuggest(s, isLocal=false, $ideDef))
suggestQuit() suggestQuit()
proc ensureIdx[T](x: var T, y: int) = proc ensureIdx[T](x: var T, y: int) =
@ -287,6 +309,10 @@ proc suggestSym*(info: TLineInfo; s: PSym) {.inline.} =
findUsages(info, s) findUsages(info, s)
elif gIdeCmd == ideDef: elif gIdeCmd == ideDef:
findDefinition(info, s) findDefinition(info, s)
elif gIdeCmd == ideDus and s != nil:
if isTracked(info, s.name.s.len):
suggestResult(symToSuggest(s, isLocal=false, $ideDef))
findUsages(info, s)
proc markUsed(info: TLineInfo; s: PSym) = proc markUsed(info: TLineInfo; s: PSym) =
incl(s.flags, sfUsed) incl(s.flags, sfUsed)
@ -306,15 +332,15 @@ proc safeSemExpr*(c: PContext, n: PNode): PNode =
except ERecoverableError: except ERecoverableError:
result = ast.emptyNode result = ast.emptyNode
proc suggestExpr*(c: PContext, node: PNode) = proc suggestExpr*(c: PContext, node: PNode) =
if nfIsCursor notin node.flags: if nfIsCursor notin node.flags:
if gTrackPos.line < 0: return if gTrackPos.line < 0: return
var cp = inCheckpoint(node.info) var cp = inCheckpoint(node.info)
if cp == cpNone: return if cp == cpNone: return
var outputs = 0 var outputs = 0
# This keeps semExpr() from coming here recursively: # This keeps semExpr() from coming here recursively:
if c.inCompilesContext > 0: return if c.compilesContextId > 0: return
inc(c.inCompilesContext) inc(c.compilesContextId)
if gIdeCmd == ideSug: if gIdeCmd == ideSug:
var n = if nfIsCursor in node.flags: node else: findClosestDot(node) var n = if nfIsCursor in node.flags: node else: findClosestDot(node)
@ -327,7 +353,7 @@ proc suggestExpr*(c: PContext, node: PNode) =
#writeStackTrace() #writeStackTrace()
else: else:
suggestEverything(c, n, outputs) suggestEverything(c, n, outputs)
elif gIdeCmd == ideCon: elif gIdeCmd == ideCon:
var n = if nfIsCursor in node.flags: node else: findClosestCall(node) var n = if nfIsCursor in node.flags: node else: findClosestCall(node)
if n == nil: n = node if n == nil: n = node
@ -342,9 +368,9 @@ proc suggestExpr*(c: PContext, node: PNode) =
if x.kind == nkEmpty or x.typ == nil: break if x.kind == nkEmpty or x.typ == nil: break
addSon(a, x) addSon(a, x)
suggestCall(c, a, n, outputs) suggestCall(c, a, n, outputs)
dec(c.inCompilesContext)
if outputs > 0 and gIdeCmd != ideUse: suggestQuit()
proc suggestStmt*(c: PContext, n: PNode) = dec(c.compilesContextId)
if outputs > 0 and gIdeCmd notin {ideUse, ideDus}: suggestQuit()
proc suggestStmt*(c: PContext, n: PNode) =
suggestExpr(c, n) suggestExpr(c, n)

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