summary refs log tree commit diff stats
path: root/compiler
diff options
context:
space:
mode:
Diffstat (limited to 'compiler')
-rw-r--r--compiler/gorgeimpl.nim83
-rw-r--r--compiler/importer.nim50
-rw-r--r--compiler/nimblecmd.nim8
-rw-r--r--compiler/sem.nim12
-rw-r--r--compiler/vm.nim2
-rw-r--r--compiler/vmdeps.nim45
6 files changed, 136 insertions, 64 deletions
diff --git a/compiler/gorgeimpl.nim b/compiler/gorgeimpl.nim
new file mode 100644
index 000000000..4e1ce6d50
--- /dev/null
+++ b/compiler/gorgeimpl.nim
@@ -0,0 +1,83 @@
+#
+#
+#           The Nim Compiler
+#        (c) Copyright 2017 Andreas Rumpf
+#
+#    See the file "copying.txt", included in this
+#    distribution, for details about the copyright.
+#
+
+## Module that implements ``gorge`` for the compiler as well as
+## the scriptable import mechanism.
+
+import msgs, securehash, os, osproc, streams, strutils, options
+
+proc readOutput(p: Process): (string, int) =
+  result[0] = ""
+  var output = p.outputStream
+  while not output.atEnd:
+    result[0].add(output.readLine)
+    result[0].add("\n")
+  if result[0].len > 0:
+    result[0].setLen(result[0].len - "\n".len)
+  result[1] = p.waitForExit
+
+proc opGorge*(cmd, input, cache: string, info: TLineInfo): (string, int) =
+  let workingDir = parentDir(info.toFullPath)
+  if cache.len > 0:# and optForceFullMake notin gGlobalOptions:
+    let h = secureHash(cmd & "\t" & input & "\t" & cache)
+    let filename = options.toGeneratedFile("gorge_" & $h, "txt")
+    var f: File
+    if open(f, filename):
+      result = (f.readAll, 0)
+      f.close
+      return
+    var readSuccessful = false
+    try:
+      var p = startProcess(cmd, workingDir,
+                           options={poEvalCommand, poStderrToStdout})
+      if input.len != 0:
+        p.inputStream.write(input)
+        p.inputStream.close()
+      result = p.readOutput
+      readSuccessful = true
+      # only cache successful runs:
+      if result[1] == 0:
+        writeFile(filename, result[0])
+    except IOError, OSError:
+      if not readSuccessful: result = ("", -1)
+  else:
+    try:
+      var p = startProcess(cmd, workingDir,
+                           options={poEvalCommand, poStderrToStdout})
+      if input.len != 0:
+        p.inputStream.write(input)
+        p.inputStream.close()
+      result = p.readOutput
+    except IOError, OSError:
+      result = ("", -1)
+
+proc scriptableImport*(pkg, subdir: string; info: TLineInfo): string =
+  var cmd = getConfigVar("resolver.exe")
+  if cmd.len == 0: cmd = "nimresolve"
+  else: cmd = quoteShell(cmd)
+  cmd.add " --source:"
+  cmd.add quoteShell(info.toFullPath())
+  cmd.add " --stdlib:"
+  cmd.add quoteShell(options.libpath)
+  cmd.add "  --project:"
+  cmd.add quoteShell(gProjectFull)
+  if subdir.len != 0:
+    cmd.add " --subdir:"
+    cmd.add quoteShell(subdir)
+  if options.gNoNimblePath:
+    cmd.add " --nonimblepath"
+  cmd.add ' '
+  cmd.add quoteShell(pkg)
+  let (res, exitCode) = opGorge(cmd, "", cmd, info)
+  if exitCode == 0:
+    result = res.strip()
+  elif res.len > 0:
+    localError(info, res)
+  else:
+    localError(info, "cannot resolve: " & (pkg / subdir))
diff --git a/compiler/importer.nim b/compiler/importer.nim
index dfc9e84ba..07f42a147 100644
--- a/compiler/importer.nim
+++ b/compiler/importer.nim
@@ -11,11 +11,22 @@
 
 import
   intsets, strutils, os, ast, astalgo, msgs, options, idents, rodread, lookups,
-  semdata, passes, renderer
+  semdata, passes, renderer, gorgeimpl
 
 proc evalImport*(c: PContext, n: PNode): PNode
 proc evalFrom*(c: PContext, n: PNode): PNode
 
+proc lookupPackage(pkg, subdir: PNode): string =
+  let sub = if subdir != nil: renderTree(subdir, {renderNoComments}).replace(" ") else: ""
+  case pkg.kind
+  of nkStrLit, nkRStrLit, nkTripleStrLit:
+    result = scriptableImport(pkg.strVal, sub, pkg.info)
+  of nkIdent:
+    result = scriptableImport(pkg.ident.s, sub, pkg.info)
+  else:
+    localError(pkg.info, "package name must be an identifier or string literal")
+    result = ""
+
 proc getModuleName*(n: PNode): string =
   # This returns a short relative module name without the nim extension
   # e.g. like "system", "importer" or "somepath/module"
@@ -31,16 +42,33 @@ proc getModuleName*(n: PNode): string =
     result = n.ident.s
   of nkSym:
     result = n.sym.name.s
-  of nkInfix, nkPrefix:
-    if n.sons[0].kind == nkIdent and n.sons[0].ident.id == getIdent("as").id:
+  of nkInfix:
+    let n0 = n[0]
+    let n1 = n[1]
+    if n0.kind == nkIdent and n0.ident.id == getIdent("as").id:
       # XXX hack ahead:
       n.kind = nkImportAs
       n.sons[0] = n.sons[1]
       n.sons[1] = n.sons[2]
       n.sons.setLen(2)
       return getModuleName(n.sons[0])
-    # hacky way to implement 'x / y /../ z':
-    result = renderTree(n, {renderNoComments}).replace(" ")
+    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:
+      # hacky way to implement 'x / y /../ z':
+      result = getModuleName(n1)
+      result.add renderTree(n0, {renderNoComments})
+      result.add getModuleName(n[2])
+  of nkPrefix:
+    if n.sons[0].kind == nkIdent and n.sons[0].ident.s == "$":
+      result = lookupPackage(n[1], nil)
+    else:
+      # hacky way to implement 'x / y /../ z':
+      result = renderTree(n, {renderNoComments}).replace(" ")
   of nkDotExpr:
     result = renderTree(n, {renderNoComments}).replace(".", "/")
   of nkImportAs:
@@ -209,12 +237,14 @@ proc evalImport(c: PContext, n: PNode): PNode =
   for i in countup(0, sonsLen(n) - 1):
     let it = n.sons[i]
     if it.kind == nkInfix and it.len == 3 and it[2].kind == nkBracket:
-      let sep = renderTree(it.sons[0], {renderNoComments})
-      let dir = renderTree(it.sons[1], {renderNoComments})
+      let sep = it[0]
+      let dir = it[1]
+      let a = newNodeI(nkInfix, it.info)
+      a.add sep
+      a.add dir
+      a.add sep # dummy entry, replaced in the loop
       for x in it[2]:
-        let f = renderTree(x, {renderNoComments})
-        let a = newStrNode(nkStrLit, (dir & sep & f).replace(" "))
-        a.info = it.info
+        a.sons[2] = x
         impMod(c, a)
     else:
       impMod(c, it)
diff --git a/compiler/nimblecmd.nim b/compiler/nimblecmd.nim
index ab63f9e12..39c3a17e7 100644
--- a/compiler/nimblecmd.nim
+++ b/compiler/nimblecmd.nim
@@ -16,11 +16,11 @@ proc addPath*(path: string, info: TLineInfo) =
     options.searchPaths.insert(path, 0)
 
 type
-  Version = distinct string
+  Version* = distinct string
 
-proc `$`(ver: Version): string {.borrow.}
+proc `$`*(ver: Version): string {.borrow.}
 
-proc newVersion(ver: string): Version =
+proc newVersion*(ver: string): Version =
   doAssert(ver.len == 0 or ver[0] in {'#', '\0'} + Digits,
            "Wrong version: " & ver)
   return Version(ver)
@@ -28,7 +28,7 @@ proc newVersion(ver: string): Version =
 proc isSpecial(ver: Version): bool =
   return ($ver).len > 0 and ($ver)[0] == '#'
 
-proc `<`(ver: Version, ver2: Version): bool =
+proc `<`*(ver: Version, ver2: Version): bool =
   ## This is synced from Nimble's version module.
 
   # Handling for special versions such as "#head" or "#branch".
diff --git a/compiler/sem.nim b/compiler/sem.nim
index 21fd0b383..ebfdafea7 100644
--- a/compiler/sem.nim
+++ b/compiler/sem.nim
@@ -495,13 +495,15 @@ proc isImportSystemStmt(n: PNode): bool =
   case n.kind
   of nkImportStmt:
     for x in n:
-      let f = checkModuleName(x, false)
+      if x.kind == nkIdent:
+        let f = checkModuleName(x, false)
+        if f == magicsys.systemModule.info.fileIndex:
+          return true
+  of nkImportExceptStmt, nkFromStmt:
+    if n[0].kind == nkIdent:
+      let f = checkModuleName(n[0], false)
       if f == magicsys.systemModule.info.fileIndex:
         return true
-  of nkImportExceptStmt, nkFromStmt:
-    let f = checkModuleName(n[0], false)
-    if f == magicsys.systemModule.info.fileIndex:
-      return true
   else: discard
 
 proc semStmtAndGenerateGenerics(c: PContext, n: PNode): PNode =
diff --git a/compiler/vm.nim b/compiler/vm.nim
index 79bfb911d..08605cad1 100644
--- a/compiler/vm.nim
+++ b/compiler/vm.nim
@@ -19,7 +19,7 @@ import ast except getstr
 import
   strutils, astalgo, msgs, vmdef, vmgen, nimsets, types, passes,
   parser, vmdeps, idents, trees, renderer, options, transf, parseutils,
-  vmmarshal
+  vmmarshal, gorgeimpl
 
 from semfold import leValueConv, ordinalValToString
 from evaltempl import evalTemplate
diff --git a/compiler/vmdeps.nim b/compiler/vmdeps.nim
index 2b3cfeeeb..29c1129b5 100644
--- a/compiler/vmdeps.nim
+++ b/compiler/vmdeps.nim
@@ -7,50 +7,7 @@
 #    distribution, for details about the copyright.
 #
 
-import ast, types, msgs, os, osproc, streams, options, idents, securehash
-
-proc readOutput(p: Process): (string, int) =
-  result[0] = ""
-  var output = p.outputStream
-  while not output.atEnd:
-    result[0].add(output.readLine)
-    result[0].add("\n")
-  if result[0].len > 0:
-    result[0].setLen(result[0].len - "\n".len)
-  result[1] = p.waitForExit
-
-proc opGorge*(cmd, input, cache: string, info: TLineInfo): (string, int) =
-  let workingDir = parentDir(info.toFullPath)
-  if cache.len > 0:# and optForceFullMake notin gGlobalOptions:
-    let h = secureHash(cmd & "\t" & input & "\t" & cache)
-    let filename = options.toGeneratedFile("gorge_" & $h, "txt")
-    var f: File
-    if open(f, filename):
-      result = (f.readAll, 0)
-      f.close
-      return
-    var readSuccessful = false
-    try:
-      var p = startProcess(cmd, workingDir,
-                           options={poEvalCommand, poStderrToStdout})
-      if input.len != 0:
-        p.inputStream.write(input)
-        p.inputStream.close()
-      result = p.readOutput
-      readSuccessful = true
-      writeFile(filename, result[0])
-    except IOError, OSError:
-      if not readSuccessful: result = ("", -1)
-  else:
-    try:
-      var p = startProcess(cmd, workingDir,
-                           options={poEvalCommand, poStderrToStdout})
-      if input.len != 0:
-        p.inputStream.write(input)
-        p.inputStream.close()
-      result = p.readOutput
-    except IOError, OSError:
-      result = ("", -1)
+import ast, types, msgs, os, streams, options, idents
 
 proc opSlurp*(file: string, info: TLineInfo, module: PSym): string =
   try: