summary refs log tree commit diff stats
path: root/compiler
diff options
context:
space:
mode:
Diffstat (limited to 'compiler')
-rw-r--r--compiler/cgen.nim9
-rw-r--r--compiler/extccomp.nim11
-rw-r--r--compiler/jsgen.nim4
-rw-r--r--compiler/main.nim2
-rw-r--r--compiler/modules.nim29
-rw-r--r--compiler/nim.nim9
-rw-r--r--compiler/nimsuggest/nimsuggest.nim210
-rw-r--r--compiler/options.nim20
-rw-r--r--compiler/passes.nim6
-rw-r--r--compiler/sigmatch.nim5
-rw-r--r--compiler/suggest.nim116
-rw-r--r--compiler/vm.nim4
12 files changed, 307 insertions, 118 deletions
diff --git a/compiler/cgen.nim b/compiler/cgen.nim
index da9c6f653..4b0bac28a 100644
--- a/compiler/cgen.nim
+++ b/compiler/cgen.nim
@@ -676,8 +676,11 @@ proc genProcAux(m: BModule, prc: PSym) =
   closureSetup(p, prc)
   genStmts(p, prc.getBody) # modifies p.locals, p.init, etc.
   var generatedProc: Rope
+  if sfNoReturn in prc.flags:
+    if hasDeclspec in extccomp.CC[extccomp.cCompiler].props:
+      header = "__declspec(noreturn) " & header
   if sfPure in prc.flags:
-    if hasNakedDeclspec in extccomp.CC[extccomp.cCompiler].props:
+    if hasDeclspec in extccomp.CC[extccomp.cCompiler].props:
       header = "__declspec(naked) " & header
     generatedProc = rfmt(nil, "$N$1 {$n$2$3$4}$N$N",
                          header, p.s(cpsLocals), p.s(cpsInit), p.s(cpsStmts))
@@ -720,8 +723,10 @@ proc genProcPrototype(m: BModule, sym: PSym) =
     var header = genProcHeader(m, sym)
     if sym.typ.callConv != ccInline and crossesCppBoundary(m, sym):
       header = "extern \"C\" " & header
-    if sfPure in sym.flags and hasNakedAttribute in CC[cCompiler].props:
+    if sfPure in sym.flags and hasAttribute in CC[cCompiler].props:
       header.add(" __attribute__((naked))")
+    if sfNoReturn in sym.flags and hasAttribute in CC[cCompiler].props:
+      header.add(" __attribute__((noreturn))")
     add(m.s[cfsProcHeaders], rfmt(nil, "$1;$n", header))
 
 proc genProcNoForward(m: BModule, prc: PSym) =
diff --git a/compiler/extccomp.nim b/compiler/extccomp.nim
index 26f0318ee..186a3884d 100644
--- a/compiler/extccomp.nim
+++ b/compiler/extccomp.nim
@@ -26,8 +26,8 @@ type
     hasAssume,                # CC has __assume (Visual C extension)
     hasGcGuard,               # CC supports GC_GUARD to keep stack roots
     hasGnuAsm,                # CC's asm uses the absurd GNU assembler syntax
-    hasNakedDeclspec,         # CC has __declspec(naked)
-    hasNakedAttribute         # CC has __attribute__((naked))
+    hasDeclspec,              # CC has __declspec(X)
+    hasAttribute,             # CC has __attribute__((X))
   TInfoCCProps* = set[TInfoCCProp]
   TInfoCC* = tuple[
     name: string,        # the short name of the compiler
@@ -85,7 +85,7 @@ compiler gcc:
     structStmtFmt: "$1 $3 $2 ", # struct|union [packed] $name
     packedPragma: "__attribute__((__packed__))",
     props: {hasSwitchRange, hasComputedGoto, hasCpp, hasGcGuard, hasGnuAsm,
-            hasNakedAttribute})
+            hasAttribute})
 
 # LLVM Frontend for GCC/G++
 compiler llvmGcc:
@@ -127,7 +127,7 @@ compiler vcc:
     asmStmtFrmt: "__asm{$n$1$n}$n",
     structStmtFmt: "$3$n$1 $2",
     packedPragma: "#pragma pack(1)",
-    props: {hasCpp, hasAssume, hasNakedDeclspec})
+    props: {hasCpp, hasAssume, hasDeclspec})
 
 # Intel C/C++ Compiler
 compiler icl:
@@ -668,7 +668,8 @@ proc callCCompiler*(projectfile: string) =
       it = PStrEntry(it.next)
 
     if optGenStaticLib in gGlobalOptions:
-      linkCmd = CC[c].buildLib % ["libfile", (libNameTmpl() % gProjectName),
+      let name = splitFile(gProjectName).name
+      linkCmd = CC[c].buildLib % ["libfile", (libNameTmpl() % name),
                                   "objfiles", objfiles]
     else:
       var linkerExe = getConfigVar(c, ".linkerexe")
diff --git a/compiler/jsgen.nim b/compiler/jsgen.nim
index 704713243..5c7071498 100644
--- a/compiler/jsgen.nim
+++ b/compiler/jsgen.nim
@@ -1052,11 +1052,13 @@ proc genArg(p: PProc, n: PNode, r: var TCompRes) =
 
 proc genArgs(p: PProc, n: PNode, r: var TCompRes) =
   add(r.res, "(")
+  var hasArgs = false
   for i in countup(1, sonsLen(n) - 1):
     let it = n.sons[i]
     if it.typ.isCompileTimeOnly: continue
-    if i > 1: add(r.res, ", ")
+    if hasArgs: add(r.res, ", ")
     genArg(p, it, r)
+    hasArgs = true
   add(r.res, ")")
   r.kind = resExpr
 
diff --git a/compiler/main.nim b/compiler/main.nim
index 0c80c19b7..a01b6fe4f 100644
--- a/compiler/main.nim
+++ b/compiler/main.nim
@@ -63,7 +63,7 @@ proc commandCompileToC =
   compileProject()
   cgenWriteModules()
   if gCmd != cmdRun:
-    extccomp.callCCompiler(if gProjectName == "-": "stdinfile" else: changeFileExt(gProjectFull, ""))
+    extccomp.callCCompiler(changeFileExt(gProjectFull, ""))
 
   if isServing:
     # caas will keep track only of the compilation commands
diff --git a/compiler/modules.nim b/compiler/modules.nim
index a2b739efc..2fa46f356 100644
--- a/compiler/modules.nim
+++ b/compiler/modules.nim
@@ -10,8 +10,8 @@
 ## implements the module handling
 
 import
-  ast, astalgo, magicsys, crc, rodread, msgs, cgendata, sigmatch, options, 
-  idents, os, lexer, idgen, passes, syntaxes
+  ast, astalgo, magicsys, crc, rodread, msgs, cgendata, sigmatch, options,
+  idents, os, lexer, idgen, passes, syntaxes, llstream
 
 type
   TNeedRecompile* = enum Maybe, No, Yes, Probing, Recompiled
@@ -39,12 +39,12 @@ template crc(x: PSym): expr =
 
 proc crcChanged(fileIdx: int32): bool =
   internalAssert fileIdx >= 0 and fileIdx < gMemCacheData.len
-  
+
   template updateStatus =
     gMemCacheData[fileIdx].crcStatus = if result: crcHasChanged
                                        else: crcNotChanged
     # echo "TESTING CRC: ", fileIdx.toFilename, " ", result
-  
+
   case gMemCacheData[fileIdx].crcStatus:
   of crcHasChanged:
     result = true
@@ -96,7 +96,7 @@ proc checkDepMem(fileIdx: int32): TNeedRecompile =
   if optForceFullMake in gGlobalOptions or
      crcChanged(fileIdx):
        markDirty
-  
+
   if gMemCacheData[fileIdx].deps != nil:
     gMemCacheData[fileIdx].needsRecompile = Probing
     for dep in gMemCacheData[fileIdx].deps:
@@ -104,30 +104,30 @@ proc checkDepMem(fileIdx: int32): TNeedRecompile =
       if d in {Yes, Recompiled}:
         # echo fileIdx.toFilename, " depends on ", dep.toFilename, " ", d
         markDirty
-  
+
   gMemCacheData[fileIdx].needsRecompile = No
   return No
 
 proc newModule(fileIdx: int32): PSym =
   # We cannot call ``newSym`` here, because we have to circumvent the ID
-  # mechanism, which we do in order to assign each module a persistent ID. 
+  # mechanism, which we do in order to assign each module a persistent ID.
   new(result)
   result.id = - 1             # for better error checking
   result.kind = skModule
   let filename = fileIdx.toFullPath
   result.name = getIdent(splitFile(filename).name)
-  if result.name.s != "-" and not isNimIdentifier(result.name.s):
+  if not isNimIdentifier(result.name.s):
     rawMessage(errInvalidModuleName, result.name.s)
-  
+
   result.info = newLineInfo(fileIdx, 1, 1)
   result.owner = newSym(skPackage, getIdent(getPackageName(filename)), nil,
                         result.info)
   result.position = fileIdx
-  
+
   growCache gMemCacheData, fileIdx
   growCache gCompiledModules, fileIdx
   gCompiledModules[result.position] = result
-  
+
   incl(result.flags, sfUsed)
   initStrTable(result.tab)
   strTableAdd(result.tab, result) # a module knows itself
@@ -143,12 +143,15 @@ proc compileModule*(fileIdx: int32, flags: TSymFlags): PSym =
     result.flags = result.flags + flags
     if gCmd in {cmdCompileToC, cmdCompileToCpp, cmdCheck, cmdIdeTools}:
       rd = handleSymbolFile(result)
-      if result.id < 0: 
+      if result.id < 0:
         internalError("handleSymbolFile should have set the module\'s ID")
         return
     else:
       result.id = getID()
-    processModule(result, nil, rd)
+    if sfMainModule in flags and gProjectIsStdin:
+      processModule(result, llStreamOpen(stdin), rd)
+    else:
+      processModule(result, nil, rd)
     if optCaasEnabled in gGlobalOptions:
       gMemCacheData[fileIdx].compiledAt = gLastCmdTime
       gMemCacheData[fileIdx].needsRecompile = Recompiled
diff --git a/compiler/nim.nim b/compiler/nim.nim
index b8ba2c6da..89db22e8f 100644
--- a/compiler/nim.nim
+++ b/compiler/nim.nim
@@ -38,7 +38,12 @@ proc handleCmdLine() =
   else:
     # Process command line arguments:
     processCmdLine(passCmd1, "")
-    if gProjectName != "":
+    if gProjectName == "-":
+      gProjectName = "stdinfile"
+      gProjectFull = "stdinfile"
+      gProjectPath = getCurrentDir()
+      gProjectIsStdin = true
+    elif gProjectName != "":
       try:
         gProjectFull = canonicalizePath(gProjectName)
       except OSError:
@@ -61,8 +66,6 @@ proc handleCmdLine() =
         if gCmd == cmdRun:
           tccgen.run(commands.arguments)
       if optRun in gGlobalOptions:
-        if gProjectName == "-":
-          gProjectFull = "stdinfile"
         if gCmd == cmdCompileToJS:
           var ex: string
           if options.outFile.len > 0:
diff --git a/compiler/nimsuggest/nimsuggest.nim b/compiler/nimsuggest/nimsuggest.nim
index 8285d81d9..2c785d118 100644
--- a/compiler/nimsuggest/nimsuggest.nim
+++ b/compiler/nimsuggest/nimsuggest.nim
@@ -9,9 +9,17 @@
 
 ## Nimsuggest is a tool that helps to give editors IDE like capabilities.
 
-import strutils, os, parseopt, parseUtils
+import strutils, os, parseopt, parseutils, sequtils, net
+# Do NOT import suggest. It will lead to wierd bugs with
+# suggestionResultHook, because suggest.nim is included by sigmatch.
+# So we import that one instead.
 import options, commands, modules, sem, passes, passaux, msgs, nimconf,
-  extccomp, condsyms, lists, net, rdstdin
+  extccomp, condsyms, lists, net, rdstdin, sexp, sigmatch, ast
+
+when defined(windows):
+  import winlean
+else:
+  import posix
 
 const Usage = """
 Nimsuggest - Tool to give every editor IDE like capabilities for Nim
@@ -23,17 +31,20 @@ Options:
   --address:HOST          binds to that address, by default ""
   --stdin                 read commands from stdin and write results to
                           stdout instead of using sockets
+  --epc                   use emacs epc mode
 
 The server then listens to the connection and takes line-based commands.
 
 In addition, all command line options of Nim that do not affect code generation
 are supported.
 """
+type
+  Mode = enum mstdin, mtcp, mepc
 
 var
   gPort = 6000.Port
   gAddress = ""
-  gUseStdin: bool
+  gMode: Mode
 
 const
   seps = {':', ';', ' ', '\t'}
@@ -42,6 +53,9 @@ const
          "type 'debug' to toggle debug mode on/off\n" &
          "type 'terse' to toggle terse mode on/off"
 
+type
+  EUnexpectedCommand = object of Exception
+
 proc parseQuoted(cmd: string; outp: var string; start: int): int =
   var i = start
   i += skipWhitespace(cmd, i)
@@ -51,7 +65,95 @@ proc parseQuoted(cmd: string; outp: var string; start: int): int =
     i += parseUntil(cmd, outp, seps, i)
   result = i
 
-proc action(cmd: string) =
+proc sexp(s: IdeCmd): SexpNode = sexp($s)
+
+proc sexp(s: TSymKind): SexpNode = sexp($s)
+
+proc sexp(s: Suggest): SexpNode =
+  # If you change the oder here, make sure to change it over in
+  # nim-mode.el too.
+  result = convertSexp([
+    s.section,
+    s.symkind,
+    s.qualifiedPath.map(newSString),
+    s.filePath,
+    s.forth,
+    s.line,
+    s.column,
+    s.doc
+  ])
+
+proc sexp(s: seq[Suggest]): SexpNode =
+  result = newSList()
+  for sug in s:
+    result.add(sexp(sug))
+
+proc listEPC(): SexpNode =
+  let
+    argspecs = sexp("file line column dirtyfile".split(" ").map(newSSymbol))
+    docstring = sexp("line starts at 1, column at 0, dirtyfile is optional")
+  result = newSList()
+  for command in ["sug", "con", "def", "use"]:
+    let
+      cmd = sexp(command)
+      methodDesc = newSList()
+    methodDesc.add(cmd)
+    methodDesc.add(argspecs)
+    methodDesc.add(docstring)
+    result.add(methodDesc)
+
+proc execute(cmd: IdeCmd, file, dirtyfile: string, line, col: int) =
+  gIdeCmd = cmd
+  if cmd == ideUse:
+    modules.resetAllModules()
+  var isKnownFile = true
+  let dirtyIdx = file.fileInfoIdx(isKnownFile)
+
+  if dirtyfile.len != 0: msgs.setDirtyFile(dirtyIdx, dirtyfile)
+  else: msgs.setDirtyFile(dirtyIdx, nil)
+
+  resetModule dirtyIdx
+  if dirtyIdx != gProjectMainIdx:
+    resetModule gProjectMainIdx
+
+  gTrackPos = newLineInfo(dirtyIdx, line, col)
+  gErrorCounter = 0
+  if not isKnownFile:
+    compileProject()
+  compileProject(dirtyIdx)
+
+proc executeEPC(cmd: IdeCmd, args: SexpNode) =
+  let
+    file = args[0].getStr
+    line = args[1].getNum
+    column = args[2].getNum
+  var dirtyfile = ""
+  if len(args) > 3:
+    dirtyfile = args[3].getStr(nil)
+  execute(cmd, file, dirtyfile, int(line), int(column))
+
+proc returnEPC(socket: var Socket, uid: BiggestInt, s: SexpNode, return_symbol = "return") =
+  let response = $convertSexp([newSSymbol(return_symbol), uid, s])
+  socket.send(toHex(len(response), 6))
+  socket.send(response)
+
+proc connectToNextFreePort(server: Socket, host: string, start = 30000): int =
+  result = start
+  while true:
+    try:
+      server.bindaddr(Port(result), host)
+      return
+    except OsError:
+      when defined(windows):
+        let checkFor = WSAEADDRINUSE.OSErrorCode
+      else:
+        let checkFor = EADDRINUSE.OSErrorCode
+      if osLastError() != checkFor:
+        raise getCurrentException()
+      else:
+        result += 1
+
+proc parseCmdLine(cmd: string) =
   template toggle(sw) =
     if sw in gGlobalOptions:
       excl(gGlobalOptions, sw)
@@ -69,9 +171,7 @@ proc action(cmd: string) =
   of "sug": gIdeCmd = ideSug
   of "con": gIdeCmd = ideCon
   of "def": gIdeCmd = ideDef
-  of "use":
-    modules.resetAllModules()
-    gIdeCmd = ideUse
+  of "use": gIdeCmd = ideUse
   of "quit": quit()
   of "debug": toggle optIdeDebug
   of "terse": toggle optIdeTerse
@@ -88,35 +188,18 @@ proc action(cmd: string) =
   i += skipWhile(cmd, seps, i)
   i += parseInt(cmd, col, i)
 
-  var isKnownFile = true
-  if orig.len == 0: err()
-  let dirtyIdx = orig.fileInfoIdx(isKnownFile)
-
-  if dirtyfile.len != 0: msgs.setDirtyFile(dirtyIdx, dirtyfile)
-  else: msgs.setDirtyFile(dirtyIdx, nil)
-
-  resetModule dirtyIdx
-  if dirtyIdx != gProjectMainIdx:
-    resetModule gProjectMainIdx
-  gTrackPos = newLineInfo(dirtyIdx, line, col-1)
-  #echo dirtyfile, gDirtyBufferIdx, " project ", gProjectMainIdx
-  gErrorCounter = 0
-  if not isKnownFile:
-    compileProject(dirtyIdx)
-  else:
-    compileProject()
+  execute(gIdeCmd, orig, dirtyfile, line, col-1)
 
 proc serve() =
-  # do not stop after the first error:
-  msgs.gErrorMax = high(int)
-  if gUseStdin:
+  case gMode:
+  of mstdin:
     echo Help
     var line = ""
     while readLineFromStdin("> ", line):
-      action line
+      parseCmdLine line
       echo ""
       flushFile(stdout)
-  else:
+  of mtcp:
     var server = newSocket()
     server.bindAddr(gPort, gAddress)
     var inp = "".TaintedString
@@ -130,10 +213,55 @@ proc serve() =
       accept(server, stdoutSocket)
 
       stdoutSocket.readLine(inp)
-      action inp.string
+      parseCmdLine inp.string
 
       stdoutSocket.send("\c\L")
       stdoutSocket.close()
+  of mepc:
+    var server = newSocket()
+    let port = connectToNextFreePort(server, "localhost")
+    var inp = "".TaintedString
+    server.listen()
+    echo(port)
+    var client = newSocket()
+    # Wait for connection
+    accept(server, client)
+    while true:
+      var sizeHex = ""
+      if client.recv(sizeHex, 6) != 6:
+        raise newException(ValueError, "didn't get all the hexbytes")
+      var size = 0
+      if parseHex(sizeHex, size) == 0:
+        raise newException(ValueError, "invalid size hex: " & $sizeHex)
+      var messageBuffer = ""
+      if client.recv(messageBuffer, size) != size:
+        raise newException(ValueError, "didn't get all the bytes")
+      let
+        message = parseSexp($messageBuffer)
+        messageType = message[0].getSymbol
+      case messageType:
+      of "call":
+        var results: seq[Suggest] = @[]
+        suggestionResultHook = proc (s: Suggest) =
+          results.add(s)
+
+        let
+          uid = message[1].getNum
+          cmd = parseIdeCmd(message[2].getSymbol)
+          args = message[3]
+        executeEPC(cmd, args)
+        returnEPC(client, uid, sexp(results))
+      of "return":
+        raise newException(EUnexpectedCommand, "no return expected")
+      of "return-error":
+        raise newException(EUnexpectedCommand, "no return expected")
+      of "epc-error":
+        stderr.writeln("recieved epc error: " & $messageBuffer)
+        raise newException(IOError, "epc error")
+      of "methods":
+        returnEPC(client, message[1].getNum, listEPC())
+      else:
+        raise newException(EUnexpectedCommand, "unexpected call: " & messageType)
 
 proc mainCommand =
   registerPass verbosePass
@@ -147,19 +275,29 @@ proc mainCommand =
     # current path is always looked first for modules
     prependStr(searchPaths, gProjectPath)
 
+  # do not stop after the first error:
+  msgs.gErrorMax = high(int)
+  compileProject()
   serve()
 
 proc processCmdLine*(pass: TCmdLinePass, cmd: string) =
   var p = parseopt.initOptParser(cmd)
-  while true:
+  while true: 
     parseopt.next(p)
     case p.kind
-    of cmdEnd: break
-    of cmdLongoption, cmdShortOption:
+    of cmdEnd: break 
+    of cmdLongoption, cmdShortOption: 
       case p.key.normalize
-      of "port": gPort = parseInt(p.val).Port
-      of "address": gAddress = p.val
-      of "stdin": gUseStdin = true
+      of "port":
+        gPort = parseInt(p.val).Port
+        gMode = mtcp
+      of "address":
+        gAddress = p.val
+        gMode = mtcp
+      of "stdin": gMode = mstdin
+      of "epc":
+        gMode = mepc
+        gVerbosity = 0          # Port number gotta be first.
       else: processSwitch(pass, p)
     of cmdArgument:
       options.gProjectName = unixToNativePath(p.key)
diff --git a/compiler/options.nim b/compiler/options.nim
index 998ab7781..b3060a180 100644
--- a/compiler/options.nim
+++ b/compiler/options.nim
@@ -83,11 +83,11 @@ type                          # please make sure we have under 32 options
   TGCMode* = enum             # the selected GC
     gcNone, gcBoehm, gcMarkAndSweep, gcRefc, gcV2, gcGenerational
 
-  TIdeCmd* = enum
+  IdeCmd* = enum
     ideNone, ideSug, ideCon, ideDef, ideUse
 
 var
-  gIdeCmd*: TIdeCmd
+  gIdeCmd*: IdeCmd
 
 const
   ChecksOptions* = {optObjCheck, optFieldCheck, optRangeCheck, optNilCheck,
@@ -149,6 +149,7 @@ var
   gProjectName* = "" # holds a name like 'nimrod'
   gProjectPath* = "" # holds a path like /home/alice/projects/nimrod/compiler/
   gProjectFull* = "" # projectPath/projectName
+  gProjectIsStdin* = false # whether we're compiling from stdin
   gProjectMainIdx*: int32 # the canonical path id of the main module
   nimcacheDir* = ""
   command* = "" # the main command (e.g. cc, check, scan, etc)
@@ -395,3 +396,18 @@ template cnimdbg*: expr = p.module.module.fileIdx == gProjectMainIdx
 template pnimdbg*: expr = p.lex.fileIdx == gProjectMainIdx
 template lnimdbg*: expr = L.fileIdx == gProjectMainIdx
 
+proc parseIdeCmd*(s: string): IdeCmd =
+  case s:
+  of "sug": ideSug
+  of "con": ideCon
+  of "def": ideDef
+  of "use": ideUse
+  else: ideNone
+
+proc `$`*(c: IdeCmd): string =
+  case c:
+  of ideSug: "sug"
+  of ideCon: "con"
+  of ideDef: "def"
+  of ideUse: "use"
+  of ideNone: "none"
diff --git a/compiler/passes.nim b/compiler/passes.nim
index 129d8ad47..e031dae10 100644
--- a/compiler/passes.nim
+++ b/compiler/passes.nim
@@ -170,11 +170,7 @@ proc processModule(module: PSym, stream: PLLStream, rd: PRodReader) =
     openPasses(a, module)
     if stream == nil:
       let filename = fileIdx.toFullPathConsiderDirty
-      if module.name.s == "-":
-        module.name.s = "stdinfile"
-        s = llStreamOpen(stdin)
-      else:
-        s = llStreamOpen(filename, fmRead)
+      s = llStreamOpen(filename, fmRead)
       if s == nil:
         rawMessage(errCannotOpenFile, filename)
         return
diff --git a/compiler/sigmatch.nim b/compiler/sigmatch.nim
index 2a9d15b5a..7ea2c3d6f 100644
--- a/compiler/sigmatch.nim
+++ b/compiler/sigmatch.nim
@@ -1281,7 +1281,10 @@ proc paramTypesMatchAux(m: var TCandidate, f, argType: PType,
     result = implicitConv(nkHiddenStdConv, f, arg, m, c)
   of isSubtype:
     inc(m.subtypeMatches)
-    result = implicitConv(nkHiddenSubConv, f, arg, m, c)
+    if f.kind == tyTypeDesc:
+      result = arg
+    else:
+      result = implicitConv(nkHiddenSubConv, f, arg, m, c)
   of isSubrange:
     inc(m.subtypeMatches)
     if f.kind == tyVar:
diff --git a/compiler/suggest.nim b/compiler/suggest.nim
index 6b168670c..659f1fa16 100644
--- a/compiler/suggest.nim
+++ b/compiler/suggest.nim
@@ -15,57 +15,79 @@ import algorithm, sequtils
 
 const
   sep = '\t'
-  sectionSuggest = "sug"
-  sectionDef = "def"
-  sectionContext = "con"
-  sectionUsage = "use"
+
+type
+  Suggest* = object
+    section*: IdeCmd
+    qualifiedPath*: seq[string]
+    filePath*: string
+    line*: int                   # Starts at 1
+    column*: int                 # Starts at 0
+    doc*: string           # Not escaped (yet)
+    symkind*: TSymKind
+    forth*: string               # XXX TODO object on symkind
+
+var
+  suggestionResultHook*: proc (result: Suggest) {.closure.}
 
 #template sectionSuggest(): expr = "##begin\n" & getStackTrace() & "##end\n"
 
 template origModuleName(m: PSym): string = m.name.s
 
-proc symToStr(s: PSym, isLocal: bool, section: string, li: TLineInfo): string = 
-  result = section
-  result.add(sep)
+proc symToSuggest(s: PSym, isLocal: bool, section: string, li: TLineInfo): Suggest = 
+  result.section = parseIdeCmd(section)
   if optIdeTerse in gGlobalOptions:
-    if s.kind in routineKinds:
-      result.add renderTree(s.ast, {renderNoBody, renderNoComments,
-                                    renderDocComments, renderNoPragmas})
-    else:
-      result.add s.name.s
-    result.add(sep)
-    result.add(toFullPath(li))
-    result.add(sep)
-    result.add($toLinenumber(li))
-    result.add(sep)
-    result.add($toColumn(li))
+    result.symkind = s.kind
+    result.filePath = toFullPath(li)
+    result.line = toLinenumber(li)
+    result.column = toColumn(li)
   else:
-    result.add($s.kind)
-    result.add(sep)
+    result.symkind = s.kind
+    result.qualifiedPath = @[]
     if not isLocal and s.kind != skModule:
       let ow = s.owner
       if ow.kind != skModule and ow.owner != nil:
         let ow2 = ow.owner
-        result.add(ow2.origModuleName)
-        result.add('.')
-      result.add(ow.origModuleName)
-      result.add('.')
-    result.add(s.name.s)
-    result.add(sep)
+        result.qualifiedPath.add(ow2.origModuleName)
+      result.qualifiedPath.add(ow.origModuleName)
+    result.qualifiedPath.add(s.name.s)
+
     if s.typ != nil: 
-      result.add(typeToString(s.typ))
-    result.add(sep)
-    result.add(toFullPath(li))
-    result.add(sep)
-    result.add($toLinenumber(li))
-    result.add(sep)
-    result.add($toColumn(li))
-    result.add(sep)
+      result.forth = typeToString(s.typ)
+    else:
+      result.forth = ""
+    result.filePath = toFullPath(li)
+    result.line = toLinenumber(li)
+    result.column = toColumn(li)
     when not defined(noDocgen):
-      result.add(s.extractDocComment.escape)
+      result.doc = s.extractDocComment
+
+proc `$`(suggest: Suggest): string = 
+  result = $suggest.section
+  result.add(sep)
+  result.add($suggest.symkind)
+  result.add(sep)
+  result.add(suggest.qualifiedPath.join("."))
+  result.add(sep)
+  result.add(suggest.forth)
+  result.add(sep)
+  result.add(suggest.filePath)
+  result.add(sep)
+  result.add($suggest.line)
+  result.add(sep)
+  result.add($suggest.column)
+  result.add(sep)
+  when not defined(noDocgen):
+    result.add(suggest.doc.escape)
 
-proc symToStr(s: PSym, isLocal: bool, section: string): string = 
-  result = symToStr(s, isLocal, section, s.info)
+proc symToSuggest(s: PSym, isLocal: bool, section: string): Suggest = 
+  result = symToSuggest(s, isLocal, section, s.info)
+
+proc suggestResult(s: Suggest) =
+  if not isNil(suggestionResultHook):
+    suggestionResultHook(s)
+  else:
+    suggestWriteln($(s))
 
 proc filterSym(s: PSym): bool {.inline.} =
   result = s.kind != skModule
@@ -84,7 +106,7 @@ proc fieldVisible*(c: PContext, f: PSym): bool {.inline.} =
 
 proc suggestField(c: PContext, s: PSym, outputs: var int) = 
   if filterSym(s) and fieldVisible(c, s):
-    suggestWriteln(symToStr(s, isLocal=true, sectionSuggest))
+    suggestResult(symToSuggest(s, isLocal=true, $ideSug))
     inc outputs
 
 template wholeSymTab(cond, section: expr) {.immediate.} =
@@ -97,7 +119,7 @@ template wholeSymTab(cond, section: expr) {.immediate.} =
     for item in entries:
       let it {.inject.} = item
       if cond:
-        suggestWriteln(symToStr(it, isLocal = isLocal, section))
+        suggestResult(symToSuggest(it, isLocal = isLocal, section))
         inc outputs
 
 proc suggestSymList(c: PContext, list: PNode, outputs: var int) = 
@@ -140,7 +162,7 @@ proc argsFit(c: PContext, candidate: PSym, n, nOrig: PNode): bool =
 
 proc suggestCall(c: PContext, n, nOrig: PNode, outputs: var int) = 
   wholeSymTab(filterSym(it) and nameFits(c, it, n) and argsFit(c, it, n, nOrig),
-              sectionContext)
+              $ideCon)
 
 proc typeFits(c: PContext, s: PSym, firstArg: PType): bool {.inline.} = 
   if s.typ != nil and sonsLen(s.typ) > 1 and s.typ.sons[1] != nil:
@@ -157,7 +179,7 @@ proc typeFits(c: PContext, s: PSym, firstArg: PType): bool {.inline.} =
 
 proc suggestOperations(c: PContext, n: PNode, typ: PType, outputs: var int) =
   assert typ != nil
-  wholeSymTab(filterSymNoOpr(it) and typeFits(c, it, typ), sectionSuggest)
+  wholeSymTab(filterSymNoOpr(it) and typeFits(c, it, typ), $ideSug)
 
 proc suggestEverything(c: PContext, n: PNode, outputs: var int) =
   # do not produce too many symbols:
@@ -166,7 +188,7 @@ proc suggestEverything(c: PContext, n: PNode, outputs: var int) =
     if scope == c.topLevelScope: isLocal = false
     for it in items(scope.symbols):
       if filterSym(it):
-        suggestWriteln(symToStr(it, isLocal = isLocal, sectionSuggest))
+        suggestResult(symToSuggest(it, isLocal = isLocal, $ideSug))
         inc outputs
     if scope == c.topLevelScope: break
 
@@ -181,12 +203,12 @@ proc suggestFieldAccess(c: PContext, n: PNode, outputs: var int) =
         # all symbols accessible, because we are in the current module:
         for it in items(c.topLevelScope.symbols):
           if filterSym(it): 
-            suggestWriteln(symToStr(it, isLocal=false, sectionSuggest))
+            suggestResult(symToSuggest(it, isLocal=false, $ideSug))
             inc outputs
       else: 
         for it in items(n.sym.tab): 
           if filterSym(it): 
-            suggestWriteln(symToStr(it, isLocal=false, sectionSuggest))
+            suggestResult(symToSuggest(it, isLocal=false, $ideSug))
             inc outputs
     else:
       # fallback:
@@ -263,16 +285,16 @@ var
 proc findUsages(info: TLineInfo; s: PSym) =
   if usageSym == nil and isTracked(info, s.name.s.len):
     usageSym = s
-    suggestWriteln(symToStr(s, isLocal=false, sectionUsage))
+    suggestResult(symToSuggest(s, isLocal=false, $ideUse))
   elif s == usageSym:
     if lastLineInfo != info:
-      suggestWriteln(symToStr(s, isLocal=false, sectionUsage, info))
+      suggestResult(symToSuggest(s, isLocal=false, $ideUse, info))
     lastLineInfo = info
 
 proc findDefinition(info: TLineInfo; s: PSym) =
   if s.isNil: return
   if isTracked(info, s.name.s.len):
-    suggestWriteln(symToStr(s, isLocal=false, sectionDef))
+    suggestResult(symToSuggest(s, isLocal=false, $ideDef))
     suggestQuit()
 
 proc ensureIdx[T](x: var T, y: int) =
diff --git a/compiler/vm.nim b/compiler/vm.nim
index 1c6c9a30b..e49bed522 100644
--- a/compiler/vm.nim
+++ b/compiler/vm.nim
@@ -1121,7 +1121,7 @@ proc rawExecute(c: PCtx, start: int, tos: PStackFrame): TFullReg =
       decodeB(rkInt)
       let a = regs[rb].node
       case a.kind
-      of nkCharLit..nkInt64Lit: regs[ra].intVal = a.intVal
+      of nkCharLit..nkUInt64Lit: regs[ra].intVal = a.intVal
       else: stackTrace(c, tos, pc, errFieldXNotFound, "intVal")
     of opcNFloatVal:
       decodeB(rkFloat)
@@ -1276,7 +1276,7 @@ proc rawExecute(c: PCtx, start: int, tos: PStackFrame): TFullReg =
     of opcNSetIntVal:
       decodeB(rkNode)
       var dest = regs[ra].node
-      if dest.kind in {nkCharLit..nkInt64Lit} and
+      if dest.kind in {nkCharLit..nkUInt64Lit} and
          regs[rb].kind in {rkInt}:
         dest.intVal = regs[rb].intVal
       else: