big refactoring: parser compiles again

This commit is contained in:
Andreas Rumpf
2018-05-10 10:49:51 +02:00
parent 79ec95a9b5
commit 61e57cfa13
15 changed files with 1156 additions and 1228 deletions

View File

@@ -1564,15 +1564,17 @@ proc getInt*(a: PNode): BiggestInt =
case a.kind
of nkCharLit..nkUInt64Lit: result = a.intVal
else:
internalError(a.info, "getInt")
result = 0
#internalError(a.info, "getInt")
doAssert false, "getInt"
#result = 0
proc getFloat*(a: PNode): BiggestFloat =
case a.kind
of nkFloatLiterals: result = a.floatVal
else:
internalError(a.info, "getFloat")
result = 0.0
doAssert false, "getFloat"
#internalError(a.info, "getFloat")
#result = 0.0
proc getStr*(a: PNode): string =
case a.kind
@@ -1581,16 +1583,18 @@ proc getStr*(a: PNode): string =
# let's hope this fixes more problems than it creates:
result = nil
else:
internalError(a.info, "getStr")
result = ""
doAssert false, "getStr"
#internalError(a.info, "getStr")
#result = ""
proc getStrOrChar*(a: PNode): string =
case a.kind
of nkStrLit..nkTripleStrLit: result = a.strVal
of nkCharLit..nkUInt64Lit: result = $chr(int(a.intVal))
else:
internalError(a.info, "getStrOrChar")
result = ""
doAssert false, "getStrOrChar"
#internalError(a.info, "getStrOrChar")
#result = ""
proc isGenericRoutine*(s: PSym): bool =
case s.kind

View File

@@ -180,7 +180,7 @@ proc lookupInRecord(n: PNode, field: PIdent): PSym =
result = lookupInRecord(n.sons[i], field)
if result != nil: return
of nkRecCase:
if (n.sons[0].kind != nkSym): internalError(n.info, "lookupInRecord")
if (n.sons[0].kind != nkSym): return nil
result = lookupInRecord(n.sons[0], field)
if result != nil: return
for i in countup(1, sonsLen(n) - 1):
@@ -188,10 +188,10 @@ proc lookupInRecord(n: PNode, field: PIdent): PSym =
of nkOfBranch, nkElse:
result = lookupInRecord(lastSon(n.sons[i]), field)
if result != nil: return
else: internalError(n.info, "lookupInRecord(record case branch)")
else: return nil
of nkSym:
if n.sym.name.id == field.id: result = n.sym
else: internalError(n.info, "lookupInRecord()")
else: return nil
proc getModule(s: PSym): PSym =
result = s
@@ -203,7 +203,7 @@ proc getSymFromList(list: PNode, ident: PIdent, start: int = 0): PSym =
if list.sons[i].kind == nkSym:
result = list.sons[i].sym
if result.name.id == ident.id: return
else: internalError(list.info, "getSymFromList")
else: return nil
result = nil
proc hashNode(p: RootRef): Hash =

View File

@@ -18,7 +18,6 @@ template bootSwitch(name, expr, userString) =
bootSwitch(usedRelease, defined(release), "-d:release")
bootSwitch(usedGnuReadline, defined(useLinenoise), "-d:useLinenoise")
bootSwitch(usedNoCaas, defined(noCaas), "-d:noCaas")
bootSwitch(usedBoehm, defined(boehmgc), "--gc:boehm")
bootSwitch(usedMarkAndSweep, defined(gcmarkandsweep), "--gc:markAndSweep")
bootSwitch(usedGenerational, defined(gcgenerational), "--gc:generational")
@@ -27,7 +26,7 @@ bootSwitch(usedNoGC, defined(nogc), "--gc:none")
import
os, msgs, options, nversion, condsyms, strutils, extccomp, platform,
wordrecg, parseutils, nimblecmd, idents, parseopt, sequtils
wordrecg, parseutils, nimblecmd, idents, parseopt, sequtils, configuration
# but some have deps to imported modules. Yay.
bootSwitch(usedTinyC, hasTinyCBackend, "-d:tinyc")
@@ -36,24 +35,15 @@ bootSwitch(usedNativeStacktrace,
"-d:nativeStackTrace")
bootSwitch(usedFFI, hasFFI, "-d:useFFI")
proc writeCommandLineUsage*()
type
TCmdLinePass* = enum
passCmd1, # first pass over the command line
passCmd2, # second pass over the command line
passPP # preprocessor called processCommand()
proc processCommand*(switch: string, pass: TCmdLinePass; config: ConfigRef)
proc processSwitch*(switch, arg: string, pass: TCmdLinePass, info: TLineInfo;
config: ConfigRef)
# implementation
const
HelpMessage = "Nim Compiler Version $1 [$2: $3]\n" &
"Compiled at $4 $5\n" &
"Compiled at $4\n" &
"Copyright (c) 2006-" & copyrightYear & " by Andreas Rumpf\n"
const
@@ -68,7 +58,7 @@ const
proc getCommandLineDesc(): string =
result = (HelpMessage % [VersionAsString, platform.OS[platform.hostOS].name,
CPU[platform.hostCPU].name, CompileDate, CompileTime]) &
CPU[platform.hostCPU].name, CompileDate]) &
Usage
proc helpOnError(pass: TCmdLinePass) =
@@ -80,7 +70,7 @@ proc writeAdvancedUsage(pass: TCmdLinePass) =
if pass == passCmd1:
msgWriteln(`%`(HelpMessage, [VersionAsString,
platform.OS[platform.hostOS].name,
CPU[platform.hostCPU].name, CompileDate, CompileTime]) &
CPU[platform.hostCPU].name, CompileDate]) &
AdvancedUsage,
{msgStdout})
msgQuit(0)
@@ -89,7 +79,7 @@ proc writeFullhelp(pass: TCmdLinePass) =
if pass == passCmd1:
msgWriteln(`%`(HelpMessage, [VersionAsString,
platform.OS[platform.hostOS].name,
CPU[platform.hostCPU].name, CompileDate, CompileTime]) &
CPU[platform.hostCPU].name, CompileDate]) &
Usage & AdvancedUsage,
{msgStdout})
msgQuit(0)
@@ -98,7 +88,7 @@ proc writeVersionInfo(pass: TCmdLinePass) =
if pass == passCmd1:
msgWriteln(`%`(HelpMessage, [VersionAsString,
platform.OS[platform.hostOS].name,
CPU[platform.hostCPU].name, CompileDate, CompileTime]),
CPU[platform.hostCPU].name, CompileDate]),
{msgStdout})
const gitHash = gorge("git log -n 1 --format=%H").strip
@@ -106,15 +96,12 @@ proc writeVersionInfo(pass: TCmdLinePass) =
msgWriteln("git hash: " & gitHash, {msgStdout})
msgWriteln("active boot switches:" & usedRelease &
usedTinyC & usedGnuReadline & usedNativeStacktrace & usedNoCaas &
usedTinyC & usedGnuReadline & usedNativeStacktrace &
usedFFI & usedBoehm & usedMarkAndSweep & usedGenerational & usedGoGC & usedNoGC,
{msgStdout})
msgQuit(0)
var
helpWritten: bool
proc writeCommandLineUsage() =
proc writeCommandLineUsage*(helpWritten: var bool) =
if not helpWritten:
msgWriteln(getCommandLineDesc(), {msgStdout})
helpWritten = true
@@ -123,11 +110,16 @@ proc addPrefix(switch: string): string =
if len(switch) == 1: result = "-" & switch
else: result = "--" & switch
proc invalidCmdLineOption(pass: TCmdLinePass, switch: string, info: TLineInfo) =
if switch == " ": localError(info, errInvalidCmdLineOption, "-")
else: localError(info, errInvalidCmdLineOption, addPrefix(switch))
const
errInvalidCmdLineOption = "invalid command line option: '$1'"
errOnOrOffExpectedButXFound = "'on' or 'off' expected, but '$1' found"
errOnOffOrListExpectedButXFound = "'on', 'off' or 'list' expected, but '$1' found"
proc splitSwitch(switch: string, cmd, arg: var string, pass: TCmdLinePass,
proc invalidCmdLineOption(conf: ConfigRef; pass: TCmdLinePass, switch: string, info: TLineInfo) =
if switch == " ": localError(conf, info, errInvalidCmdLineOption % "-")
else: localError(conf, info, errInvalidCmdLineOption % addPrefix(switch))
proc splitSwitch(conf: ConfigRef; switch: string, cmd, arg: var string, pass: TCmdLinePass,
info: TLineInfo) =
cmd = ""
var i = 0
@@ -140,46 +132,41 @@ proc splitSwitch(switch: string, cmd, arg: var string, pass: TCmdLinePass,
inc(i)
if i >= len(switch): arg = ""
elif switch[i] in {':', '=', '['}: arg = substr(switch, i + 1)
else: invalidCmdLineOption(pass, switch, info)
else: invalidCmdLineOption(conf, pass, switch, info)
proc processOnOffSwitch(op: TOptions, arg: string, pass: TCmdLinePass,
proc processOnOffSwitch(conf: ConfigRef; op: TOptions, arg: string, pass: TCmdLinePass,
info: TLineInfo) =
case arg.normalize
of "on": gOptions = gOptions + op
of "off": gOptions = gOptions - op
else: localError(info, errOnOrOffExpectedButXFound, arg)
else: localError(conf, info, errOnOrOffExpectedButXFound % arg)
proc processOnOffSwitchOrList(op: TOptions, arg: string, pass: TCmdLinePass,
proc processOnOffSwitchOrList(conf: ConfigRef; op: TOptions, arg: string, pass: TCmdLinePass,
info: TLineInfo): bool =
result = false
case arg.normalize
of "on": gOptions = gOptions + op
of "off": gOptions = gOptions - op
else:
if arg == "list":
result = true
else:
localError(info, errOnOffOrListExpectedButXFound, arg)
of "list": result = true
else: localError(conf, info, errOnOffOrListExpectedButXFound % arg)
proc processOnOffSwitchG(op: TGlobalOptions, arg: string, pass: TCmdLinePass,
proc processOnOffSwitchG(conf: ConfigRef; op: TGlobalOptions, arg: string, pass: TCmdLinePass,
info: TLineInfo) =
case arg.normalize
of "on": gGlobalOptions = gGlobalOptions + op
of "off": gGlobalOptions = gGlobalOptions - op
else: localError(info, errOnOrOffExpectedButXFound, arg)
else: localError(conf, info, errOnOrOffExpectedButXFound % arg)
proc expectArg(switch, arg: string, pass: TCmdLinePass, info: TLineInfo) =
if arg == "": localError(info, errCmdLineArgExpected, addPrefix(switch))
proc expectArg(conf: ConfigRef; switch, arg: string, pass: TCmdLinePass, info: TLineInfo) =
if arg == "":
localError(conf, info, "argument for command line option expected: '$1'" % addPrefix(switch))
proc expectNoArg(switch, arg: string, pass: TCmdLinePass, info: TLineInfo) =
if arg != "": localError(info, errCmdLineNoArgExpected, addPrefix(switch))
var
enableNotes: TNoteKinds
disableNotes: TNoteKinds
proc expectNoArg(conf: ConfigRef; switch, arg: string, pass: TCmdLinePass, info: TLineInfo) =
if arg != "":
localError(conf, info, "invalid argument for command line option: '$1'" % addPrefix(switch))
proc processSpecificNote*(arg: string, state: TSpecialWord, pass: TCmdLinePass,
info: TLineInfo; orig: string) =
info: TLineInfo; orig: string; conf: ConfigRef) =
var id = "" # arg = "X]:on|off"
var i = 0
var n = hintMin
@@ -187,35 +174,40 @@ proc processSpecificNote*(arg: string, state: TSpecialWord, pass: TCmdLinePass,
add(id, arg[i])
inc(i)
if i < len(arg) and (arg[i] == ']'): inc(i)
else: invalidCmdLineOption(pass, orig, info)
else: invalidCmdLineOption(conf, pass, orig, info)
if i < len(arg) and (arg[i] in {':', '='}): inc(i)
else: invalidCmdLineOption(pass, orig, info)
else: invalidCmdLineOption(conf, pass, orig, info)
if state == wHint:
let x = findStr(msgs.HintsToStr, id)
let x = findStr(configuration.HintsToStr, id)
if x >= 0: n = TNoteKind(x + ord(hintMin))
else: localError(info, "unknown hint: " & id)
else: localError(conf, info, "unknown hint: " & id)
else:
let x = findStr(msgs.WarningsToStr, id)
let x = findStr(configuration.WarningsToStr, id)
if x >= 0: n = TNoteKind(x + ord(warnMin))
else: localError(info, "unknown warning: " & id)
else: localError(conf, info, "unknown warning: " & id)
case substr(arg, i).normalize
of "on":
incl(gNotes, n)
incl(gMainPackageNotes, n)
incl(enableNotes, n)
incl(conf.notes, n)
incl(conf.mainPackageNotes, n)
incl(conf.enableNotes, n)
of "off":
excl(gNotes, n)
excl(gMainPackageNotes, n)
incl(disableNotes, n)
excl(ForeignPackageNotes, n)
else: localError(info, errOnOrOffExpectedButXFound, arg)
excl(conf.notes, n)
excl(conf.mainPackageNotes, n)
incl(conf.disableNotes, n)
excl(conf.foreignPackageNotes, n)
else: localError(conf, info, errOnOrOffExpectedButXFound % arg)
proc processCompile(filename: string) =
var found = findFile(filename)
proc processCompile(conf: ConfigRef; filename: string) =
var found = findFile(conf, filename)
if found == "": found = filename
extccomp.addExternalFileToCompile(found)
extccomp.addExternalFileToCompile(conf, found)
proc testCompileOptionArg*(switch, arg: string, info: TLineInfo): bool =
const
errNoneBoehmRefcExpectedButXFound = "'none', 'boehm' or 'refc' expected, but '$1' found"
errNoneSpeedOrSizeExpectedButXFound = "'none', 'speed' or 'size' expected, but '$1' found"
errGuiConsoleOrLibExpectedButXFound = "'gui', 'console' or 'lib' expected, but '$1' found"
proc testCompileOptionArg*(conf: ConfigRef; switch, arg: string, info: TLineInfo): bool =
case switch.normalize
of "gc":
case arg.normalize
@@ -227,13 +219,13 @@ proc testCompileOptionArg*(switch, arg: string, info: TLineInfo): bool =
of "go": result = gSelectedGC == gcGo
of "none": result = gSelectedGC == gcNone
of "stack", "regions": result = gSelectedGC == gcRegions
else: localError(info, errNoneBoehmRefcExpectedButXFound, arg)
else: localError(conf, info, errNoneBoehmRefcExpectedButXFound % arg)
of "opt":
case arg.normalize
of "speed": result = contains(gOptions, optOptimizeSpeed)
of "size": result = contains(gOptions, optOptimizeSize)
of "none": result = gOptions * {optOptimizeSpeed, optOptimizeSize} == {}
else: localError(info, errNoneSpeedOrSizeExpectedButXFound, arg)
else: localError(conf, info, errNoneSpeedOrSizeExpectedButXFound % arg)
of "verbosity": result = $gVerbosity == arg
of "app":
case arg.normalize
@@ -243,12 +235,12 @@ proc testCompileOptionArg*(switch, arg: string, info: TLineInfo): bool =
not contains(gGlobalOptions, optGenGuiApp)
of "staticlib": result = contains(gGlobalOptions, optGenStaticLib) and
not contains(gGlobalOptions, optGenGuiApp)
else: localError(info, errGuiConsoleOrLibExpectedButXFound, arg)
else: localError(conf, info, errGuiConsoleOrLibExpectedButXFound % arg)
of "dynliboverride":
result = isDynlibOverride(arg)
else: invalidCmdLineOption(passCmd1, switch, info)
result = isDynlibOverride(conf, arg)
else: invalidCmdLineOption(conf, passCmd1, switch, info)
proc testCompileOption*(switch: string, info: TLineInfo): bool =
proc testCompileOption*(conf: ConfigRef; switch: string, info: TLineInfo): bool =
case switch.normalize
of "debuginfo": result = contains(gGlobalOptions, optCDebug)
of "compileonly", "c": result = contains(gGlobalOptions, optCompileOnly)
@@ -286,9 +278,9 @@ proc testCompileOption*(switch: string, info: TLineInfo): bool =
of "implicitstatic": result = contains(gOptions, optImplicitStatic)
of "patterns": result = contains(gOptions, optPatterns)
of "excessivestacktrace": result = contains(gGlobalOptions, optExcessiveStackTrace)
else: invalidCmdLineOption(passCmd1, switch, info)
else: invalidCmdLineOption(conf, passCmd1, switch, info)
proc processPath(path: string, info: TLineInfo,
proc processPath(conf: ConfigRef; path: string, info: TLineInfo,
notRelativeToProj = false): string =
let p = if os.isAbsolute(path) or '$' in path:
path
@@ -297,12 +289,12 @@ proc processPath(path: string, info: TLineInfo,
else:
options.gProjectPath / path
try:
result = pathSubs(p, info.toFullPath().splitFile().dir)
result = pathSubs(conf, p, info.toFullPath().splitFile().dir)
except ValueError:
localError(info, "invalid path: " & p)
localError(conf, info, "invalid path: " & p)
result = p
proc processCfgPath(path: string, info: TLineInfo): string =
proc processCfgPath(conf: ConfigRef; path: string, info: TLineInfo): string =
let path = if path[0] == '"': strutils.unescape(path) else: path
let basedir = info.toFullPath().splitFile().dir
let p = if os.isAbsolute(path) or '$' in path:
@@ -310,225 +302,228 @@ proc processCfgPath(path: string, info: TLineInfo): string =
else:
basedir / path
try:
result = pathSubs(p, basedir)
result = pathSubs(conf, p, basedir)
except ValueError:
localError(info, "invalid path: " & p)
localError(conf, info, "invalid path: " & p)
result = p
proc trackDirty(arg: string, info: TLineInfo) =
const
errInvalidNumber = "$1 is not a valid number"
proc trackDirty(conf: ConfigRef; arg: string, info: TLineInfo) =
var a = arg.split(',')
if a.len != 4: localError(info, errTokenExpected,
"DIRTY_BUFFER,ORIGINAL_FILE,LINE,COLUMN")
if a.len != 4: localError(conf, info,
"DIRTY_BUFFER,ORIGINAL_FILE,LINE,COLUMN expected")
var line, column: int
if parseUtils.parseInt(a[2], line) <= 0:
localError(info, errInvalidNumber, a[1])
localError(conf, info, errInvalidNumber % a[1])
if parseUtils.parseInt(a[3], column) <= 0:
localError(info, errInvalidNumber, a[2])
localError(conf, info, errInvalidNumber % a[2])
let dirtyOriginalIdx = a[1].fileInfoIdx
let dirtyOriginalIdx = fileInfoIdx(conf, a[1])
if dirtyOriginalIdx.int32 >= 0:
msgs.setDirtyFile(dirtyOriginalIdx, a[0])
gTrackPos = newLineInfo(dirtyOriginalIdx, line, column)
proc track(arg: string, info: TLineInfo) =
proc track(conf: ConfigRef; arg: string, info: TLineInfo) =
var a = arg.split(',')
if a.len != 3: localError(info, errTokenExpected, "FILE,LINE,COLUMN")
if a.len != 3: localError(conf, info, "FILE,LINE,COLUMN expected")
var line, column: int
if parseUtils.parseInt(a[1], line) <= 0:
localError(info, errInvalidNumber, a[1])
localError(conf, info, errInvalidNumber % a[1])
if parseUtils.parseInt(a[2], column) <= 0:
localError(info, errInvalidNumber, a[2])
gTrackPos = newLineInfo(a[0], line, column)
localError(conf, info, errInvalidNumber % a[2])
gTrackPos = newLineInfo(conf, a[0], line, column)
proc dynlibOverride(switch, arg: string, pass: TCmdLinePass, info: TLineInfo) =
proc dynlibOverride(conf: ConfigRef; switch, arg: string, pass: TCmdLinePass, info: TLineInfo) =
if pass in {passCmd2, passPP}:
expectArg(switch, arg, pass, info)
options.inclDynlibOverride(arg)
expectArg(conf, switch, arg, pass, info)
options.inclDynlibOverride(conf, arg)
proc processSwitch(switch, arg: string, pass: TCmdLinePass, info: TLineInfo;
config: ConfigRef) =
proc processSwitch*(switch, arg: string, pass: TCmdLinePass, info: TLineInfo;
conf: ConfigRef) =
var
theOS: TSystemOS
cpu: TSystemCPU
key, val: string
case switch.normalize
of "path", "p":
expectArg(switch, arg, pass, info)
addPath(if pass == passPP: processCfgPath(arg, info) else: processPath(arg, info), info)
expectArg(conf, switch, arg, pass, info)
addPath(conf, if pass == passPP: processCfgPath(conf, arg, info) else: processPath(conf, arg, info), info)
of "nimblepath", "babelpath":
# keep the old name for compat
if pass in {passCmd2, passPP} and not options.gNoNimblePath:
expectArg(switch, arg, pass, info)
var path = processPath(arg, info, notRelativeToProj=true)
expectArg(conf, switch, arg, pass, info)
var path = processPath(conf, arg, info, notRelativeToProj=true)
let nimbleDir = getEnv("NIMBLE_DIR")
if nimbleDir.len > 0 and pass == passPP: path = nimbleDir / "pkgs"
nimblePath(path, info)
nimblePath(conf, path, info)
of "nonimblepath", "nobabelpath":
expectNoArg(switch, arg, pass, info)
disableNimblePath()
expectNoArg(conf, switch, arg, pass, info)
disableNimblePath(conf)
of "excludepath":
expectArg(switch, arg, pass, info)
let path = processPath(arg, info)
expectArg(conf, switch, arg, pass, info)
let path = processPath(conf, arg, info)
options.searchPaths.keepItIf( cmpPaths(it, path) != 0 )
options.lazyPaths.keepItIf( cmpPaths(it, path) != 0 )
options.searchPaths.keepItIf(cmpPaths(it, path) != 0)
options.lazyPaths.keepItIf(cmpPaths(it, path) != 0)
if (len(path) > 0) and (path[len(path) - 1] == DirSep):
let strippedPath = path[0 .. (len(path) - 2)]
options.searchPaths.keepItIf( cmpPaths(it, strippedPath) != 0 )
options.lazyPaths.keepItIf( cmpPaths(it, strippedPath) != 0 )
options.searchPaths.keepItIf(cmpPaths(it, strippedPath) != 0)
options.lazyPaths.keepItIf(cmpPaths(it, strippedPath) != 0)
of "nimcache":
expectArg(switch, arg, pass, info)
options.nimcacheDir = processPath(arg, info, true)
expectArg(conf, switch, arg, pass, info)
options.nimcacheDir = processPath(conf, arg, info, true)
of "out", "o":
expectArg(switch, arg, pass, info)
expectArg(conf, switch, arg, pass, info)
options.outFile = arg
of "docseesrcurl":
expectArg(switch, arg, pass, info)
expectArg(conf, switch, arg, pass, info)
options.docSeeSrcUrl = arg
of "mainmodule", "m":
discard "allow for backwards compatibility, but don't do anything"
of "define", "d":
expectArg(switch, arg, pass, info)
expectArg(conf, switch, arg, pass, info)
if {':', '='} in arg:
splitSwitch(arg, key, val, pass, info)
defineSymbol(key, val)
splitSwitch(conf, arg, key, val, pass, info)
defineSymbol(conf.symbols, key, val)
else:
defineSymbol(arg)
defineSymbol(conf.symbols, arg)
of "undef", "u":
expectArg(switch, arg, pass, info)
undefSymbol(arg)
expectArg(conf, switch, arg, pass, info)
undefSymbol(conf.symbols, arg)
of "symbol":
expectArg(switch, arg, pass, info)
expectArg(conf, switch, arg, pass, info)
# deprecated, do nothing
of "compile":
expectArg(switch, arg, pass, info)
if pass in {passCmd2, passPP}: processCompile(arg)
expectArg(conf, switch, arg, pass, info)
if pass in {passCmd2, passPP}: processCompile(conf, arg)
of "link":
expectArg(switch, arg, pass, info)
if pass in {passCmd2, passPP}: addExternalFileToLink(arg)
expectArg(conf, switch, arg, pass, info)
if pass in {passCmd2, passPP}: addExternalFileToLink(conf, arg)
of "debuginfo":
expectNoArg(switch, arg, pass, info)
expectNoArg(conf, switch, arg, pass, info)
incl(gGlobalOptions, optCDebug)
of "embedsrc":
expectNoArg(switch, arg, pass, info)
expectNoArg(conf, switch, arg, pass, info)
incl(gGlobalOptions, optEmbedOrigSrc)
of "compileonly", "c":
expectNoArg(switch, arg, pass, info)
expectNoArg(conf, switch, arg, pass, info)
incl(gGlobalOptions, optCompileOnly)
of "nolinking":
expectNoArg(switch, arg, pass, info)
expectNoArg(conf, switch, arg, pass, info)
incl(gGlobalOptions, optNoLinking)
of "nomain":
expectNoArg(switch, arg, pass, info)
expectNoArg(conf, switch, arg, pass, info)
incl(gGlobalOptions, optNoMain)
of "forcebuild", "f":
expectNoArg(switch, arg, pass, info)
expectNoArg(conf, switch, arg, pass, info)
incl(gGlobalOptions, optForceFullMake)
of "project":
expectNoArg(switch, arg, pass, info)
expectNoArg(conf, switch, arg, pass, info)
gWholeProject = true
of "gc":
expectArg(switch, arg, pass, info)
expectArg(conf, switch, arg, pass, info)
case arg.normalize
of "boehm":
gSelectedGC = gcBoehm
defineSymbol("boehmgc")
defineSymbol(conf.symbols, "boehmgc")
of "refc":
gSelectedGC = gcRefc
of "v2":
gSelectedGC = gcV2
of "markandsweep":
gSelectedGC = gcMarkAndSweep
defineSymbol("gcmarkandsweep")
defineSymbol(conf.symbols, "gcmarkandsweep")
of "generational":
gSelectedGC = gcGenerational
defineSymbol("gcgenerational")
defineSymbol(conf.symbols, "gcgenerational")
of "go":
gSelectedGC = gcGo
defineSymbol("gogc")
defineSymbol(conf.symbols, "gogc")
of "none":
gSelectedGC = gcNone
defineSymbol("nogc")
defineSymbol(conf.symbols, "nogc")
of "stack", "regions":
gSelectedGC= gcRegions
defineSymbol("gcregions")
else: localError(info, errNoneBoehmRefcExpectedButXFound, arg)
defineSymbol(conf.symbols, "gcregions")
else: localError(conf, info, errNoneBoehmRefcExpectedButXFound % arg)
of "warnings", "w":
if processOnOffSwitchOrList({optWarns}, arg, pass, info): listWarnings()
of "warning": processSpecificNote(arg, wWarning, pass, info, switch)
of "hint": processSpecificNote(arg, wHint, pass, info, switch)
if processOnOffSwitchOrList(conf, {optWarns}, arg, pass, info): listWarnings(conf)
of "warning": processSpecificNote(arg, wWarning, pass, info, switch, conf)
of "hint": processSpecificNote(arg, wHint, pass, info, switch, conf)
of "hints":
if processOnOffSwitchOrList({optHints}, arg, pass, info): listHints()
of "threadanalysis": processOnOffSwitchG({optThreadAnalysis}, arg, pass, info)
of "stacktrace": processOnOffSwitch({optStackTrace}, arg, pass, info)
of "excessivestacktrace": processOnOffSwitchG({optExcessiveStackTrace}, arg, pass, info)
of "linetrace": processOnOffSwitch({optLineTrace}, arg, pass, info)
if processOnOffSwitchOrList(conf, {optHints}, arg, pass, info): listHints(conf)
of "threadanalysis": processOnOffSwitchG(conf, {optThreadAnalysis}, arg, pass, info)
of "stacktrace": processOnOffSwitch(conf, {optStackTrace}, arg, pass, info)
of "excessivestacktrace": processOnOffSwitchG(conf, {optExcessiveStackTrace}, arg, pass, info)
of "linetrace": processOnOffSwitch(conf, {optLineTrace}, arg, pass, info)
of "debugger":
case arg.normalize
of "on", "endb":
gOptions.incl optEndb
defineSymbol("endb")
defineSymbol(conf.symbols, "endb")
of "off":
gOptions.excl optEndb
undefSymbol("endb")
undefSymbol(conf.symbols, "endb")
of "native", "gdb":
incl(gGlobalOptions, optCDebug)
gOptions = gOptions + {optLineDir} - {optEndb}
defineSymbol("nimTypeNames", nil) # type names are used in gdb pretty printing
undefSymbol("endb")
defineSymbol(conf.symbols, "nimTypeNames") # type names are used in gdb pretty printing
undefSymbol(conf.symbols, "endb")
else:
localError(info, "expected endb|gdb but found " & arg)
localError(conf, info, "expected endb|gdb but found " & arg)
of "profiler":
processOnOffSwitch({optProfiler}, arg, pass, info)
if optProfiler in gOptions: defineSymbol("profiler")
else: undefSymbol("profiler")
processOnOffSwitch(conf, {optProfiler}, arg, pass, info)
if optProfiler in gOptions: defineSymbol(conf.symbols, "profiler")
else: undefSymbol(conf.symbols, "profiler")
of "memtracker":
processOnOffSwitch({optMemTracker}, arg, pass, info)
if optMemTracker in gOptions: defineSymbol("memtracker")
else: undefSymbol("memtracker")
processOnOffSwitch(conf, {optMemTracker}, arg, pass, info)
if optMemTracker in gOptions: defineSymbol(conf.symbols, "memtracker")
else: undefSymbol(conf.symbols, "memtracker")
of "hotcodereloading":
processOnOffSwitch({optHotCodeReloading}, arg, pass, info)
if optHotCodeReloading in gOptions: defineSymbol("hotcodereloading")
else: undefSymbol("hotcodereloading")
processOnOffSwitch(conf, {optHotCodeReloading}, arg, pass, info)
if optHotCodeReloading in gOptions: defineSymbol(conf.symbols, "hotcodereloading")
else: undefSymbol(conf.symbols, "hotcodereloading")
of "oldnewlines":
case arg.normalize
of "on":
options.gOldNewlines = true
defineSymbol("nimOldNewlines")
defineSymbol(conf.symbols, "nimOldNewlines")
of "off":
options.gOldNewlines = false
undefSymbol("nimOldNewlines")
undefSymbol(conf.symbols, "nimOldNewlines")
else:
localError(info, errOnOrOffExpectedButXFound, arg)
of "laxstrings": processOnOffSwitch({optLaxStrings}, arg, pass, info)
of "checks", "x": processOnOffSwitch(ChecksOptions, arg, pass, info)
localError(conf, info, errOnOrOffExpectedButXFound % arg)
of "laxstrings": processOnOffSwitch(conf, {optLaxStrings}, arg, pass, info)
of "checks", "x": processOnOffSwitch(conf, ChecksOptions, arg, pass, info)
of "floatchecks":
processOnOffSwitch({optNaNCheck, optInfCheck}, arg, pass, info)
of "infchecks": processOnOffSwitch({optInfCheck}, arg, pass, info)
of "nanchecks": processOnOffSwitch({optNaNCheck}, arg, pass, info)
of "nilchecks": processOnOffSwitch({optNilCheck}, arg, pass, info)
of "objchecks": processOnOffSwitch({optObjCheck}, arg, pass, info)
of "fieldchecks": processOnOffSwitch({optFieldCheck}, arg, pass, info)
of "rangechecks": processOnOffSwitch({optRangeCheck}, arg, pass, info)
of "boundchecks": processOnOffSwitch({optBoundsCheck}, arg, pass, info)
of "overflowchecks": processOnOffSwitch({optOverflowCheck}, arg, pass, info)
of "movechecks": processOnOffSwitch({optMoveCheck}, arg, pass, info)
of "linedir": processOnOffSwitch({optLineDir}, arg, pass, info)
of "assertions", "a": processOnOffSwitch({optAssert}, arg, pass, info)
processOnOffSwitch(conf, {optNaNCheck, optInfCheck}, arg, pass, info)
of "infchecks": processOnOffSwitch(conf, {optInfCheck}, arg, pass, info)
of "nanchecks": processOnOffSwitch(conf, {optNaNCheck}, arg, pass, info)
of "nilchecks": processOnOffSwitch(conf, {optNilCheck}, arg, pass, info)
of "objchecks": processOnOffSwitch(conf, {optObjCheck}, arg, pass, info)
of "fieldchecks": processOnOffSwitch(conf, {optFieldCheck}, arg, pass, info)
of "rangechecks": processOnOffSwitch(conf, {optRangeCheck}, arg, pass, info)
of "boundchecks": processOnOffSwitch(conf, {optBoundsCheck}, arg, pass, info)
of "overflowchecks": processOnOffSwitch(conf, {optOverflowCheck}, arg, pass, info)
of "movechecks": processOnOffSwitch(conf, {optMoveCheck}, arg, pass, info)
of "linedir": processOnOffSwitch(conf, {optLineDir}, arg, pass, info)
of "assertions", "a": processOnOffSwitch(conf, {optAssert}, arg, pass, info)
of "deadcodeelim": discard # deprecated, dead code elim always on
of "threads":
processOnOffSwitchG({optThreads}, arg, pass, info)
#if optThreads in gGlobalOptions: incl(gNotes, warnGcUnsafe)
of "tlsemulation": processOnOffSwitchG({optTlsEmulation}, arg, pass, info)
of "taintmode": processOnOffSwitchG({optTaintMode}, arg, pass, info)
processOnOffSwitchG(conf, {optThreads}, arg, pass, info)
#if optThreads in gGlobalOptions: incl(conf.notes, warnGcUnsafe)
of "tlsemulation": processOnOffSwitchG(conf, {optTlsEmulation}, arg, pass, info)
of "taintmode": processOnOffSwitchG(conf, {optTaintMode}, arg, pass, info)
of "implicitstatic":
processOnOffSwitch({optImplicitStatic}, arg, pass, info)
processOnOffSwitch(conf, {optImplicitStatic}, arg, pass, info)
of "patterns":
processOnOffSwitch({optPatterns}, arg, pass, info)
processOnOffSwitch(conf, {optPatterns}, arg, pass, info)
of "opt":
expectArg(switch, arg, pass, info)
expectArg(conf, switch, arg, pass, info)
case arg.normalize
of "speed":
incl(gOptions, optOptimizeSpeed)
@@ -539,99 +534,99 @@ proc processSwitch(switch, arg: string, pass: TCmdLinePass, info: TLineInfo;
of "none":
excl(gOptions, optOptimizeSpeed)
excl(gOptions, optOptimizeSize)
else: localError(info, errNoneSpeedOrSizeExpectedButXFound, arg)
else: localError(conf, info, errNoneSpeedOrSizeExpectedButXFound % arg)
of "app":
expectArg(switch, arg, pass, info)
expectArg(conf, switch, arg, pass, info)
case arg.normalize
of "gui":
incl(gGlobalOptions, optGenGuiApp)
defineSymbol("executable")
defineSymbol("guiapp")
defineSymbol(conf.symbols, "executable")
defineSymbol(conf.symbols, "guiapp")
of "console":
excl(gGlobalOptions, optGenGuiApp)
defineSymbol("executable")
defineSymbol("consoleapp")
defineSymbol(conf.symbols, "executable")
defineSymbol(conf.symbols, "consoleapp")
of "lib":
incl(gGlobalOptions, optGenDynLib)
excl(gGlobalOptions, optGenGuiApp)
defineSymbol("library")
defineSymbol("dll")
defineSymbol(conf.symbols, "library")
defineSymbol(conf.symbols, "dll")
of "staticlib":
incl(gGlobalOptions, optGenStaticLib)
excl(gGlobalOptions, optGenGuiApp)
defineSymbol("library")
defineSymbol("staticlib")
else: localError(info, errGuiConsoleOrLibExpectedButXFound, arg)
defineSymbol(conf.symbols, "library")
defineSymbol(conf.symbols, "staticlib")
else: localError(conf, info, errGuiConsoleOrLibExpectedButXFound % arg)
of "passc", "t":
expectArg(switch, arg, pass, info)
if pass in {passCmd2, passPP}: extccomp.addCompileOptionCmd(arg)
expectArg(conf, switch, arg, pass, info)
if pass in {passCmd2, passPP}: extccomp.addCompileOptionCmd(conf, arg)
of "passl", "l":
expectArg(switch, arg, pass, info)
if pass in {passCmd2, passPP}: extccomp.addLinkOptionCmd(arg)
expectArg(conf, switch, arg, pass, info)
if pass in {passCmd2, passPP}: extccomp.addLinkOptionCmd(conf, arg)
of "cincludes":
expectArg(switch, arg, pass, info)
if pass in {passCmd2, passPP}: cIncludes.add arg.processPath(info)
expectArg(conf, switch, arg, pass, info)
if pass in {passCmd2, passPP}: cIncludes.add processPath(conf, arg, info)
of "clibdir":
expectArg(switch, arg, pass, info)
if pass in {passCmd2, passPP}: cLibs.add arg.processPath(info)
expectArg(conf, switch, arg, pass, info)
if pass in {passCmd2, passPP}: cLibs.add processPath(conf, arg, info)
of "clib":
expectArg(switch, arg, pass, info)
if pass in {passCmd2, passPP}: cLinkedLibs.add arg.processPath(info)
expectArg(conf, switch, arg, pass, info)
if pass in {passCmd2, passPP}: cLinkedLibs.add processPath(conf, arg, info)
of "header":
if config != nil: config.headerFile = arg
if conf != nil: conf.headerFile = arg
incl(gGlobalOptions, optGenIndex)
of "index":
processOnOffSwitchG({optGenIndex}, arg, pass, info)
processOnOffSwitchG(conf, {optGenIndex}, arg, pass, info)
of "import":
expectArg(switch, arg, pass, info)
expectArg(conf, switch, arg, pass, info)
if pass in {passCmd2, passPP}: implicitImports.add arg
of "include":
expectArg(switch, arg, pass, info)
expectArg(conf, switch, arg, pass, info)
if pass in {passCmd2, passPP}: implicitIncludes.add arg
of "listcmd":
expectNoArg(switch, arg, pass, info)
expectNoArg(conf, switch, arg, pass, info)
incl(gGlobalOptions, optListCmd)
of "genmapping":
expectNoArg(switch, arg, pass, info)
expectNoArg(conf, switch, arg, pass, info)
incl(gGlobalOptions, optGenMapping)
of "os":
expectArg(switch, arg, pass, info)
expectArg(conf, switch, arg, pass, info)
if pass in {passCmd1, passPP}:
theOS = platform.nameToOS(arg)
if theOS == osNone: localError(info, errUnknownOS, arg)
if theOS == osNone: localError(conf, info, "unknown OS: '$1'" % arg)
elif theOS != platform.hostOS:
setTarget(theOS, targetCPU)
of "cpu":
expectArg(switch, arg, pass, info)
expectArg(conf, switch, arg, pass, info)
if pass in {passCmd1, passPP}:
cpu = platform.nameToCPU(arg)
if cpu == cpuNone: localError(info, errUnknownCPU, arg)
if cpu == cpuNone: localError(conf, info, "unknown CPU: '$1'" % arg)
elif cpu != platform.hostCPU:
setTarget(targetOS, cpu)
of "run", "r":
expectNoArg(switch, arg, pass, info)
expectNoArg(conf, switch, arg, pass, info)
incl(gGlobalOptions, optRun)
of "verbosity":
expectArg(switch, arg, pass, info)
expectArg(conf, switch, arg, pass, info)
gVerbosity = parseInt(arg)
gNotes = NotesVerbosity[gVerbosity]
incl(gNotes, enableNotes)
excl(gNotes, disableNotes)
gMainPackageNotes = gNotes
conf.notes = NotesVerbosity[gVerbosity]
incl(conf.notes, conf.enableNotes)
excl(conf.notes, conf.disableNotes)
conf.mainPackageNotes = conf.notes
of "parallelbuild":
expectArg(switch, arg, pass, info)
expectArg(conf, switch, arg, pass, info)
gNumberOfProcessors = parseInt(arg)
of "version", "v":
expectNoArg(switch, arg, pass, info)
expectNoArg(conf, switch, arg, pass, info)
writeVersionInfo(pass)
of "advanced":
expectNoArg(switch, arg, pass, info)
expectNoArg(conf, switch, arg, pass, info)
writeAdvancedUsage(pass)
of "fullhelp":
expectNoArg(switch, arg, pass, info)
expectNoArg(conf, switch, arg, pass, info)
writeFullhelp(pass)
of "help", "h":
expectNoArg(switch, arg, pass, info)
expectNoArg(conf, switch, arg, pass, info)
helpOnError(pass)
of "symbolfiles":
case arg.normalize
@@ -640,101 +635,103 @@ proc processSwitch(switch, arg: string, pass: TCmdLinePass, info: TLineInfo;
of "writeonly": gSymbolFiles = writeOnlySf
of "readonly": gSymbolFiles = readOnlySf
of "v2": gSymbolFiles = v2Sf
else: localError(info, errOnOrOffExpectedButXFound, arg)
else: localError(conf, info, "invalid option for --symbolFiles: " & arg)
of "skipcfg":
expectNoArg(switch, arg, pass, info)
expectNoArg(conf, switch, arg, pass, info)
incl(gGlobalOptions, optSkipConfigFile)
of "skipprojcfg":
expectNoArg(switch, arg, pass, info)
expectNoArg(conf, switch, arg, pass, info)
incl(gGlobalOptions, optSkipProjConfigFile)
of "skipusercfg":
expectNoArg(switch, arg, pass, info)
expectNoArg(conf, switch, arg, pass, info)
incl(gGlobalOptions, optSkipUserConfigFile)
of "skipparentcfg":
expectNoArg(switch, arg, pass, info)
expectNoArg(conf, switch, arg, pass, info)
incl(gGlobalOptions, optSkipParentConfigFiles)
of "genscript", "gendeps":
expectNoArg(switch, arg, pass, info)
expectNoArg(conf, switch, arg, pass, info)
incl(gGlobalOptions, optGenScript)
incl(gGlobalOptions, optCompileOnly)
of "colors": processOnOffSwitchG({optUseColors}, arg, pass, info)
of "colors": processOnOffSwitchG(conf, {optUseColors}, arg, pass, info)
of "lib":
expectArg(switch, arg, pass, info)
libpath = processPath(arg, info, notRelativeToProj=true)
expectArg(conf, switch, arg, pass, info)
libpath = processPath(conf, arg, info, notRelativeToProj=true)
of "putenv":
expectArg(switch, arg, pass, info)
splitSwitch(arg, key, val, pass, info)
expectArg(conf, switch, arg, pass, info)
splitSwitch(conf, arg, key, val, pass, info)
os.putEnv(key, val)
of "cc":
expectArg(switch, arg, pass, info)
setCC(arg)
expectArg(conf, switch, arg, pass, info)
setCC(conf, arg, info)
of "track":
expectArg(switch, arg, pass, info)
track(arg, info)
expectArg(conf, switch, arg, pass, info)
track(conf, arg, info)
of "trackdirty":
expectArg(switch, arg, pass, info)
trackDirty(arg, info)
expectArg(conf, switch, arg, pass, info)
trackDirty(conf, arg, info)
of "suggest":
expectNoArg(switch, arg, pass, info)
expectNoArg(conf, switch, arg, pass, info)
gIdeCmd = ideSug
of "def":
expectNoArg(switch, arg, pass, info)
expectNoArg(conf, switch, arg, pass, info)
gIdeCmd = ideDef
of "eval":
expectArg(switch, arg, pass, info)
expectArg(conf, switch, arg, pass, info)
gEvalExpr = arg
of "context":
expectNoArg(switch, arg, pass, info)
expectNoArg(conf, switch, arg, pass, info)
gIdeCmd = ideCon
of "usages":
expectNoArg(switch, arg, pass, info)
expectNoArg(conf, switch, arg, pass, info)
gIdeCmd = ideUse
of "stdout":
expectNoArg(switch, arg, pass, info)
expectNoArg(conf, switch, arg, pass, info)
incl(gGlobalOptions, optStdout)
of "listfullpaths":
expectNoArg(switch, arg, pass, info)
expectNoArg(conf, switch, arg, pass, info)
gListFullPaths = true
of "dynliboverride":
dynlibOverride(switch, arg, pass, info)
dynlibOverride(conf, switch, arg, pass, info)
of "dynliboverrideall":
expectNoArg(switch, arg, pass, info)
expectNoArg(conf, switch, arg, pass, info)
gDynlibOverrideAll = true
of "cs":
# only supported for compatibility. Does nothing.
expectArg(switch, arg, pass, info)
expectArg(conf, switch, arg, pass, info)
of "experimental":
if arg.len == 0:
config.features.incl oldExperimentalFeatures
conf.features.incl oldExperimentalFeatures
else:
try:
config.features.incl parseEnum[Feature](arg)
conf.features.incl parseEnum[Feature](arg)
except ValueError:
localError(info, "unknown experimental feature")
localError(conf, info, "unknown experimental feature")
of "nocppexceptions":
expectNoArg(switch, arg, pass, info)
expectNoArg(conf, switch, arg, pass, info)
incl(gGlobalOptions, optNoCppExceptions)
defineSymbol("noCppExceptions")
defineSymbol(conf.symbols, "noCppExceptions")
of "cppdefine":
expectArg(switch, arg, pass, info)
if config != nil:
config.cppDefine(arg)
expectArg(conf, switch, arg, pass, info)
if conf != nil:
conf.cppDefine(arg)
of "newruntime":
expectNoArg(switch, arg, pass, info)
doAssert(config != nil)
incl(config.features, destructor)
defineSymbol("nimNewRuntime")
expectNoArg(conf, switch, arg, pass, info)
doAssert(conf != nil)
incl(conf.features, destructor)
defineSymbol(conf.symbols, "nimNewRuntime")
of "cppcompiletonamespace":
expectNoArg(switch, arg, pass, info)
expectNoArg(conf, switch, arg, pass, info)
useNimNamespace = true
defineSymbol("cppCompileToNamespace")
defineSymbol(conf.symbols, "cppCompileToNamespace")
else:
if strutils.find(switch, '.') >= 0: options.setConfigVar(switch, arg)
else: invalidCmdLineOption(pass, switch, info)
if strutils.find(switch, '.') >= 0: options.setConfigVar(conf, switch, arg)
else: invalidCmdLineOption(conf, pass, switch, info)
proc processCommand(switch: string, pass: TCmdLinePass; config: ConfigRef) =
template gCmdLineInfo*(): untyped = newLineInfo(FileIndex(0), 1, 1)
proc processCommand*(switch: string, pass: TCmdLinePass; config: ConfigRef) =
var cmd, arg: string
splitSwitch(switch, cmd, arg, pass, gCmdLineInfo)
splitSwitch(config, switch, cmd, arg, pass, gCmdLineInfo)
processSwitch(cmd, arg, pass, gCmdLineInfo, config)

View File

@@ -12,77 +12,30 @@
import
strtabs, platform, strutils, idents
# We need to use a StringTableRef here as defined symbols are always guaranteed
# to be style insensitive. Otherwise hell would break lose.
var gSymbols: StringTableRef
const
catNone = "false"
proc defineSymbol*(symbol: string, value: string = "true") =
gSymbols[symbol] = value
proc defineSymbol*(symbols: StringTableRef; symbol: string, value: string = "true") =
symbols[symbol] = value
proc undefSymbol*(symbol: string) =
gSymbols[symbol] = catNone
proc undefSymbol*(symbols: StringTableRef; symbol: string) =
symbols[symbol] = catNone
proc isDefined*(symbol: string): bool =
if gSymbols.hasKey(symbol):
result = gSymbols[symbol] != catNone
elif cmpIgnoreStyle(symbol, CPU[targetCPU].name) == 0:
result = true
elif cmpIgnoreStyle(symbol, platform.OS[targetOS].name) == 0:
result = true
else:
case symbol.normalize
of "x86": result = targetCPU == cpuI386
of "itanium": result = targetCPU == cpuIa64
of "x8664": result = targetCPU == cpuAmd64
of "posix", "unix":
result = targetOS in {osLinux, osMorphos, osSkyos, osIrix, osPalmos,
osQnx, osAtari, osAix,
osHaiku, osVxWorks, osSolaris, osNetbsd,
osFreebsd, osOpenbsd, osDragonfly, osMacosx,
osAndroid}
of "linux":
result = targetOS in {osLinux, osAndroid}
of "bsd":
result = targetOS in {osNetbsd, osFreebsd, osOpenbsd, osDragonfly}
of "emulatedthreadvars":
result = platform.OS[targetOS].props.contains(ospLacksThreadVars)
of "msdos": result = targetOS == osDos
of "mswindows", "win32": result = targetOS == osWindows
of "macintosh": result = targetOS in {osMacos, osMacosx}
of "sunos": result = targetOS == osSolaris
of "littleendian": result = CPU[targetCPU].endian == platform.littleEndian
of "bigendian": result = CPU[targetCPU].endian == platform.bigEndian
of "cpu8": result = CPU[targetCPU].bit == 8
of "cpu16": result = CPU[targetCPU].bit == 16
of "cpu32": result = CPU[targetCPU].bit == 32
of "cpu64": result = CPU[targetCPU].bit == 64
of "nimrawsetjmp":
result = targetOS in {osSolaris, osNetbsd, osFreebsd, osOpenbsd,
osDragonfly, osMacosx}
else: discard
#proc lookupSymbol*(symbols: StringTableRef; symbol: string): string =
# result = if isDefined(symbol): gSymbols[symbol] else: nil
proc isDefined*(symbol: PIdent): bool = isDefined(symbol.s)
proc lookupSymbol*(symbol: string): string =
result = if isDefined(symbol): gSymbols[symbol] else: nil
proc lookupSymbol*(symbol: PIdent): string = lookupSymbol(symbol.s)
iterator definedSymbolNames*: string =
for key, val in pairs(gSymbols):
iterator definedSymbolNames*(symbols: StringTableRef): string =
for key, val in pairs(symbols):
if val != catNone: yield key
proc countDefinedSymbols*(): int =
proc countDefinedSymbols*(symbols: StringTableRef): int =
result = 0
for key, val in pairs(gSymbols):
for key, val in pairs(symbols):
if val != catNone: inc(result)
proc initDefines*() =
gSymbols = newStringTable(modeStyleInsensitive)
proc initDefines*(symbols: StringTableRef) =
# for bootstrapping purposes and old code:
template defineSymbol(s) = symbols.defineSymbol(s)
defineSymbol("nimhygiene")
defineSymbol("niminheritable")
defineSymbol("nimmixin")

389
compiler/configuration.nim Normal file
View File

@@ -0,0 +1,389 @@
#
#
# The Nim Compiler
# (c) Copyright 2018 Andreas Rumpf
#
# See the file "copying.txt", included in this
# distribution, for details about the copyright.
#
## This module contains the rather excessive configuration object that
## needs to be passed around to everything so that the compiler becomes
## more useful as a library.
import tables
const
explanationsBaseUrl* = "https://nim-lang.org/docs/manual"
type
TMsgKind* = enum
errUnknown, errInternal, errIllFormedAstX, errCannotOpenFile, errGenerated,
errUser,
warnCannotOpenFile,
warnOctalEscape, warnXIsNeverRead, warnXmightNotBeenInit,
warnDeprecated, warnConfigDeprecated,
warnSmallLshouldNotBeUsed, warnUnknownMagic, warnRedefinitionOfLabel,
warnUnknownSubstitutionX, warnLanguageXNotSupported,
warnFieldXNotSupported, warnCommentXIgnored,
warnTypelessParam,
warnUseBase, warnWriteToForeignHeap, warnUnsafeCode,
warnEachIdentIsTuple, warnShadowIdent,
warnProveInit, warnProveField, warnProveIndex, warnGcUnsafe, warnGcUnsafe2,
warnUninit, warnGcMem, warnDestructor, warnLockLevel, warnResultShadowed,
warnInconsistentSpacing, warnUser,
hintSuccess, hintSuccessX,
hintLineTooLong, hintXDeclaredButNotUsed, hintConvToBaseNotNeeded,
hintConvFromXtoItselfNotNeeded, hintExprAlwaysX, hintQuitCalled,
hintProcessing, hintCodeBegin, hintCodeEnd, hintConf, hintPath,
hintConditionAlwaysTrue, hintName, hintPattern,
hintExecuting, hintLinking, hintDependency,
hintSource, hintPerformance, hintStackTrace, hintGCStats,
hintUser, hintUserRaw
const
MsgKindToStr*: array[TMsgKind, string] = [
errUnknown: "unknown error",
errInternal: "internal error: $1",
errIllFormedAstX: "illformed AST: $1",
errCannotOpenFile: "cannot open '$1'",
errGenerated: "$1",
errUser: "$1",
warnCannotOpenFile: "cannot open '$1'",
warnOctalEscape: "octal escape sequences do not exist; leading zero is ignored",
warnXIsNeverRead: "'$1' is never read",
warnXmightNotBeenInit: "'$1' might not have been initialized",
warnDeprecated: "$1 is deprecated",
warnConfigDeprecated: "config file '$1' is deprecated",
warnSmallLshouldNotBeUsed: "'l' should not be used as an identifier; may look like '1' (one)",
warnUnknownMagic: "unknown magic '$1' might crash the compiler",
warnRedefinitionOfLabel: "redefinition of label '$1'",
warnUnknownSubstitutionX: "unknown substitution '$1'",
warnLanguageXNotSupported: "language '$1' not supported",
warnFieldXNotSupported: "field '$1' not supported",
warnCommentXIgnored: "comment '$1' ignored",
warnTypelessParam: "'$1' has no type. Typeless parameters are deprecated; only allowed for 'template'",
warnUseBase: "use {.base.} for base methods; baseless methods are deprecated",
warnWriteToForeignHeap: "write to foreign heap",
warnUnsafeCode: "unsafe code: '$1'",
warnEachIdentIsTuple: "each identifier is a tuple",
warnShadowIdent: "shadowed identifier: '$1'",
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",
warnProveIndex: "cannot prove index '$1' is valid",
warnGcUnsafe: "not GC-safe: '$1'",
warnGcUnsafe2: "$1",
warnUninit: "'$1' might not have been initialized",
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.",
warnLockLevel: "$1",
warnResultShadowed: "Special variable 'result' is shadowed.",
warnInconsistentSpacing: "Number of spaces around '$#' is not consistent",
warnUser: "$1",
hintSuccess: "operation successful",
hintSuccessX: "operation successful ($# lines compiled; $# sec total; $#; $#)",
hintLineTooLong: "line too long",
hintXDeclaredButNotUsed: "'$1' is declared but not used",
hintConvToBaseNotNeeded: "conversion to base object is not needed",
hintConvFromXtoItselfNotNeeded: "conversion from $1 to itself is pointless",
hintExprAlwaysX: "expression evaluates always to '$1'",
hintQuitCalled: "quit() called",
hintProcessing: "$1",
hintCodeBegin: "generated code listing:",
hintCodeEnd: "end of listing",
hintConf: "used config file '$1'",
hintPath: "added path: '$1'",
hintConditionAlwaysTrue: "condition is always true: '$1'",
hintName: "name should be: '$1'",
hintPattern: "$1",
hintExecuting: "$1",
hintLinking: "",
hintDependency: "$1",
hintSource: "$1",
hintPerformance: "$1",
hintStackTrace: "$1",
hintGCStats: "$1",
hintUser: "$1",
hintUserRaw: "$1"]
const
WarningsToStr* = ["CannotOpenFile", "OctalEscape",
"XIsNeverRead", "XmightNotBeenInit",
"Deprecated", "ConfigDeprecated",
"SmallLshouldNotBeUsed", "UnknownMagic",
"RedefinitionOfLabel", "UnknownSubstitutionX",
"LanguageXNotSupported", "FieldXNotSupported",
"CommentXIgnored",
"TypelessParam", "UseBase", "WriteToForeignHeap",
"UnsafeCode", "EachIdentIsTuple", "ShadowIdent",
"ProveInit", "ProveField", "ProveIndex", "GcUnsafe", "GcUnsafe2", "Uninit",
"GcMem", "Destructor", "LockLevel", "ResultShadowed",
"Spacing", "User"]
HintsToStr* = ["Success", "SuccessX", "LineTooLong",
"XDeclaredButNotUsed", "ConvToBaseNotNeeded", "ConvFromXtoItselfNotNeeded",
"ExprAlwaysX", "QuitCalled", "Processing", "CodeBegin", "CodeEnd", "Conf",
"Path", "CondTrue", "Name", "Pattern", "Exec", "Link", "Dependency",
"Source", "Performance", "StackTrace", "GCStats",
"User", "UserRaw"]
const
fatalMin* = errUnknown
fatalMax* = errInternal
errMin* = errUnknown
errMax* = errUser
warnMin* = warnCannotOpenFile
warnMax* = pred(hintSuccess)
hintMin* = hintSuccess
hintMax* = high(TMsgKind)
static:
doAssert HintsToStr.len == ord(hintMax) - ord(hintMin) + 1
doAssert WarningsToStr.len == ord(warnMax) - ord(warnMin) + 1
type
TNoteKind* = range[warnMin..hintMax] # "notes" are warnings or hints
TNoteKinds* = set[TNoteKind]
const
NotesVerbosity*: array[0..3, TNoteKinds] = [
{low(TNoteKind)..high(TNoteKind)} - {warnShadowIdent, warnUninit,
warnProveField, warnProveIndex,
warnGcUnsafe,
hintSuccessX, hintPath, hintConf,
hintProcessing, hintPattern,
hintDependency,
hintExecuting, hintLinking,
hintCodeBegin, hintCodeEnd,
hintSource, hintStackTrace,
hintGCStats},
{low(TNoteKind)..high(TNoteKind)} - {warnShadowIdent, warnUninit,
warnProveField, warnProveIndex,
warnGcUnsafe,
hintPath,
hintDependency,
hintCodeBegin, hintCodeEnd,
hintSource, hintStackTrace,
hintGCStats},
{low(TNoteKind)..high(TNoteKind)} - {hintStackTrace, warnUninit},
{low(TNoteKind)..high(TNoteKind)}]
#[
errStringLiteralExpected: "string literal expected",
errIntLiteralExpected: "integer literal expected",
errIdentifierExpected: "identifier expected, but found '$1'",
errNewlineExpected: "newline expected, but found '$1'",
errInvalidModuleName: "invalid module name: '$1'",
errRecursiveDependencyX: "recursive dependency: '$1'",
errOnOrOffExpected: "'on' or 'off' expected",
errNoneSpeedOrSizeExpected: "'none', 'speed' or 'size' expected",
errInvalidPragma: "invalid pragma",
errUnknownPragma: "unknown pragma: '$1'",
errAtPopWithoutPush: "'pop' without a 'push' pragma",
errEmptyAsm: "empty asm statement",
errInvalidIndentation: "invalid indentation",
errExceptionAlreadyHandled: "exception already handled",
errYieldNotAllowedHere: "'yield' only allowed in an iterator",
errYieldNotAllowedInTryStmt: "'yield' cannot be used within 'try' in a non-inlined iterator",
errInvalidNumberOfYieldExpr: "invalid number of 'yield' expressions",
errCannotReturnExpr: "current routine cannot return an expression",
errNoReturnWithReturnTypeNotAllowed: "routines with NoReturn pragma are not allowed to have return type",
errAttemptToRedefine: "redefinition of '$1'",
errStmtInvalidAfterReturn: "statement not allowed after 'return', 'break', 'raise', 'continue' or proc call with noreturn pragma",
errStmtExpected: "statement expected",
errInvalidLabel: "'$1' is no label",
errInvalidCmdLineOption: "invalid command line option: '$1'",
errCmdLineArgExpected: "argument for command line option expected: '$1'",
errCmdLineNoArgExpected: "invalid argument for command line option: '$1'",
errInvalidVarSubstitution: "invalid variable substitution in '$1'",
errUnknownVar: "unknown variable: '$1'",
errUnknownCcompiler: "unknown C compiler: '$1'",
errOnOrOffExpectedButXFound: "'on' or 'off' expected, but '$1' found",
errOnOffOrListExpectedButXFound: "'on', 'off' or 'list' expected, but '$1' found",
errGenOutExpectedButXFound: "'c', 'c++' or 'yaml' expected, but '$1' found",
errArgsNeedRunOption: "arguments can only be given if the '--run' option is selected",
errInvalidMultipleAsgn: "multiple assignment is not allowed",
errColonOrEqualsExpected: "':' or '=' expected, but found '$1'",
errUndeclaredField: "undeclared field: '$1'",
errUndeclaredRoutine: "attempting to call undeclared routine: '$1'",
errUseQualifier: "ambiguous identifier: '$1' -- use a qualifier",
errTypeExpected: "type expected",
errSystemNeeds: "system module needs '$1'",
errExecutionOfProgramFailed: "execution of an external program failed: '$1'",
errNotOverloadable: "overloaded '$1' leads to ambiguous calls",
errInvalidArgForX: "invalid argument for '$1'",
errStmtHasNoEffect: "statement has no effect",
errXExpectsTypeOrValue: "'$1' expects a type or value",
errXExpectsArrayType: "'$1' expects an array type",
errIteratorCannotBeInstantiated: "'$1' cannot be instantiated because its body has not been compiled yet",
errExprXAmbiguous: "expression '$1' ambiguous in this context",
errConstantDivisionByZero: "division by zero",
errOrdinalTypeExpected: "ordinal type expected",
errOrdinalOrFloatTypeExpected: "ordinal or float type expected",
errOverOrUnderflow: "over- or underflow",
errCannotEvalXBecauseIncompletelyDefined: "cannot evaluate '$1' because type is not defined completely",
errChrExpectsRange0_255: "'chr' expects an int in the range 0..255",
errDynlibRequiresExportc: "'dynlib' requires 'exportc'",
errUndeclaredFieldX: "undeclared field: '$1'",
errNilAccess: "attempt to access a nil address",
errIndexOutOfBounds: "index out of bounds",
errIndexTypesDoNotMatch: "index types do not match",
errBracketsInvalidForType: "'[]' operator invalid for this type",
errValueOutOfSetBounds: "value out of set bounds",
errFieldInitTwice: "field initialized twice: '$1'",
errFieldNotInit: "field '$1' not initialized",
errExprXCannotBeCalled: "expression '$1' cannot be called",
errExprHasNoType: "expression has no type",
errExprXHasNoType: "expression '$1' has no type (or is ambiguous)",
errCastNotInSafeMode: "'cast' not allowed in safe mode",
errExprCannotBeCastToX: "expression cannot be cast to $1",
errCommaOrParRiExpected: "',' or ')' expected",
errCurlyLeOrParLeExpected: "'{' or '(' expected",
errSectionExpected: "section ('type', 'proc', etc.) expected",
errRangeExpected: "range expected",
errMagicOnlyInSystem: "'magic' only allowed in system module",
errPowerOfTwoExpected: "power of two expected",
errStringMayNotBeEmpty: "string literal may not be empty",
errCallConvExpected: "calling convention expected",
errProcOnlyOneCallConv: "a proc can only have one calling convention",
errSymbolMustBeImported: "symbol must be imported if 'lib' pragma is used",
errExprMustBeBool: "expression must be of type 'bool'",
errConstExprExpected: "constant expression expected",
errDuplicateCaseLabel: "duplicate case label",
errRangeIsEmpty: "range is empty",
errSelectorMustBeOfCertainTypes: "selector must be of an ordinal type, float or string",
errSelectorMustBeOrdinal: "selector must be of an ordinal type",
errOrdXMustNotBeNegative: "ord($1) must not be negative",
errLenXinvalid: "len($1) must be less than 32768",
errWrongNumberOfVariables: "wrong number of variables",
errExprCannotBeRaised: "only a 'ref object' can be raised",
errBreakOnlyInLoop: "'break' only allowed in loop construct",
errTypeXhasUnknownSize: "type '$1' has unknown size",
errConstNeedsConstExpr: "a constant can only be initialized with a constant expression",
errConstNeedsValue: "a constant needs a value",
errResultCannotBeOpenArray: "the result type cannot be on open array",
errSizeTooBig: "computing the type's size produced an overflow",
errSetTooBig: "set is too large",
errBaseTypeMustBeOrdinal: "base type of a set must be an ordinal",
errInheritanceOnlyWithNonFinalObjects: "inheritance only works with non-final objects",
errInheritanceOnlyWithEnums: "inheritance only works with an enum",
errIllegalRecursionInTypeX: "illegal recursion in type '$1'",
errCannotInstantiateX: "cannot instantiate: '$1'",
errExprHasNoAddress: "expression has no address",
errXStackEscape: "address of '$1' may not escape its stack frame",
errVarForOutParamNeededX: "for a 'var' type a variable needs to be passed; but '$1' is immutable",
errPureTypeMismatch: "type mismatch",
errTypeMismatch: "type mismatch: got <",
errButExpected: "but expected one of: ",
errButExpectedX: "but expected '$1'",
errAmbiguousCallXYZ: "ambiguous call; both $1 and $2 match for: $3",
errWrongNumberOfArguments: "wrong number of arguments",
errWrongNumberOfArgumentsInCall: "wrong number of arguments in call to '$1'",
errMissingGenericParamsForTemplate: "'$1' has unspecified generic parameters",
errXCannotBePassedToProcVar: "'$1' cannot be passed to a procvar",
errPragmaOnlyInHeaderOfProcX: "pragmas are only allowed in the header of a proc; redefinition of $1",
errImplOfXNotAllowed: "implementation of '$1' is not allowed",
errImplOfXexpected: "implementation of '$1' expected",
errNoSymbolToBorrowFromFound: "no symbol to borrow from found",
errDiscardValueX: "value of type '$1' has to be discarded",
errInvalidDiscard: "statement returns no value that can be discarded",
errIllegalConvFromXtoY: "conversion from $1 to $2 is invalid",
errCannotBindXTwice: "cannot bind parameter '$1' twice",
errInvalidOrderInArrayConstructor: "invalid order in array constructor",
errInvalidOrderInEnumX: "invalid order in enum '$1'",
errEnumXHasHoles: "enum '$1' has holes",
errExceptExpected: "'except' or 'finally' expected",
errInvalidTry: "after catch all 'except' or 'finally' no section may follow",
errOptionExpected: "option expected, but found '$1'",
errXisNoLabel: "'$1' is not a label",
errNotAllCasesCovered: "not all cases are covered",
errUnknownSubstitionVar: "unknown substitution variable: '$1'",
errComplexStmtRequiresInd: "complex statement requires indentation",
errXisNotCallable: "'$1' is not callable",
errNoPragmasAllowedForX: "no pragmas allowed for $1",
errNoGenericParamsAllowedForX: "no generic parameters allowed for $1",
errInvalidParamKindX: "invalid param kind: '$1'",
errDefaultArgumentInvalid: "default argument invalid",
errNamedParamHasToBeIdent: "named parameter has to be an identifier",
errNoReturnTypeForX: "no return type allowed for $1",
errConvNeedsOneArg: "a type conversion needs exactly one argument",
errInvalidPragmaX: "invalid pragma: $1",
errXNotAllowedHere: "$1 not allowed here",
errInvalidControlFlowX: "invalid control flow: $1",
errXisNoType: "invalid type: '$1'",
errCircumNeedsPointer: "'[]' needs a pointer or reference type",
errInvalidExpression: "invalid expression",
errInvalidExpressionX: "invalid expression: '$1'",
errEnumHasNoValueX: "enum has no value '$1'",
errNamedExprExpected: "named expression expected",
errNamedExprNotAllowed: "named expression not allowed here",
errXExpectsOneTypeParam: "'$1' expects one type parameter",
errArrayExpectsTwoTypeParams: "array expects two type parameters",
errInvalidVisibilityX: "invalid visibility: '$1'",
errInitHereNotAllowed: "initialization not allowed here",
errXCannotBeAssignedTo: "'$1' cannot be assigned to",
errIteratorNotAllowed: "iterators can only be defined at the module's top level",
errXNeedsReturnType: "$1 needs a return type",
errNoReturnTypeDeclared: "no return type declared",
errNoCommand: "no command given",
errInvalidCommandX: "invalid command: '$1'",
errXOnlyAtModuleScope: "'$1' is only allowed at top level",
errXNeedsParamObjectType: "'$1' needs a parameter that has an object type",
errTemplateInstantiationTooNested: "template instantiation too nested, try --evalTemplateLimit:N",
errMacroInstantiationTooNested: "macro instantiation too nested, try --evalMacroLimit:N",
errInstantiationFrom: "template/generic instantiation from here",
errInvalidIndexValueForTuple: "invalid index value for tuple subscript",
errCommandExpectsFilename: "command expects a filename argument",
errMainModuleMustBeSpecified: "please, specify a main module in the project configuration file",
errXExpected: "'$1' expected",
errTIsNotAConcreteType: "'$1' is not a concrete type.",
errCastToANonConcreteType: "cannot cast to a non concrete type: '$1'",
errInvalidSectionStart: "invalid section start",
errGridTableNotImplemented: "grid table is not implemented",
errGeneralParseError: "general parse error",
errNewSectionExpected: "new section expected",
errWhitespaceExpected: "whitespace expected, got '$1'",
errXisNoValidIndexFile: "'$1' is no valid index file",
errCannotRenderX: "cannot render reStructuredText element '$1'",
errVarVarTypeNotAllowed: "type 'var var' is not allowed",
errInstantiateXExplicitly: "instantiate '$1' explicitly",
errOnlyACallOpCanBeDelegator: "only a call operator can be a delegator",
errUsingNoSymbol: "'$1' is not a variable, constant or a proc name",
errMacroBodyDependsOnGenericTypes: "the macro body cannot be compiled, " &
"because the parameter '$1' has a generic type",
errDestructorNotGenericEnough: "Destructor signature is too specific. " &
"A destructor must be associated will all instantiations of a generic type",
errInlineIteratorsAsProcParams: "inline iterators can be used as parameters only for " &
"templates, macros and other inline iterators",
errXExpectsTwoArguments: "'$1' expects two arguments",
errXExpectsObjectTypes: "'$1' expects object types",
errXcanNeverBeOfThisSubtype: "'$1' can never be of this subtype",
errTooManyIterations: "interpretation requires too many iterations; " &
"if you are sure this is not a bug in your code edit " &
"compiler/vmdef.MaxLoopIterations and rebuild the compiler",
errCannotInterpretNodeX: "cannot evaluate '$1'",
errFieldXNotFound: "field '$1' cannot be found",
errInvalidConversionFromTypeX: "invalid conversion from type '$1'",
errAssertionFailed: "assertion failed",
errCannotGenerateCodeForX: "cannot generate code for '$1'",
errXRequiresOneArgument: "$1 requires one parameter",
errUnhandledExceptionX: "unhandled exception: $1",
errCyclicTree: "macro returned a cyclic abstract syntax tree",
errXisNoMacroOrTemplate: "'$1' is no macro or template",
errXhasSideEffects: "'$1' can have side effects",
errIteratorExpected: "iterator within for loop context expected",
errLetNeedsInit: "'let' symbol requires an initialization",
errThreadvarCannotInit: "a thread var cannot be initialized explicitly; this would only run for the main thread",
errWrongSymbolX: "usage of '$1' is a user-defined error",
errIllegalCaptureX: "illegal capture '$1'",
errXCannotBeClosure: "'$1' cannot have 'closure' calling convention",
errXMustBeCompileTime: "'$1' can only be used in compile-time context",
errCannotInferTypeOfTheLiteral: "cannot infer the type of the $1",
errCannotInferReturnType: "cannot infer the return type of the proc",
errCannotInferStaticParam: "cannot infer the value of the static param `$1`",
errGenericLambdaNotAllowed: "A nested proc can have generic parameters only when " &
"it is used as an operand to another routine and the types " &
"of the generic paramers can be inferred from the expected signature.",
errProcHasNoConcreteType: "'$1' doesn't have a concrete type, due to unspecified generic parameters.",
errInOutFlagNotExtern: "The `$1` modifier can be used only with imported types",
]#

View File

@@ -14,7 +14,7 @@
import
ropes, os, strutils, osproc, platform, condsyms, options, msgs,
std / sha1, streams
configuration, std / sha1, streams
#from debuginfo import writeDebugInfo
@@ -193,9 +193,9 @@ compiler bcc:
pic: "",
asmStmtFrmt: "__asm{$n$1$n}$n",
structStmtFmt: "$1 $2",
props: {hasSwitchRange, hasComputedGoto, hasCpp, hasGcGuard,
props: {hasSwitchRange, hasComputedGoto, hasCpp, hasGcGuard,
hasAttribute})
# Digital Mars C Compiler
compiler dmc:
@@ -376,7 +376,7 @@ proc nameToCC*(name: string): TSystemCC =
return i
result = ccNone
proc getConfigVar(c: TSystemCC, suffix: string): string =
proc getConfigVar(conf: ConfigRef; c: TSystemCC, suffix: string): string =
# use ``cpu.os.cc`` for cross compilation, unless ``--compileOnly`` is given
# for niminst support
let fullSuffix =
@@ -394,62 +394,63 @@ proc getConfigVar(c: TSystemCC, suffix: string): string =
let fullCCname = platform.CPU[targetCPU].name & '.' &
platform.OS[targetOS].name & '.' &
CC[c].name & fullSuffix
result = getConfigVar(fullCCname)
result = getConfigVar(conf, fullCCname)
if result.len == 0:
# not overriden for this cross compilation setting?
result = getConfigVar(CC[c].name & fullSuffix)
result = getConfigVar(conf, CC[c].name & fullSuffix)
else:
result = getConfigVar(CC[c].name & fullSuffix)
result = getConfigVar(conf, CC[c].name & fullSuffix)
proc setCC*(ccname: string) =
proc setCC*(conf: ConfigRef; ccname: string; info: TLineInfo) =
cCompiler = nameToCC(ccname)
if cCompiler == ccNone: rawMessage(errUnknownCcompiler, ccname)
compileOptions = getConfigVar(cCompiler, ".options.always")
if cCompiler == ccNone:
localError(conf, info, "unknown C compiler: '$1'" % ccname)
compileOptions = getConfigVar(conf, cCompiler, ".options.always")
linkOptions = ""
ccompilerpath = getConfigVar(cCompiler, ".path")
for i in countup(low(CC), high(CC)): undefSymbol(CC[i].name)
defineSymbol(CC[cCompiler].name)
ccompilerpath = getConfigVar(conf, cCompiler, ".path")
for i in countup(low(CC), high(CC)): undefSymbol(conf.symbols, CC[i].name)
defineSymbol(conf.symbols, CC[cCompiler].name)
proc addOpt(dest: var string, src: string) =
if len(dest) == 0 or dest[len(dest)-1] != ' ': add(dest, " ")
add(dest, src)
proc addLinkOption*(option: string) =
proc addLinkOption*(conf: ConfigRef; option: string) =
addOpt(linkOptions, option)
proc addCompileOption*(option: string) =
proc addCompileOption*(conf: ConfigRef; option: string) =
if strutils.find(compileOptions, option, 0) < 0:
addOpt(compileOptions, option)
proc addLinkOptionCmd*(option: string) =
proc addLinkOptionCmd*(conf: ConfigRef; option: string) =
addOpt(linkOptionsCmd, option)
proc addCompileOptionCmd*(option: string) =
proc addCompileOptionCmd*(conf: ConfigRef; option: string) =
compileOptionsCmd.add(option)
proc initVars*() =
proc initVars*(conf: ConfigRef) =
# we need to define the symbol here, because ``CC`` may have never been set!
for i in countup(low(CC), high(CC)): undefSymbol(CC[i].name)
defineSymbol(CC[cCompiler].name)
addCompileOption(getConfigVar(cCompiler, ".options.always"))
for i in countup(low(CC), high(CC)): undefSymbol(conf.symbols, CC[i].name)
defineSymbol(conf.symbols, CC[cCompiler].name)
addCompileOption(conf, getConfigVar(conf, cCompiler, ".options.always"))
#addLinkOption(getConfigVar(cCompiler, ".options.linker"))
if len(ccompilerpath) == 0:
ccompilerpath = getConfigVar(cCompiler, ".path")
ccompilerpath = getConfigVar(conf, cCompiler, ".path")
proc completeCFilePath*(cfile: string, createSubDir: bool = true): string =
result = completeGeneratedFilePath(cfile, createSubDir)
proc completeCFilePath*(conf: ConfigRef; cfile: string, createSubDir: bool = true): string =
result = completeGeneratedFilePath(conf, cfile, createSubDir)
proc toObjFile*(filename: string): string =
proc toObjFile*(conf: ConfigRef; filename: string): string =
# Object file for compilation
#if filename.endsWith(".cpp"):
# result = changeFileExt(filename, "cpp." & CC[cCompiler].objExt)
#else:
result = changeFileExt(filename, CC[cCompiler].objExt)
proc addFileToCompile*(cf: Cfile) =
proc addFileToCompile*(conf: ConfigRef; cf: Cfile) =
toCompile.add(cf)
proc resetCompilationLists* =
proc resetCompilationLists*(conf: ConfigRef) =
toCompile.setLen 0
## XXX: we must associate these with their originating module
# when the module is loaded/unloaded it adds/removes its items
@@ -457,107 +458,110 @@ proc resetCompilationLists* =
# Maybe we can do that in checkDep on the other hand?
externalToLink.setLen 0
proc addExternalFileToLink*(filename: string) =
proc addExternalFileToLink*(conf: ConfigRef; filename: string) =
externalToLink.insert(filename, 0)
proc execWithEcho(cmd: string, msg = hintExecuting): int =
rawMessage(msg, cmd)
proc execWithEcho(conf: ConfigRef; cmd: string, msg = hintExecuting): int =
rawMessage(conf, msg, cmd)
result = execCmd(cmd)
proc execExternalProgram*(cmd: string, msg = hintExecuting) =
if execWithEcho(cmd, msg) != 0:
rawMessage(errExecutionOfProgramFailed, cmd)
proc execExternalProgram*(conf: ConfigRef; cmd: string, msg = hintExecuting) =
if execWithEcho(conf, cmd, msg) != 0:
rawMessage(conf, errGenerated, "execution of an external program failed: '$1'" %
cmd)
proc generateScript(projectFile: string, script: Rope) =
proc generateScript(conf: ConfigRef; projectFile: string, script: Rope) =
let (dir, name, ext) = splitFile(projectFile)
writeRope(script, getNimcacheDir() / addFileExt("compile_" & name,
writeRope(script, getNimcacheDir(conf) / addFileExt("compile_" & name,
platform.OS[targetOS].scriptExt))
copyFile(libpath / "nimbase.h", getNimcacheDir() / "nimbase.h")
copyFile(libpath / "nimbase.h", getNimcacheDir(conf) / "nimbase.h")
proc getOptSpeed(c: TSystemCC): string =
result = getConfigVar(c, ".options.speed")
proc getOptSpeed(conf: ConfigRef; c: TSystemCC): string =
result = getConfigVar(conf, c, ".options.speed")
if result == "":
result = CC[c].optSpeed # use default settings from this file
proc getDebug(c: TSystemCC): string =
result = getConfigVar(c, ".options.debug")
proc getDebug(conf: ConfigRef; c: TSystemCC): string =
result = getConfigVar(conf, c, ".options.debug")
if result == "":
result = CC[c].debug # use default settings from this file
proc getOptSize(c: TSystemCC): string =
result = getConfigVar(c, ".options.size")
proc getOptSize(conf: ConfigRef; c: TSystemCC): string =
result = getConfigVar(conf, c, ".options.size")
if result == "":
result = CC[c].optSize # use default settings from this file
proc noAbsolutePaths: bool {.inline.} =
proc noAbsolutePaths(conf: ConfigRef): bool {.inline.} =
# We used to check current OS != specified OS, but this makes no sense
# really: Cross compilation from Linux to Linux for example is entirely
# reasonable.
# `optGenMapping` is included here for niminst.
result = gGlobalOptions * {optGenScript, optGenMapping} != {}
proc cFileSpecificOptions(cfilename: string): string =
proc cFileSpecificOptions(conf: ConfigRef; cfilename: string): string =
result = compileOptions
for option in compileOptionsCmd:
if strutils.find(result, option, 0) < 0:
addOpt(result, option)
var trunk = splitFile(cfilename).name
let trunk = splitFile(cfilename).name
if optCDebug in gGlobalOptions:
var key = trunk & ".debug"
if existsConfigVar(key): addOpt(result, getConfigVar(key))
else: addOpt(result, getDebug(cCompiler))
let key = trunk & ".debug"
if existsConfigVar(conf, key): addOpt(result, getConfigVar(conf, key))
else: addOpt(result, getDebug(conf, cCompiler))
if optOptimizeSpeed in gOptions:
var key = trunk & ".speed"
if existsConfigVar(key): addOpt(result, getConfigVar(key))
else: addOpt(result, getOptSpeed(cCompiler))
let key = trunk & ".speed"
if existsConfigVar(conf, key): addOpt(result, getConfigVar(conf, key))
else: addOpt(result, getOptSpeed(conf, cCompiler))
elif optOptimizeSize in gOptions:
var key = trunk & ".size"
if existsConfigVar(key): addOpt(result, getConfigVar(key))
else: addOpt(result, getOptSize(cCompiler))
var key = trunk & ".always"
if existsConfigVar(key): addOpt(result, getConfigVar(key))
let key = trunk & ".size"
if existsConfigVar(conf, key): addOpt(result, getConfigVar(conf, key))
else: addOpt(result, getOptSize(conf, cCompiler))
let key = trunk & ".always"
if existsConfigVar(conf, key): addOpt(result, getConfigVar(conf, key))
proc getCompileOptions: string =
result = cFileSpecificOptions("__dummy__")
proc getCompileOptions(conf: ConfigRef): string =
result = cFileSpecificOptions(conf, "__dummy__")
proc getLinkOptions: string =
proc getLinkOptions(conf: ConfigRef): string =
result = linkOptions & " " & linkOptionsCmd & " "
for linkedLib in items(cLinkedLibs):
result.add(CC[cCompiler].linkLibCmd % linkedLib.quoteShell)
for libDir in items(cLibs):
result.add(join([CC[cCompiler].linkDirCmd, libDir.quoteShell]))
proc needsExeExt(): bool {.inline.} =
proc needsExeExt(conf: ConfigRef): bool {.inline.} =
result = (optGenScript in gGlobalOptions and targetOS == osWindows) or
(platform.hostOS == osWindows)
proc getCompilerExe(compiler: TSystemCC; cfile: string): string =
proc getCompilerExe(conf: ConfigRef; compiler: TSystemCC; cfile: string): string =
result = if gCmd == cmdCompileToCpp and not cfile.endsWith(".c"):
CC[compiler].cppCompiler
else:
CC[compiler].compilerExe
if result.len == 0:
rawMessage(errCompilerDoesntSupportTarget, CC[compiler].name)
rawMessage(conf, errGenerated,
"Compiler '$1' doesn't support the requested target" %
CC[compiler].name)
proc getLinkerExe(compiler: TSystemCC): string =
proc getLinkerExe(conf: ConfigRef; compiler: TSystemCC): string =
result = if CC[compiler].linkerExe.len > 0: CC[compiler].linkerExe
elif gMixedMode and gCmd != cmdCompileToCpp: CC[compiler].cppCompiler
else: compiler.getCompilerExe("")
else: getCompilerExe(conf, compiler, "")
proc getCompileCFileCmd*(cfile: Cfile): string =
proc getCompileCFileCmd*(conf: ConfigRef; cfile: Cfile): string =
var c = cCompiler
var options = cFileSpecificOptions(cfile.cname)
var exe = getConfigVar(c, ".exe")
if exe.len == 0: exe = c.getCompilerExe(cfile.cname)
var options = cFileSpecificOptions(conf, cfile.cname)
var exe = getConfigVar(conf, c, ".exe")
if exe.len == 0: exe = getCompilerExe(conf, c, cfile.cname)
if needsExeExt(): exe = addFileExt(exe, "exe")
if needsExeExt(conf): exe = addFileExt(exe, "exe")
if optGenDynLib in gGlobalOptions and
ospNeedsPIC in platform.OS[targetOS].props:
add(options, ' ' & CC[c].pic)
var includeCmd, compilePattern: string
if not noAbsolutePaths():
if not noAbsolutePaths(conf):
# compute include paths:
includeCmd = CC[c].includeCmd & quoteShell(libpath)
@@ -567,18 +571,18 @@ proc getCompileCFileCmd*(cfile: Cfile): string =
compilePattern = joinPath(ccompilerpath, exe)
else:
includeCmd = ""
compilePattern = c.getCompilerExe(cfile.cname)
compilePattern = getCompilerExe(conf, c, cfile.cname)
var cf = if noAbsolutePaths(): extractFilename(cfile.cname)
var cf = if noAbsolutePaths(conf): extractFilename(cfile.cname)
else: cfile.cname
var objfile =
if cfile.obj.len == 0:
if not cfile.flags.contains(CfileFlag.External) or noAbsolutePaths():
toObjFile(cf)
if not cfile.flags.contains(CfileFlag.External) or noAbsolutePaths(conf):
toObjFile(conf, cf)
else:
completeCFilePath(toObjFile(cf))
elif noAbsolutePaths():
completeCFilePath(conf, toObjFile(conf, cf))
elif noAbsolutePaths(conf):
extractFilename(cfile.obj)
else:
cfile.obj
@@ -587,30 +591,30 @@ proc getCompileCFileCmd*(cfile: Cfile): string =
cf = quoteShell(cf)
result = quoteShell(compilePattern % [
"file", cf, "objfile", objfile, "options", options,
"include", includeCmd, "nim", getPrefixDir(),
"nim", getPrefixDir(), "lib", libpath])
"include", includeCmd, "nim", getPrefixDir(conf),
"nim", getPrefixDir(conf), "lib", libpath])
add(result, ' ')
addf(result, CC[c].compileTmpl, [
"file", cf, "objfile", objfile,
"options", options, "include", includeCmd,
"nim", quoteShell(getPrefixDir()),
"nim", quoteShell(getPrefixDir()),
"nim", quoteShell(getPrefixDir(conf)),
"nim", quoteShell(getPrefixDir(conf)),
"lib", quoteShell(libpath)])
proc footprint(cfile: Cfile): SecureHash =
proc footprint(conf: ConfigRef; cfile: Cfile): SecureHash =
result = secureHash(
$secureHashFile(cfile.cname) &
platform.OS[targetOS].name &
platform.CPU[targetCPU].name &
extccomp.CC[extccomp.cCompiler].name &
getCompileCFileCmd(cfile))
getCompileCFileCmd(conf, cfile))
proc externalFileChanged(cfile: Cfile): bool =
proc externalFileChanged(conf: ConfigRef; cfile: Cfile): bool =
if gCmd notin {cmdCompileToC, cmdCompileToCpp, cmdCompileToOC, cmdCompileToLLVM}:
return false
var hashFile = toGeneratedFile(cfile.cname.withPackageName, "sha1")
var currentHash = footprint(cfile)
var hashFile = toGeneratedFile(conf, cfile.cname.withPackageName, "sha1")
var currentHash = footprint(conf, cfile)
var f: File
if open(f, hashFile, fmRead):
let oldHash = parseSecureHash(f.readLine())
@@ -623,23 +627,23 @@ proc externalFileChanged(cfile: Cfile): bool =
f.writeLine($currentHash)
close(f)
proc addExternalFileToCompile*(c: var Cfile) =
if optForceFullMake notin gGlobalOptions and not externalFileChanged(c):
proc addExternalFileToCompile*(conf: ConfigRef; c: var Cfile) =
if optForceFullMake notin gGlobalOptions and not externalFileChanged(conf, c):
c.flags.incl CfileFlag.Cached
toCompile.add(c)
proc addExternalFileToCompile*(filename: string) =
proc addExternalFileToCompile*(conf: ConfigRef; filename: string) =
var c = Cfile(cname: filename,
obj: toObjFile(completeCFilePath(changeFileExt(filename, ""), false)),
obj: toObjFile(conf, completeCFilePath(conf, changeFileExt(filename, ""), false)),
flags: {CfileFlag.External})
addExternalFileToCompile(c)
addExternalFileToCompile(conf, c)
proc compileCFile(list: CFileList, script: var Rope, cmds: var TStringSeq,
proc compileCFile(conf: ConfigRef; list: CFileList, script: var Rope, cmds: var TStringSeq,
prettyCmds: var TStringSeq) =
for it in list:
# call the C compiler for the .c file:
if it.flags.contains(CfileFlag.Cached): continue
var compileCmd = getCompileCFileCmd(it)
var compileCmd = getCompileCFileCmd(conf, it)
if optCompileOnly notin gGlobalOptions:
add(cmds, compileCmd)
let (_, name, _) = splitFile(it.cname)
@@ -648,7 +652,7 @@ proc compileCFile(list: CFileList, script: var Rope, cmds: var TStringSeq,
add(script, compileCmd)
add(script, tnl)
proc getLinkCmd(projectfile, objfiles: string): string =
proc getLinkCmd(conf: ConfigRef; projectfile, objfiles: string): string =
if optGenStaticLib in gGlobalOptions:
var libname: string
if options.outFile.len > 0:
@@ -660,11 +664,11 @@ proc getLinkCmd(projectfile, objfiles: string): string =
result = CC[cCompiler].buildLib % ["libfile", libname,
"objfiles", objfiles]
else:
var linkerExe = getConfigVar(cCompiler, ".linkerexe")
if len(linkerExe) == 0: linkerExe = cCompiler.getLinkerExe
var linkerExe = getConfigVar(conf, cCompiler, ".linkerexe")
if len(linkerExe) == 0: linkerExe = getLinkerExe(conf, cCompiler)
# bug #6452: We must not use ``quoteShell`` here for ``linkerExe``
if needsExeExt(): linkerExe = addFileExt(linkerExe, "exe")
if noAbsolutePaths(): result = linkerExe
if needsExeExt(conf): linkerExe = addFileExt(linkerExe, "exe")
if noAbsolutePaths(conf): result = linkerExe
else: result = joinPath(ccompilerpath, linkerExe)
let buildgui = if optGenGuiApp in gGlobalOptions: CC[cCompiler].buildGui
else: ""
@@ -679,60 +683,63 @@ proc getLinkCmd(projectfile, objfiles: string): string =
exefile = options.outFile.expandTilde
if not exefile.isAbsolute():
exefile = getCurrentDir() / exefile
if not noAbsolutePaths():
if not noAbsolutePaths(conf):
if not exefile.isAbsolute():
exefile = joinPath(splitFile(projectfile).dir, exefile)
when false:
if optCDebug in gGlobalOptions:
writeDebugInfo(exefile.changeFileExt("ndb"))
exefile = quoteShell(exefile)
let linkOptions = getLinkOptions() & " " &
getConfigVar(cCompiler, ".options.linker")
var linkTmpl = getConfigVar(cCompiler, ".linkTmpl")
let linkOptions = getLinkOptions(conf) & " " &
getConfigVar(conf, cCompiler, ".options.linker")
var linkTmpl = getConfigVar(conf, cCompiler, ".linkTmpl")
if linkTmpl.len == 0:
linkTmpl = CC[cCompiler].linkTmpl
result = quoteShell(result % ["builddll", builddll,
"buildgui", buildgui, "options", linkOptions, "objfiles", objfiles,
"exefile", exefile, "nim", getPrefixDir(), "lib", libpath])
"exefile", exefile, "nim", getPrefixDir(conf), "lib", libpath])
result.add ' '
addf(result, linkTmpl, ["builddll", builddll,
"buildgui", buildgui, "options", linkOptions,
"objfiles", objfiles, "exefile", exefile,
"nim", quoteShell(getPrefixDir()),
"nim", quoteShell(getPrefixDir(conf)),
"lib", quoteShell(libpath)])
template tryExceptOSErrorMessage(errorPrefix: string = "", body: untyped): typed =
template tryExceptOSErrorMessage(conf: ConfigRef; errorPrefix: string = "", body: untyped): typed =
try:
body
except OSError:
let ose = (ref OSError)(getCurrentException())
if errorPrefix.len > 0:
rawMessage(errGenerated, errorPrefix & " " & ose.msg & " " & $ose.errorCode)
rawMessage(conf, errGenerated, errorPrefix & " " & ose.msg & " " & $ose.errorCode)
else:
rawMessage(errExecutionOfProgramFailed, ose.msg & " " & $ose.errorCode)
rawMessage(conf, errGenerated, "execution of an external program failed: '$1'" %
(ose.msg & " " & $ose.errorCode))
raise
proc execLinkCmd(linkCmd: string) =
tryExceptOSErrorMessage("invocation of external linker program failed."):
execExternalProgram(linkCmd,
proc execLinkCmd(conf: ConfigRef; linkCmd: string) =
tryExceptOSErrorMessage(conf, "invocation of external linker program failed."):
execExternalProgram(conf, linkCmd,
if optListCmd in gGlobalOptions or gVerbosity > 1: hintExecuting else: hintLinking)
proc execCmdsInParallel(cmds: seq[string]; prettyCb: proc (idx: int)) =
proc execCmdsInParallel(conf: ConfigRef; cmds: seq[string]; prettyCb: proc (idx: int)) =
let runCb = proc (idx: int, p: Process) =
let exitCode = p.peekExitCode
if exitCode != 0:
rawMessage(errGenerated, "execution of an external compiler program '" &
rawMessage(conf, errGenerated, "execution of an external compiler program '" &
cmds[idx] & "' failed with exit code: " & $exitCode & "\n\n" &
p.outputStream.readAll.strip)
if gNumberOfProcessors == 0: gNumberOfProcessors = countProcessors()
var res = 0
if gNumberOfProcessors <= 1:
for i in countup(0, high(cmds)):
tryExceptOSErrorMessage("invocation of external compiler program failed."):
res = execWithEcho(cmds[i])
if res != 0: rawMessage(errExecutionOfProgramFailed, cmds[i])
tryExceptOSErrorMessage(conf, "invocation of external compiler program failed."):
res = execWithEcho(conf, cmds[i])
if res != 0:
rawMessage(conf, errGenerated, "execution of an external program failed: '$1'" %
cmds[i])
else:
tryExceptOSErrorMessage("invocation of external compiler program failed."):
tryExceptOSErrorMessage(conf, "invocation of external compiler program failed."):
if optListCmd in gGlobalOptions or gVerbosity > 1:
res = execProcesses(cmds, {poEchoCmd, poStdErrToStdOut, poUsePath},
gNumberOfProcessors, afterRunEvent=runCb)
@@ -744,9 +751,10 @@ proc execCmdsInParallel(cmds: seq[string]; prettyCb: proc (idx: int)) =
gNumberOfProcessors, afterRunEvent=runCb)
if res != 0:
if gNumberOfProcessors <= 1:
rawMessage(errExecutionOfProgramFailed, cmds.join())
rawMessage(conf, errGenerated, "execution of an external program failed: '$1'" %
cmds.join())
proc callCCompiler*(projectfile: string) =
proc callCCompiler*(conf: ConfigRef; projectfile: string) =
var
linkCmd: string
if gGlobalOptions * {optCompileOnly, optGenScript} == {optCompileOnly}:
@@ -758,36 +766,36 @@ proc callCCompiler*(projectfile: string) =
var prettyCmds: TStringSeq = @[]
let prettyCb = proc (idx: int) =
echo prettyCmds[idx]
compileCFile(toCompile, script, cmds, prettyCmds)
compileCFile(conf, toCompile, script, cmds, prettyCmds)
if optCompileOnly notin gGlobalOptions:
execCmdsInParallel(cmds, prettyCb)
execCmdsInParallel(conf, cmds, prettyCb)
if optNoLinking notin gGlobalOptions:
# call the linker:
var objfiles = ""
for it in externalToLink:
let objFile = if noAbsolutePaths(): it.extractFilename else: it
let objFile = if noAbsolutePaths(conf): it.extractFilename else: it
add(objfiles, ' ')
add(objfiles, quoteShell(
addFileExt(objFile, CC[cCompiler].objExt)))
for x in toCompile:
let objFile = if noAbsolutePaths(): x.obj.extractFilename else: x.obj
let objFile = if noAbsolutePaths(conf): x.obj.extractFilename else: x.obj
add(objfiles, ' ')
add(objfiles, quoteShell(objFile))
linkCmd = getLinkCmd(projectfile, objfiles)
linkCmd = getLinkCmd(conf, projectfile, objfiles)
if optCompileOnly notin gGlobalOptions:
execLinkCmd(linkCmd)
execLinkCmd(conf, linkCmd)
else:
linkCmd = ""
if optGenScript in gGlobalOptions:
add(script, linkCmd)
add(script, tnl)
generateScript(projectfile, script)
generateScript(conf, projectfile, script)
#from json import escapeJson
import json
proc writeJsonBuildInstructions*(projectfile: string) =
proc writeJsonBuildInstructions*(conf: ConfigRef; projectfile: string) =
template lit(x: untyped) = f.write x
template str(x: untyped) =
when compiles(escapeJson(x, buf)):
@@ -797,11 +805,11 @@ proc writeJsonBuildInstructions*(projectfile: string) =
else:
f.write escapeJson(x)
proc cfiles(f: File; buf: var string; clist: CfileList, isExternal: bool) =
proc cfiles(conf: ConfigRef; f: File; buf: var string; clist: CfileList, isExternal: bool) =
var pastStart = false
for it in clist:
if CfileFlag.Cached in it.flags: continue
let compileCmd = getCompileCFileCmd(it)
let compileCmd = getCompileCFileCmd(conf, it)
if pastStart: lit "],\L"
lit "["
str it.cname
@@ -810,11 +818,11 @@ proc writeJsonBuildInstructions*(projectfile: string) =
pastStart = true
lit "]\L"
proc linkfiles(f: File; buf, objfiles: var string; clist: CfileList;
proc linkfiles(conf: ConfigRef; f: File; buf, objfiles: var string; clist: CfileList;
llist: seq[string]) =
var pastStart = false
for it in llist:
let objfile = if noAbsolutePaths(): it.extractFilename
let objfile = if noAbsolutePaths(conf): it.extractFilename
else: it
let objstr = addFileExt(objfile, CC[cCompiler].objExt)
add(objfiles, ' ')
@@ -835,25 +843,25 @@ proc writeJsonBuildInstructions*(projectfile: string) =
var buf = newStringOfCap(50)
let file = projectfile.splitFile.name
let jsonFile = toGeneratedFile(file, "json")
let jsonFile = toGeneratedFile(conf, file, "json")
var f: File
if open(f, jsonFile, fmWrite):
lit "{\"compile\":[\L"
cfiles(f, buf, toCompile, false)
cfiles(conf, f, buf, toCompile, false)
lit "],\L\"link\":[\L"
var objfiles = ""
# XXX add every file here that is to link
linkfiles(f, buf, objfiles, toCompile, externalToLink)
linkfiles(conf, f, buf, objfiles, toCompile, externalToLink)
lit "],\L\"linkcmd\": "
str getLinkCmd(projectfile, objfiles)
str getLinkCmd(conf, projectfile, objfiles)
lit "\L}\L"
close(f)
proc runJsonBuildInstructions*(projectfile: string) =
proc runJsonBuildInstructions*(conf: ConfigRef; projectfile: string) =
let file = projectfile.splitFile.name
let jsonFile = toGeneratedFile(file, "json")
let jsonFile = toGeneratedFile(conf, file, "json")
try:
let data = json.parseFile(jsonFile)
let toCompile = data["compile"]
@@ -870,32 +878,32 @@ proc runJsonBuildInstructions*(projectfile: string) =
let prettyCb = proc (idx: int) =
echo prettyCmds[idx]
execCmdsInParallel(cmds, prettyCb)
execCmdsInParallel(conf, cmds, prettyCb)
let linkCmd = data["linkcmd"]
doAssert linkCmd.kind == JString
execLinkCmd(linkCmd.getStr)
execLinkCmd(conf, linkCmd.getStr)
except:
echo getCurrentException().getStackTrace()
quit "error evaluating JSON file: " & jsonFile
proc genMappingFiles(list: CFileList): Rope =
proc genMappingFiles(conf: ConfigRef; list: CFileList): Rope =
for it in list:
addf(result, "--file:r\"$1\"$N", [rope(it.cname)])
proc writeMapping*(gSymbolMapping: Rope) =
proc writeMapping*(conf: ConfigRef; symbolMapping: Rope) =
if optGenMapping notin gGlobalOptions: return
var code = rope("[C_Files]\n")
add(code, genMappingFiles(toCompile))
add(code, genMappingFiles(conf, toCompile))
add(code, "\n[C_Compiler]\nFlags=")
add(code, strutils.escape(getCompileOptions()))
add(code, strutils.escape(getCompileOptions(conf)))
add(code, "\n[Linker]\nFlags=")
add(code, strutils.escape(getLinkOptions() & " " &
getConfigVar(cCompiler, ".options.linker")))
add(code, strutils.escape(getLinkOptions(conf) & " " &
getConfigVar(conf, cCompiler, ".options.linker")))
add(code, "\n[Environment]\nlibpath=")
add(code, strutils.escape(libpath))
addf(code, "\n[Symbols]$n$1", [gSymbolMapping])
addf(code, "\n[Symbols]$n$1", [symbolMapping])
writeRope(code, joinPath(gProjectPath, "mapping.txt"))

View File

@@ -36,20 +36,20 @@ proc setId*(id: int) {.inline.} =
proc idSynchronizationPoint*(idRange: int) =
gFrontEndId = (gFrontEndId div idRange + 1) * idRange + 1
proc toGid(f: string): string =
proc toGid(conf: ConfigRef; f: string): string =
# we used to use ``f.addFileExt("gid")`` (aka ``$project.gid``), but this
# will cause strange bugs if multiple projects are in the same folder, so
# we simply use a project independent name:
result = options.completeGeneratedFilePath("nim.gid")
result = options.completeGeneratedFilePath(conf, "nim.gid")
proc saveMaxIds*(project: string) =
var f = open(project.toGid, fmWrite)
proc saveMaxIds*(conf: ConfigRef; project: string) =
var f = open(toGid(conf, project), fmWrite)
f.writeLine($gFrontEndId)
f.close()
proc loadMaxIds*(project: string) =
proc loadMaxIds*(conf: ConfigRef; project: string) =
var f: File
if open(f, project.toGid, fmRead):
if open(f, toGid(conf, project), fmRead):
var line = newStringOfCap(20)
if f.readLine(line):
var frontEndId = parseInt(line)

View File

@@ -17,7 +17,7 @@
import
hashes, options, msgs, strutils, platform, idents, nimlexbase, llstream,
wordrecg
wordrecg, configuration
const
MaxLineLength* = 80 # lines longer than this lead to a warning
@@ -131,7 +131,7 @@ type
# like 0b01 or r"\L" are unaffected
commentOffsetA*, commentOffsetB*: int
TErrorHandler* = proc (info: TLineInfo; msg: TMsgKind; arg: string)
TErrorHandler* = proc (conf: ConfigRef; info: TLineInfo; msg: TMsgKind; arg: string)
TLexer* = object of TBaseLexer
fileIdx*: FileIndex
indentAhead*: int # if > 0 an indendation has already been read
@@ -234,7 +234,7 @@ proc openLexer*(lex: var TLexer, fileIdx: FileIndex, inputstream: PLLStream;
proc openLexer*(lex: var TLexer, filename: string, inputstream: PLLStream;
cache: IdentCache; config: ConfigRef) =
openLexer(lex, filename.fileInfoIdx, inputstream, cache, config)
openLexer(lex, fileInfoIdx(config, filename), inputstream, cache, config)
proc closeLexer*(lex: var TLexer) =
if lex.config != nil:
@@ -246,9 +246,9 @@ proc getLineInfo(L: TLexer): TLineInfo =
proc dispMessage(L: TLexer; info: TLineInfo; msg: TMsgKind; arg: string) =
if L.errorHandler.isNil:
msgs.message(info, msg, arg)
msgs.message(L.config, info, msg, arg)
else:
L.errorHandler(info, msg, arg)
L.errorHandler(L.config, info, msg, arg)
proc lexMessage*(L: TLexer, msg: TMsgKind, arg = "") =
L.dispMessage(getLineInfo(L), msg, arg)
@@ -341,7 +341,8 @@ proc getNumber(L: var TLexer, result: var TToken) =
break
if buf[pos] == '_':
if buf[pos+1] notin chars:
lexMessage(L, errInvalidToken, "_")
lexMessage(L, errGenerated,
"only single underscores may occur in a token: '__' is invalid")
break
add(tok.literal, '_')
inc(pos)
@@ -355,7 +356,7 @@ proc getNumber(L: var TLexer, result: var TToken) =
inc(pos)
L.bufpos = pos
proc lexMessageLitNum(L: var TLexer, msg: TMsgKind, startpos: int) =
proc lexMessageLitNum(L: var TLexer, msg: string, 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',
@@ -376,7 +377,7 @@ proc getNumber(L: var TLexer, result: var TToken) =
add(t.literal, L.buf[L.bufpos])
matchChars(L, t, {'0'..'9'})
L.bufpos = msgPos
lexMessage(L, msg, t.literal)
lexMessage(L, errGenerated, msg % t.literal)
var
startpos, endpos: int
@@ -398,7 +399,7 @@ proc getNumber(L: var TLexer, result: var TToken) =
eatChar(L, result, '0')
case L.buf[L.bufpos]
of 'O':
lexMessageLitNum(L, errInvalidNumberOctalCode, startpos)
lexMessageLitNum(L, "$1 is not a valid number; did you mean octal? Then use one of '0o', '0c' or '0C'.", startpos)
of 'x', 'X':
eatChar(L, result, 'x')
matchUnderscoreChars(L, result, {'0'..'9', 'a'..'f', 'A'..'F'})
@@ -409,7 +410,7 @@ proc getNumber(L: var TLexer, result: var TToken) =
eatChar(L, result, 'b')
matchUnderscoreChars(L, result, {'0'..'1'})
else:
internalError(getLineInfo(L), "getNumber")
internalError(L.config, getLineInfo(L), "getNumber")
else:
matchUnderscoreChars(L, result, {'0'..'9'})
if (L.buf[L.bufpos] == '.') and (L.buf[L.bufpos + 1] in {'0'..'9'}):
@@ -464,7 +465,7 @@ proc getNumber(L: var TLexer, result: var TToken) =
result.tokType = tkInt8Lit
inc(postPos)
else:
lexMessageLitNum(L, errInvalidNumber, startpos)
lexMessageLitNum(L, "invalid number: '$1'", startpos)
of 'u', 'U':
inc(postPos)
if (L.buf[postPos] == '6') and (L.buf[postPos + 1] == '4'):
@@ -482,12 +483,12 @@ proc getNumber(L: var TLexer, result: var TToken) =
else:
result.tokType = tkUIntLit
else:
lexMessageLitNum(L, errInvalidNumber, startpos)
lexMessageLitNum(L, "invalid number: '$1'", startpos)
# 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)
lexMessageLitNum(L, "invalid number: '$1'", startpos)
# Third stage, extract actual number
L.bufpos = startpos # restore position
@@ -528,7 +529,7 @@ proc getNumber(L: var TLexer, result: var TToken) =
else:
break
else:
internalError(getLineInfo(L), "getNumber")
internalError(L.config, getLineInfo(L), "getNumber")
case result.tokType
of tkIntLit, tkInt64Lit: result.iNumber = xi
@@ -545,7 +546,7 @@ proc getNumber(L: var TLexer, result: var TToken) =
# XXX: Test this on big endian machine!
of tkFloat64Lit, tkFloatLit:
result.fNumber = (cast[PFloat64](addr(xi)))[]
else: internalError(getLineInfo(L), "getNumber")
else: internalError(L.config, getLineInfo(L), "getNumber")
# Bounds checks. Non decimal literals are allowed to overflow the range of
# the datatype as long as their pattern don't overflow _bitwise_, hence
@@ -561,7 +562,7 @@ proc getNumber(L: var TLexer, result: var TToken) =
if outOfRange:
#echo "out of range num: ", result.iNumber, " vs ", xi
lexMessageLitNum(L, errNumberOutOfRange, startpos)
lexMessageLitNum(L, "number out of range: '$1'", startpos)
else:
case result.tokType
@@ -590,7 +591,7 @@ proc getNumber(L: var TLexer, result: var TToken) =
result.iNumber > BiggestInt(uint32.high))
else: false
if outOfRange: lexMessageLitNum(L, errNumberOutOfRange, startpos)
if outOfRange: lexMessageLitNum(L, "number out of range: '$1'", startpos)
# Promote int literal to int64? Not always necessary, but more consistent
if result.tokType == tkIntLit:
@@ -598,9 +599,9 @@ proc getNumber(L: var TLexer, result: var TToken) =
result.tokType = tkInt64Lit
except ValueError:
lexMessageLitNum(L, errInvalidNumber, startpos)
lexMessageLitNum(L, "invalid number: '$1'", startpos)
except OverflowError, RangeError:
lexMessageLitNum(L, errNumberOutOfRange, startpos)
lexMessageLitNum(L, "number out of range: '$1'", startpos)
tokenEnd(result, postPos-1)
L.bufpos = postPos
@@ -627,7 +628,8 @@ proc getEscapedChar(L: var TLexer, tok: var TToken) =
case L.buf[L.bufpos]
of 'n', 'N':
if gOldNewlines:
if tok.tokType == tkCharLit: lexMessage(L, errNnotAllowedInCharacter)
if tok.tokType == tkCharLit:
lexMessage(L, errGenerated, "\\n not allowed in character literal")
add(tok.literal, tnl)
else:
add(tok.literal, '\L')
@@ -696,8 +698,8 @@ proc getEscapedChar(L: var TLexer, tok: var TToken) =
var xi = 0
handleDecChars(L, xi)
if (xi <= 255): add(tok.literal, chr(xi))
else: lexMessage(L, errInvalidCharacterConstant)
else: lexMessage(L, errInvalidCharacterConstant)
else: lexMessage(L, errGenerated, "invalid character constant")
else: lexMessage(L, errGenerated, "invalid character constant")
proc newString(s: cstring, len: int): string =
## XXX, how come there is no support for this?
@@ -761,7 +763,7 @@ proc getString(L: var TLexer, tok: var TToken, rawMode: bool) =
tokenEndIgnore(tok, pos)
var line2 = L.lineNumber
L.lineNumber = line
lexMessagePos(L, errClosingTripleQuoteExpected, L.lineStart)
lexMessagePos(L, errGenerated, L.lineStart, "closing \"\"\" expected, but end of file reached")
L.lineNumber = line2
L.bufpos = pos
break
@@ -784,7 +786,7 @@ proc getString(L: var TLexer, tok: var TToken, rawMode: bool) =
break
elif c in {CR, LF, nimlexbase.EndOfFile}:
tokenEndIgnore(tok, pos)
lexMessage(L, errClosingQuoteExpected)
lexMessage(L, errGenerated, "closing \" expected")
break
elif (c == '\\') and not rawMode:
L.bufpos = pos
@@ -800,12 +802,13 @@ proc getCharacter(L: var TLexer, tok: var TToken) =
inc(L.bufpos) # skip '
var c = L.buf[L.bufpos]
case c
of '\0'..pred(' '), '\'': lexMessage(L, errInvalidCharacterConstant)
of '\0'..pred(' '), '\'': lexMessage(L, errGenerated, "invalid character literal")
of '\\': getEscapedChar(L, tok)
else:
tok.literal = $c
inc(L.bufpos)
if L.buf[L.bufpos] != '\'': lexMessage(L, errMissingFinalQuote)
if L.buf[L.bufpos] != '\'':
lexMessage(L, errGenerated, "missing closing ' for character literal")
tokenEndIgnore(tok, L.bufpos)
inc(L.bufpos) # skip '
@@ -826,7 +829,7 @@ proc getSymbol(L: var TLexer, tok: var TToken) =
inc(pos)
of '_':
if buf[pos+1] notin SymChars:
lexMessage(L, errInvalidToken, "_")
lexMessage(L, errGenerated, "invalid token: trailing underscore")
break
inc(pos)
else: break
@@ -1014,7 +1017,7 @@ proc skip(L: var TLexer, tok: var TToken) =
inc(pos)
inc(tok.strongSpaceA)
of '\t':
if not L.allowTabs: lexMessagePos(L, errTabulatorsAreNotAllowed, pos)
if not L.allowTabs: lexMessagePos(L, errGenerated, pos, "tabulators are not allowed")
inc(pos)
of CR, LF:
tokenEndPrevious(tok, pos)
@@ -1182,7 +1185,7 @@ proc rawGetTok*(L: var TLexer, tok: var TToken) =
else:
tok.literal = $c
tok.tokType = tkInvalid
lexMessage(L, errInvalidToken, c & " (\\" & $(ord(c)) & ')')
lexMessage(L, errGenerated, "invalid token: " & c & " (\\" & $(ord(c)) & ')')
of '\"':
# check for extended raw string literal:
var rawMode = L.bufpos > 0 and L.buf[L.bufpos-1] in SymChars
@@ -1199,7 +1202,7 @@ proc rawGetTok*(L: var TLexer, tok: var TToken) =
getNumber(L, tok)
let c = L.buf[L.bufpos]
if c in SymChars+{'_'}:
lexMessage(L, errInvalidToken, c & " (\\" & $(ord(c)) & ')')
lexMessage(L, errGenerated, "invalid token: no whitespace between number and identifier")
else:
if c in OpChars:
getOperator(L, tok)
@@ -1209,6 +1212,6 @@ proc rawGetTok*(L: var TLexer, tok: var TToken) =
else:
tok.literal = $c
tok.tokType = tkInvalid
lexMessage(L, errInvalidToken, c & " (\\" & $(ord(c)) & ')')
lexMessage(L, errGenerated, "invalid token: " & c & " (\\" & $(ord(c)) & ')')
inc(L.bufpos)
atTokenEnd()

View File

@@ -8,481 +8,13 @@
#
import
options, strutils, os, tables, ropes, platform, terminal, macros
options, strutils, os, tables, ropes, platform, terminal, macros,
configuration
const
explanationsBaseUrl* = "https://nim-lang.org/docs/manual"
#type
# MsgConfig* = ref object of RootObj
type
TMsgKind* = enum
errUnknown, errInternal, errIllFormedAstX, errCannotOpenFile, errGenerated,
errStringLiteralExpected,
errIntLiteralExpected, errInvalidCharacterConstant,
errClosingTripleQuoteExpected, errClosingQuoteExpected,
errTabulatorsAreNotAllowed, errInvalidToken,
errInvalidNumber, errInvalidNumberOctalCode, errNumberOutOfRange,
errNnotAllowedInCharacter, errClosingBracketExpected, errMissingFinalQuote,
errIdentifierExpected, errNewlineExpected, errInvalidModuleName,
errOperatorExpected, errTokenExpected,
errRecursiveDependencyX, errOnOrOffExpected, errNoneSpeedOrSizeExpected,
errInvalidPragma, errUnknownPragma, errInvalidDirectiveX,
errAtPopWithoutPush, errEmptyAsm, errInvalidIndentation,
errExceptionAlreadyHandled,
errYieldNotAllowedHere, errYieldNotAllowedInTryStmt,
errInvalidNumberOfYieldExpr, errCannotReturnExpr,
errNoReturnWithReturnTypeNotAllowed, errAttemptToRedefine,
errStmtInvalidAfterReturn, errStmtExpected, errInvalidLabel,
errInvalidCmdLineOption, errCmdLineArgExpected, errCmdLineNoArgExpected,
errInvalidVarSubstitution, errUnknownVar, errUnknownCcompiler,
errOnOrOffExpectedButXFound, errOnOffOrListExpectedButXFound,
errNoneBoehmRefcExpectedButXFound,
errNoneSpeedOrSizeExpectedButXFound, errGuiConsoleOrLibExpectedButXFound,
errUnknownOS, errUnknownCPU, errGenOutExpectedButXFound,
errArgsNeedRunOption, errInvalidMultipleAsgn, errColonOrEqualsExpected,
errExprExpected, errUndeclaredField,
errUndeclaredRoutine, errUseQualifier,
errTypeExpected,
errSystemNeeds, errExecutionOfProgramFailed, errNotOverloadable,
errInvalidArgForX, errStmtHasNoEffect, errXExpectsTypeOrValue,
errXExpectsArrayType, errIteratorCannotBeInstantiated, errExprXAmbiguous,
errConstantDivisionByZero, errOrdinalTypeExpected,
errOrdinalOrFloatTypeExpected, errOverOrUnderflow,
errCannotEvalXBecauseIncompletelyDefined, errChrExpectsRange0_255,
errDynlibRequiresExportc, errUndeclaredFieldX, errNilAccess,
errIndexOutOfBounds, errIndexTypesDoNotMatch, errBracketsInvalidForType,
errValueOutOfSetBounds, errFieldInitTwice, errFieldNotInit,
errExprXCannotBeCalled, errExprHasNoType, errExprXHasNoType,
errCastNotInSafeMode, errExprCannotBeCastToX, errCommaOrParRiExpected,
errCurlyLeOrParLeExpected, errSectionExpected, errRangeExpected,
errMagicOnlyInSystem, errPowerOfTwoExpected,
errStringMayNotBeEmpty, errCallConvExpected, errProcOnlyOneCallConv,
errSymbolMustBeImported, errExprMustBeBool, errConstExprExpected,
errDuplicateCaseLabel, errRangeIsEmpty, errSelectorMustBeOfCertainTypes,
errSelectorMustBeOrdinal, errOrdXMustNotBeNegative, errLenXinvalid,
errWrongNumberOfVariables, errExprCannotBeRaised, errBreakOnlyInLoop,
errTypeXhasUnknownSize, errConstNeedsConstExpr, errConstNeedsValue,
errResultCannotBeOpenArray, errSizeTooBig, errSetTooBig,
errBaseTypeMustBeOrdinal, errInheritanceOnlyWithNonFinalObjects,
errInheritanceOnlyWithEnums, errIllegalRecursionInTypeX,
errCannotInstantiateX, errExprHasNoAddress, errXStackEscape,
errVarForOutParamNeededX,
errPureTypeMismatch, errTypeMismatch, errButExpected, errButExpectedX,
errAmbiguousCallXYZ, errWrongNumberOfArguments,
errWrongNumberOfArgumentsInCall,
errMissingGenericParamsForTemplate,
errXCannotBePassedToProcVar,
errPragmaOnlyInHeaderOfProcX, errImplOfXNotAllowed,
errImplOfXexpected, errNoSymbolToBorrowFromFound, errDiscardValueX,
errInvalidDiscard, errIllegalConvFromXtoY, errCannotBindXTwice,
errInvalidOrderInArrayConstructor,
errInvalidOrderInEnumX, errEnumXHasHoles, errExceptExpected, errInvalidTry,
errOptionExpected, errXisNoLabel, errNotAllCasesCovered,
errUnknownSubstitionVar, errComplexStmtRequiresInd, errXisNotCallable,
errNoPragmasAllowedForX, errNoGenericParamsAllowedForX,
errInvalidParamKindX, errDefaultArgumentInvalid, errNamedParamHasToBeIdent,
errNoReturnTypeForX, errConvNeedsOneArg, errInvalidPragmaX,
errXNotAllowedHere, errInvalidControlFlowX,
errXisNoType, errCircumNeedsPointer, errInvalidExpression,
errInvalidExpressionX, errEnumHasNoValueX, errNamedExprExpected,
errNamedExprNotAllowed, errXExpectsOneTypeParam,
errArrayExpectsTwoTypeParams, errInvalidVisibilityX, errInitHereNotAllowed,
errXCannotBeAssignedTo, errIteratorNotAllowed, errXNeedsReturnType,
errNoReturnTypeDeclared,
errNoCommand, errInvalidCommandX, errXOnlyAtModuleScope,
errXNeedsParamObjectType,
errTemplateInstantiationTooNested, errMacroInstantiationTooNested,
errInstantiationFrom,
errInvalidIndexValueForTuple, errCommandExpectsFilename,
errMainModuleMustBeSpecified,
errXExpected,
errTIsNotAConcreteType,
errCastToANonConcreteType,
errInvalidSectionStart, errGridTableNotImplemented, errGeneralParseError,
errNewSectionExpected, errWhitespaceExpected, errXisNoValidIndexFile,
errCannotRenderX, errVarVarTypeNotAllowed, errInstantiateXExplicitly,
errOnlyACallOpCanBeDelegator, errUsingNoSymbol,
errMacroBodyDependsOnGenericTypes,
errDestructorNotGenericEnough,
errInlineIteratorsAsProcParams,
errXExpectsTwoArguments,
errXExpectsObjectTypes, errXcanNeverBeOfThisSubtype, errTooManyIterations,
errCannotInterpretNodeX, errFieldXNotFound, errInvalidConversionFromTypeX,
errAssertionFailed, errCannotGenerateCodeForX, errXRequiresOneArgument,
errUnhandledExceptionX, errCyclicTree, errXisNoMacroOrTemplate,
errXhasSideEffects, errIteratorExpected, errLetNeedsInit,
errThreadvarCannotInit, errWrongSymbolX, errIllegalCaptureX,
errXCannotBeClosure, errXMustBeCompileTime,
errCannotInferTypeOfTheLiteral,
errCannotInferReturnType,
errCannotInferStaticParam,
errGenericLambdaNotAllowed,
errProcHasNoConcreteType,
errCompilerDoesntSupportTarget,
errInOutFlagNotExtern,
errUser,
warnCannotOpenFile,
warnOctalEscape, warnXIsNeverRead, warnXmightNotBeenInit,
warnDeprecated, warnConfigDeprecated,
warnSmallLshouldNotBeUsed, warnUnknownMagic, warnRedefinitionOfLabel,
warnUnknownSubstitutionX, warnLanguageXNotSupported,
warnFieldXNotSupported, warnCommentXIgnored,
warnTypelessParam,
warnUseBase, warnWriteToForeignHeap, warnUnsafeCode,
warnEachIdentIsTuple, warnShadowIdent,
warnProveInit, warnProveField, warnProveIndex, warnGcUnsafe, warnGcUnsafe2,
warnUninit, warnGcMem, warnDestructor, warnLockLevel, warnResultShadowed,
warnInconsistentSpacing, warnUser,
hintSuccess, hintSuccessX,
hintLineTooLong, hintXDeclaredButNotUsed, hintConvToBaseNotNeeded,
hintConvFromXtoItselfNotNeeded, hintExprAlwaysX, hintQuitCalled,
hintProcessing, hintCodeBegin, hintCodeEnd, hintConf, hintPath,
hintConditionAlwaysTrue, hintName, hintPattern,
hintExecuting, hintLinking, hintDependency,
hintSource, hintPerformance, hintStackTrace, hintGCStats,
hintUser, hintUserRaw
const
MsgKindToStr*: array[TMsgKind, string] = [
errUnknown: "unknown error",
errInternal: "internal error: $1",
errIllFormedAstX: "illformed AST: $1",
errCannotOpenFile: "cannot open '$1'",
errGenerated: "$1",
errStringLiteralExpected: "string literal expected",
errIntLiteralExpected: "integer literal expected",
errInvalidCharacterConstant: "invalid character constant",
errClosingTripleQuoteExpected: "closing \"\"\" expected, but end of file reached",
errClosingQuoteExpected: "closing \" expected",
errTabulatorsAreNotAllowed: "tabulators are not allowed",
errInvalidToken: "invalid token: $1",
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",
errNnotAllowedInCharacter: "\\n not allowed in character literal",
errClosingBracketExpected: "closing ']' expected, but end of file reached",
errMissingFinalQuote: "missing final ' for character literal",
errIdentifierExpected: "identifier expected, but found '$1'",
errNewlineExpected: "newline expected, but found '$1'",
errInvalidModuleName: "invalid module name: '$1'",
errOperatorExpected: "operator expected, but found '$1'",
errTokenExpected: "'$1' expected",
errRecursiveDependencyX: "recursive dependency: '$1'",
errOnOrOffExpected: "'on' or 'off' expected",
errNoneSpeedOrSizeExpected: "'none', 'speed' or 'size' expected",
errInvalidPragma: "invalid pragma",
errUnknownPragma: "unknown pragma: '$1'",
errInvalidDirectiveX: "invalid directive: '$1'",
errAtPopWithoutPush: "'pop' without a 'push' pragma",
errEmptyAsm: "empty asm statement",
errInvalidIndentation: "invalid indentation",
errExceptionAlreadyHandled: "exception already handled",
errYieldNotAllowedHere: "'yield' only allowed in an iterator",
errYieldNotAllowedInTryStmt: "'yield' cannot be used within 'try' in a non-inlined iterator",
errInvalidNumberOfYieldExpr: "invalid number of 'yield' expressions",
errCannotReturnExpr: "current routine cannot return an expression",
errNoReturnWithReturnTypeNotAllowed: "routines with NoReturn pragma are not allowed to have return type",
errAttemptToRedefine: "redefinition of '$1'",
errStmtInvalidAfterReturn: "statement not allowed after 'return', 'break', 'raise', 'continue' or proc call with noreturn pragma",
errStmtExpected: "statement expected",
errInvalidLabel: "'$1' is no label",
errInvalidCmdLineOption: "invalid command line option: '$1'",
errCmdLineArgExpected: "argument for command line option expected: '$1'",
errCmdLineNoArgExpected: "invalid argument for command line option: '$1'",
errInvalidVarSubstitution: "invalid variable substitution in '$1'",
errUnknownVar: "unknown variable: '$1'",
errUnknownCcompiler: "unknown C compiler: '$1'",
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",
errNoneSpeedOrSizeExpectedButXFound: "'none', 'speed' or 'size' expected, but '$1' found",
errGuiConsoleOrLibExpectedButXFound: "'gui', 'console' or 'lib' expected, but '$1' found",
errUnknownOS: "unknown OS: '$1'",
errUnknownCPU: "unknown CPU: '$1'",
errGenOutExpectedButXFound: "'c', 'c++' or 'yaml' expected, but '$1' found",
errArgsNeedRunOption: "arguments can only be given if the '--run' option is selected",
errInvalidMultipleAsgn: "multiple assignment is not allowed",
errColonOrEqualsExpected: "':' or '=' expected, but found '$1'",
errExprExpected: "expression expected, but found '$1'",
errUndeclaredField: "undeclared field: '$1'",
errUndeclaredRoutine: "attempting to call undeclared routine: '$1'",
errUseQualifier: "ambiguous identifier: '$1' -- use a qualifier",
errTypeExpected: "type expected",
errSystemNeeds: "system module needs '$1'",
errExecutionOfProgramFailed: "execution of an external program failed: '$1'",
errNotOverloadable: "overloaded '$1' leads to ambiguous calls",
errInvalidArgForX: "invalid argument for '$1'",
errStmtHasNoEffect: "statement has no effect",
errXExpectsTypeOrValue: "'$1' expects a type or value",
errXExpectsArrayType: "'$1' expects an array type",
errIteratorCannotBeInstantiated: "'$1' cannot be instantiated because its body has not been compiled yet",
errExprXAmbiguous: "expression '$1' ambiguous in this context",
errConstantDivisionByZero: "division by zero",
errOrdinalTypeExpected: "ordinal type expected",
errOrdinalOrFloatTypeExpected: "ordinal or float type expected",
errOverOrUnderflow: "over- or underflow",
errCannotEvalXBecauseIncompletelyDefined: "cannot evaluate '$1' because type is not defined completely",
errChrExpectsRange0_255: "'chr' expects an int in the range 0..255",
errDynlibRequiresExportc: "'dynlib' requires 'exportc'",
errUndeclaredFieldX: "undeclared field: '$1'",
errNilAccess: "attempt to access a nil address",
errIndexOutOfBounds: "index out of bounds",
errIndexTypesDoNotMatch: "index types do not match",
errBracketsInvalidForType: "'[]' operator invalid for this type",
errValueOutOfSetBounds: "value out of set bounds",
errFieldInitTwice: "field initialized twice: '$1'",
errFieldNotInit: "field '$1' not initialized",
errExprXCannotBeCalled: "expression '$1' cannot be called",
errExprHasNoType: "expression has no type",
errExprXHasNoType: "expression '$1' has no type (or is ambiguous)",
errCastNotInSafeMode: "'cast' not allowed in safe mode",
errExprCannotBeCastToX: "expression cannot be cast to $1",
errCommaOrParRiExpected: "',' or ')' expected",
errCurlyLeOrParLeExpected: "'{' or '(' expected",
errSectionExpected: "section ('type', 'proc', etc.) expected",
errRangeExpected: "range expected",
errMagicOnlyInSystem: "'magic' only allowed in system module",
errPowerOfTwoExpected: "power of two expected",
errStringMayNotBeEmpty: "string literal may not be empty",
errCallConvExpected: "calling convention expected",
errProcOnlyOneCallConv: "a proc can only have one calling convention",
errSymbolMustBeImported: "symbol must be imported if 'lib' pragma is used",
errExprMustBeBool: "expression must be of type 'bool'",
errConstExprExpected: "constant expression expected",
errDuplicateCaseLabel: "duplicate case label",
errRangeIsEmpty: "range is empty",
errSelectorMustBeOfCertainTypes: "selector must be of an ordinal type, float or string",
errSelectorMustBeOrdinal: "selector must be of an ordinal type",
errOrdXMustNotBeNegative: "ord($1) must not be negative",
errLenXinvalid: "len($1) must be less than 32768",
errWrongNumberOfVariables: "wrong number of variables",
errExprCannotBeRaised: "only a 'ref object' can be raised",
errBreakOnlyInLoop: "'break' only allowed in loop construct",
errTypeXhasUnknownSize: "type '$1' has unknown size",
errConstNeedsConstExpr: "a constant can only be initialized with a constant expression",
errConstNeedsValue: "a constant needs a value",
errResultCannotBeOpenArray: "the result type cannot be on open array",
errSizeTooBig: "computing the type's size produced an overflow",
errSetTooBig: "set is too large",
errBaseTypeMustBeOrdinal: "base type of a set must be an ordinal",
errInheritanceOnlyWithNonFinalObjects: "inheritance only works with non-final objects",
errInheritanceOnlyWithEnums: "inheritance only works with an enum",
errIllegalRecursionInTypeX: "illegal recursion in type '$1'",
errCannotInstantiateX: "cannot instantiate: '$1'",
errExprHasNoAddress: "expression has no address",
errXStackEscape: "address of '$1' may not escape its stack frame",
errVarForOutParamNeededX: "for a 'var' type a variable needs to be passed; but '$1' is immutable",
errPureTypeMismatch: "type mismatch",
errTypeMismatch: "type mismatch: got <",
errButExpected: "but expected one of: ",
errButExpectedX: "but expected '$1'",
errAmbiguousCallXYZ: "ambiguous call; both $1 and $2 match for: $3",
errWrongNumberOfArguments: "wrong number of arguments",
errWrongNumberOfArgumentsInCall: "wrong number of arguments in call to '$1'",
errMissingGenericParamsForTemplate: "'$1' has unspecified generic parameters",
errXCannotBePassedToProcVar: "'$1' cannot be passed to a procvar",
errPragmaOnlyInHeaderOfProcX: "pragmas are only allowed in the header of a proc; redefinition of $1",
errImplOfXNotAllowed: "implementation of '$1' is not allowed",
errImplOfXexpected: "implementation of '$1' expected",
errNoSymbolToBorrowFromFound: "no symbol to borrow from found",
errDiscardValueX: "value of type '$1' has to be discarded",
errInvalidDiscard: "statement returns no value that can be discarded",
errIllegalConvFromXtoY: "conversion from $1 to $2 is invalid",
errCannotBindXTwice: "cannot bind parameter '$1' twice",
errInvalidOrderInArrayConstructor: "invalid order in array constructor",
errInvalidOrderInEnumX: "invalid order in enum '$1'",
errEnumXHasHoles: "enum '$1' has holes",
errExceptExpected: "'except' or 'finally' expected",
errInvalidTry: "after catch all 'except' or 'finally' no section may follow",
errOptionExpected: "option expected, but found '$1'",
errXisNoLabel: "'$1' is not a label",
errNotAllCasesCovered: "not all cases are covered",
errUnknownSubstitionVar: "unknown substitution variable: '$1'",
errComplexStmtRequiresInd: "complex statement requires indentation",
errXisNotCallable: "'$1' is not callable",
errNoPragmasAllowedForX: "no pragmas allowed for $1",
errNoGenericParamsAllowedForX: "no generic parameters allowed for $1",
errInvalidParamKindX: "invalid param kind: '$1'",
errDefaultArgumentInvalid: "default argument invalid",
errNamedParamHasToBeIdent: "named parameter has to be an identifier",
errNoReturnTypeForX: "no return type allowed for $1",
errConvNeedsOneArg: "a type conversion needs exactly one argument",
errInvalidPragmaX: "invalid pragma: $1",
errXNotAllowedHere: "$1 not allowed here",
errInvalidControlFlowX: "invalid control flow: $1",
errXisNoType: "invalid type: '$1'",
errCircumNeedsPointer: "'[]' needs a pointer or reference type",
errInvalidExpression: "invalid expression",
errInvalidExpressionX: "invalid expression: '$1'",
errEnumHasNoValueX: "enum has no value '$1'",
errNamedExprExpected: "named expression expected",
errNamedExprNotAllowed: "named expression not allowed here",
errXExpectsOneTypeParam: "'$1' expects one type parameter",
errArrayExpectsTwoTypeParams: "array expects two type parameters",
errInvalidVisibilityX: "invalid visibility: '$1'",
errInitHereNotAllowed: "initialization not allowed here",
errXCannotBeAssignedTo: "'$1' cannot be assigned to",
errIteratorNotAllowed: "iterators can only be defined at the module's top level",
errXNeedsReturnType: "$1 needs a return type",
errNoReturnTypeDeclared: "no return type declared",
errNoCommand: "no command given",
errInvalidCommandX: "invalid command: '$1'",
errXOnlyAtModuleScope: "'$1' is only allowed at top level",
errXNeedsParamObjectType: "'$1' needs a parameter that has an object type",
errTemplateInstantiationTooNested: "template instantiation too nested, try --evalTemplateLimit:N",
errMacroInstantiationTooNested: "macro instantiation too nested, try --evalMacroLimit:N",
errInstantiationFrom: "template/generic instantiation from here",
errInvalidIndexValueForTuple: "invalid index value for tuple subscript",
errCommandExpectsFilename: "command expects a filename argument",
errMainModuleMustBeSpecified: "please, specify a main module in the project configuration file",
errXExpected: "'$1' expected",
errTIsNotAConcreteType: "'$1' is not a concrete type.",
errCastToANonConcreteType: "cannot cast to a non concrete type: '$1'",
errInvalidSectionStart: "invalid section start",
errGridTableNotImplemented: "grid table is not implemented",
errGeneralParseError: "general parse error",
errNewSectionExpected: "new section expected",
errWhitespaceExpected: "whitespace expected, got '$1'",
errXisNoValidIndexFile: "'$1' is no valid index file",
errCannotRenderX: "cannot render reStructuredText element '$1'",
errVarVarTypeNotAllowed: "type 'var var' is not allowed",
errInstantiateXExplicitly: "instantiate '$1' explicitly",
errOnlyACallOpCanBeDelegator: "only a call operator can be a delegator",
errUsingNoSymbol: "'$1' is not a variable, constant or a proc name",
errMacroBodyDependsOnGenericTypes: "the macro body cannot be compiled, " &
"because the parameter '$1' has a generic type",
errDestructorNotGenericEnough: "Destructor signature is too specific. " &
"A destructor must be associated will all instantiations of a generic type",
errInlineIteratorsAsProcParams: "inline iterators can be used as parameters only for " &
"templates, macros and other inline iterators",
errXExpectsTwoArguments: "'$1' expects two arguments",
errXExpectsObjectTypes: "'$1' expects object types",
errXcanNeverBeOfThisSubtype: "'$1' can never be of this subtype",
errTooManyIterations: "interpretation requires too many iterations; " &
"if you are sure this is not a bug in your code edit " &
"compiler/vmdef.MaxLoopIterations and rebuild the compiler",
errCannotInterpretNodeX: "cannot evaluate '$1'",
errFieldXNotFound: "field '$1' cannot be found",
errInvalidConversionFromTypeX: "invalid conversion from type '$1'",
errAssertionFailed: "assertion failed",
errCannotGenerateCodeForX: "cannot generate code for '$1'",
errXRequiresOneArgument: "$1 requires one parameter",
errUnhandledExceptionX: "unhandled exception: $1",
errCyclicTree: "macro returned a cyclic abstract syntax tree",
errXisNoMacroOrTemplate: "'$1' is no macro or template",
errXhasSideEffects: "'$1' can have side effects",
errIteratorExpected: "iterator within for loop context expected",
errLetNeedsInit: "'let' symbol requires an initialization",
errThreadvarCannotInit: "a thread var cannot be initialized explicitly; this would only run for the main thread",
errWrongSymbolX: "usage of '$1' is a user-defined error",
errIllegalCaptureX: "illegal capture '$1'",
errXCannotBeClosure: "'$1' cannot have 'closure' calling convention",
errXMustBeCompileTime: "'$1' can only be used in compile-time context",
errCannotInferTypeOfTheLiteral: "cannot infer the type of the $1",
errCannotInferReturnType: "cannot infer the return type of the proc",
errCannotInferStaticParam: "cannot infer the value of the static param `$1`",
errGenericLambdaNotAllowed: "A nested proc can have generic parameters only when " &
"it is used as an operand to another routine and the types " &
"of the generic paramers can be inferred from the expected signature.",
errProcHasNoConcreteType: "'$1' doesn't have a concrete type, due to unspecified generic parameters.",
errCompilerDoesntSupportTarget: "The current compiler '$1' doesn't support the requested compilation target",
errInOutFlagNotExtern: "The `$1` modifier can be used only with imported types",
errUser: "$1",
warnCannotOpenFile: "cannot open '$1'",
warnOctalEscape: "octal escape sequences do not exist; leading zero is ignored",
warnXIsNeverRead: "'$1' is never read",
warnXmightNotBeenInit: "'$1' might not have been initialized",
warnDeprecated: "$1 is deprecated",
warnConfigDeprecated: "config file '$1' is deprecated",
warnSmallLshouldNotBeUsed: "'l' should not be used as an identifier; may look like '1' (one)",
warnUnknownMagic: "unknown magic '$1' might crash the compiler",
warnRedefinitionOfLabel: "redefinition of label '$1'",
warnUnknownSubstitutionX: "unknown substitution '$1'",
warnLanguageXNotSupported: "language '$1' not supported",
warnFieldXNotSupported: "field '$1' not supported",
warnCommentXIgnored: "comment '$1' ignored",
warnTypelessParam: "'$1' has no type. Typeless parameters are deprecated; only allowed for 'template'",
warnUseBase: "use {.base.} for base methods; baseless methods are deprecated",
warnWriteToForeignHeap: "write to foreign heap",
warnUnsafeCode: "unsafe code: '$1'",
warnEachIdentIsTuple: "each identifier is a tuple",
warnShadowIdent: "shadowed identifier: '$1'",
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",
warnProveIndex: "cannot prove index '$1' is valid",
warnGcUnsafe: "not GC-safe: '$1'",
warnGcUnsafe2: "$1",
warnUninit: "'$1' might not have been initialized",
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.",
warnLockLevel: "$1",
warnResultShadowed: "Special variable 'result' is shadowed.",
warnInconsistentSpacing: "Number of spaces around '$#' is not consistent",
warnUser: "$1",
hintSuccess: "operation successful",
hintSuccessX: "operation successful ($# lines compiled; $# sec total; $#; $#)",
hintLineTooLong: "line too long",
hintXDeclaredButNotUsed: "'$1' is declared but not used",
hintConvToBaseNotNeeded: "conversion to base object is not needed",
hintConvFromXtoItselfNotNeeded: "conversion from $1 to itself is pointless",
hintExprAlwaysX: "expression evaluates always to '$1'",
hintQuitCalled: "quit() called",
hintProcessing: "$1",
hintCodeBegin: "generated code listing:",
hintCodeEnd: "end of listing",
hintConf: "used config file '$1'",
hintPath: "added path: '$1'",
hintConditionAlwaysTrue: "condition is always true: '$1'",
hintName: "name should be: '$1'",
hintPattern: "$1",
hintExecuting: "$1",
hintLinking: "",
hintDependency: "$1",
hintSource: "$1",
hintPerformance: "$1",
hintStackTrace: "$1",
hintGCStats: "$1",
hintUser: "$1",
hintUserRaw: "$1"]
const
WarningsToStr* = ["CannotOpenFile", "OctalEscape",
"XIsNeverRead", "XmightNotBeenInit",
"Deprecated", "ConfigDeprecated",
"SmallLshouldNotBeUsed", "UnknownMagic",
"RedefinitionOfLabel", "UnknownSubstitutionX",
"LanguageXNotSupported", "FieldXNotSupported",
"CommentXIgnored",
"TypelessParam", "UseBase", "WriteToForeignHeap",
"UnsafeCode", "EachIdentIsTuple", "ShadowIdent",
"ProveInit", "ProveField", "ProveIndex", "GcUnsafe", "GcUnsafe2", "Uninit",
"GcMem", "Destructor", "LockLevel", "ResultShadowed",
"Spacing", "User"]
HintsToStr* = ["Success", "SuccessX", "LineTooLong",
"XDeclaredButNotUsed", "ConvToBaseNotNeeded", "ConvFromXtoItselfNotNeeded",
"ExprAlwaysX", "QuitCalled", "Processing", "CodeBegin", "CodeEnd", "Conf",
"Path", "CondTrue", "Name", "Pattern", "Exec", "Link", "Dependency",
"Source", "Performance", "StackTrace", "GCStats",
"User", "UserRaw"]
const
fatalMin* = errUnknown
fatalMax* = errInternal
errMin* = errUnknown
errMax* = errUser
warnMin* = warnCannotOpenFile
warnMax* = pred(hintSuccess)
hintMin* = hintSuccess
hintMax* = high(TMsgKind)
static:
doAssert HintsToStr.len == ord(hintMax) - ord(hintMin) + 1
doAssert WarningsToStr.len == ord(warnMax) - ord(warnMin) + 1
type
TNoteKind* = range[warnMin..hintMax] # "notes" are warnings or hints
TNoteKinds* = set[TNoteKind]
TFileInfo* = object
fullPath: string # This is a canonical full filesystem path
projPath*: string # This is relative to the project's root
@@ -527,35 +59,11 @@ type
proc `==`*(a, b: FileIndex): bool {.borrow.}
const
NotesVerbosity*: array[0..3, TNoteKinds] = [
{low(TNoteKind)..high(TNoteKind)} - {warnShadowIdent, warnUninit,
warnProveField, warnProveIndex,
warnGcUnsafe,
hintSuccessX, hintPath, hintConf,
hintProcessing, hintPattern,
hintDependency,
hintExecuting, hintLinking,
hintCodeBegin, hintCodeEnd,
hintSource, hintStackTrace,
hintGCStats},
{low(TNoteKind)..high(TNoteKind)} - {warnShadowIdent, warnUninit,
warnProveField, warnProveIndex,
warnGcUnsafe,
hintPath,
hintDependency,
hintCodeBegin, hintCodeEnd,
hintSource, hintStackTrace,
hintGCStats},
{low(TNoteKind)..high(TNoteKind)} - {hintStackTrace, warnUninit},
{low(TNoteKind)..high(TNoteKind)}]
const
InvalidFileIDX* = FileIndex(-1)
var
ForeignPackageNotes*: TNoteKinds = {hintProcessing, warnUnknownMagic,
hintQuitCalled, hintExecuting}
filenameToIndexTbl = initTable[string, FileIndex]()
fileInfos*: seq[TFileInfo] = @[]
systemFileIdx*: FileIndex
@@ -606,22 +114,22 @@ when defined(nimpretty):
proc fileSection*(fid: FileIndex; a, b: int): string =
substr(fileInfos[fid.int].fullContent, a, b)
proc fileInfoKnown*(filename: string): bool =
proc fileInfoKnown*(conf: ConfigRef; filename: string): bool =
var
canon: string
try:
canon = canonicalizePath(filename)
canon = canonicalizePath(conf, filename)
except:
canon = filename
result = filenameToIndexTbl.hasKey(canon)
proc fileInfoIdx*(filename: string; isKnownFile: var bool): FileIndex =
proc fileInfoIdx*(conf: ConfigRef; filename: string; isKnownFile: var bool): FileIndex =
var
canon: string
pseudoPath = false
try:
canon = canonicalizePath(filename)
canon = canonicalizePath(conf, filename)
shallow(canon)
except:
canon = filename
@@ -635,40 +143,33 @@ proc fileInfoIdx*(filename: string; isKnownFile: var bool): FileIndex =
isKnownFile = false
result = fileInfos.len.FileIndex
fileInfos.add(newFileInfo(canon, if pseudoPath: filename
else: canon.shortenDir))
else: shortenDir(conf, canon)))
filenameToIndexTbl[canon] = result
proc fileInfoIdx*(filename: string): FileIndex =
proc fileInfoIdx*(conf: ConfigRef; filename: string): FileIndex =
var dummy: bool
result = fileInfoIdx(filename, dummy)
result = fileInfoIdx(conf, filename, dummy)
proc newLineInfo*(fileInfoIdx: FileIndex, line, col: int): TLineInfo =
result.fileIndex = fileInfoIdx
result.line = uint16(line)
result.col = int16(col)
proc newLineInfo*(filename: string, line, col: int): TLineInfo {.inline.} =
result = newLineInfo(filename.fileInfoIdx, line, col)
proc newLineInfo*(conf: ConfigRef; filename: string, line, col: int): TLineInfo {.inline.} =
result = newLineInfo(fileInfoIdx(conf, filename), line, col)
fileInfos.add(newFileInfo("", "command line"))
var gCmdLineInfo* = newLineInfo(FileIndex(0), 1, 1)
when false:
fileInfos.add(newFileInfo("", "command line"))
var gCmdLineInfo* = newLineInfo(FileIndex(0), 1, 1)
fileInfos.add(newFileInfo("", "compilation artifact"))
var gCodegenLineInfo* = newLineInfo(FileIndex(1), 1, 1)
fileInfos.add(newFileInfo("", "compilation artifact"))
var gCodegenLineInfo* = newLineInfo(FileIndex(1), 1, 1)
proc raiseRecoverableError*(msg: string) {.noinline, noreturn.} =
raise newException(ERecoverableError, msg)
proc sourceLine*(i: TLineInfo): Rope
var
gNotes*: TNoteKinds = NotesVerbosity[1] # defaults to verbosity of 1
gErrorCounter*: int = 0 # counts the number of errors
gHintCounter*: int = 0
gWarnCounter*: int = 0
gErrorMax*: int = 1 # stop after gErrorMax errors
gMainPackageNotes*: TNoteKinds = NotesVerbosity[1]
proc unknownLineInfo*(): TLineInfo =
result.line = uint16(0)
result.col = int16(-1)
@@ -892,8 +393,8 @@ proc log*(s: string) {.procvar.} =
f.writeLine(s)
close(f)
proc quit(msg: TMsgKind) =
if defined(debug) or msg == errInternal or hintStackTrace in gNotes:
proc quit(conf: ConfigRef; msg: TMsgKind) =
if defined(debug) or msg == errInternal or hintStackTrace in conf.notes:
if stackTraceAvailable() and isNil(writelnHook):
writeStackTrace()
else:
@@ -902,17 +403,17 @@ proc quit(msg: TMsgKind) =
options.command & " <file>")
quit 1
proc handleError(msg: TMsgKind, eh: TErrorHandling, s: string) =
proc handleError(conf: ConfigRef; msg: TMsgKind, eh: TErrorHandling, s: string) =
if msg >= fatalMin and msg <= fatalMax:
if gCmd == cmdIdeTools: log(s)
quit(msg)
quit(conf, msg)
if msg >= errMin and msg <= errMax:
inc(gErrorCounter)
inc(conf.errorCounter)
options.gExitcode = 1'i8
if gErrorCounter >= gErrorMax:
quit(msg)
if conf.errorCounter >= conf.errorMax:
quit(conf, msg)
elif eh == doAbort and gCmd != cmdIdeTools:
quit(msg)
quit(conf, msg)
elif eh == doRaise:
raiseRecoverableError(s)
@@ -922,12 +423,13 @@ proc `==`*(a, b: TLineInfo): bool =
proc exactEquals*(a, b: TLineInfo): bool =
result = a.fileIndex == b.fileIndex and a.line == b.line and a.col == b.col
proc writeContext(lastinfo: TLineInfo) =
proc writeContext(conf: ConfigRef; lastinfo: TLineInfo) =
const instantiationFrom = "template/generic instantiation from here"
var info = lastinfo
for i in countup(0, len(msgContext) - 1):
if msgContext[i] != lastinfo and msgContext[i] != info:
if structuredErrorHook != nil:
structuredErrorHook(msgContext[i], getMessageStr(errInstantiationFrom, ""),
structuredErrorHook(msgContext[i], instantiationFrom,
Severity.Error)
else:
styledMsgWriteln(styleBright,
@@ -935,13 +437,13 @@ proc writeContext(lastinfo: TLineInfo) =
coordToStr(msgContext[i].line.int),
coordToStr(msgContext[i].col+1)],
resetStyle,
getMessageStr(errInstantiationFrom, ""))
instantiationFrom)
info = msgContext[i]
proc ignoreMsgBecauseOfIdeTools(msg: TMsgKind): bool =
proc ignoreMsgBecauseOfIdeTools(conf: ConfigRef; msg: TMsgKind): bool =
msg >= errGenerated and gCmd == cmdIdeTools and optIdeDebug notin gGlobalOptions
proc rawMessage*(msg: TMsgKind, args: openArray[string]) =
proc rawMessage*(conf: ConfigRef; msg: TMsgKind, args: openArray[string]) =
var
title: string
color: ForegroundColor
@@ -950,43 +452,43 @@ proc rawMessage*(msg: TMsgKind, args: openArray[string]) =
case msg
of errMin..errMax:
sev = Severity.Error
writeContext(unknownLineInfo())
writeContext(conf, unknownLineInfo())
title = ErrorTitle
color = ErrorColor
of warnMin..warnMax:
sev = Severity.Warning
if optWarns notin gOptions: return
if msg notin gNotes: return
writeContext(unknownLineInfo())
if msg notin conf.notes: return
writeContext(conf, unknownLineInfo())
title = WarningTitle
color = WarningColor
kind = WarningsToStr[ord(msg) - ord(warnMin)]
inc(gWarnCounter)
inc(conf.warnCounter)
of hintMin..hintMax:
sev = Severity.Hint
if optHints notin gOptions: return
if msg notin gNotes: return
if msg notin conf.notes: return
title = HintTitle
color = HintColor
if msg != hintUserRaw: kind = HintsToStr[ord(msg) - ord(hintMin)]
inc(gHintCounter)
inc(conf.hintCounter)
let s = msgKindToString(msg) % args
if structuredErrorHook != nil:
structuredErrorHook(unknownLineInfo(), s & (if kind != nil: KindFormat % kind else: ""), sev)
if not ignoreMsgBecauseOfIdeTools(msg):
if not ignoreMsgBecauseOfIdeTools(conf, msg):
if kind != nil:
styledMsgWriteln(color, title, resetStyle, s,
KindColor, `%`(KindFormat, kind))
else:
styledMsgWriteln(color, title, resetStyle, s)
handleError(msg, doAbort, s)
handleError(conf, msg, doAbort, s)
proc rawMessage*(msg: TMsgKind, arg: string) =
rawMessage(msg, [arg])
proc rawMessage*(conf: ConfigRef; msg: TMsgKind, arg: string) =
rawMessage(conf, msg, [arg])
proc resetAttributes* =
proc resetAttributes*(conf: ConfigRef) =
if {optUseColors, optStdout} * gGlobalOptions == {optUseColors}:
terminal.resetAttributes(stderr)
@@ -1005,7 +507,7 @@ proc formatMsg*(info: TLineInfo, msg: TMsgKind, arg: string): string =
title &
getMessageStr(msg, arg)
proc liMessage(info: TLineInfo, msg: TMsgKind, arg: string,
proc liMessage(conf: ConfigRef; info: TLineInfo, msg: TMsgKind, arg: string,
eh: TErrorHandling) =
var
title: string
@@ -1016,7 +518,7 @@ proc liMessage(info: TLineInfo, msg: TMsgKind, arg: string,
case msg
of errMin..errMax:
sev = Severity.Error
writeContext(info)
writeContext(conf, info)
title = ErrorTitle
color = ErrorColor
# we try to filter error messages so that not two error message
@@ -1025,19 +527,19 @@ proc liMessage(info: TLineInfo, msg: TMsgKind, arg: string,
lastError = info
of warnMin..warnMax:
sev = Severity.Warning
ignoreMsg = optWarns notin gOptions or msg notin gNotes
if not ignoreMsg: writeContext(info)
ignoreMsg = optWarns notin gOptions or msg notin conf.notes
if not ignoreMsg: writeContext(conf, info)
title = WarningTitle
color = WarningColor
kind = WarningsToStr[ord(msg) - ord(warnMin)]
inc(gWarnCounter)
inc(conf.warnCounter)
of hintMin..hintMax:
sev = Severity.Hint
ignoreMsg = optHints notin gOptions or msg notin gNotes
ignoreMsg = optHints notin gOptions or msg notin conf.notes
title = HintTitle
color = HintColor
if msg != hintUserRaw: kind = HintsToStr[ord(msg) - ord(hintMin)]
inc(gHintCounter)
inc(conf.hintCounter)
# NOTE: currently line info line numbers start with 1,
# but column numbers start with 0, however most editors expect
# first column to be 1, so we need to +1 here
@@ -1048,56 +550,56 @@ proc liMessage(info: TLineInfo, msg: TMsgKind, arg: string,
if not ignoreMsg:
if structuredErrorHook != nil:
structuredErrorHook(info, s & (if kind != nil: KindFormat % kind else: ""), sev)
if not ignoreMsgBecauseOfIdeTools(msg):
if not ignoreMsgBecauseOfIdeTools(conf, msg):
if kind != nil:
styledMsgWriteln(styleBright, x, resetStyle, color, title, resetStyle, s,
KindColor, `%`(KindFormat, kind))
else:
styledMsgWriteln(styleBright, x, resetStyle, color, title, resetStyle, s)
if hintSource in gNotes:
info.writeSurroundingSrc
handleError(msg, eh, s)
if hintSource in conf.notes:
info.writeSurroundingSrc()
handleError(conf, msg, eh, s)
proc fatal*(info: TLineInfo, msg: TMsgKind, arg = "") =
proc fatal*(conf: ConfigRef; info: TLineInfo, msg: TMsgKind, arg = "") =
# this fixes bug #7080 so that it is at least obvious 'fatal'
# was executed.
errorOutputs = {eStdOut, eStdErr}
liMessage(info, msg, arg, doAbort)
liMessage(conf, info, msg, arg, doAbort)
proc globalError*(info: TLineInfo, msg: TMsgKind, arg = "") =
liMessage(info, msg, arg, doRaise)
proc globalError*(conf: ConfigRef; info: TLineInfo, msg: TMsgKind, arg = "") =
liMessage(conf, info, msg, arg, doRaise)
proc globalError*(info: TLineInfo, arg: string) =
liMessage(info, errGenerated, arg, doRaise)
proc globalError*(conf: ConfigRef; info: TLineInfo, arg: string) =
liMessage(conf, info, errGenerated, arg, doRaise)
proc localError*(info: TLineInfo, msg: TMsgKind, arg = "") =
liMessage(info, msg, arg, doNothing)
proc localError*(conf: ConfigRef; info: TLineInfo, msg: TMsgKind, arg = "") =
liMessage(conf, info, msg, arg, doNothing)
proc localError*(info: TLineInfo, arg: string) =
liMessage(info, errGenerated, arg, doNothing)
proc localError*(conf: ConfigRef; info: TLineInfo, arg: string) =
liMessage(conf, info, errGenerated, arg, doNothing)
proc localError*(info: TLineInfo, format: string, params: openarray[string]) =
localError(info, format % params)
proc localError*(conf: ConfigRef; info: TLineInfo, format: string, params: openarray[string]) =
localError(conf, info, format % params)
proc message*(info: TLineInfo, msg: TMsgKind, arg = "") =
liMessage(info, msg, arg, doNothing)
proc message*(conf: ConfigRef; info: TLineInfo, msg: TMsgKind, arg = "") =
liMessage(conf, info, msg, arg, doNothing)
proc internalError*(info: TLineInfo, errMsg: string) =
proc internalError*(conf: ConfigRef; info: TLineInfo, errMsg: string) =
if gCmd == cmdIdeTools and structuredErrorHook.isNil: return
writeContext(info)
liMessage(info, errInternal, errMsg, doAbort)
writeContext(conf, info)
liMessage(conf, info, errInternal, errMsg, doAbort)
proc internalError*(errMsg: string) =
proc internalError*(conf: ConfigRef; errMsg: string) =
if gCmd == cmdIdeTools and structuredErrorHook.isNil: return
writeContext(unknownLineInfo())
rawMessage(errInternal, errMsg)
writeContext(conf, unknownLineInfo())
rawMessage(conf, errInternal, errMsg)
template assertNotNil*(e): untyped =
if e == nil: internalError($instantiationInfo())
template assertNotNil*(conf, e): untyped =
if e == nil: internalError(conf, $instantiationInfo())
e
template internalAssert*(e: bool) =
if not e: internalError($instantiationInfo())
template internalAssert*(conf, e: bool) =
if not e: internalError(conf, $instantiationInfo())
proc addSourceLine*(fileIdx: FileIndex, line: string) =
fileInfos[fileIdx.int32].lines.add line.rope
@@ -1111,14 +613,14 @@ proc sourceLine*(i: TLineInfo): Rope =
addSourceLine i.fileIndex, line.string
except IOError:
discard
internalAssert i.fileIndex.int32 < fileInfos.len
assert i.fileIndex.int32 < fileInfos.len
# can happen if the error points to EOF:
if i.line.int > fileInfos[i.fileIndex.int32].lines.len: return nil
result = fileInfos[i.fileIndex.int32].lines[i.line.int-1]
proc quotedFilename*(i: TLineInfo): Rope =
internalAssert i.fileIndex.int32 >= 0
assert i.fileIndex.int32 >= 0
if optExcessiveStackTrace in gGlobalOptions:
result = fileInfos[i.fileIndex.int32].quotedFullName
else:
@@ -1127,24 +629,24 @@ proc quotedFilename*(i: TLineInfo): Rope =
ropes.errorHandler = proc (err: RopesError, msg: string, useWarning: bool) =
case err
of rInvalidFormatStr:
internalError("ropes: invalid format string: " & msg)
internalError(newConfigRef(), "ropes: invalid format string: " & msg)
of rCannotOpenFile:
rawMessage(if useWarning: warnCannotOpenFile else: errCannotOpenFile, msg)
rawMessage(newConfigRef(), if useWarning: warnCannotOpenFile else: errCannotOpenFile, msg)
proc listWarnings*() =
proc listWarnings*(conf: ConfigRef) =
msgWriteln("Warnings:")
for warn in warnMin..warnMax:
msgWriteln(" [$1] $2" % [
if warn in gNotes: "x" else: " ",
msgs.WarningsToStr[ord(warn) - ord(warnMin)]
if warn in conf.notes: "x" else: " ",
configuration.WarningsToStr[ord(warn) - ord(warnMin)]
])
proc listHints*() =
proc listHints*(conf: ConfigRef) =
msgWriteln("Hints:")
for hint in hintMin..hintMax:
msgWriteln(" [$1] $2" % [
if hint in gNotes: "x" else: " ",
msgs.HintsToStr[ord(hint) - ord(hintMin)]
if hint in conf.notes: "x" else: " ",
configuration.HintsToStr[ord(hint) - ord(hintMin)]
])
# enable colors by default on terminals

View File

@@ -39,7 +39,7 @@ proc prependCurDir(f: string): string =
proc handleCmdLine(cache: IdentCache; config: ConfigRef) =
if paramCount() == 0:
writeCommandLineUsage()
writeCommandLineUsage(config.helpWritten)
else:
# Process command line arguments:
processCmdLine(passCmd1, "", config)

View File

@@ -9,9 +9,10 @@
## Implements some helper procs for Nimble (Nim's package manager) support.
import parseutils, strutils, strtabs, os, options, msgs, sequtils
import parseutils, strutils, strtabs, os, options, msgs, sequtils,
configuration
proc addPath*(path: string, info: TLineInfo) =
proc addPath*(conf: ConfigRef; path: string, info: TLineInfo) =
if not options.searchPaths.contains(path):
options.searchPaths.insert(path, 0)
@@ -84,7 +85,7 @@ proc getPathVersion*(p: string): tuple[name, version: string] =
result.name = p[0 .. sepIdx - 1]
result.version = p.substr(sepIdx + 1)
proc addPackage(packages: StringTableRef, p: string; info: TLineInfo) =
proc addPackage(conf: ConfigRef; packages: StringTableRef, p: string; info: TLineInfo) =
let (name, ver) = getPathVersion(p)
if isValidVersion(ver):
let version = newVersion(ver)
@@ -92,14 +93,14 @@ proc addPackage(packages: StringTableRef, p: string; info: TLineInfo) =
(not packages.hasKey(name)):
packages[name] = $version
else:
localError(info, "invalid package name: " & p)
localError(conf, info, "invalid package name: " & p)
iterator chosen(packages: StringTableRef): string =
for key, val in pairs(packages):
let res = if val.len == 0: key else: key & '-' & val
yield res
proc addNimblePath(p: string, info: TLineInfo) =
proc addNimblePath(conf: ConfigRef; p: string, info: TLineInfo) =
var path = p
let nimbleLinks = toSeq(walkPattern(p / "*.nimble-link"))
if nimbleLinks.len > 0:
@@ -112,22 +113,22 @@ proc addNimblePath(p: string, info: TLineInfo) =
path = p / path
if not contains(options.searchPaths, path):
message(info, hintPath, path)
message(conf, info, hintPath, path)
options.lazyPaths.insert(path, 0)
proc addPathRec(dir: string, info: TLineInfo) =
proc addPathRec(conf: ConfigRef; dir: string, info: TLineInfo) =
var packages = newStringTable(modeStyleInsensitive)
var pos = dir.len-1
if dir[pos] in {DirSep, AltSep}: inc(pos)
for k,p in os.walkDir(dir):
if k == pcDir and p[pos] != '.':
addPackage(packages, p, info)
addPackage(conf, packages, p, info)
for p in packages.chosen:
addNimblePath(p, info)
addNimblePath(conf, p, info)
proc nimblePath*(path: string, info: TLineInfo) =
addPathRec(path, info)
addNimblePath(path, info)
proc nimblePath*(conf: ConfigRef; path: string, info: TLineInfo) =
addPathRec(conf, path, info)
addNimblePath(conf, path, info)
when isMainModule:
proc v(s: string): Version = s.newVersion

View File

@@ -11,7 +11,7 @@
import
llstream, nversion, commands, os, strutils, msgs, platform, condsyms, lexer,
options, idents, wordrecg, strtabs
options, idents, wordrecg, strtabs, configuration
# ---------------- configuration file parser -----------------------------
# we use Nim's scanner here to save space and work
@@ -27,12 +27,12 @@ proc parseAtom(L: var TLexer, tok: var TToken; config: ConfigRef): bool =
ppGetTok(L, tok)
result = parseExpr(L, tok, config)
if tok.tokType == tkParRi: ppGetTok(L, tok)
else: lexMessage(L, errTokenExpected, "\')\'")
else: lexMessage(L, errGenerated, "expected closing ')'")
elif tok.ident.id == ord(wNot):
ppGetTok(L, tok)
result = not parseAtom(L, tok, config)
else:
result = isDefined(tok.ident)
result = isDefined(config, tok.ident.s)
ppGetTok(L, tok)
proc parseAndExpr(L: var TLexer, tok: var TToken; config: ConfigRef): bool =
@@ -53,12 +53,12 @@ proc evalppIf(L: var TLexer, tok: var TToken; config: ConfigRef): bool =
ppGetTok(L, tok) # skip 'if' or 'elif'
result = parseExpr(L, tok, config)
if tok.tokType == tkColon: ppGetTok(L, tok)
else: lexMessage(L, errTokenExpected, "\':\'")
else: lexMessage(L, errGenerated, "expected ':'")
var condStack: seq[bool] = @[]
#var condStack: seq[bool] = @[]
proc doEnd(L: var TLexer, tok: var TToken) =
if high(condStack) < 0: lexMessage(L, errTokenExpected, "@if")
proc doEnd(L: var TLexer, tok: var TToken; condStack: var seq[bool]) =
if high(condStack) < 0: lexMessage(L, errGenerated, "expected @if")
ppGetTok(L, tok) # skip 'end'
setLen(condStack, high(condStack))
@@ -66,20 +66,22 @@ type
TJumpDest = enum
jdEndif, jdElseEndif
proc jumpToDirective(L: var TLexer, tok: var TToken, dest: TJumpDest; config: ConfigRef)
proc doElse(L: var TLexer, tok: var TToken; config: ConfigRef) =
if high(condStack) < 0: lexMessage(L, errTokenExpected, "@if")
proc jumpToDirective(L: var TLexer, tok: var TToken, dest: TJumpDest; config: ConfigRef;
condStack: var seq[bool])
proc doElse(L: var TLexer, tok: var TToken; config: ConfigRef; condStack: var seq[bool]) =
if high(condStack) < 0: lexMessage(L, errGenerated, "expected @if")
ppGetTok(L, tok)
if tok.tokType == tkColon: ppGetTok(L, tok)
if condStack[high(condStack)]: jumpToDirective(L, tok, jdEndif, config)
if condStack[high(condStack)]: jumpToDirective(L, tok, jdEndif, config, condStack)
proc doElif(L: var TLexer, tok: var TToken; config: ConfigRef) =
if high(condStack) < 0: lexMessage(L, errTokenExpected, "@if")
proc doElif(L: var TLexer, tok: var TToken; config: ConfigRef; condStack: var seq[bool]) =
if high(condStack) < 0: lexMessage(L, errGenerated, "expected @if")
var res = evalppIf(L, tok, config)
if condStack[high(condStack)] or not res: jumpToDirective(L, tok, jdElseEndif, config)
if condStack[high(condStack)] or not res: jumpToDirective(L, tok, jdElseEndif, config, condStack)
else: condStack[high(condStack)] = true
proc jumpToDirective(L: var TLexer, tok: var TToken, dest: TJumpDest; config: ConfigRef) =
proc jumpToDirective(L: var TLexer, tok: var TToken, dest: TJumpDest; config: ConfigRef;
condStack: var seq[bool]) =
var nestedIfs = 0
while true:
if tok.ident != nil and tok.ident.s == "@":
@@ -89,36 +91,36 @@ proc jumpToDirective(L: var TLexer, tok: var TToken, dest: TJumpDest; config: Co
inc(nestedIfs)
of wElse:
if dest == jdElseEndif and nestedIfs == 0:
doElse(L, tok, config)
doElse(L, tok, config, condStack)
break
of wElif:
if dest == jdElseEndif and nestedIfs == 0:
doElif(L, tok, config)
doElif(L, tok, config, condStack)
break
of wEnd:
if nestedIfs == 0:
doEnd(L, tok)
doEnd(L, tok, condStack)
break
if nestedIfs > 0: dec(nestedIfs)
else:
discard
ppGetTok(L, tok)
elif tok.tokType == tkEof:
lexMessage(L, errTokenExpected, "@end")
lexMessage(L, errGenerated, "expected @end")
else:
ppGetTok(L, tok)
proc parseDirective(L: var TLexer, tok: var TToken; config: ConfigRef) =
proc parseDirective(L: var TLexer, tok: var TToken; config: ConfigRef; condStack: var seq[bool]) =
ppGetTok(L, tok) # skip @
case whichKeyword(tok.ident)
of wIf:
setLen(condStack, len(condStack) + 1)
let res = evalppIf(L, tok, config)
condStack[high(condStack)] = res
if not res: jumpToDirective(L, tok, jdElseEndif, config)
of wElif: doElif(L, tok, config)
of wElse: doElse(L, tok, config)
of wEnd: doEnd(L, tok)
if not res: jumpToDirective(L, tok, jdElseEndif, config, condStack)
of wElif: doElif(L, tok, config, condStack)
of wElse: doElse(L, tok, config, condStack)
of wEnd: doEnd(L, tok, condStack)
of wWrite:
ppGetTok(L, tok)
msgs.msgWriteln(strtabs.`%`(tokToStr(tok), options.gConfigVars,
@@ -144,53 +146,55 @@ proc parseDirective(L: var TLexer, tok: var TToken; config: ConfigRef) =
ppGetTok(L, tok)
os.putEnv(key, os.getEnv(key) & tokToStr(tok))
ppGetTok(L, tok)
else: lexMessage(L, errInvalidDirectiveX, tokToStr(tok))
else:
lexMessage(L, errGenerated, "invalid directive: '$1'" % tokToStr(tok))
proc confTok(L: var TLexer, tok: var TToken; config: ConfigRef) =
proc confTok(L: var TLexer, tok: var TToken; config: ConfigRef; condStack: var seq[bool]) =
ppGetTok(L, tok)
while tok.ident != nil and tok.ident.s == "@":
parseDirective(L, tok, config) # else: give the token to the parser
parseDirective(L, tok, config, condStack) # else: give the token to the parser
proc checkSymbol(L: TLexer, tok: TToken) =
if tok.tokType notin {tkSymbol..tkInt64Lit, tkStrLit..tkTripleStrLit}:
lexMessage(L, errIdentifierExpected, tokToStr(tok))
lexMessage(L, errGenerated, "expected identifier, but got: " & tokToStr(tok))
proc parseAssignment(L: var TLexer, tok: var TToken; config: ConfigRef) =
proc parseAssignment(L: var TLexer, tok: var TToken;
config: ConfigRef; condStack: var seq[bool]) =
if tok.ident.s == "-" or tok.ident.s == "--":
confTok(L, tok, config) # skip unnecessary prefix
confTok(L, tok, config, condStack) # skip unnecessary prefix
var info = getLineInfo(L, tok) # save for later in case of an error
checkSymbol(L, tok)
var s = tokToStr(tok)
confTok(L, tok, config) # skip symbol
confTok(L, tok, config, condStack) # skip symbol
var val = ""
while tok.tokType == tkDot:
add(s, '.')
confTok(L, tok, config)
confTok(L, tok, config, condStack)
checkSymbol(L, tok)
add(s, tokToStr(tok))
confTok(L, tok, config)
confTok(L, tok, config, condStack)
if tok.tokType == tkBracketLe:
# BUGFIX: val, not s!
# BUGFIX: do not copy '['!
confTok(L, tok, config)
confTok(L, tok, config, condStack)
checkSymbol(L, tok)
add(val, tokToStr(tok))
confTok(L, tok, config)
if tok.tokType == tkBracketRi: confTok(L, tok, config)
else: lexMessage(L, errTokenExpected, "']'")
confTok(L, tok, config, condStack)
if tok.tokType == tkBracketRi: confTok(L, tok, config, condStack)
else: lexMessage(L, errGenerated, "expected closing ']'")
add(val, ']')
let percent = tok.ident != nil and tok.ident.s == "%="
if tok.tokType in {tkColon, tkEquals} or percent:
if len(val) > 0: add(val, ':')
confTok(L, tok, config) # skip ':' or '=' or '%'
confTok(L, tok, config, condStack) # skip ':' or '=' or '%'
checkSymbol(L, tok)
add(val, tokToStr(tok))
confTok(L, tok, config) # skip symbol
confTok(L, tok, config, condStack) # skip symbol
while tok.ident != nil and tok.ident.s == "&":
confTok(L, tok, config)
confTok(L, tok, config, condStack)
checkSymbol(L, tok)
add(val, tokToStr(tok))
confTok(L, tok, config)
confTok(L, tok, config, condStack)
if percent:
processSwitch(s, strtabs.`%`(val, options.gConfigVars,
{useEnvironment, useEmpty}), passPP, info, config)
@@ -207,34 +211,35 @@ proc readConfigFile(filename: string; cache: IdentCache; config: ConfigRef) =
initToken(tok)
openLexer(L, filename, stream, cache, config)
tok.tokType = tkEof # to avoid a pointless warning
confTok(L, tok, config) # read in the first token
while tok.tokType != tkEof: parseAssignment(L, tok, config)
if len(condStack) > 0: lexMessage(L, errTokenExpected, "@end")
var condStack: seq[bool] = @[]
confTok(L, tok, config, condStack) # read in the first token
while tok.tokType != tkEof: parseAssignment(L, tok, config, condStack)
if len(condStack) > 0: lexMessage(L, errGenerated, "expected @end")
closeLexer(L)
rawMessage(hintConf, filename)
rawMessage(config, hintConf, filename)
proc getUserConfigPath(filename: string): string =
result = joinPath(getConfigDir(), filename)
proc getSystemConfigPath(filename: string): string =
proc getSystemConfigPath(conf: ConfigRef; filename: string): string =
# try standard configuration file (installation did not distribute files
# the UNIX way)
let p = getPrefixDir()
let p = getPrefixDir(conf)
result = joinPath([p, "config", filename])
when defined(unix):
if not existsFile(result): result = joinPath([p, "etc", filename])
if not existsFile(result): result = "/etc/" & filename
proc loadConfigs*(cfg: string; cache: IdentCache; config: ConfigRef = nil) =
setDefaultLibpath()
setDefaultLibpath(config)
if optSkipConfigFile notin gGlobalOptions:
readConfigFile(getSystemConfigPath(cfg), cache, config)
readConfigFile(getSystemConfigPath(config, cfg), cache, config)
if optSkipUserConfigFile notin gGlobalOptions:
readConfigFile(getUserConfigPath(cfg), cache, config)
var pd = if gProjectPath.len > 0: gProjectPath else: getCurrentDir()
let pd = if gProjectPath.len > 0: gProjectPath else: getCurrentDir()
if optSkipParentConfigFiles notin gGlobalOptions:
for dir in parentDirs(pd, fromRoot=true, inclusive=false):
readConfigFile(dir / cfg, cache, config)

View File

@@ -8,7 +8,7 @@
#
import
os, strutils, strtabs, osproc, sets
os, strutils, strtabs, osproc, sets, configuration, platform
const
hasTinyCBackend* = defined(tinyc)
@@ -118,16 +118,71 @@ type
features*: set[Feature]
arguments*: string ## the arguments to be passed to the program that
## should be run
helpWritten*: bool
enableNotes*: TNoteKinds
disableNotes*: TNoteKinds
foreignPackageNotes*: TNoteKinds
notes*: TNoteKinds
mainPackageNotes*: TNoteKinds
errorCounter*: int
hintCounter*: int
warnCounter*: int
errorMax*: int
symbols*: StringTableRef ## We need to use a StringTableRef here as defined
## symbols are always guaranteed to be style
## insensitive. Otherwise hell would break lose.
const oldExperimentalFeatures* = {implicitDeref, dotOperators, callOperator, parallel}
proc newConfigRef*(): ConfigRef =
result = ConfigRef(cppDefines: initSet[string](),
headerFile: "", features: {})
headerFile: "", features: {}, foreignPackageNotes: {hintProcessing, warnUnknownMagic,
hintQuitCalled, hintExecuting},
notes: NotesVerbosity[1], mainPackageNotes: NotesVerbosity[1],
symbols: newStringTable(modeStyleInsensitive))
proc cppDefine*(c: ConfigRef; define: string) =
c.cppDefines.incl define
proc isDefined*(conf: ConfigRef; symbol: string): bool =
if conf.symbols.hasKey(symbol):
result = conf.symbols[symbol] != "false"
elif cmpIgnoreStyle(symbol, CPU[targetCPU].name) == 0:
result = true
elif cmpIgnoreStyle(symbol, platform.OS[targetOS].name) == 0:
result = true
else:
case symbol.normalize
of "x86": result = targetCPU == cpuI386
of "itanium": result = targetCPU == cpuIa64
of "x8664": result = targetCPU == cpuAmd64
of "posix", "unix":
result = targetOS in {osLinux, osMorphos, osSkyos, osIrix, osPalmos,
osQnx, osAtari, osAix,
osHaiku, osVxWorks, osSolaris, osNetbsd,
osFreebsd, osOpenbsd, osDragonfly, osMacosx,
osAndroid}
of "linux":
result = targetOS in {osLinux, osAndroid}
of "bsd":
result = targetOS in {osNetbsd, osFreebsd, osOpenbsd, osDragonfly}
of "emulatedthreadvars":
result = platform.OS[targetOS].props.contains(ospLacksThreadVars)
of "msdos": result = targetOS == osDos
of "mswindows", "win32": result = targetOS == osWindows
of "macintosh": result = targetOS in {osMacos, osMacosx}
of "sunos": result = targetOS == osSolaris
of "littleendian": result = CPU[targetCPU].endian == platform.littleEndian
of "bigendian": result = CPU[targetCPU].endian == platform.bigEndian
of "cpu8": result = CPU[targetCPU].bit == 8
of "cpu16": result = CPU[targetCPU].bit == 16
of "cpu32": result = CPU[targetCPU].bit == 32
of "cpu64": result = CPU[targetCPU].bit == 64
of "nimrawsetjmp":
result = targetOS in {osSolaris, osNetbsd, osFreebsd, osOpenbsd,
osDragonfly, osMacosx}
else: discard
var
gIdeCmd*: IdeCmd
gOldNewlines*: bool
@@ -226,20 +281,20 @@ proc mainCommandArg*: string =
else:
result = gProjectName
proc existsConfigVar*(key: string): bool =
proc existsConfigVar*(conf: ConfigRef; key: string): bool =
result = hasKey(gConfigVars, key)
proc getConfigVar*(key: string): string =
proc getConfigVar*(conf: ConfigRef; key: string): string =
result = gConfigVars.getOrDefault key
proc setConfigVar*(key, val: string) =
proc setConfigVar*(conf: ConfigRef; key, val: string) =
gConfigVars[key] = val
proc getOutFile*(filename, ext: string): string =
proc getOutFile*(conf: ConfigRef; filename, ext: string): string =
if options.outFile != "": result = options.outFile
else: result = changeFileExt(filename, ext)
proc getPrefixDir*(): string =
proc getPrefixDir*(conf: ConfigRef): string =
## Gets the prefix dir, usually the parent directory where the binary resides.
##
## This is overridden by some tools (namely nimsuggest) via the ``gPrefixDir``
@@ -248,11 +303,11 @@ proc getPrefixDir*(): string =
else:
result = splitPath(getAppDir()).head
proc setDefaultLibpath*() =
proc setDefaultLibpath*(conf: ConfigRef) =
# set default value (can be overwritten):
if libpath == "":
# choose default libpath:
var prefix = getPrefixDir()
var prefix = getPrefixDir(conf)
when defined(posix):
if prefix == "/usr": libpath = "/usr/lib/nim"
elif prefix == "/usr/local": libpath = "/usr/local/lib/nim"
@@ -263,12 +318,12 @@ proc setDefaultLibpath*() =
# modules and make use of them.
let realNimPath = findExe("nim")
# Find out if $nim/../../lib/system.nim exists.
let parentNimLibPath = realNimPath.parentDir().parentDir() / "lib"
let parentNimLibPath = realNimPath.parentDir.parentDir / "lib"
if not fileExists(libpath / "system.nim") and
fileExists(parentNimlibPath / "system.nim"):
libpath = parentNimLibPath
proc canonicalizePath*(path: string): string =
proc canonicalizePath*(conf: ConfigRef; path: string): string =
# on Windows, 'expandFilename' calls getFullPathName which doesn't do
# case corrections, so we have to use this convoluted way of retrieving
# the true filename (see tests/modules and Nimble uses 'import Uri' instead
@@ -280,12 +335,12 @@ proc canonicalizePath*(path: string): string =
else:
result = path.expandFilename
proc shortenDir*(dir: string): string =
proc shortenDir*(conf: ConfigRef; dir: string): string =
## returns the interesting part of a dir
var prefix = gProjectPath & DirSep
if startsWith(dir, prefix):
return substr(dir, len(prefix))
prefix = getPrefixDir() & DirSep
prefix = getPrefixDir(conf) & DirSep
if startsWith(dir, prefix):
return substr(dir, len(prefix))
result = dir
@@ -296,42 +351,42 @@ proc removeTrailingDirSep*(path: string): string =
else:
result = path
proc disableNimblePath*() =
proc disableNimblePath*(conf: ConfigRef) =
gNoNimblePath = true
lazyPaths.setLen(0)
include packagehandling
proc getNimcacheDir*: string =
result = if nimcacheDir.len > 0: nimcacheDir else: gProjectPath.shortenDir /
proc getNimcacheDir*(conf: ConfigRef): string =
result = if nimcacheDir.len > 0: nimcacheDir else: shortenDir(conf, gProjectPath) /
genSubDir
proc pathSubs*(p, config: string): string =
proc pathSubs*(conf: ConfigRef; p, config: string): string =
let home = removeTrailingDirSep(os.getHomeDir())
result = unixToNativePath(p % [
"nim", getPrefixDir(),
"nim", getPrefixDir(conf),
"lib", libpath,
"home", home,
"config", config,
"projectname", options.gProjectName,
"projectpath", options.gProjectPath,
"projectdir", options.gProjectPath,
"nimcache", getNimcacheDir()])
"nimcache", getNimcacheDir(conf)])
if "~/" in result:
result = result.replace("~/", home & '/')
proc toGeneratedFile*(path, ext: string): string =
proc toGeneratedFile*(conf: ConfigRef; path, ext: string): string =
## converts "/home/a/mymodule.nim", "rod" to "/home/a/nimcache/mymodule.rod"
var (head, tail) = splitPath(path)
#if len(head) > 0: head = shortenDir(head & dirSep)
result = joinPath([getNimcacheDir(), changeFileExt(tail, ext)])
result = joinPath([getNimcacheDir(conf), changeFileExt(tail, ext)])
#echo "toGeneratedFile(", path, ", ", ext, ") = ", result
proc completeGeneratedFilePath*(f: string, createSubDir: bool = true): string =
proc completeGeneratedFilePath*(conf: ConfigRef; f: string, createSubDir: bool = true): string =
var (head, tail) = splitPath(f)
#if len(head) > 0: head = removeTrailingDirSep(shortenDir(head & dirSep))
var subdir = getNimcacheDir() # / head
var subdir = getNimcacheDir(conf) # / head
if createSubDir:
try:
createDir(subdir)
@@ -341,14 +396,14 @@ proc completeGeneratedFilePath*(f: string, createSubDir: bool = true): string =
result = joinPath(subdir, tail)
#echo "completeGeneratedFilePath(", f, ") = ", result
proc rawFindFile(f: string): string =
proc rawFindFile(conf: ConfigRef; f: string): string =
for it in searchPaths:
result = joinPath(it, f)
if existsFile(result):
return result.canonicalizePath
return canonicalizePath(conf, result)
result = ""
proc rawFindFile2(f: string): string =
proc rawFindFile2(conf: ConfigRef; f: string): string =
for i, it in lazyPaths:
result = joinPath(it, f)
if existsFile(result):
@@ -356,30 +411,30 @@ proc rawFindFile2(f: string): string =
for j in countDown(i,1):
swap(lazyPaths[j], lazyPaths[j-1])
return result.canonicalizePath
return canonicalizePath(conf, result)
result = ""
template patchModule() {.dirty.} =
template patchModule(conf: ConfigRef) {.dirty.} =
if result.len > 0 and gModuleOverrides.len > 0:
let key = getPackageName(result) & "_" & splitFile(result).name
if gModuleOverrides.hasKey(key):
let ov = gModuleOverrides[key]
if ov.len > 0: result = ov
proc findFile*(f: string): string {.procvar.} =
proc findFile*(conf: ConfigRef; f: string): string {.procvar.} =
if f.isAbsolute:
result = if f.existsFile: f else: ""
else:
result = f.rawFindFile
result = rawFindFile(conf, f)
if result.len == 0:
result = f.toLowerAscii.rawFindFile
result = rawFindFile(conf, f.toLowerAscii)
if result.len == 0:
result = f.rawFindFile2
result = rawFindFile2(conf, f)
if result.len == 0:
result = f.toLowerAscii.rawFindFile2
patchModule()
result = rawFindFile2(conf, f.toLowerAscii)
patchModule(conf)
proc findModule*(modulename, currentModule: string): string =
proc findModule*(conf: ConfigRef; modulename, currentModule: string): string =
# returns path to module
when defined(nimfix):
# '.nimfix' modules are preferred over '.nim' modules so that specialized
@@ -389,16 +444,16 @@ proc findModule*(modulename, currentModule: string): string =
let currentPath = currentModule.splitFile.dir
result = currentPath / m
if not existsFile(result):
result = findFile(m)
result = findFile(conf, m)
if existsFile(result): return result
let m = addFileExt(modulename, NimExt)
let currentPath = currentModule.splitFile.dir
result = currentPath / m
if not existsFile(result):
result = findFile(m)
patchModule()
result = findFile(conf, m)
patchModule(conf)
proc findProjectNimFile*(pkg: string): string =
proc findProjectNimFile*(conf: ConfigRef; pkg: string): string =
const extensions = [".nims", ".cfg", ".nimcfg", ".nimble"]
var candidates: seq[string] = @[]
for k, f in os.walkDir(pkg, relative=true):
@@ -423,10 +478,10 @@ proc canonDynlibName(s: string): string =
else:
result = s.substr(start)
proc inclDynlibOverride*(lib: string) =
proc inclDynlibOverride*(conf: ConfigRef; lib: string) =
gDllOverrides[lib.canonDynlibName] = "true"
proc isDynlibOverride*(lib: string): bool =
proc isDynlibOverride*(conf: ConfigRef; lib: string): bool =
result = gDynlibOverrideAll or gDllOverrides.hasKey(lib.canonDynlibName)
proc binaryStrSearch*(x: openArray[string], y: string): int =

View File

@@ -27,7 +27,7 @@ when isMainModule:
outp.close
import
llstream, lexer, idents, strutils, ast, astalgo, msgs, options
llstream, lexer, idents, strutils, ast, astalgo, msgs, options, configuration
type
TParser* = object # A TParser object represents a file that
@@ -97,7 +97,7 @@ proc openParser*(p: var TParser, fileIdx: FileIndex, inputStream: PLLStream,
proc openParser*(p: var TParser, filename: string, inputStream: PLLStream,
cache: IdentCache; config: ConfigRef;
strongSpaces=false) =
openParser(p, filename.fileInfoIdx, inputStream, cache, config, strongSpaces)
openParser(p, fileInfoIdx(config, filename), inputStream, cache, config, strongSpaces)
proc closeParser(p: var TParser) =
## Close a parser, freeing up its resources.
@@ -107,9 +107,13 @@ proc parMessage(p: TParser, msg: TMsgKind, arg = "") =
## Produce and emit the parser message `arg` to output.
lexMessageTok(p.lex, msg, p.tok, arg)
proc parMessage(p: TParser, msg: TMsgKind, tok: TToken) =
proc parMessage(p: TParser, msg: string, tok: TToken) =
## Produce and emit a parser message to output about the token `tok`
parMessage(p, msg, prettyTok(tok))
parMessage(p, errGenerated, msg % prettyTok(tok))
proc parMessage(p: TParser, arg: string) =
## Produce and emit the parser message `arg` to output.
lexMessageTok(p.lex, errGenerated, p.tok, arg)
template withInd(p, body: untyped) =
let oldInd = p.currInd
@@ -142,6 +146,12 @@ proc skipComment(p: var TParser, node: PNode) =
proc flexComment(p: var TParser, node: PNode) =
if p.tok.indent < 0 or realInd(p): rawSkipComment(p, node)
const
errInvalidIndentation = "invalid indentation"
errIdentifierExpected = "identifier expected, but got '$1'"
errExprExpected = "expression expected, but found '$1'"
errTokenExpected = "'$1' expected"
proc skipInd(p: var TParser) =
if p.tok.indent >= 0:
if not realInd(p): parMessage(p, errInvalidIndentation)
@@ -160,11 +170,11 @@ proc getTokNoInd(p: var TParser) =
proc expectIdentOrKeyw(p: TParser) =
if p.tok.tokType != tkSymbol and not isKeyword(p.tok.tokType):
lexMessage(p.lex, errIdentifierExpected, prettyTok(p.tok))
lexMessage(p.lex, errGenerated, errIdentifierExpected % prettyTok(p.tok))
proc expectIdent(p: TParser) =
if p.tok.tokType != tkSymbol:
lexMessage(p.lex, errIdentifierExpected, prettyTok(p.tok))
lexMessage(p.lex, errGenerated, errIdentifierExpected % prettyTok(p.tok))
proc eat(p: var TParser, tokType: TTokType) =
## Move the parser to the next token if the current token is of type
@@ -172,7 +182,8 @@ proc eat(p: var TParser, tokType: TTokType) =
if p.tok.tokType == tokType:
getTok(p)
else:
lexMessageTok(p.lex, errTokenExpected, p.tok, TokTypeToStr[tokType])
lexMessage(p.lex, errGenerated,
"expected " & TokTypeToStr[tokType] & ", but got: " & prettyTok(p.tok))
proc parLineInfo(p: TParser): TLineInfo =
## Retrieve the line information associated with the parser's current state.
@@ -878,7 +889,7 @@ proc parsePragma(p: var TParser): PNode =
skipComment(p, a)
optPar(p)
if p.tok.tokType in {tkCurlyDotRi, tkCurlyRi}: getTok(p)
else: parMessage(p, errTokenExpected, ".}")
else: parMessage(p, "expected '.}'")
dec p.inPragma
proc identVis(p: var TParser; allowDot=false): PNode =
@@ -939,7 +950,7 @@ proc parseIdentColonEquals(p: var TParser, flags: TDeclaredIdentFlags): PNode =
else:
addSon(result, newNodeP(nkEmpty, p))
if p.tok.tokType != tkEquals and withBothOptional notin flags:
parMessage(p, errColonOrEqualsExpected, p.tok)
parMessage(p, "':' or '=' expected, but got '$1'", p.tok)
if p.tok.tokType == tkEquals:
getTok(p)
optInd(p, result)
@@ -1012,7 +1023,7 @@ proc parseParamList(p: var TParser, retColon = true): PNode =
parMessage(p, errGenerated, "the syntax is 'parameter: var T', not 'var parameter: T'")
break
else:
parMessage(p, errTokenExpected, ")")
parMessage(p, "expected closing ')'")
break
addSon(result, a)
if p.tok.tokType notin {tkComma, tkSemiColon}: break
@@ -1173,7 +1184,7 @@ proc primary(p: var TParser, mode: TPrimaryMode): PNode =
if mode == pmTypeDef:
result = parseTypeClass(p)
else:
parMessage(p, errInvalidToken, p.tok)
parMessage(p, "the 'concept' keyword is only valid in 'type' sections")
of tkStatic:
let info = parLineInfo(p)
getTokNoInd(p)
@@ -1283,7 +1294,7 @@ proc postExprBlocks(p: var TParser, x: PNode): PNode =
if nextBlock.kind == nkElse: break
else:
if openingParams.kind != nkEmpty:
parMessage(p, errTokenExpected, ":")
parMessage(p, "expected ':'")
proc parseExprStmt(p: var TParser): PNode =
#| exprStmt = simpleExpr
@@ -1518,7 +1529,7 @@ proc parseTry(p: var TParser; isExpr: bool): PNode =
addSon(b, parseStmt(p))
addSon(result, b)
if b.kind == nkFinally: break
if b == nil: parMessage(p, errTokenExpected, "except")
if b == nil: parMessage(p, "expected 'except'")
proc parseExceptBlock(p: var TParser, kind: TNodeKind): PNode =
#| exceptBlock = 'except' colcom stmt
@@ -1573,7 +1584,7 @@ proc parseAsm(p: var TParser): PNode =
of tkTripleStrLit: addSon(result,
newStrNodeP(nkTripleStrLit, p.tok.literal, p))
else:
parMessage(p, errStringLiteralExpected)
parMessage(p, "the 'asm' statement takes a string literal")
addSon(result, ast.emptyNode)
return
getTok(p)
@@ -1752,7 +1763,7 @@ proc parseEnum(p: var TParser): PNode =
p.tok.tokType == tkEof:
break
if result.len <= 1:
lexMessageTok(p.lex, errIdentifierExpected, p.tok, prettyTok(p.tok))
parMessage(p, errIdentifierExpected, p.tok)
proc parseObjectPart(p: var TParser): PNode
proc parseObjectWhen(p: var TParser): PNode =
@@ -2115,7 +2126,7 @@ proc parseStmt(p: var TParser): PNode =
case p.tok.tokType
of tkIf, tkWhile, tkCase, tkTry, tkFor, tkBlock, tkAsm, tkProc, tkFunc,
tkIterator, tkMacro, tkType, tkConst, tkWhen, tkVar:
parMessage(p, errComplexStmtRequiresInd)
parMessage(p, "complex statement requires indentation")
result = ast.emptyNode
else:
if p.inSemiStmtList > 0:

View File

@@ -131,10 +131,10 @@ proc setupVM*(module: PSym; cache: IdentCache; scriptName: string;
processSwitch(a.getString 0, a.getString 1, passPP, module.info, config)
cbconf hintImpl:
processSpecificNote(a.getString 0, wHint, passPP, module.info,
a.getString 1)
a.getString 1, config)
cbconf warningImpl:
processSpecificNote(a.getString 0, wWarning, passPP, module.info,
a.getString 1)
a.getString 1, config)
cbconf patchFile:
let key = a.getString(0) & "_" & a.getString(1)
var val = a.getString(2).addFileExt(NimExt)