summary refs log tree commit diff stats
diff options
context:
space:
mode:
-rw-r--r--compiler/commands.nim2
-rw-r--r--compiler/main.nim3
-rw-r--r--compiler/modules.nim20
-rw-r--r--compiler/nim.nim9
-rw-r--r--compiler/options.nim6
-rw-r--r--compiler/scriptconfig.nim119
-rw-r--r--compiler/vm.nim4
-rw-r--r--compiler/vmhooks.nim12
-rw-r--r--lib/system.nim108
-rw-r--r--lib/system/nimscript.nim152
-rw-r--r--lib/system/sysio.nim11
-rw-r--r--tests/newconfig/tfoo.nim7
-rw-r--r--tests/newconfig/tfoo.nims14
13 files changed, 398 insertions, 69 deletions
diff --git a/compiler/commands.nim b/compiler/commands.nim
index 7a908b270..fc28577aa 100644
--- a/compiler/commands.nim
+++ b/compiler/commands.nim
@@ -641,7 +641,7 @@ proc processSwitch*(pass: TCmdLinePass; p: OptParser) =
 proc processArgument*(pass: TCmdLinePass; p: OptParser;
                       argsCount: var int): bool =
   if argsCount == 0:
-    options.command = p.key
+    if pass != passCmd2: options.command = p.key
   else:
     if pass == passCmd1: options.commandArgs.add p.key
     if argsCount == 1:
diff --git a/compiler/main.nim b/compiler/main.nim
index 47fae7fa7..014605cc9 100644
--- a/compiler/main.nim
+++ b/compiler/main.nim
@@ -116,7 +116,7 @@ proc interactivePasses =
   #incl(gGlobalOptions, optSafeCode)
   #setTarget(osNimrodVM, cpuNimrodVM)
   initDefines()
-  defineSymbol("nimrodvm")
+  defineSymbol("nimscript")
   when hasFFI: defineSymbol("nimffi")
   registerPass(verbosePass)
   registerPass(semPass)
@@ -356,6 +356,7 @@ proc mainCommand* =
     gGlobalOptions.incl(optCaasEnabled)
     msgs.gErrorMax = high(int)  # do not stop after first error
     serve(mainCommand)
+  of "nop": discard
   else:
     rawMessage(errInvalidCommandX, command)
 
diff --git a/compiler/modules.nim b/compiler/modules.nim
index 2d0267c93..ad68e6315 100644
--- a/compiler/modules.nim
+++ b/compiler/modules.nim
@@ -85,6 +85,15 @@ proc resetAllModules* =
   resetPackageCache()
   # for m in cgenModules(): echo "CGEN MODULE FOUND"
 
+proc resetAllModulesHard* =
+  resetPackageCache()
+  gCompiledModules.setLen 0
+  gMemCacheData.setLen 0
+  magicsys.resetSysTypes()
+  # XXX
+  #gOwners = @[]
+  #rangeDestructorProc = nil
+
 proc checkDepMem(fileIdx: int32): TNeedRecompile =
   template markDirty =
     resetModule(fileIdx)
@@ -205,9 +214,8 @@ proc compileProject*(projectFileIdx = -1'i32) =
     compileSystemModule()
     discard compileModule(projectFile, {sfMainModule})
 
-var stdinModule: PSym
-proc makeStdinModule*(): PSym =
-  if stdinModule == nil:
-    stdinModule = newModule(fileInfoIdx"stdin")
-    stdinModule.id = getID()
-  result = stdinModule
+proc makeModule*(filename: string): PSym =
+  result = newModule(fileInfoIdx filename)
+  result.id = getID()
+
+proc makeStdinModule*(): PSym = makeModule"stdin"
diff --git a/compiler/nim.nim b/compiler/nim.nim
index ce9028d53..51f4cae92 100644
--- a/compiler/nim.nim
+++ b/compiler/nim.nim
@@ -1,7 +1,7 @@
 #
 #
 #           The Nim Compiler
-#        (c) Copyright 2013 Andreas Rumpf
+#        (c) Copyright 2015 Andreas Rumpf
 #
 #    See the file "copying.txt", included in this
 #    distribution, for details about the copyright.
@@ -16,7 +16,7 @@ when defined(gcc) and defined(windows):
 import
   commands, lexer, condsyms, options, msgs, nversion, nimconf, ropes,
   extccomp, strutils, os, osproc, platform, main, parseopt, service,
-  nodejs
+  nodejs, scriptconfig
 
 when hasTinyCBackend:
   import tccgen
@@ -54,6 +54,11 @@ proc handleCmdLine() =
     else:
       gProjectPath = getCurrentDir()
     loadConfigs(DefaultConfig) # load all config files
+    let scriptFile = gProjectFull.changeFileExt("nims")
+    if fileExists(scriptFile):
+      runNimScript(scriptFile)
+      # 'nim foo.nims' means to just run the NimScript file and do nothing more:
+      if scriptFile == gProjectFull: return
     # now process command line arguments again, because some options in the
     # command line can overwite the config file's settings
     extccomp.initVars()
diff --git a/compiler/options.nim b/compiler/options.nim
index af1e21e60..adf2017d6 100644
--- a/compiler/options.nim
+++ b/compiler/options.nim
@@ -210,7 +210,7 @@ proc removeTrailingDirSep*(path: string): string =
   else:
     result = path
 
-proc getGeneratedPath: string =
+proc getNimcacheDir*: string =
   result = if nimcacheDir.len > 0: nimcacheDir else: gProjectPath.shortenDir /
                                                          genSubDir
 
@@ -266,7 +266,7 @@ proc toGeneratedFile*(path, ext: string): string =
   ## converts "/home/a/mymodule.nim", "rod" to "/home/a/nimcache/mymodule.rod"
   var (head, tail) = splitPath(path)
   #if len(head) > 0: head = shortenDir(head & dirSep)
-  result = joinPath([getGeneratedPath(), changeFileExt(tail, ext)])
+  result = joinPath([getNimcacheDir(), changeFileExt(tail, ext)])
   #echo "toGeneratedFile(", path, ", ", ext, ") = ", result
 
 when noTimeMachine:
@@ -294,7 +294,7 @@ when noTimeMachine:
 proc completeGeneratedFilePath*(f: string, createSubDir: bool = true): string =
   var (head, tail) = splitPath(f)
   #if len(head) > 0: head = removeTrailingDirSep(shortenDir(head & dirSep))
-  var subdir = getGeneratedPath() # / head
+  var subdir = getNimcacheDir() # / head
   if createSubDir:
     try:
       createDir(subdir)
diff --git a/compiler/scriptconfig.nim b/compiler/scriptconfig.nim
new file mode 100644
index 000000000..1e4fc25af
--- /dev/null
+++ b/compiler/scriptconfig.nim
@@ -0,0 +1,119 @@
+#
+#
+#           The Nim Compiler
+#        (c) Copyright 2015 Andreas Rumpf
+#
+#    See the file "copying.txt", included in this
+#    distribution, for details about the copyright.
+#
+
+## Implements the new configuration system for Nim. Uses Nim as a scripting
+## language.
+
+import
+  ast, modules, passes, passaux, condsyms,
+  options, nimconf, lists, sem, semdata, llstream, vm, vmdef, commands, msgs,
+  os, times
+
+# we support 'cmpIgnoreStyle' natively for efficiency:
+from strutils import cmpIgnoreStyle
+
+proc listDirs(a: VmArgs, filter: set[PathComponent]) =
+  let dir = getString(a, 0)
+  var result: seq[string] = @[]
+  for kind, path in walkDir(dir):
+    if kind in filter: result.add path
+  setResult(a, result)
+
+proc setupVM(module: PSym; scriptName: string): PEvalContext =
+  result = newCtx(module)
+  result.mode = emRepl
+  registerAdditionalOps(result)
+
+  # captured vars:
+  var errorMsg: string
+  var vthisDir = scriptName.splitFile.dir
+
+  template cbconf(name, body) {.dirty.} =
+    result.registerCallback "stdlib.system." & astToStr(name),
+      proc (a: VmArgs) =
+        body
+
+  template cbos(name, body) {.dirty.} =
+    result.registerCallback "stdlib.system." & astToStr(name),
+      proc (a: VmArgs) =
+        try:
+          body
+        except OSError:
+          errorMsg = getCurrentExceptionMsg()
+
+  # Idea: Treat link to file as a file, but ignore link to directory to prevent
+  # endless recursions out of the box.
+  cbos listFiles:
+    listDirs(a, {pcFile, pcLinkToFile})
+  cbos listDirs:
+    listDirs(a, {pcDir})
+  cbos removeDir:
+    os.removeDir getString(a, 0)
+  cbos removeFile:
+    os.removeFile getString(a, 0)
+  cbos createDir:
+    os.createDir getString(a, 0)
+  cbos getOsError:
+    setResult(a, errorMsg)
+  cbos setCurrentDir:
+    os.setCurrentDir getString(a, 0)
+  cbos getCurrentDir:
+    setResult(a, os.getCurrentDir())
+  cbos moveFile:
+    os.moveFile(getString(a, 0), getString(a, 1))
+  cbos getLastModificationTime:
+    setResult(a, toSeconds(getLastModificationTime(getString(a, 0))))
+
+  cbconf thisDir:
+    setResult(a, vthisDir)
+  cbconf put:
+    options.setConfigVar(getString(a, 0), getString(a, 1))
+  cbconf get:
+    setResult(a, options.getConfigVar(a.getString 0))
+  cbconf exists:
+    setResult(a, options.existsConfigVar(a.getString 0))
+  cbconf nimcacheDir:
+    setResult(a, options.getNimcacheDir())
+  cbconf paramStr:
+    setResult(a, os.paramStr(int a.getInt 0))
+  cbconf paramCount:
+    setResult(a, os.paramCount())
+  cbconf cmpIgnoreStyle:
+    setResult(a, strutils.cmpIgnoreStyle(a.getString 0, a.getString 1))
+  cbconf setCommand:
+    options.command = a.getString 0
+  cbconf getCommand:
+    setResult(a, options.command)
+  cbconf switch:
+    processSwitch(a.getString 0, a.getString 1, passPP, unknownLineInfo())
+
+
+proc runNimScript*(scriptName: string) =
+  passes.gIncludeFile = includeModule
+  passes.gImportModule = importModule
+  initDefines()
+
+  defineSymbol("nimscript")
+  defineSymbol("nimconfig")
+  registerPass(semPass)
+  registerPass(evalPass)
+
+  appendStr(searchPaths, options.libpath)
+
+  var m = makeModule(scriptName)
+  incl(m.flags, sfMainModule)
+  vm.globalCtx = setupVM(m, scriptName)
+
+  compileSystemModule()
+  processModule(m, llStreamOpen(scriptName, fmRead), nil)
+
+  # ensure we load 'system.nim' again for the real non-config stuff!
+  resetAllModulesHard()
+  vm.globalCtx = nil
+  initDefines()
diff --git a/compiler/vm.nim b/compiler/vm.nim
index b1d71d73b..57ed8397c 100644
--- a/compiler/vm.nim
+++ b/compiler/vm.nim
@@ -1407,7 +1407,7 @@ include vmops
 # storing&loading the 'globals' environment to get what a component system
 # requires.
 var
-  globalCtx: PCtx
+  globalCtx*: PCtx
 
 proc setupGlobalCtx(module: PSym) =
   if globalCtx.isNil:
@@ -1516,7 +1516,7 @@ proc evalMacroCall*(module: PSym, n, nOrig: PNode, sym: PSym): PNode =
   # return value:
   tos.slots[0].kind = rkNode
   tos.slots[0].node = newNodeIT(nkEmpty, n.info, sym.typ.sons[0])
-  
+
   # setup parameters:
   for i in 1.. <sym.typ.len:
     tos.slots[i] = setupMacroParam(n.sons[i], sym.typ.sons[i])
diff --git a/compiler/vmhooks.nim b/compiler/vmhooks.nim
index 6ec5f6044..5dd27feda 100644
--- a/compiler/vmhooks.nim
+++ b/compiler/vmhooks.nim
@@ -17,7 +17,7 @@ template setX(k, field) {.immediate, dirty.} =
 
 proc setResult*(a: VmArgs; v: BiggestInt) = setX(rkInt, intVal)
 proc setResult*(a: VmArgs; v: BiggestFloat) = setX(rkFloat, floatVal)
-proc setResult*(a: VmArgs; v: bool) = 
+proc setResult*(a: VmArgs; v: bool) =
   let v = v.ord
   setX(rkInt, intVal)
 
@@ -30,6 +30,16 @@ proc setResult*(a: VmArgs; v: string) =
   s[a.ra].node = newNode(nkStrLit)
   s[a.ra].node.strVal = v
 
+proc setResult*(a: VmArgs; v: seq[string]) =
+  var s: seq[TFullReg]
+  move(s, cast[seq[TFullReg]](a.slots))
+  if s[a.ra].kind != rkNode:
+    myreset(s[a.ra])
+    s[a.ra].kind = rkNode
+  var n = newNode(nkBracket)
+  for x in v: n.add newStrNode(nkStrLit, x)
+  s[a.ra].node = n
+
 template getX(k, field) {.immediate, dirty.} =
   doAssert i < a.rc-1
   let s = cast[seq[TFullReg]](a.slots)
diff --git a/lib/system.nim b/lib/system.nim
index 7dae074f3..b2660b1f4 100644
--- a/lib/system.nim
+++ b/lib/system.nim
@@ -190,9 +190,9 @@ proc new*(T: typedesc): auto =
   ## When ``T`` is a ref type then the resulting type will be ``T``,
   ## otherwise it will be ``ref T``.
   when (T is ref):
-      var r: T
+    var r: T
   else:
-      var r: ref T
+    var r: ref T
   new(r)
   return r
 
@@ -347,7 +347,7 @@ const
 
 include "system/inclrtl"
 
-const NoFakeVars* = defined(NimrodVM) ## true if the backend doesn't support \
+const NoFakeVars* = defined(nimscript) ## true if the backend doesn't support \
   ## "fake variables" like 'var EBADF {.importc.}: cint'.
 
 const ArrayDummySize = when defined(cpu16): 10_000 else: 100_000_000
@@ -365,7 +365,7 @@ when not defined(JS):
       data: UncheckedCharArray
     NimString = ptr NimStringDesc
 
-when not defined(JS) and not defined(NimrodVM):
+when not defined(JS) and not defined(nimscript):
   template space(s: PGenericSeq): int {.dirty.} =
     s.reserved and not seqShallowFlag
 
@@ -1260,11 +1260,11 @@ template sysAssert(cond: bool, msg: string) =
       echo "[SYSASSERT] ", msg
       quit 1
 
-const hasAlloc = hostOS != "standalone" or not defined(nogc)
+const hasAlloc = (hostOS != "standalone" or not defined(nogc)) and not defined(nimscript)
 
-when not defined(JS) and not defined(nimrodVm) and hostOS != "standalone":
+when not defined(JS) and not defined(nimscript) and hostOS != "standalone":
   include "system/cgprocs"
-when not defined(JS) and not defined(nimrodVm) and hasAlloc:
+when not defined(JS) and not defined(nimscript) and hasAlloc:
   proc setStackBottom(theStackBottom: pointer) {.compilerRtl, noinline, benign.}
   proc addChar(s: NimString, c: char): NimString {.compilerProc, benign.}
 
@@ -1445,7 +1445,7 @@ proc substr*(s: string, first, last: int): string {.
   ## is used instead: This means ``substr`` can also be used to `cut`:idx:
   ## or `limit`:idx: a string's length.
 
-when not defined(nimrodVM):
+when not defined(nimscript):
   proc zeroMem*(p: pointer, size: Natural) {.importc, noDecl, benign.}
     ## overwrites the contents of the memory at ``p`` with the value 0.
     ## Exactly ``size`` bytes will be overwritten. Like any procedure
@@ -1607,7 +1607,7 @@ proc `$`*(x: int64): string {.magic: "Int64ToStr", noSideEffect.}
   ## The stringify operator for an integer argument. Returns `x`
   ## converted to a decimal string.
 
-when not defined(NimrodVM):
+when not defined(nimscript):
   when not defined(JS) and hasAlloc:
     proc `$` *(x: uint64): string {.noSideEffect.}
       ## The stringify operator for an unsigned integer argument. Returns `x`
@@ -1675,7 +1675,7 @@ const
 
 # GC interface:
 
-when not defined(nimrodVM) and hasAlloc:
+when not defined(nimscript) and hasAlloc:
   proc getOccupiedMem*(): int {.rtl.}
     ## returns the number of bytes that are owned by the process and hold data.
 
@@ -2016,7 +2016,7 @@ proc `&` *[T](x: T, y: seq[T]): seq[T] {.noSideEffect.} =
   for i in 0..y.len-1:
     result[i+1] = y[i]
 
-when not defined(NimrodVM):
+when not defined(nimscript):
   when not defined(JS):
     proc seqToPtr[T](x: seq[T]): pointer {.inline, nosideeffect.} =
       result = cast[pointer](x)
@@ -2235,7 +2235,7 @@ when false:
 
 # ----------------- GC interface ---------------------------------------------
 
-when not defined(nimrodVM) and hasAlloc:
+when not defined(nimscript) and hasAlloc:
   proc GC_disable*() {.rtl, inl, benign.}
     ## disables the GC. If called n-times, n calls to `GC_enable` are needed to
     ## reactivate the GC. Note that in most circumstances one should only disable
@@ -2457,10 +2457,10 @@ else:
     if x < 0: -x else: x
 {.pop.}
 
-when not defined(JS): #and not defined(NimrodVM):
+when not defined(JS): #and not defined(nimscript):
   {.push stack_trace: off, profiler:off.}
 
-  when not defined(NimrodVM) and not defined(nogc):
+  when not defined(nimscript) and not defined(nogc):
     proc initGC()
     when not defined(boehmgc) and not defined(useMalloc) and not defined(gogc):
       proc initAllocator() {.inline.}
@@ -2488,13 +2488,19 @@ when not defined(JS): #and not defined(NimrodVM):
     strDesc.kind = tyString
     strDesc.flags = {ntfAcyclic}
 
-  include "system/ansi_c"
+  when not defined(nimscript):
+    include "system/ansi_c"
 
-  proc cmp(x, y: string): int =
-    result = int(c_strcmp(x, y))
+    proc cmp(x, y: string): int =
+      result = int(c_strcmp(x, y))
+  else:
+    proc cmp(x, y: string): int =
+      if x < y: result = -1
+      elif x > y: result = 1
+      else: result = 0
 
   const pccHack = if defined(pcc): "_" else: "" # Hack for PCC
-  when not defined(NimrodVM):
+  when not defined(nimscript):
     when defined(windows):
       # work-around C's sucking abstraction:
       # BUGFIX: stdin and stdout should be binary files!
@@ -2536,14 +2542,15 @@ when not defined(JS): #and not defined(NimrodVM):
 
     {.deprecated: [TFile: File, TFileHandle: FileHandle, TFileMode: FileMode].}
 
-    # text file handling:
-    var
-      stdin* {.importc: "stdin", header: "<stdio.h>".}: File
-        ## The standard input stream.
-      stdout* {.importc: "stdout", header: "<stdio.h>".}: File
-        ## The standard output stream.
-      stderr* {.importc: "stderr", header: "<stdio.h>".}: File
-        ## The standard error stream.
+    when not defined(nimscript):
+      # text file handling:
+      var
+        stdin* {.importc: "stdin", header: "<stdio.h>".}: File
+          ## The standard input stream.
+        stdout* {.importc: "stdout", header: "<stdio.h>".}: File
+          ## The standard output stream.
+        stderr* {.importc: "stderr", header: "<stdio.h>".}: File
+          ## The standard error stream.
 
     when defined(useStdoutAsStdmsg):
       template stdmsg*: File = stdout
@@ -2738,7 +2745,7 @@ when not defined(JS): #and not defined(NimrodVM):
         inc(i)
       dealloc(a)
 
-  when not defined(NimrodVM):
+  when not defined(nimscript):
     proc atomicInc*(memLoc: var int, x: int = 1): int {.inline,
       discardable, benign.}
       ## atomic increment of `memLoc`. Returns the value after the operation.
@@ -2749,27 +2756,27 @@ when not defined(JS): #and not defined(NimrodVM):
 
     include "system/atomics"
 
-  type
-    PSafePoint = ptr TSafePoint
-    TSafePoint {.compilerproc, final.} = object
-      prev: PSafePoint # points to next safe point ON THE STACK
-      status: int
-      context: C_JmpBuf
-      hasRaiseAction: bool
-      raiseAction: proc (e: ref Exception): bool {.closure.}
-    SafePoint = TSafePoint
-#  {.deprecated: [TSafePoint: SafePoint].}
+    type
+      PSafePoint = ptr TSafePoint
+      TSafePoint {.compilerproc, final.} = object
+        prev: PSafePoint # points to next safe point ON THE STACK
+        status: int
+        context: C_JmpBuf
+        hasRaiseAction: bool
+        raiseAction: proc (e: ref Exception): bool {.closure.}
+      SafePoint = TSafePoint
+  #  {.deprecated: [TSafePoint: SafePoint].}
 
   when declared(initAllocator):
     initAllocator()
   when hasThreadSupport:
     include "system/syslocks"
     when hostOS != "standalone": include "system/threads"
-  elif not defined(nogc) and not defined(NimrodVM):
+  elif not defined(nogc) and not defined(nimscript):
     when not defined(useNimRtl) and not defined(createNimRtl): initStackBottom()
     when declared(initGC): initGC()
 
-  when not defined(NimrodVM):
+  when not defined(nimscript):
     proc setControlCHook*(hook: proc () {.noconv.} not nil)
       ## allows you to override the behaviour of your application when CTRL+C
       ## is pressed. Only one such hook is supported.
@@ -2798,9 +2805,9 @@ when not defined(JS): #and not defined(NimrodVM):
     {.pop.} # stack trace
   {.pop.} # stack trace
 
-  when hostOS != "standalone" and not defined(NimrodVM):
+  when hostOS != "standalone" and not defined(nimscript):
     include "system/dyncalls"
-  when not defined(NimrodVM):
+  when not defined(nimscript):
     include "system/sets"
 
     when defined(gogc):
@@ -2875,11 +2882,11 @@ when not defined(JS): #and not defined(NimrodVM):
       var res = TaintedString(newStringOfCap(80))
       while f.readLine(res): yield res
 
-  when not defined(NimrodVM) and hasAlloc:
+  when not defined(nimscript) and hasAlloc:
     include "system/assign"
     include "system/repr"
 
-  when hostOS != "standalone" and not defined(NimrodVM):
+  when hostOS != "standalone" and not defined(nimscript):
     proc getCurrentException*(): ref Exception {.compilerRtl, inl, benign.} =
       ## retrieves the current exception; if there is none, nil is returned.
       result = currException
@@ -2907,14 +2914,14 @@ when not defined(JS): #and not defined(NimrodVM):
       currException = exc
 
   {.push stack_trace: off, profiler:off.}
-  when defined(endb) and not defined(NimrodVM):
+  when defined(endb) and not defined(nimscript):
     include "system/debugger"
 
   when defined(profiler) or defined(memProfiler):
     include "system/profiler"
   {.pop.} # stacktrace
 
-  when not defined(NimrodVM):
+  when not defined(nimscript):
     proc likely*(val: bool): bool {.importc: "likely", nodecl, nosideeffect.}
       ## Hints the optimizer that `val` is likely going to be true.
       ##
@@ -2992,7 +2999,7 @@ elif defined(JS):
   when defined(JS):
     include "system/jssys"
     include "system/reprjs"
-  elif defined(NimrodVM):
+  elif defined(nimscript):
     proc cmp(x, y: string): int =
       if x == y: return 0
       if x < y: return -1
@@ -3305,7 +3312,7 @@ proc shallow*[T](s: var seq[T]) {.noSideEffect, inline.} =
   ## marks a sequence `s` as `shallow`:idx:. Subsequent assignments will not
   ## perform deep copies of `s`. This is only useful for optimization
   ## purposes.
-  when not defined(JS) and not defined(NimrodVM):
+  when not defined(JS) and not defined(nimscript):
     var s = cast[PGenericSeq](s)
     s.reserved = s.reserved or seqShallowFlag
 
@@ -3313,7 +3320,7 @@ proc shallow*(s: var string) {.noSideEffect, inline.} =
   ## marks a string `s` as `shallow`:idx:. Subsequent assignments will not
   ## perform deep copies of `s`. This is only useful for optimization
   ## purposes.
-  when not defined(JS) and not defined(NimrodVM):
+  when not defined(JS) and not defined(nimscript):
     var s = cast[PGenericSeq](s)
     s.reserved = s.reserved or seqShallowFlag
 
@@ -3402,7 +3409,7 @@ proc locals*(): RootObj {.magic: "Plugin", noSideEffect.} =
   ##   # -> B is 1
   discard
 
-when hasAlloc and not defined(NimrodVM) and not defined(JS):
+when hasAlloc and not defined(nimscript) and not defined(JS):
   proc deepCopy*[T](x: var T, y: T) {.noSideEffect, magic: "DeepCopy".} =
     ## performs a deep copy of `x`. This is also used by the code generator
     ## for the implementation of ``spawn``.
@@ -3444,3 +3451,6 @@ proc xlen*[T](x: seq[T]): int {.magic: "XLenSeq", noSideEffect.} =
   discard
 
 {.pop.} #{.push warning[GcMem]: off, warning[Uninit]: off.}
+
+when defined(nimconfig):
+  include "system/nimscript"
diff --git a/lib/system/nimscript.nim b/lib/system/nimscript.nim
new file mode 100644
index 000000000..6f5977869
--- /dev/null
+++ b/lib/system/nimscript.nim
@@ -0,0 +1,152 @@
+#
+#
+#            Nim's Runtime Library
+#        (c) Copyright 2015 Andreas Rumpf
+#
+#    See the file "copying.txt", included in this
+#    distribution, for details about the copyright.
+#
+
+
+# Nim's configuration system now uses Nim for scripting. This module provides
+# a few things that are required for this to work.
+
+template builtin = discard
+
+proc listDirs*(dir: string): seq[string] = builtin
+proc listFiles*(dir: string): seq[string] = builtin
+
+proc removeDir(dir: string) = builtin
+proc removeFile(dir: string) = builtin
+proc moveFile(src, dest: string) = builtin
+proc createDir(dir: string) = builtin
+proc getOsError: string = builtin
+proc setCurrentDir(dir: string) = builtin
+proc getCurrentDir(): string = builtin
+proc paramStr*(i: int): string = builtin
+proc paramCount*(): int = builtin
+
+proc switch*(key: string, val="") = builtin
+proc getCommand*(): string = builtin
+proc setCommand*(cmd: string) = builtin
+proc cmpIgnoreStyle(a, b: string): int = builtin
+
+proc strip(s: string): string =
+  var i = 0
+  while s[i] in {' ', '\c', '\L'}: inc i
+  result = s.substr(i)
+
+template `--`*(key, val: untyped) = switch(astToStr(key), strip astToStr(val))
+template `--`*(key: untyped) = switch(astToStr(key), "")
+
+type
+  ScriptMode* {.pure.} = enum
+    Silent,
+    Verbose,
+    Whatif
+
+var
+  mode*: ScriptMode ## Set this to influence how mkDir, rmDir, rmFile etc.
+                    ## behave
+
+template checkOsError =
+  let err = getOsError()
+  if err.len > 0: raise newException(OSError, err)
+
+template log(msg: string, body: untyped) =
+  if mode == ScriptMode.Verbose or mode == ScriptMode.Whatif:
+    echo "[NimScript] ", msg
+  if mode != ScriptMode.WhatIf:
+    body
+
+proc rmDir*(dir: string) {.raises: [OSError].} =
+  log "rmDir: " & dir:
+    removeDir dir
+    checkOsError()
+
+proc rmFile*(dir: string) {.raises: [OSError].} =
+  log "rmFile: " & dir:
+    removeFile dir
+    checkOsError()
+
+proc mkDir*(dir: string) {.raises: [OSError].} =
+  log "mkDir: " & dir:
+    createDir dir
+    checkOsError()
+
+proc mvFile*(`from`, to: string) {.raises: [OSError].} =
+  log "mvFile: " & `from` & ", " & to:
+    moveFile `from`, to
+    checkOsError()
+
+proc exec*(command: string, input = "", cache = "") =
+  ## Executes an external process.
+  log "exec: " & command:
+    echo staticExec(command, input, cache)
+
+proc put*(key, value: string) =
+  ## Sets a configuration 'key' like 'gcc.options.always' to its value.
+  builtin
+
+proc get*(key: string): string =
+  ## Retrieves a configuration 'key' like 'gcc.options.always'.
+  builtin
+
+proc exists*(key: string): bool =
+  ## Checks for the existance of a configuration 'key'
+  ## like 'gcc.options.always'.
+  builtin
+
+proc nimcacheDir*(): string =
+  ## Retrieves the location of 'nimcache'.
+  builtin
+
+proc thisDir*(): string =
+  ## Retrieves the location of the current ``nims`` script file.
+  builtin
+
+proc cd*(dir: string) {.raises: [OSError].} =
+  ## Changes the current directory.
+  ##
+  ## The change is permanent for the rest of the execution, since this is just
+  ## a shortcut for `os.setCurrentDir()
+  ## <http://nim-lang.org/os.html#setCurrentDir,string>`_ . Use the `withDir()
+  ## <#withDir>`_ template if you want to perform a temporary change only.
+  setCurrentDir(dir)
+  checkOsError()
+
+template withDir*(dir: string; body: untyped): untyped =
+  ## Changes the current directory temporarily.
+  ##
+  ## If you need a permanent change, use the `cd() <#cd>`_ proc. Usage example:
+  ##
+  ## .. code-block:: nimrod
+  ##   withDir "foo":
+  ##     # inside foo
+  ##   #back to last dir
+  var curDir = getCurrentDir()
+  try:
+    cd(dir)
+    body
+  finally:
+    cd(curDir)
+
+template `==?`(a, b: string): bool = cmpIgnoreStyle(a, b) == 0
+
+proc writeTask(name, desc: string) =
+  if desc.len > 0:
+    var spaces = " "
+    for i in 0 ..< 20 - name.len: spaces.add ' '
+    echo name, spaces, desc
+
+template task*(name: untyped; description: string; body: untyped): untyped =
+  ## Defines a task. Hidden tasks are supported via an empty description.
+  proc `name Task`() = body
+
+  let cmd = getCommand()
+  if cmd.len == 0 or cmd ==? "help" or cmd == "nop":
+    setCommand "nop"
+    writeTask(astToStr(name), description)
+  elif cmd ==? astToStr(name):
+    setCommand "nop"
+    `name Task`()
diff --git a/lib/system/sysio.nim b/lib/system/sysio.nim
index 5464ee126..b3316920e 100644
--- a/lib/system/sysio.nim
+++ b/lib/system/sysio.nim
@@ -182,7 +182,10 @@ proc readAllFile(file: File): string =
 proc readAll(file: File): TaintedString =
   # Separate handling needed because we need to buffer when we
   # don't know the overall length of the File.
-  let len = if file != stdin: rawFileSize(file) else: -1
+  when declared(stdin):
+    let len = if file != stdin: rawFileSize(file) else: -1
+  else:
+    let len = rawFileSize(file)
   if len > 0:
     result = readAllFile(file, len).TaintedString
   else:
@@ -216,9 +219,9 @@ proc writeLine[Ty](f: File, x: varargs[Ty, `$`]) =
   for i in items(x): write(f, i)
   write(f, "\n")
 
-
-proc rawEcho(x: string) {.inline, compilerproc.} = write(stdout, x)
-proc rawEchoNL() {.inline, compilerproc.} = write(stdout, "\n")
+when declared(stdout):
+  proc rawEcho(x: string) {.inline, compilerproc.} = write(stdout, x)
+  proc rawEchoNL() {.inline, compilerproc.} = write(stdout, "\n")
 
 # interface to the C procs:
 
diff --git a/tests/newconfig/tfoo.nim b/tests/newconfig/tfoo.nim
new file mode 100644
index 000000000..0ace7c88a
--- /dev/null
+++ b/tests/newconfig/tfoo.nim
@@ -0,0 +1,7 @@
+discard """
+  cmd: "nim default $file"
+  output: '''hello world!'''
+  msg: '''[NimScript] exec: gcc -v'''
+"""
+
+echo "hello world!"
diff --git a/tests/newconfig/tfoo.nims b/tests/newconfig/tfoo.nims
new file mode 100644
index 000000000..90205cddb
--- /dev/null
+++ b/tests/newconfig/tfoo.nims
@@ -0,0 +1,14 @@
+
+mode = ScriptMode.Whatif
+
+exec "gcc -v"
+
+--forceBuild
+
+task listDirs, "lists every subdirectory":
+  for x in listDirs("."):
+    echo "DIR ", x
+
+task default, "default target":
+  setCommand "c"
+