path canonicalization and proper project relative paths

This commit is contained in:
Zahary Karadjov 2011-12-09 04:40:59 +02:00
commit e4e74034c2
9 changed files with 61 additions and 47 deletions

View file

@ -97,7 +97,7 @@ type
next*: ref TToken # for C we need arbitrary look-ahead :-( next*: ref TToken # for C we need arbitrary look-ahead :-(
TLexer* = object of TBaseLexer TLexer* = object of TBaseLexer
filename*: string fileIdx*: int32
inDirective: bool inDirective: bool
proc getTok*(L: var TLexer, tok: var TToken) proc getTok*(L: var TLexer, tok: var TToken)
@ -117,7 +117,7 @@ proc fillToken(L: var TToken) =
proc openLexer*(lex: var TLexer, filename: string, inputstream: PLLStream) = proc openLexer*(lex: var TLexer, filename: string, inputstream: PLLStream) =
openBaseLexer(lex, inputstream) openBaseLexer(lex, inputstream)
lex.filename = filename lex.fileIdx = filename.fileInfoIdx
proc closeLexer*(lex: var TLexer) = proc closeLexer*(lex: var TLexer) =
inc(gLinesCompiled, lex.LineNumber) inc(gLinesCompiled, lex.LineNumber)
@ -127,13 +127,13 @@ proc getColumn*(L: TLexer): int =
result = getColNumber(L, L.bufPos) result = getColNumber(L, L.bufPos)
proc getLineInfo*(L: TLexer): TLineInfo = proc getLineInfo*(L: TLexer): TLineInfo =
result = newLineInfo(L.filename, L.linenumber, getColNumber(L, L.bufpos)) result = newLineInfo(L.fileIdx, L.linenumber, getColNumber(L, L.bufpos))
proc lexMessage*(L: TLexer, msg: TMsgKind, arg = "") = proc lexMessage*(L: TLexer, msg: TMsgKind, arg = "") =
msgs.GenericMessage(getLineInfo(L), msg, arg) msgs.GenericMessage(getLineInfo(L), msg, arg)
proc lexMessagePos(L: var TLexer, msg: TMsgKind, pos: int, arg = "") = proc lexMessagePos(L: var TLexer, msg: TMsgKind, pos: int, arg = "") =
var info = newLineInfo(L.filename, L.linenumber, pos - L.lineStart) var info = newLineInfo(L.fileIdx, L.linenumber, pos - L.lineStart)
msgs.GenericMessage(info, msg, arg) msgs.GenericMessage(info, msg, arg)
proc TokKindToStr*(k: TTokKind): string = proc TokKindToStr*(k: TTokKind): string =

View file

@ -459,9 +459,7 @@ proc processSwitch(switch, arg: string, pass: TCmdlinePass, info: TLineInfo) =
if strutils.find(switch, '.') >= 0: options.setConfigVar(switch, arg) if strutils.find(switch, '.') >= 0: options.setConfigVar(switch, arg)
else: InvalidCmdLineOption(pass, switch, info) else: InvalidCmdLineOption(pass, switch, info)
proc ProcessCommand(switch: string, pass: TCmdLinePass) = proc ProcessCommand(switch: string, pass: TCmdLinePass) =
var var cmd, arg: string
cmd, arg: string splitSwitch(switch, cmd, arg, pass, gCmdLineInfo)
var info = newLineInfo("command line", 1, 1) processSwitch(cmd, arg, pass, gCmdLineInfo)
splitSwitch(switch, cmd, arg, pass, info)
ProcessSwitch(cmd, arg, pass, info)

View file

@ -96,7 +96,7 @@ type
# documentation comments are here too # documentation comments are here too
TLexer* = object of TBaseLexer TLexer* = object of TBaseLexer
filename*: string fileIdx*: int32
indentStack*: seq[int] # the indentation stack indentStack*: seq[int] # the indentation stack
dedent*: int # counter for DED token generation dedent*: int # counter for DED token generation
indentAhead*: int # if > 0 an indendation has already been read indentAhead*: int # if > 0 an indendation has already been read
@ -198,7 +198,7 @@ proc fillToken(L: var TToken) =
proc openLexer(lex: var TLexer, filename: string, inputstream: PLLStream) = proc openLexer(lex: var TLexer, filename: string, inputstream: PLLStream) =
openBaseLexer(lex, inputstream) openBaseLexer(lex, inputstream)
lex.indentStack = @[0] lex.indentStack = @[0]
lex.filename = filename lex.fileIdx = filename.fileInfoIdx
lex.indentAhead = - 1 lex.indentAhead = - 1
inc(lex.Linenumber, inputstream.lineOffset) inc(lex.Linenumber, inputstream.lineOffset)
@ -210,13 +210,13 @@ proc getColumn(L: TLexer): int =
result = getColNumber(L, L.bufPos) result = getColNumber(L, L.bufPos)
proc getLineInfo(L: TLexer): TLineInfo = proc getLineInfo(L: TLexer): TLineInfo =
result = newLineInfo(L.filename, L.linenumber, getColNumber(L, L.bufpos)) result = newLineInfo(L.fileIdx, L.linenumber, getColNumber(L, L.bufpos))
proc lexMessage(L: TLexer, msg: TMsgKind, arg = "") = proc lexMessage(L: TLexer, msg: TMsgKind, arg = "") =
msgs.Message(getLineInfo(L), msg, arg) msgs.Message(getLineInfo(L), msg, arg)
proc lexMessagePos(L: var TLexer, msg: TMsgKind, pos: int, arg = "") = proc lexMessagePos(L: var TLexer, msg: TMsgKind, pos: int, arg = "") =
var info = newLineInfo(L.filename, L.linenumber, pos - L.lineStart) var info = newLineInfo(L.fileIdx, L.linenumber, pos - L.lineStart)
msgs.Message(info, msg, arg) msgs.Message(info, msg, arg)
proc matchUnderscoreChars(L: var TLexer, tok: var TToken, chars: TCharSet) = proc matchUnderscoreChars(L: var TLexer, tok: var TToken, chars: TCharSet) =

View file

@ -176,7 +176,7 @@ proc CommandSuggest =
proc wantMainModule = proc wantMainModule =
if gProjectFull.len == 0: if gProjectFull.len == 0:
Fatal(newLineInfo("command line", 1, 1), errCommandExpectsFilename) Fatal(gCmdLineInfo, errCommandExpectsFilename)
proc MainCommand = proc MainCommand =
appendStr(searchPaths, options.libpath) appendStr(searchPaths, options.libpath)

View file

@ -8,7 +8,7 @@
# #
import import
options, strutils, os options, strutils, os, tables
type type
TMsgKind* = enum TMsgKind* = enum
@ -384,6 +384,11 @@ const
type type
TNoteKind* = range[warnMin..hintMax] # "notes" are warnings or hints TNoteKind* = range[warnMin..hintMax] # "notes" are warnings or hints
TNoteKinds* = set[TNoteKind] TNoteKinds* = set[TNoteKind]
TFileInfo*{.final.} = object
fullPath*: string # This is a canonical full filesystem path
projPath*: string # This is relative to the project's root
TLineInfo*{.final.} = object # This is designed to be as small as possible, TLineInfo*{.final.} = object # This is designed to be as small as possible,
# because it is used # because it is used
# in syntax nodes. We safe space here by using # in syntax nodes. We safe space here by using
@ -395,7 +400,37 @@ type
ERecoverableError* = object of EInvalidValue ERecoverableError* = object of EInvalidValue
proc newLineInfo*(filename: string, line, col: int): TLineInfo var
filenameToIndexTbl = initTable[string, int32]()
fileInfos: seq[TFileInfo] = @[]
proc newFileInfo(fullPath, projPath: string): TFileInfo =
result.fullPath = fullPath
result.projPath = projPath
proc fileInfoIdx*(filename: string): int32 =
var canonical = canonicalizePath(filename)
if filenameToIndexTbl.hasKey(canonical):
result = filenameToIndexTbl[canonical]
else:
result = fileInfos.len.int32
fileInfos.add(newFileInfo(canonical, canonical.shortenDir))
filenameToIndexTbl[canonical] = result
proc newLineInfo*(filename: string, line, col: int): TLineInfo =
result.fileIndex = filename.fileInfoIdx
result.line = int16(line)
result.col = int16(col)
proc newLineInfo*(fileInfoIdx: int32, line, col: int): TLineInfo =
result.fileIndex = fileInfoIdx
result.line = int16(line)
result.col = int16(col)
fileInfos.add(newFileInfo("command line", ""))
var gCmdLineInfo* = newLineInfo(int32(0), 1, 1)
proc raiseRecoverableError*() {.noinline, noreturn.} = proc raiseRecoverableError*() {.noinline, noreturn.} =
raise newException(ERecoverableError, "") raise newException(ERecoverableError, "")
@ -423,9 +458,7 @@ proc UnknownLineInfo*(): TLineInfo =
result.fileIndex = -1 result.fileIndex = -1
var var
filenames: seq[tuple[filename: string, fullpath: string]] = @[]
msgContext: seq[TLineInfo] = @[] msgContext: seq[TLineInfo] = @[]
gCmdLineInfo* = newLineInfo("command line", -1, -1)
proc pushInfoContext*(info: TLineInfo) = proc pushInfoContext*(info: TLineInfo) =
msgContext.add(info) msgContext.add(info)
@ -433,31 +466,13 @@ proc pushInfoContext*(info: TLineInfo) =
proc popInfoContext*() = proc popInfoContext*() =
setlen(msgContext, len(msgContext) - 1) setlen(msgContext, len(msgContext) - 1)
proc includeFilename*(f: string): int = proc ToFilename*(info: TLineInfo): string =
for i in countdown(high(filenames), low(filenames)):
if filenames[i].filename == f:
return i
result = len(filenames)
var fullpath: string
try: fullpath = expandFilename(f)
except: fullpath = ""
filenames.add((filename: f, fullpath: fullpath))
proc newLineInfo(filename: string, line, col: int): TLineInfo =
result.fileIndex = includeFilename(filename)
result.line = int16(line)
result.col = int16(col)
proc ToFilename*(info: TLineInfo): string =
if info.fileIndex < 0: result = "???" if info.fileIndex < 0: result = "???"
else: result = filenames[info.fileIndex].filename else: result = fileInfos[info.fileIndex].projPath
proc toFullPath*(info: TLineInfo): string = proc toFullPath*(info: TLineInfo): string =
if info.fileIndex < 0: result = "???" if info.fileIndex < 0: result = "???"
else: result = filenames[info.fileIndex].fullpath else: result = fileInfos[info.fileIndex].fullPath
proc ToLinenumber*(info: TLineInfo): int {.inline.} = proc ToLinenumber*(info: TLineInfo): int {.inline.} =
result = info.line result = info.line

View file

@ -70,14 +70,14 @@ proc HandleCmdLine() =
ProcessCmdLine(passCmd1) ProcessCmdLine(passCmd1)
if gProjectName != "": if gProjectName != "":
try: try:
gProjectFull = expandFilename(gProjectName) gProjectFull = canonicalizePath(gProjectName)
except EOS: except EOS:
gProjectFull = gProjectName gProjectFull = gProjectName
var p = splitFile(gProjectFull) var p = splitFile(gProjectFull)
gProjectPath = p.dir gProjectPath = p.dir
gProjectName = p.name gProjectName = p.name
else: else:
gProjectPath = getCurrentDir() gProjectPath = getCurrentDir()
LoadConfigs(DefaultConfig) # load all config files LoadConfigs(DefaultConfig) # load all config files
# now process command line arguments again, because some options in the # now process command line arguments again, because some options in the
# command line can overwite the config file's settings # command line can overwite the config file's settings

View file

@ -138,12 +138,13 @@ proc getPrefixDir*(): string =
## gets the application directory ## gets the application directory
result = SplitPath(getAppDir()).head result = SplitPath(getAppDir()).head
proc canonicalizePath*(path: string): string =
result = path.expandFilename
when not FileSystemCaseSensitive: result = result.toLower
proc shortenDir*(dir: string): string = proc shortenDir*(dir: string): string =
## returns the interesting part of a dir ## returns the interesting part of a dir
var prefix = getPrefixDir() & dirSep var prefix = getPrefixDir() & dirSep
if startsWith(dir, prefix):
return substr(dir, len(prefix))
prefix = getCurrentDir() & dirSep
if startsWith(dir, prefix): if startsWith(dir, prefix):
return substr(dir, len(prefix)) return substr(dir, len(prefix))
prefix = gProjectPath & dirSep prefix = gProjectPath & dirSep

View file

@ -796,7 +796,7 @@ proc evalInclude(c: PContext, n: PNode): PNode =
addSon(result, n) addSon(result, n)
for i in countup(0, sonsLen(n) - 1): for i in countup(0, sonsLen(n) - 1):
var f = getModuleFile(n.sons[i]) var f = getModuleFile(n.sons[i])
var fileIndex = includeFilename(f) var fileIndex = f.fileInfoIdx
if ContainsOrIncl(c.includedFiles, fileIndex): if ContainsOrIncl(c.includedFiles, fileIndex):
GlobalError(n.info, errRecursiveDependencyX, f) GlobalError(n.info, errRecursiveDependencyX, f)
addSon(result, semStmt(c, gIncludeFile(f))) addSon(result, semStmt(c, gIncludeFile(f)))

View file

@ -713,7 +713,7 @@ proc matches*(c: PContext, n: PNode, m: var TCandidate) =
when false: when false:
if sfSystemModule notin c.module.flags: if sfSystemModule notin c.module.flags:
if includeFilename("temp.nim") == c.module.info.fileIndex: if fileInfoIdx("temp.nim") == c.module.info.fileIndex:
echo "########################" echo "########################"
echo m.call.renderTree echo m.call.renderTree
for i in 1..m.call.len-1: for i in 1..m.call.len-1: