summary refs log tree commit diff stats
diff options
context:
space:
mode:
-rwxr-xr-xcompiler/ast.nim15
-rwxr-xr-xcompiler/ccgexprs.nim2
-rwxr-xr-xcompiler/ecmasgen.nim2
-rwxr-xr-xcompiler/evals.nim44
-rwxr-xr-xcompiler/msgs.nim3
-rwxr-xr-xcompiler/sem.nim11
-rwxr-xr-xcompiler/semdata.nim2
-rwxr-xr-xcompiler/semexprs.nim8
-rwxr-xr-xcompiler/semfold.nim2
-rw-r--r--compiler/semmagic.nim14
-rwxr-xr-xcompiler/semstmts.nim2
-rwxr-xr-xlib/system.nim15
-rwxr-xr-xweb/news.txt2
13 files changed, 83 insertions, 39 deletions
diff --git a/compiler/ast.nim b/compiler/ast.nim
index 949c28c5f..c59c47453 100755
--- a/compiler/ast.nim
+++ b/compiler/ast.nim
@@ -370,7 +370,7 @@ type
   TMagic* = enum # symbols that require compiler magic:
     mNone,
     mDefined, mDefinedInScope, mLow, mHigh, mSizeOf, mTypeTrait, mIs, mOf,
-    mEcho, mShallowCopy, mSlurp,
+    mEcho, mShallowCopy, mSlurp, mStaticExec,
     mParseExprToAst, mParseStmtToAst, mExpandToAst,
     mUnaryLt, mSucc, 
     mPred, mInc, mDec, mOrd, mNew, mNewFinalize, mNewSeq, mLengthOpenArray, 
@@ -541,6 +541,9 @@ type
                               # for easy generation of proper error messages
                               # for variant record fields the discriminant
                               # expression
+                              # for modules, it's a placeholder for compiler
+                              # generated code that will be appended to the
+                              # module after the sem pass (see appendToModule)
     options*: TOptions
     position*: int            # used for many different things:
                               # for enum fields its position;
@@ -742,6 +745,16 @@ proc linkTo*(s: PSym, t: PType): PSym {.discardable.} =
   s.typ = t
   result = s
 
+proc appendToModule*(m: PSym, n: PNode) =
+  ## The compiler will use this internally to add nodes that will be
+  ## appended to the module after the sem pass
+  if m.ast == nil:
+    m.ast = newNode(nkStmtList)
+    m.ast.sons = @[n]
+  else:
+    assert m.ast.kind == nkStmtList
+    m.ast.sons.add(n)
+  
 const                         # for all kind of hash tables:
   GrowthFactor* = 2           # must be power of 2, > 0
   StartSize* = 8              # must be power of 2, > 0
diff --git a/compiler/ccgexprs.nim b/compiler/ccgexprs.nim
index 34418f353..eb20153fc 100755
--- a/compiler/ccgexprs.nim
+++ b/compiler/ccgexprs.nim
@@ -1436,6 +1436,8 @@ proc genMagicExpr(p: BProc, e: PNode, d: var TLoc, op: TMagic) =
   of mArrToSeq: genArrToSeq(p, e, d)
   of mNLen..mNError:
     localError(e.info, errCannotGenerateCodeForX, e.sons[0].sym.name.s)
+  of mSlurp, mStaticExec:
+    localError(e.info, errXMustBeCompileTime, e.sons[0].sym.name.s)
   else: internalError(e.info, "genMagicExpr: " & $op)
 
 proc genConstExpr(p: BProc, n: PNode): PRope
diff --git a/compiler/ecmasgen.nim b/compiler/ecmasgen.nim
index 3ab4303c2..9c9c01734 100755
--- a/compiler/ecmasgen.nim
+++ b/compiler/ecmasgen.nim
@@ -1173,6 +1173,8 @@ proc genMagic(p: var TProc, n: PNode, r: var TCompRes) =
     localError(n.info, errCannotGenerateCodeForX, n.sons[0].sym.name.s)
   of mNewSeq: binaryStmt(p, n, r, "", "$1 = new Array($2)")
   of mEcho: genEcho(p, n, r)
+  of mSlurp, mStaticExec:
+    localError(n.info, errXMustBeCompileTime, n.sons[0].sym.name.s)
   else:
     genCall(p, n, r)
     #else internalError(e.info, 'genMagic: ' + magicToStr[op]);
diff --git a/compiler/evals.nim b/compiler/evals.nim
index a4d4cdd9e..002fed194 100755
--- a/compiler/evals.nim
+++ b/compiler/evals.nim
@@ -16,7 +16,7 @@
 import 
   strutils, magicsys, lists, options, ast, astalgo, trees, treetab, nimsets, 
   msgs, os, condsyms, idents, renderer, types, passes, semfold, transf, 
-  parser, ropes, rodread, idgen
+  parser, ropes, rodread, idgen, osproc, streams
 
 type 
   PStackFrame* = ref TStackFrame
@@ -910,6 +910,43 @@ proc evalTypeTrait*(n: PNode, context: PSym): PNode =
   else:
     internalAssert false
 
+proc expectString(n: PNode) =
+  if n.kind notin {nkStrLit, nkRStrLit, nkTripleStrLit}:
+    GlobalError(n.info, errStringLiteralExpected)
+
+proc evalSlurp*(e: PNode, module: PSym): PNode =
+  expectString(e)
+  try:
+    var filename = e.strVal.FindFile
+    var content = readFile(filename)
+    result = newStrNode(nkStrLit, content)
+    result.typ = getSysType(tyString)
+    result.info = e.info
+    # we produce a fake include statement for every slurped filename, so that
+    # the module dependencies are accurate:    
+    appendToModule(module, newNode(nkIncludeStmt, e.info, @[
+      newStrNode(nkStrLit, filename)]))
+  except EIO:
+    GlobalError(e.info, errCannotOpenFile, e.strVal)
+
+proc readOutput(p: PProcess): string =
+  result = ""
+  var output = p.outputStream
+  discard p.waitForExit
+  while not output.atEnd:
+    result.add(output.readLine)
+
+proc evalStaticExec*(cmd, input: PNode): PNode =
+  expectString(cmd)
+  var p = startCmd(cmd.strVal)
+  if input != nil:
+    expectString(input)
+    p.inputStream.write(input.strVal)
+    p.inputStream.close()
+  result = newStrNode(nkStrLit, p.readOutput)
+  result.typ = getSysType(tyString)
+  result.info = cmd.info
+
 proc evalExpandToAst(c: PEvalContext, original: PNode): PNode =
   var
     n = original.copyTree
@@ -960,6 +997,11 @@ proc evalMagicOrCall(c: PEvalContext, n: PNode): PNode =
   of mParseStmtToAst: result = evalParseStmt(c, n)
   of mExpandToAst: result = evalExpandToAst(c, n)
   of mTypeTrait: result = evalTypeTrait(n, c.module)
+  of mSlurp: result = evalSlurp(evalAux(c, n.sons[1], {}), c.module)
+  of mStaticExec:
+    let cmd = evalAux(c, n.sons[1], {})
+    let input = if n.sonsLen == 3: evalAux(c, n.sons[2], {}) else: nil
+    result = evalStaticExec(cmd, input)
   of mNLen:
     result = evalAux(c, n.sons[1], {efLValue})
     if isSpecial(result): return 
diff --git a/compiler/msgs.nim b/compiler/msgs.nim
index 17cfeae4d..2af512af7 100755
--- a/compiler/msgs.nim
+++ b/compiler/msgs.nim
@@ -94,7 +94,7 @@ type
     errUnhandledExceptionX, errCyclicTree, errXisNoMacroOrTemplate, 
     errXhasSideEffects, errIteratorExpected, errLetNeedsInit,
     errThreadvarCannotInit, errWrongSymbolX, errIllegalCaptureX,
-    errXCannotBeClosure,
+    errXCannotBeClosure, errXMustBeCompileTime,
     errUser,
     warnCannotOpenFile, 
     warnOctalEscape, warnXIsNeverRead, warnXmightNotBeenInit, 
@@ -327,6 +327,7 @@ const
     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",
     errUser: "$1", 
     warnCannotOpenFile: "cannot open \'$1\' [CannotOpenFile]",
     warnOctalEscape: "octal escape sequences do not exist; leading zero is ignored [OctalEscape]", 
diff --git a/compiler/sem.nim b/compiler/sem.nim
index 8afbe3c51..721b4b040 100755
--- a/compiler/sem.nim
+++ b/compiler/sem.nim
@@ -89,9 +89,6 @@ proc semConstExpr(c: PContext, n: PNode): PNode =
     return nil
   result = evalTypedExpr(c, e)
 
-proc semAndEvalConstExpr(c: PContext, n: PNode): PNode = 
-  result = semConstExpr(c, n)
-
 include seminst, semcall
 
 proc semAfterMacroCall(c: PContext, n: PNode, s: PSym): PNode = 
@@ -219,12 +216,8 @@ proc myClose(context: PPassContext, n: PNode): PNode =
   else:
     InternalError(n.info, "n is not nil") #result := n;
   addCodeForGenerics(c, result)
-  # we produce a fake include statement for every slurped filename, so that
-  # the module dependencies are accurate:
-  var ics = newNode(nkIncludeStmt)
-  for s in items(c.slurpedFiles): ics.add(newStrNode(nkStrLit, s))
-  result.add(ics)
-  
+  if c.module.ast != nil:
+    result.add(c.module.ast)
   checkThreads(c)
   popOwner()
   popProcCon(c)
diff --git a/compiler/semdata.nim b/compiler/semdata.nim
index f97da4717..81e45f71c 100755
--- a/compiler/semdata.nim
+++ b/compiler/semdata.nim
@@ -73,7 +73,6 @@ type
     filename*: string          # the module's filename
     userPragmas*: TStrTable
     evalContext*: PEvalContext
-    slurpedFiles*: seq[string]
 
 var
   gGenericsCache: PGenericsCache # save for modularity
@@ -153,7 +152,6 @@ proc newContext(module: PSym, nimfile: string): PContext =
   result.filename = nimfile
   result.includedFiles = initIntSet()
   initStrTable(result.userPragmas)
-  result.slurpedFiles = @[]
   if optSymbolFiles notin gGlobalOptions:
     # re-usage of generic instantiations across module boundaries is
     # very nice for code size:
diff --git a/compiler/semexprs.nim b/compiler/semexprs.nim
index fe35607cb..ac9075d4f 100755
--- a/compiler/semexprs.nim
+++ b/compiler/semexprs.nim
@@ -491,12 +491,6 @@ proc analyseIfAddressTakenInCall(c: PContext, n: PNode) =
         skipTypes(t.sons[i], abstractInst).kind == tyVar:
       n.sons[i] = analyseIfAddressTaken(c, n.sons[i])
   
-
-proc expectStringArg(c: PContext, n: PNode, i: int): PNode =
-  result = c.semAndEvalConstExpr(n.sons[i+1])
-  if result.kind notin {nkStrLit, nkRStrLit, nkTripleStrLit}:
-    GlobalError(result.info, errStringLiteralExpected)
-  
 include semmagic
 
 proc evalAtCompileTime(c: PContext, n: PNode): PNode =
@@ -901,7 +895,7 @@ proc semSubscript(c: PContext, n: PNode, flags: TExprFlags): PNode =
     checkSonsLen(n, 2)
     n.sons[0] = makeDeref(n.sons[0])
     # [] operator for tuples requires constant expression:
-    n.sons[1] = semAndEvalConstExpr(c, n.sons[1])
+    n.sons[1] = semConstExpr(c, n.sons[1])
     if skipTypes(n.sons[1].typ, {tyGenericInst, tyRange, tyOrdinal}).kind in
         {tyInt..tyInt64}: 
       var idx = getOrdValue(n.sons[1])
diff --git a/compiler/semfold.nim b/compiler/semfold.nim
index 82ee7de13..565155791 100755
--- a/compiler/semfold.nim
+++ b/compiler/semfold.nim
@@ -207,7 +207,7 @@ proc evalOp(m: TMagic, n, a, b, c: PNode): PNode =
      mExit, mInc, ast.mDec, mEcho, mSwap, mAppendStrCh, 
      mAppendStrStr, mAppendSeqElem, mSetLengthStr, mSetLengthSeq, 
      mParseExprToAst, mParseStmtToAst, mExpandToAst, mTypeTrait,
-     mNLen..mNError, mEqRef: 
+     mNLen..mNError, mEqRef, mSlurp, mStaticExec: 
     nil
   of mRand:
     result = newIntNodeT(math.random(a.getInt.int), n)
diff --git a/compiler/semmagic.nim b/compiler/semmagic.nim
index e6861f2e4..b7e890e67 100644
--- a/compiler/semmagic.nim
+++ b/compiler/semmagic.nim
@@ -14,19 +14,6 @@ proc semIsPartOf(c: PContext, n: PNode, flags: TExprFlags): PNode =
   var r = isPartOf(n[1], n[2])
   result = newIntNodeT(ord(r), n)
   
-proc semSlurp(c: PContext, n: PNode, flags: TExprFlags): PNode = 
-  assert sonsLen(n) == 2
-  var a = expectStringArg(c, n, 0)
-  try:
-    var filename = a.strVal.FindFile
-    var content = readFile(filename)
-    result = newStrNode(nkStrLit, content)
-    result.typ = getSysType(tyString)
-    result.info = n.info
-    c.slurpedFiles.add(a.strVal)
-  except EIO:
-    GlobalError(a.info, errCannotOpenFile, a.strVal)
-
 proc expectIntLit(c: PContext, n: PNode): int =
   let x = c.semConstExpr(c, n)
   case x.kind
@@ -56,7 +43,6 @@ proc semTypeTraits(c: PContext, n: PNode): PNode =
 proc magicsAfterOverloadResolution(c: PContext, n: PNode, 
                                    flags: TExprFlags): PNode =
   case n[0].sym.magic
-  of mSlurp: result = semSlurp(c, n, flags)
   of mIsPartOf: result = semIsPartOf(c, n, flags)
   of mTypeTrait: result = semTypeTraits(c, n)
   of mAstToStr:
diff --git a/compiler/semstmts.nim b/compiler/semstmts.nim
index f10c56880..943c49bad 100755
--- a/compiler/semstmts.nim
+++ b/compiler/semstmts.nim
@@ -27,7 +27,7 @@ proc semWhen(c: PContext, n: PNode, semCheck = true): PNode =
     case it.kind
     of nkElifBranch, nkElifExpr: 
       checkSonsLen(it, 2)
-      var e = semAndEvalConstExpr(c, it.sons[0])
+      var e = semConstExpr(c, it.sons[0])
       if e.kind != nkIntLit: InternalError(n.info, "semWhen")
       if e.intVal != 0 and result == nil:
         setResult(it.sons[1]) 
diff --git a/lib/system.nim b/lib/system.nim
index 5e575c814..4a7ac0654 100755
--- a/lib/system.nim
+++ b/lib/system.nim
@@ -2150,10 +2150,21 @@ proc getTypeInfo*[T](x: T): pointer {.magic: "GetTypeInfo".}
   ## the `typeinfo` module instead.
   
 proc slurp*(filename: string): string {.magic: "Slurp".}
-  ## compiletime ``readFile`` proc for easy `resource`:idx: embedding:
+proc staticRead*(filename: string): string {.magic: "Slurp".}
+  ## compile-time ``readFile`` proc for easy `resource`:idx: embedding:
   ## .. code-block:: nimrod
   ##
-  ##   const myResource = slurp"mydatafile.bin"
+  ##   const myResource = staticRead"mydatafile.bin"
+  ##
+
+proc staticExec*(command: string, input = ""): string {.magic: "StaticExec".}
+  ## executes an external process at compile-time.
+  ## if `input` is not an empty string, it will be passed as a standard input
+  ## to the executed program.
+  ## .. code-block:: nimrod
+  ##
+  ##   const buildInfo = "Revision " & staticExec("git rev-parse HEAD") & 
+  ##                     "\nCompiled on " & staticExec("uname -v")
   ##
 
 proc `+=`*[T](x, y: ordinal[T]) {.magic: "Inc", noSideEffect.}
diff --git a/web/news.txt b/web/news.txt
index d6f2acef2..cdc318a6a 100755
--- a/web/news.txt
+++ b/web/news.txt
@@ -27,6 +27,8 @@ Library Additions
   assignments.
 - Added ``system.eval`` that can execute an anonymous block of code at
   compile time as if was a macro.
+- Added ``system.staticExec`` for compile-time execution of external programs
+- Added ``system.staticRead`` as a synonym for slurp
 - Added ``macros.emit`` that can emit an arbitrary computed string as nimrod
   code during compilation.
 - Added ``strutils.parseEnum``.