diff options
Diffstat (limited to 'compiler/modulepaths.nim')
-rw-r--r-- | compiler/modulepaths.nim | 95 |
1 files changed, 95 insertions, 0 deletions
diff --git a/compiler/modulepaths.nim b/compiler/modulepaths.nim new file mode 100644 index 000000000..c9e6060e5 --- /dev/null +++ b/compiler/modulepaths.nim @@ -0,0 +1,95 @@ +# +# +# The Nim Compiler +# (c) Copyright 2017 Contributors +# +# See the file "copying.txt", included in this +# distribution, for details about the copyright. +# + +import ast, renderer, msgs, options, idents, lineinfos, + pathutils + +import std/[strutils, os] + +proc getModuleName*(conf: ConfigRef; n: PNode): string = + # This returns a short relative module name without the nim extension + # e.g. like "system", "importer" or "somepath/module" + # The proc won't perform any checks that the path is actually valid + case n.kind + of nkStrLit, nkRStrLit, nkTripleStrLit: + try: + result = pathSubs(conf, n.strVal, toFullPath(conf, n.info).splitFile().dir) + except ValueError: + localError(conf, n.info, "invalid path: " & n.strVal) + result = n.strVal + of nkIdent: + result = n.ident.s + of nkSym: + result = n.sym.name.s + of nkInfix: + let n0 = n[0] + let n1 = n[1] + when false: + if n1.kind == nkPrefix and n1[0].kind == nkIdent and n1[0].ident.s == "$": + if n0.kind == nkIdent and n0.ident.s == "/": + result = lookupPackage(n1[1], n[2]) + else: + localError(n.info, "only '/' supported with $package notation") + result = "" + else: + if n0.kind in nkIdentKinds: + let ident = n0.getPIdent + if ident != nil and ident.s[0] == '/': + let modname = getModuleName(conf, n[2]) + # hacky way to implement 'x / y /../ z': + result = getModuleName(conf, n1) + result.add renderTree(n0, {renderNoComments}).replace(" ") + result.add modname + else: + result = "" + else: + result = "" + of nkPrefix: + when false: + if n[0].kind == nkIdent and n[0].ident.s == "$": + result = lookupPackage(n[1], nil) + else: + discard + # hacky way to implement 'x / y /../ z': + result = renderTree(n, {renderNoComments}).replace(" ") + of nkDotExpr: + localError(conf, n.info, warnDeprecated, "using '.' instead of '/' in import paths is deprecated") + result = renderTree(n, {renderNoComments}).replace(".", "/") + of nkImportAs: + result = getModuleName(conf, n[0]) + else: + localError(conf, n.info, "invalid module name: '$1'" % n.renderTree) + result = "" + +proc checkModuleName*(conf: ConfigRef; n: PNode; doLocalError=true): FileIndex = + # This returns the full canonical path for a given module import + let modulename = getModuleName(conf, n) + let fullPath = findModule(conf, modulename, toFullPath(conf, n.info)) + if fullPath.isEmpty: + if doLocalError: + let m = if modulename.len > 0: modulename else: $n + localError(conf, n.info, "cannot open file: " & m) + result = InvalidFileIdx + else: + result = fileInfoIdx(conf, fullPath) + +proc mangleModuleName*(conf: ConfigRef; path: AbsoluteFile): string = + ## Mangle a relative module path to avoid path and symbol collisions. + ## + ## Used by backends that need to generate intermediary files from Nim modules. + ## This is needed because the compiler uses a flat cache file hierarchy. + ## + ## Example: + ## `foo-#head/../bar` becomes `@foo-@hhead@s..@sbar` + "@m" & relativeTo(path, conf.projectPath).string.multiReplace( + {$os.DirSep: "@s", $os.AltSep: "@s", "#": "@h", "@": "@@", ":": "@c"}) + +proc demangleModuleName*(path: string): string = + ## Demangle a relative module path. + result = path.multiReplace({"@@": "@", "@h": "#", "@s": "/", "@m": "", "@c": ":"}) |