big refactoring: parser compiles again

This commit is contained in:
Andreas Rumpf 2018-05-10 10:49:51 +02:00
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 case a.kind
of nkCharLit..nkUInt64Lit: result = a.intVal of nkCharLit..nkUInt64Lit: result = a.intVal
else: else:
internalError(a.info, "getInt") #internalError(a.info, "getInt")
result = 0 doAssert false, "getInt"
#result = 0
proc getFloat*(a: PNode): BiggestFloat = proc getFloat*(a: PNode): BiggestFloat =
case a.kind case a.kind
of nkFloatLiterals: result = a.floatVal of nkFloatLiterals: result = a.floatVal
else: else:
internalError(a.info, "getFloat") doAssert false, "getFloat"
result = 0.0 #internalError(a.info, "getFloat")
#result = 0.0
proc getStr*(a: PNode): string = proc getStr*(a: PNode): string =
case a.kind case a.kind
@ -1581,16 +1583,18 @@ proc getStr*(a: PNode): string =
# let's hope this fixes more problems than it creates: # let's hope this fixes more problems than it creates:
result = nil result = nil
else: else:
internalError(a.info, "getStr") doAssert false, "getStr"
result = "" #internalError(a.info, "getStr")
#result = ""
proc getStrOrChar*(a: PNode): string = proc getStrOrChar*(a: PNode): string =
case a.kind case a.kind
of nkStrLit..nkTripleStrLit: result = a.strVal of nkStrLit..nkTripleStrLit: result = a.strVal
of nkCharLit..nkUInt64Lit: result = $chr(int(a.intVal)) of nkCharLit..nkUInt64Lit: result = $chr(int(a.intVal))
else: else:
internalError(a.info, "getStrOrChar") doAssert false, "getStrOrChar"
result = "" #internalError(a.info, "getStrOrChar")
#result = ""
proc isGenericRoutine*(s: PSym): bool = proc isGenericRoutine*(s: PSym): bool =
case s.kind case s.kind

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -8,481 +8,13 @@
# #
import import
options, strutils, os, tables, ropes, platform, terminal, macros options, strutils, os, tables, ropes, platform, terminal, macros,
configuration
const #type
explanationsBaseUrl* = "https://nim-lang.org/docs/manual" # MsgConfig* = ref object of RootObj
type 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 TFileInfo* = object
fullPath: string # This is a canonical full filesystem path fullPath: string # This is a canonical full filesystem path
projPath*: string # This is relative to the project's root projPath*: string # This is relative to the project's root
@ -527,35 +59,11 @@ type
proc `==`*(a, b: FileIndex): bool {.borrow.} 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 const
InvalidFileIDX* = FileIndex(-1) InvalidFileIDX* = FileIndex(-1)
var var
ForeignPackageNotes*: TNoteKinds = {hintProcessing, warnUnknownMagic,
hintQuitCalled, hintExecuting}
filenameToIndexTbl = initTable[string, FileIndex]() filenameToIndexTbl = initTable[string, FileIndex]()
fileInfos*: seq[TFileInfo] = @[] fileInfos*: seq[TFileInfo] = @[]
systemFileIdx*: FileIndex systemFileIdx*: FileIndex
@ -606,22 +114,22 @@ when defined(nimpretty):
proc fileSection*(fid: FileIndex; a, b: int): string = proc fileSection*(fid: FileIndex; a, b: int): string =
substr(fileInfos[fid.int].fullContent, a, b) substr(fileInfos[fid.int].fullContent, a, b)
proc fileInfoKnown*(filename: string): bool = proc fileInfoKnown*(conf: ConfigRef; filename: string): bool =
var var
canon: string canon: string
try: try:
canon = canonicalizePath(filename) canon = canonicalizePath(conf, filename)
except: except:
canon = filename canon = filename
result = filenameToIndexTbl.hasKey(canon) result = filenameToIndexTbl.hasKey(canon)
proc fileInfoIdx*(filename: string; isKnownFile: var bool): FileIndex = proc fileInfoIdx*(conf: ConfigRef; filename: string; isKnownFile: var bool): FileIndex =
var var
canon: string canon: string
pseudoPath = false pseudoPath = false
try: try:
canon = canonicalizePath(filename) canon = canonicalizePath(conf, filename)
shallow(canon) shallow(canon)
except: except:
canon = filename canon = filename
@ -635,40 +143,33 @@ proc fileInfoIdx*(filename: string; isKnownFile: var bool): FileIndex =
isKnownFile = false isKnownFile = false
result = fileInfos.len.FileIndex result = fileInfos.len.FileIndex
fileInfos.add(newFileInfo(canon, if pseudoPath: filename fileInfos.add(newFileInfo(canon, if pseudoPath: filename
else: canon.shortenDir)) else: shortenDir(conf, canon)))
filenameToIndexTbl[canon] = result filenameToIndexTbl[canon] = result
proc fileInfoIdx*(filename: string): FileIndex = proc fileInfoIdx*(conf: ConfigRef; filename: string): FileIndex =
var dummy: bool var dummy: bool
result = fileInfoIdx(filename, dummy) result = fileInfoIdx(conf, filename, dummy)
proc newLineInfo*(fileInfoIdx: FileIndex, line, col: int): TLineInfo = proc newLineInfo*(fileInfoIdx: FileIndex, line, col: int): TLineInfo =
result.fileIndex = fileInfoIdx result.fileIndex = fileInfoIdx
result.line = uint16(line) result.line = uint16(line)
result.col = int16(col) result.col = int16(col)
proc newLineInfo*(filename: string, line, col: int): TLineInfo {.inline.} = proc newLineInfo*(conf: ConfigRef; filename: string, line, col: int): TLineInfo {.inline.} =
result = newLineInfo(filename.fileInfoIdx, line, col) result = newLineInfo(fileInfoIdx(conf, filename), line, col)
fileInfos.add(newFileInfo("", "command line")) when false:
var gCmdLineInfo* = newLineInfo(FileIndex(0), 1, 1) fileInfos.add(newFileInfo("", "command line"))
var gCmdLineInfo* = newLineInfo(FileIndex(0), 1, 1)
fileInfos.add(newFileInfo("", "compilation artifact")) fileInfos.add(newFileInfo("", "compilation artifact"))
var gCodegenLineInfo* = newLineInfo(FileIndex(1), 1, 1) var gCodegenLineInfo* = newLineInfo(FileIndex(1), 1, 1)
proc raiseRecoverableError*(msg: string) {.noinline, noreturn.} = proc raiseRecoverableError*(msg: string) {.noinline, noreturn.} =
raise newException(ERecoverableError, msg) raise newException(ERecoverableError, msg)
proc sourceLine*(i: TLineInfo): Rope 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 = proc unknownLineInfo*(): TLineInfo =
result.line = uint16(0) result.line = uint16(0)
result.col = int16(-1) result.col = int16(-1)
@ -892,8 +393,8 @@ proc log*(s: string) {.procvar.} =
f.writeLine(s) f.writeLine(s)
close(f) close(f)
proc quit(msg: TMsgKind) = proc quit(conf: ConfigRef; msg: TMsgKind) =
if defined(debug) or msg == errInternal or hintStackTrace in gNotes: if defined(debug) or msg == errInternal or hintStackTrace in conf.notes:
if stackTraceAvailable() and isNil(writelnHook): if stackTraceAvailable() and isNil(writelnHook):
writeStackTrace() writeStackTrace()
else: else:
@ -902,17 +403,17 @@ proc quit(msg: TMsgKind) =
options.command & " <file>") options.command & " <file>")
quit 1 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 msg >= fatalMin and msg <= fatalMax:
if gCmd == cmdIdeTools: log(s) if gCmd == cmdIdeTools: log(s)
quit(msg) quit(conf, msg)
if msg >= errMin and msg <= errMax: if msg >= errMin and msg <= errMax:
inc(gErrorCounter) inc(conf.errorCounter)
options.gExitcode = 1'i8 options.gExitcode = 1'i8
if gErrorCounter >= gErrorMax: if conf.errorCounter >= conf.errorMax:
quit(msg) quit(conf, msg)
elif eh == doAbort and gCmd != cmdIdeTools: elif eh == doAbort and gCmd != cmdIdeTools:
quit(msg) quit(conf, msg)
elif eh == doRaise: elif eh == doRaise:
raiseRecoverableError(s) raiseRecoverableError(s)
@ -922,12 +423,13 @@ proc `==`*(a, b: TLineInfo): bool =
proc exactEquals*(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 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 var info = lastinfo
for i in countup(0, len(msgContext) - 1): for i in countup(0, len(msgContext) - 1):
if msgContext[i] != lastinfo and msgContext[i] != info: if msgContext[i] != lastinfo and msgContext[i] != info:
if structuredErrorHook != nil: if structuredErrorHook != nil:
structuredErrorHook(msgContext[i], getMessageStr(errInstantiationFrom, ""), structuredErrorHook(msgContext[i], instantiationFrom,
Severity.Error) Severity.Error)
else: else:
styledMsgWriteln(styleBright, styledMsgWriteln(styleBright,
@ -935,13 +437,13 @@ proc writeContext(lastinfo: TLineInfo) =
coordToStr(msgContext[i].line.int), coordToStr(msgContext[i].line.int),
coordToStr(msgContext[i].col+1)], coordToStr(msgContext[i].col+1)],
resetStyle, resetStyle,
getMessageStr(errInstantiationFrom, "")) instantiationFrom)
info = msgContext[i] info = msgContext[i]
proc ignoreMsgBecauseOfIdeTools(msg: TMsgKind): bool = proc ignoreMsgBecauseOfIdeTools(conf: ConfigRef; msg: TMsgKind): bool =
msg >= errGenerated and gCmd == cmdIdeTools and optIdeDebug notin gGlobalOptions msg >= errGenerated and gCmd == cmdIdeTools and optIdeDebug notin gGlobalOptions
proc rawMessage*(msg: TMsgKind, args: openArray[string]) = proc rawMessage*(conf: ConfigRef; msg: TMsgKind, args: openArray[string]) =
var var
title: string title: string
color: ForegroundColor color: ForegroundColor
@ -950,43 +452,43 @@ proc rawMessage*(msg: TMsgKind, args: openArray[string]) =
case msg case msg
of errMin..errMax: of errMin..errMax:
sev = Severity.Error sev = Severity.Error
writeContext(unknownLineInfo()) writeContext(conf, unknownLineInfo())
title = ErrorTitle title = ErrorTitle
color = ErrorColor color = ErrorColor
of warnMin..warnMax: of warnMin..warnMax:
sev = Severity.Warning sev = Severity.Warning
if optWarns notin gOptions: return if optWarns notin gOptions: return
if msg notin gNotes: return if msg notin conf.notes: return
writeContext(unknownLineInfo()) writeContext(conf, unknownLineInfo())
title = WarningTitle title = WarningTitle
color = WarningColor color = WarningColor
kind = WarningsToStr[ord(msg) - ord(warnMin)] kind = WarningsToStr[ord(msg) - ord(warnMin)]
inc(gWarnCounter) inc(conf.warnCounter)
of hintMin..hintMax: of hintMin..hintMax:
sev = Severity.Hint sev = Severity.Hint
if optHints notin gOptions: return if optHints notin gOptions: return
if msg notin gNotes: return if msg notin conf.notes: return
title = HintTitle title = HintTitle
color = HintColor color = HintColor
if msg != hintUserRaw: kind = HintsToStr[ord(msg) - ord(hintMin)] if msg != hintUserRaw: kind = HintsToStr[ord(msg) - ord(hintMin)]
inc(gHintCounter) inc(conf.hintCounter)
let s = msgKindToString(msg) % args let s = msgKindToString(msg) % args
if structuredErrorHook != nil: if structuredErrorHook != nil:
structuredErrorHook(unknownLineInfo(), s & (if kind != nil: KindFormat % kind else: ""), sev) structuredErrorHook(unknownLineInfo(), s & (if kind != nil: KindFormat % kind else: ""), sev)
if not ignoreMsgBecauseOfIdeTools(msg): if not ignoreMsgBecauseOfIdeTools(conf, msg):
if kind != nil: if kind != nil:
styledMsgWriteln(color, title, resetStyle, s, styledMsgWriteln(color, title, resetStyle, s,
KindColor, `%`(KindFormat, kind)) KindColor, `%`(KindFormat, kind))
else: else:
styledMsgWriteln(color, title, resetStyle, s) styledMsgWriteln(color, title, resetStyle, s)
handleError(msg, doAbort, s) handleError(conf, msg, doAbort, s)
proc rawMessage*(msg: TMsgKind, arg: string) = proc rawMessage*(conf: ConfigRef; msg: TMsgKind, arg: string) =
rawMessage(msg, [arg]) rawMessage(conf, msg, [arg])
proc resetAttributes* = proc resetAttributes*(conf: ConfigRef) =
if {optUseColors, optStdout} * gGlobalOptions == {optUseColors}: if {optUseColors, optStdout} * gGlobalOptions == {optUseColors}:
terminal.resetAttributes(stderr) terminal.resetAttributes(stderr)
@ -1005,7 +507,7 @@ proc formatMsg*(info: TLineInfo, msg: TMsgKind, arg: string): string =
title & title &
getMessageStr(msg, arg) getMessageStr(msg, arg)
proc liMessage(info: TLineInfo, msg: TMsgKind, arg: string, proc liMessage(conf: ConfigRef; info: TLineInfo, msg: TMsgKind, arg: string,
eh: TErrorHandling) = eh: TErrorHandling) =
var var
title: string title: string
@ -1016,7 +518,7 @@ proc liMessage(info: TLineInfo, msg: TMsgKind, arg: string,
case msg case msg
of errMin..errMax: of errMin..errMax:
sev = Severity.Error sev = Severity.Error
writeContext(info) writeContext(conf, info)
title = ErrorTitle title = ErrorTitle
color = ErrorColor color = ErrorColor
# we try to filter error messages so that not two error message # we try to filter error messages so that not two error message
@ -1025,19 +527,19 @@ proc liMessage(info: TLineInfo, msg: TMsgKind, arg: string,
lastError = info lastError = info
of warnMin..warnMax: of warnMin..warnMax:
sev = Severity.Warning sev = Severity.Warning
ignoreMsg = optWarns notin gOptions or msg notin gNotes ignoreMsg = optWarns notin gOptions or msg notin conf.notes
if not ignoreMsg: writeContext(info) if not ignoreMsg: writeContext(conf, info)
title = WarningTitle title = WarningTitle
color = WarningColor color = WarningColor
kind = WarningsToStr[ord(msg) - ord(warnMin)] kind = WarningsToStr[ord(msg) - ord(warnMin)]
inc(gWarnCounter) inc(conf.warnCounter)
of hintMin..hintMax: of hintMin..hintMax:
sev = Severity.Hint sev = Severity.Hint
ignoreMsg = optHints notin gOptions or msg notin gNotes ignoreMsg = optHints notin gOptions or msg notin conf.notes
title = HintTitle title = HintTitle
color = HintColor color = HintColor
if msg != hintUserRaw: kind = HintsToStr[ord(msg) - ord(hintMin)] if msg != hintUserRaw: kind = HintsToStr[ord(msg) - ord(hintMin)]
inc(gHintCounter) inc(conf.hintCounter)
# NOTE: currently line info line numbers start with 1, # NOTE: currently line info line numbers start with 1,
# but column numbers start with 0, however most editors expect # but column numbers start with 0, however most editors expect
# first column to be 1, so we need to +1 here # first column to be 1, so we need to +1 here
@ -1048,56 +550,56 @@ proc liMessage(info: TLineInfo, msg: TMsgKind, arg: string,
if not ignoreMsg: if not ignoreMsg:
if structuredErrorHook != nil: if structuredErrorHook != nil:
structuredErrorHook(info, s & (if kind != nil: KindFormat % kind else: ""), sev) structuredErrorHook(info, s & (if kind != nil: KindFormat % kind else: ""), sev)
if not ignoreMsgBecauseOfIdeTools(msg): if not ignoreMsgBecauseOfIdeTools(conf, msg):
if kind != nil: if kind != nil:
styledMsgWriteln(styleBright, x, resetStyle, color, title, resetStyle, s, styledMsgWriteln(styleBright, x, resetStyle, color, title, resetStyle, s,
KindColor, `%`(KindFormat, kind)) KindColor, `%`(KindFormat, kind))
else: else:
styledMsgWriteln(styleBright, x, resetStyle, color, title, resetStyle, s) styledMsgWriteln(styleBright, x, resetStyle, color, title, resetStyle, s)
if hintSource in gNotes: if hintSource in conf.notes:
info.writeSurroundingSrc info.writeSurroundingSrc()
handleError(msg, eh, s) 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' # this fixes bug #7080 so that it is at least obvious 'fatal'
# was executed. # was executed.
errorOutputs = {eStdOut, eStdErr} errorOutputs = {eStdOut, eStdErr}
liMessage(info, msg, arg, doAbort) liMessage(conf, info, msg, arg, doAbort)
proc globalError*(info: TLineInfo, msg: TMsgKind, arg = "") = proc globalError*(conf: ConfigRef; info: TLineInfo, msg: TMsgKind, arg = "") =
liMessage(info, msg, arg, doRaise) liMessage(conf, info, msg, arg, doRaise)
proc globalError*(info: TLineInfo, arg: string) = proc globalError*(conf: ConfigRef; info: TLineInfo, arg: string) =
liMessage(info, errGenerated, arg, doRaise) liMessage(conf, info, errGenerated, arg, doRaise)
proc localError*(info: TLineInfo, msg: TMsgKind, arg = "") = proc localError*(conf: ConfigRef; info: TLineInfo, msg: TMsgKind, arg = "") =
liMessage(info, msg, arg, doNothing) liMessage(conf, info, msg, arg, doNothing)
proc localError*(info: TLineInfo, arg: string) = proc localError*(conf: ConfigRef; info: TLineInfo, arg: string) =
liMessage(info, errGenerated, arg, doNothing) liMessage(conf, info, errGenerated, arg, doNothing)
proc localError*(info: TLineInfo, format: string, params: openarray[string]) = proc localError*(conf: ConfigRef; info: TLineInfo, format: string, params: openarray[string]) =
localError(info, format % params) localError(conf, info, format % params)
proc message*(info: TLineInfo, msg: TMsgKind, arg = "") = proc message*(conf: ConfigRef; info: TLineInfo, msg: TMsgKind, arg = "") =
liMessage(info, msg, arg, doNothing) 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 if gCmd == cmdIdeTools and structuredErrorHook.isNil: return
writeContext(info) writeContext(conf, info)
liMessage(info, errInternal, errMsg, doAbort) liMessage(conf, info, errInternal, errMsg, doAbort)
proc internalError*(errMsg: string) = proc internalError*(conf: ConfigRef; errMsg: string) =
if gCmd == cmdIdeTools and structuredErrorHook.isNil: return if gCmd == cmdIdeTools and structuredErrorHook.isNil: return
writeContext(unknownLineInfo()) writeContext(conf, unknownLineInfo())
rawMessage(errInternal, errMsg) rawMessage(conf, errInternal, errMsg)
template assertNotNil*(e): untyped = template assertNotNil*(conf, e): untyped =
if e == nil: internalError($instantiationInfo()) if e == nil: internalError(conf, $instantiationInfo())
e e
template internalAssert*(e: bool) = template internalAssert*(conf, e: bool) =
if not e: internalError($instantiationInfo()) if not e: internalError(conf, $instantiationInfo())
proc addSourceLine*(fileIdx: FileIndex, line: string) = proc addSourceLine*(fileIdx: FileIndex, line: string) =
fileInfos[fileIdx.int32].lines.add line.rope fileInfos[fileIdx.int32].lines.add line.rope
@ -1111,14 +613,14 @@ proc sourceLine*(i: TLineInfo): Rope =
addSourceLine i.fileIndex, line.string addSourceLine i.fileIndex, line.string
except IOError: except IOError:
discard discard
internalAssert i.fileIndex.int32 < fileInfos.len assert i.fileIndex.int32 < fileInfos.len
# can happen if the error points to EOF: # can happen if the error points to EOF:
if i.line.int > fileInfos[i.fileIndex.int32].lines.len: return nil if i.line.int > fileInfos[i.fileIndex.int32].lines.len: return nil
result = fileInfos[i.fileIndex.int32].lines[i.line.int-1] result = fileInfos[i.fileIndex.int32].lines[i.line.int-1]
proc quotedFilename*(i: TLineInfo): Rope = proc quotedFilename*(i: TLineInfo): Rope =
internalAssert i.fileIndex.int32 >= 0 assert i.fileIndex.int32 >= 0
if optExcessiveStackTrace in gGlobalOptions: if optExcessiveStackTrace in gGlobalOptions:
result = fileInfos[i.fileIndex.int32].quotedFullName result = fileInfos[i.fileIndex.int32].quotedFullName
else: else:
@ -1127,24 +629,24 @@ proc quotedFilename*(i: TLineInfo): Rope =
ropes.errorHandler = proc (err: RopesError, msg: string, useWarning: bool) = ropes.errorHandler = proc (err: RopesError, msg: string, useWarning: bool) =
case err case err
of rInvalidFormatStr: of rInvalidFormatStr:
internalError("ropes: invalid format string: " & msg) internalError(newConfigRef(), "ropes: invalid format string: " & msg)
of rCannotOpenFile: 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:") msgWriteln("Warnings:")
for warn in warnMin..warnMax: for warn in warnMin..warnMax:
msgWriteln(" [$1] $2" % [ msgWriteln(" [$1] $2" % [
if warn in gNotes: "x" else: " ", if warn in conf.notes: "x" else: " ",
msgs.WarningsToStr[ord(warn) - ord(warnMin)] configuration.WarningsToStr[ord(warn) - ord(warnMin)]
]) ])
proc listHints*() = proc listHints*(conf: ConfigRef) =
msgWriteln("Hints:") msgWriteln("Hints:")
for hint in hintMin..hintMax: for hint in hintMin..hintMax:
msgWriteln(" [$1] $2" % [ msgWriteln(" [$1] $2" % [
if hint in gNotes: "x" else: " ", if hint in conf.notes: "x" else: " ",
msgs.HintsToStr[ord(hint) - ord(hintMin)] configuration.HintsToStr[ord(hint) - ord(hintMin)]
]) ])
# enable colors by default on terminals # enable colors by default on terminals

View file

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

View file

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

View file

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

View file

@ -8,7 +8,7 @@
# #
import import
os, strutils, strtabs, osproc, sets os, strutils, strtabs, osproc, sets, configuration, platform
const const
hasTinyCBackend* = defined(tinyc) hasTinyCBackend* = defined(tinyc)
@ -118,16 +118,71 @@ type
features*: set[Feature] features*: set[Feature]
arguments*: string ## the arguments to be passed to the program that arguments*: string ## the arguments to be passed to the program that
## should be run ## 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} const oldExperimentalFeatures* = {implicitDeref, dotOperators, callOperator, parallel}
proc newConfigRef*(): ConfigRef = proc newConfigRef*(): ConfigRef =
result = ConfigRef(cppDefines: initSet[string](), 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) = proc cppDefine*(c: ConfigRef; define: string) =
c.cppDefines.incl define 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 var
gIdeCmd*: IdeCmd gIdeCmd*: IdeCmd
gOldNewlines*: bool gOldNewlines*: bool
@ -226,20 +281,20 @@ proc mainCommandArg*: string =
else: else:
result = gProjectName result = gProjectName
proc existsConfigVar*(key: string): bool = proc existsConfigVar*(conf: ConfigRef; key: string): bool =
result = hasKey(gConfigVars, key) result = hasKey(gConfigVars, key)
proc getConfigVar*(key: string): string = proc getConfigVar*(conf: ConfigRef; key: string): string =
result = gConfigVars.getOrDefault key result = gConfigVars.getOrDefault key
proc setConfigVar*(key, val: string) = proc setConfigVar*(conf: ConfigRef; key, val: string) =
gConfigVars[key] = val gConfigVars[key] = val
proc getOutFile*(filename, ext: string): string = proc getOutFile*(conf: ConfigRef; filename, ext: string): string =
if options.outFile != "": result = options.outFile if options.outFile != "": result = options.outFile
else: result = changeFileExt(filename, ext) 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. ## Gets the prefix dir, usually the parent directory where the binary resides.
## ##
## This is overridden by some tools (namely nimsuggest) via the ``gPrefixDir`` ## This is overridden by some tools (namely nimsuggest) via the ``gPrefixDir``
@ -248,11 +303,11 @@ proc getPrefixDir*(): string =
else: else:
result = splitPath(getAppDir()).head result = splitPath(getAppDir()).head
proc setDefaultLibpath*() = proc setDefaultLibpath*(conf: ConfigRef) =
# set default value (can be overwritten): # set default value (can be overwritten):
if libpath == "": if libpath == "":
# choose default libpath: # choose default libpath:
var prefix = getPrefixDir() var prefix = getPrefixDir(conf)
when defined(posix): when defined(posix):
if prefix == "/usr": libpath = "/usr/lib/nim" if prefix == "/usr": libpath = "/usr/lib/nim"
elif prefix == "/usr/local": libpath = "/usr/local/lib/nim" elif prefix == "/usr/local": libpath = "/usr/local/lib/nim"
@ -263,12 +318,12 @@ proc setDefaultLibpath*() =
# modules and make use of them. # modules and make use of them.
let realNimPath = findExe("nim") let realNimPath = findExe("nim")
# Find out if $nim/../../lib/system.nim exists. # 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 if not fileExists(libpath / "system.nim") and
fileExists(parentNimlibPath / "system.nim"): fileExists(parentNimlibPath / "system.nim"):
libpath = parentNimLibPath libpath = parentNimLibPath
proc canonicalizePath*(path: string): string = proc canonicalizePath*(conf: ConfigRef; path: string): string =
# on Windows, 'expandFilename' calls getFullPathName which doesn't do # on Windows, 'expandFilename' calls getFullPathName which doesn't do
# case corrections, so we have to use this convoluted way of retrieving # case corrections, so we have to use this convoluted way of retrieving
# the true filename (see tests/modules and Nimble uses 'import Uri' instead # the true filename (see tests/modules and Nimble uses 'import Uri' instead
@ -280,12 +335,12 @@ proc canonicalizePath*(path: string): string =
else: else:
result = path.expandFilename result = path.expandFilename
proc shortenDir*(dir: string): string = proc shortenDir*(conf: ConfigRef; dir: string): string =
## returns the interesting part of a dir ## returns the interesting part of a dir
var prefix = gProjectPath & DirSep var prefix = gProjectPath & DirSep
if startsWith(dir, prefix): if startsWith(dir, prefix):
return substr(dir, len(prefix)) return substr(dir, len(prefix))
prefix = getPrefixDir() & DirSep prefix = getPrefixDir(conf) & DirSep
if startsWith(dir, prefix): if startsWith(dir, prefix):
return substr(dir, len(prefix)) return substr(dir, len(prefix))
result = dir result = dir
@ -296,42 +351,42 @@ proc removeTrailingDirSep*(path: string): string =
else: else:
result = path result = path
proc disableNimblePath*() = proc disableNimblePath*(conf: ConfigRef) =
gNoNimblePath = true gNoNimblePath = true
lazyPaths.setLen(0) lazyPaths.setLen(0)
include packagehandling include packagehandling
proc getNimcacheDir*: string = proc getNimcacheDir*(conf: ConfigRef): string =
result = if nimcacheDir.len > 0: nimcacheDir else: gProjectPath.shortenDir / result = if nimcacheDir.len > 0: nimcacheDir else: shortenDir(conf, gProjectPath) /
genSubDir genSubDir
proc pathSubs*(p, config: string): string = proc pathSubs*(conf: ConfigRef; p, config: string): string =
let home = removeTrailingDirSep(os.getHomeDir()) let home = removeTrailingDirSep(os.getHomeDir())
result = unixToNativePath(p % [ result = unixToNativePath(p % [
"nim", getPrefixDir(), "nim", getPrefixDir(conf),
"lib", libpath, "lib", libpath,
"home", home, "home", home,
"config", config, "config", config,
"projectname", options.gProjectName, "projectname", options.gProjectName,
"projectpath", options.gProjectPath, "projectpath", options.gProjectPath,
"projectdir", options.gProjectPath, "projectdir", options.gProjectPath,
"nimcache", getNimcacheDir()]) "nimcache", getNimcacheDir(conf)])
if "~/" in result: if "~/" in result:
result = result.replace("~/", home & '/') 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" ## converts "/home/a/mymodule.nim", "rod" to "/home/a/nimcache/mymodule.rod"
var (head, tail) = splitPath(path) var (head, tail) = splitPath(path)
#if len(head) > 0: head = shortenDir(head & dirSep) #if len(head) > 0: head = shortenDir(head & dirSep)
result = joinPath([getNimcacheDir(), changeFileExt(tail, ext)]) result = joinPath([getNimcacheDir(conf), changeFileExt(tail, ext)])
#echo "toGeneratedFile(", path, ", ", ext, ") = ", result #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) var (head, tail) = splitPath(f)
#if len(head) > 0: head = removeTrailingDirSep(shortenDir(head & dirSep)) #if len(head) > 0: head = removeTrailingDirSep(shortenDir(head & dirSep))
var subdir = getNimcacheDir() # / head var subdir = getNimcacheDir(conf) # / head
if createSubDir: if createSubDir:
try: try:
createDir(subdir) createDir(subdir)
@ -341,14 +396,14 @@ proc completeGeneratedFilePath*(f: string, createSubDir: bool = true): string =
result = joinPath(subdir, tail) result = joinPath(subdir, tail)
#echo "completeGeneratedFilePath(", f, ") = ", result #echo "completeGeneratedFilePath(", f, ") = ", result
proc rawFindFile(f: string): string = proc rawFindFile(conf: ConfigRef; f: string): string =
for it in searchPaths: for it in searchPaths:
result = joinPath(it, f) result = joinPath(it, f)
if existsFile(result): if existsFile(result):
return result.canonicalizePath return canonicalizePath(conf, result)
result = "" result = ""
proc rawFindFile2(f: string): string = proc rawFindFile2(conf: ConfigRef; f: string): string =
for i, it in lazyPaths: for i, it in lazyPaths:
result = joinPath(it, f) result = joinPath(it, f)
if existsFile(result): if existsFile(result):
@ -356,30 +411,30 @@ proc rawFindFile2(f: string): string =
for j in countDown(i,1): for j in countDown(i,1):
swap(lazyPaths[j], lazyPaths[j-1]) swap(lazyPaths[j], lazyPaths[j-1])
return result.canonicalizePath return canonicalizePath(conf, result)
result = "" result = ""
template patchModule() {.dirty.} = template patchModule(conf: ConfigRef) {.dirty.} =
if result.len > 0 and gModuleOverrides.len > 0: if result.len > 0 and gModuleOverrides.len > 0:
let key = getPackageName(result) & "_" & splitFile(result).name let key = getPackageName(result) & "_" & splitFile(result).name
if gModuleOverrides.hasKey(key): if gModuleOverrides.hasKey(key):
let ov = gModuleOverrides[key] let ov = gModuleOverrides[key]
if ov.len > 0: result = ov if ov.len > 0: result = ov
proc findFile*(f: string): string {.procvar.} = proc findFile*(conf: ConfigRef; f: string): string {.procvar.} =
if f.isAbsolute: if f.isAbsolute:
result = if f.existsFile: f else: "" result = if f.existsFile: f else: ""
else: else:
result = f.rawFindFile result = rawFindFile(conf, f)
if result.len == 0: if result.len == 0:
result = f.toLowerAscii.rawFindFile result = rawFindFile(conf, f.toLowerAscii)
if result.len == 0: if result.len == 0:
result = f.rawFindFile2 result = rawFindFile2(conf, f)
if result.len == 0: if result.len == 0:
result = f.toLowerAscii.rawFindFile2 result = rawFindFile2(conf, f.toLowerAscii)
patchModule() patchModule(conf)
proc findModule*(modulename, currentModule: string): string = proc findModule*(conf: ConfigRef; modulename, currentModule: string): string =
# returns path to module # returns path to module
when defined(nimfix): when defined(nimfix):
# '.nimfix' modules are preferred over '.nim' modules so that specialized # '.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 let currentPath = currentModule.splitFile.dir
result = currentPath / m result = currentPath / m
if not existsFile(result): if not existsFile(result):
result = findFile(m) result = findFile(conf, m)
if existsFile(result): return result if existsFile(result): return result
let m = addFileExt(modulename, NimExt) let m = addFileExt(modulename, NimExt)
let currentPath = currentModule.splitFile.dir let currentPath = currentModule.splitFile.dir
result = currentPath / m result = currentPath / m
if not existsFile(result): if not existsFile(result):
result = findFile(m) result = findFile(conf, m)
patchModule() patchModule(conf)
proc findProjectNimFile*(pkg: string): string = proc findProjectNimFile*(conf: ConfigRef; pkg: string): string =
const extensions = [".nims", ".cfg", ".nimcfg", ".nimble"] const extensions = [".nims", ".cfg", ".nimcfg", ".nimble"]
var candidates: seq[string] = @[] var candidates: seq[string] = @[]
for k, f in os.walkDir(pkg, relative=true): for k, f in os.walkDir(pkg, relative=true):
@ -423,10 +478,10 @@ proc canonDynlibName(s: string): string =
else: else:
result = s.substr(start) result = s.substr(start)
proc inclDynlibOverride*(lib: string) = proc inclDynlibOverride*(conf: ConfigRef; lib: string) =
gDllOverrides[lib.canonDynlibName] = "true" gDllOverrides[lib.canonDynlibName] = "true"
proc isDynlibOverride*(lib: string): bool = proc isDynlibOverride*(conf: ConfigRef; lib: string): bool =
result = gDynlibOverrideAll or gDllOverrides.hasKey(lib.canonDynlibName) result = gDynlibOverrideAll or gDllOverrides.hasKey(lib.canonDynlibName)
proc binaryStrSearch*(x: openArray[string], y: string): int = proc binaryStrSearch*(x: openArray[string], y: string): int =

View file

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