nimsuggest: first version

This commit is contained in:
Araq 2015-01-26 21:38:39 +01:00
commit 26b853923c
21 changed files with 364 additions and 218 deletions

View file

@ -428,6 +428,7 @@ type
nfExplicitCall # x.y() was used instead of x.y nfExplicitCall # x.y() was used instead of x.y
nfExprCall # this is an attempt to call a regular expression nfExprCall # this is an attempt to call a regular expression
nfIsRef # this node is a 'ref' node; used for the VM nfIsRef # this node is a 'ref' node; used for the VM
nfIsCursor # this node is attached a cursor; used for idetools
TNodeFlags* = set[TNodeFlag] TNodeFlags* = set[TNodeFlag]
TTypeFlag* = enum # keep below 32 for efficiency reasons (now: 28) TTypeFlag* = enum # keep below 32 for efficiency reasons (now: 28)
@ -883,7 +884,7 @@ const
skMacro, skTemplate, skConverter, skEnumField, skLet, skStub, skAlias} skMacro, skTemplate, skConverter, skEnumField, skLet, skStub, skAlias}
PersistentNodeFlags*: TNodeFlags = {nfBase2, nfBase8, nfBase16, PersistentNodeFlags*: TNodeFlags = {nfBase2, nfBase8, nfBase16,
nfDotSetter, nfDotField, nfDotSetter, nfDotField,
nfIsRef} nfIsRef, nfIsCursor}
namePos* = 0 namePos* = 0
patternPos* = 1 # empty except for term rewriting macros patternPos* = 1 # empty except for term rewriting macros
genericParamsPos* = 2 genericParamsPos* = 2

View file

@ -65,14 +65,14 @@ proc getCommandLineDesc(): string =
proc helpOnError(pass: TCmdLinePass) = proc helpOnError(pass: TCmdLinePass) =
if pass == passCmd1: if pass == passCmd1:
msgWriteln(getCommandLineDesc()) msgWriteln(getCommandLineDesc())
quit(0) msgQuit(0)
proc writeAdvancedUsage(pass: TCmdLinePass) = proc writeAdvancedUsage(pass: TCmdLinePass) =
if pass == passCmd1: if pass == passCmd1:
msgWriteln(`%`(HelpMessage, [VersionAsString, msgWriteln(`%`(HelpMessage, [VersionAsString,
platform.OS[platform.hostOS].name, platform.OS[platform.hostOS].name,
CPU[platform.hostCPU].name]) & AdvancedUsage) CPU[platform.hostCPU].name]) & AdvancedUsage)
quit(0) msgQuit(0)
proc writeVersionInfo(pass: TCmdLinePass) = proc writeVersionInfo(pass: TCmdLinePass) =
if pass == passCmd1: if pass == passCmd1:
@ -87,7 +87,7 @@ proc writeVersionInfo(pass: TCmdLinePass) =
msgWriteln("active boot switches:" & usedRelease & usedAvoidTimeMachine & msgWriteln("active boot switches:" & usedRelease & usedAvoidTimeMachine &
usedTinyC & usedGnuReadline & usedNativeStacktrace & usedNoCaas & usedTinyC & usedGnuReadline & usedNativeStacktrace & usedNoCaas &
usedFFI & usedBoehm & usedMarkAndSweep & usedGenerational & usedNoGC) usedFFI & usedBoehm & usedMarkAndSweep & usedGenerational & usedNoGC)
quit(0) msgQuit(0)
var var
helpWritten: bool helpWritten: bool
@ -255,8 +255,7 @@ proc trackDirty(arg: string, info: TLineInfo) =
gDirtyBufferIdx = a[0].fileInfoIdx gDirtyBufferIdx = a[0].fileInfoIdx
gDirtyOriginalIdx = a[1].fileInfoIdx gDirtyOriginalIdx = a[1].fileInfoIdx
optTrackPos = newLineInfo(gDirtyBufferIdx, line, column) gTrackPos = newLineInfo(gDirtyBufferIdx, line, column)
msgs.addCheckpoint(optTrackPos)
proc track(arg: string, info: TLineInfo) = proc track(arg: string, info: TLineInfo) =
var a = arg.split(',') var a = arg.split(',')
@ -266,8 +265,7 @@ proc track(arg: string, info: TLineInfo) =
localError(info, errInvalidNumber, a[1]) localError(info, errInvalidNumber, a[1])
if parseUtils.parseInt(a[2], column) <= 0: if parseUtils.parseInt(a[2], column) <= 0:
localError(info, errInvalidNumber, a[2]) localError(info, errInvalidNumber, a[2])
optTrackPos = newLineInfo(a[0], line, column) gTrackPos = newLineInfo(a[0], line, column)
msgs.addCheckpoint(optTrackPos)
proc dynlibOverride(switch, arg: string, pass: TCmdLinePass, info: TLineInfo) = proc dynlibOverride(switch, arg: string, pass: TCmdLinePass, info: TLineInfo) =
if pass in {passCmd2, passPP}: if pass in {passCmd2, passPP}:
@ -541,19 +539,19 @@ proc processSwitch(switch, arg: string, pass: TCmdLinePass, info: TLineInfo) =
trackDirty(arg, info) trackDirty(arg, info)
of "suggest": of "suggest":
expectNoArg(switch, arg, pass, info) expectNoArg(switch, arg, pass, info)
incl(gGlobalOptions, optSuggest) gIdeCmd = ideSug
of "def": of "def":
expectNoArg(switch, arg, pass, info) expectNoArg(switch, arg, pass, info)
incl(gGlobalOptions, optDef) gIdeCmd = ideDef
of "eval": of "eval":
expectArg(switch, arg, pass, info) expectArg(switch, arg, pass, info)
gEvalExpr = arg gEvalExpr = arg
of "context": of "context":
expectNoArg(switch, arg, pass, info) expectNoArg(switch, arg, pass, info)
incl(gGlobalOptions, optContext) gIdeCmd = ideCon
of "usages": of "usages":
expectNoArg(switch, arg, pass, info) expectNoArg(switch, arg, pass, info)
incl(gGlobalOptions, optUsages) gIdeCmd = ideUse
of "stdout": of "stdout":
expectNoArg(switch, arg, pass, info) expectNoArg(switch, arg, pass, info)
incl(gGlobalOptions, optStdout) incl(gGlobalOptions, optStdout)

View file

@ -25,7 +25,7 @@ type
next*: PIdent # for hash-table chaining next*: PIdent # for hash-table chaining
h*: THash # hash value of s h*: THash # hash value of s
var firstCharIsCS*: bool var firstCharIsCS*: bool = true
var buckets*: array[0..4096 * 2 - 1, PIdent] var buckets*: array[0..4096 * 2 - 1, PIdent]
proc cmpIgnoreStyle(a, b: cstring, blen: int): int = proc cmpIgnoreStyle(a, b: cstring, blen: int): int =

View file

@ -693,7 +693,15 @@ proc scanComment(L: var TLexer, tok: var TToken) =
when not defined(nimfix): when not defined(nimfix):
assert buf[pos+1] == '#' assert buf[pos+1] == '#'
if buf[pos+2] == '[': if buf[pos+2] == '[':
if buf[pos+3] == ']':
# ##[] is the (rather complex) "cursor token" for idetools
tok.tokType = tkComment
tok.literal = "[]"
inc(L.bufpos, 4)
return
else:
lexMessagePos(L, warnDeprecated, pos, "use '## [' instead; '##['") lexMessagePos(L, warnDeprecated, pos, "use '## [' instead; '##['")
tok.tokType = tkComment tok.tokType = tkComment
# iNumber contains the number of '\n' in the token # iNumber contains the number of '\n' in the token
tok.iNumber = 0 tok.iNumber = 0

View file

@ -58,11 +58,6 @@ proc commandCompileToC =
registerPass(cgenPass) registerPass(cgenPass)
rodPass() rodPass()
#registerPass(cleanupPass()) #registerPass(cleanupPass())
if optCaasEnabled in gGlobalOptions:
# echo "BEFORE CHECK DEP"
# discard checkDepMem(gProjectMainIdx)
# echo "CHECK DEP COMPLETE"
discard
compileProject() compileProject()
cgenWriteModules() cgenWriteModules()
@ -177,17 +172,15 @@ proc commandSuggest =
if gDirtyBufferIdx != 0: if gDirtyBufferIdx != 0:
discard compileModule(gDirtyBufferIdx, {sfDirty}) discard compileModule(gDirtyBufferIdx, {sfDirty})
resetModule(gDirtyBufferIdx) resetModule(gDirtyBufferIdx)
if optDef in gGlobalOptions:
defFromSourceMap(optTrackPos)
else: else:
msgs.gErrorMax = high(int) # do not stop after first error msgs.gErrorMax = high(int) # do not stop after first error
semanticPasses() semanticPasses()
rodPass() rodPass()
# XXX: this handles the case when the dirty buffer is the main file, # XXX: this handles the case when the dirty buffer is the main file,
# but doesn't handle the case when it's imported module # but doesn't handle the case when it's imported module
var projFile = if gProjectMainIdx == gDirtyOriginalIdx: gDirtyBufferIdx #var projFile = if gProjectMainIdx == gDirtyOriginalIdx: gDirtyBufferIdx
else: gProjectMainIdx # else: gProjectMainIdx
compileProject(projFile) compileProject() #(projFile)
proc resetMemory = proc resetMemory =
resetCompilationLists() resetCompilationLists()
@ -218,7 +211,6 @@ proc resetMemory =
# rodread.gMods # rodread.gMods
# !! ropes.cache # !! ropes.cache
# semthreads.computed?
# #
# suggest.usageSym # suggest.usageSym
# #

View file

@ -34,9 +34,6 @@ proc getModule(fileIdx: int32): PSym =
if fileIdx >= 0 and fileIdx < gCompiledModules.len: if fileIdx >= 0 and fileIdx < gCompiledModules.len:
result = gCompiledModules[fileIdx] result = gCompiledModules[fileIdx]
template compiledAt(x: PSym): expr =
gMemCacheData[x.position].compiledAt
template crc(x: PSym): expr = template crc(x: PSym): expr =
gMemCacheData[x.position].crc gMemCacheData[x.position].crc
@ -74,10 +71,12 @@ proc addDep(x: PSym, dep: int32) =
proc resetModule*(fileIdx: int32) = proc resetModule*(fileIdx: int32) =
# echo "HARD RESETTING ", fileIdx.toFilename # echo "HARD RESETTING ", fileIdx.toFilename
if fileIdx <% gMemCacheData.len:
gMemCacheData[fileIdx].needsRecompile = Yes gMemCacheData[fileIdx].needsRecompile = Yes
if fileIdx <% gCompiledModules.len:
gCompiledModules[fileIdx] = nil gCompiledModules[fileIdx] = nil
if fileIdx <% cgendata.gModules.len:
cgendata.gModules[fileIdx] = nil cgendata.gModules[fileIdx] = nil
resetSourceMap(fileIdx)
proc resetAllModules* = proc resetAllModules* =
for i in 0..gCompiledModules.high: for i in 0..gCompiledModules.high:

View file

@ -10,9 +10,6 @@
import import
options, strutils, os, tables, ropes, platform options, strutils, os, tables, ropes, platform
when useCaas:
import sockets
type type
TMsgKind* = enum TMsgKind* = enum
errUnknown, errIllFormedAstX, errInternal, errCannotOpenFile, errGenerated, errUnknown, errIllFormedAstX, errInternal, errCannotOpenFile, errGenerated,
@ -572,9 +569,6 @@ var
gWarnCounter*: int = 0 gWarnCounter*: int = 0
gErrorMax*: int = 1 # stop after gErrorMax errors gErrorMax*: int = 1 # stop after gErrorMax errors
when useCaas:
var stdoutSocket*: Socket
proc unknownLineInfo*(): TLineInfo = proc unknownLineInfo*(): TLineInfo =
result.line = int16(-1) result.line = int16(-1)
result.col = int16(-1) result.col = int16(-1)
@ -586,25 +580,28 @@ var
bufferedMsgs*: seq[string] bufferedMsgs*: seq[string]
errorOutputs* = {eStdOut, eStdErr} errorOutputs* = {eStdOut, eStdErr}
writelnHook*: proc (output: string) {.closure.}
proc clearBufferedMsgs* = proc clearBufferedMsgs* =
bufferedMsgs = nil bufferedMsgs = nil
proc suggestWriteln*(s: string) = proc suggestWriteln*(s: string) =
if eStdOut in errorOutputs: if eStdOut in errorOutputs:
when useCaas: if isNil(writelnHook): writeln(stdout, s)
if isNil(stdoutSocket): writeln(stdout, s) else: writelnHook(s)
else:
writeln(stdout, s)
stdoutSocket.send(s & "\c\L")
else:
writeln(stdout, s)
if eInMemory in errorOutputs: if eInMemory in errorOutputs:
bufferedMsgs.safeAdd(s) bufferedMsgs.safeAdd(s)
proc msgQuit*(x: int8) = quit x
proc msgQuit*(x: string) = quit x
proc suggestQuit*() = proc suggestQuit*() =
when true:
raise newException(ESuggestDone, "suggest done")
else:
if not isServing: if not isServing:
assert false
quit(0) quit(0)
elif isWorkingWithDirtyBuffer: elif isWorkingWithDirtyBuffer:
# No need to compile the rest if we are working with a # No need to compile the rest if we are working with a
@ -679,14 +676,7 @@ proc `??`* (info: TLineInfo, filename: string): bool =
# only for debugging purposes # only for debugging purposes
result = filename in info.toFilename result = filename in info.toFilename
var checkPoints*: seq[TLineInfo] = @[] var gTrackPos*: TLineInfo
var optTrackPos*: TLineInfo
proc addCheckpoint*(info: TLineInfo) =
checkPoints.add(info)
proc addCheckpoint*(filename: string, line: int) =
addCheckpoint(newLineInfo(filename, line, - 1))
proc outWriteln*(s: string) = proc outWriteln*(s: string) =
## Writes to stdout. Always. ## Writes to stdout. Always.
@ -694,7 +684,8 @@ proc outWriteln*(s: string) =
proc msgWriteln*(s: string) = proc msgWriteln*(s: string) =
## Writes to stdout. If --stdout option is given, writes to stderr instead. ## Writes to stdout. If --stdout option is given, writes to stderr instead.
if gCmd == cmdIdeTools and optCDebug notin gGlobalOptions: return
#if gCmd == cmdIdeTools and optCDebug notin gGlobalOptions: return
if optStdout in gGlobalOptions: if optStdout in gGlobalOptions:
if eStdErr in errorOutputs: writeln(stderr, s) if eStdErr in errorOutputs: writeln(stderr, s)
@ -714,19 +705,6 @@ proc msgKindToString*(kind: TMsgKind): string =
proc getMessageStr(msg: TMsgKind, arg: string): string = proc getMessageStr(msg: TMsgKind, arg: string): string =
result = msgKindToString(msg) % [arg] result = msgKindToString(msg) % [arg]
type
TCheckPointResult* = enum
cpNone, cpFuzzy, cpExact
proc inCheckpoint*(current: TLineInfo): TCheckPointResult =
for i in countup(0, high(checkPoints)):
if current.fileIndex == checkPoints[i].fileIndex:
if current.line == checkPoints[i].line and
abs(current.col-checkPoints[i].col) < 4:
return cpExact
if current.line >= checkPoints[i].line:
return cpFuzzy
type type
TErrorHandling = enum doNothing, doAbort, doRaise TErrorHandling = enum doNothing, doAbort, doRaise
@ -798,6 +776,9 @@ proc formatMsg*(info: TLineInfo, msg: TMsgKind, arg: string): string =
result = frmt % [toMsgFilename(info), coordToStr(info.line), result = frmt % [toMsgFilename(info), coordToStr(info.line),
coordToStr(info.col), getMessageStr(msg, arg)] coordToStr(info.col), getMessageStr(msg, arg)]
proc ignoreMsgBecauseOfIdeTools(msg: TMsgKind): bool =
msg >= errGenerated and gCmd == cmdIdeTools and optIdeDebug notin gGlobalOptions
proc liMessage(info: TLineInfo, msg: TMsgKind, arg: string, proc liMessage(info: TLineInfo, msg: TMsgKind, arg: string,
eh: TErrorHandling) = eh: TErrorHandling) =
var frmt: string var frmt: string
@ -821,7 +802,7 @@ proc liMessage(info: TLineInfo, msg: TMsgKind, arg: string,
inc(gHintCounter) inc(gHintCounter)
let s = frmt % [toMsgFilename(info), coordToStr(info.line), let s = frmt % [toMsgFilename(info), coordToStr(info.line),
coordToStr(info.col), getMessageStr(msg, arg)] coordToStr(info.col), getMessageStr(msg, arg)]
if not ignoreMsg: if not ignoreMsg and not ignoreMsgBecauseOfIdeTools(msg):
msgWriteln(s) msgWriteln(s)
if optPrintSurroundingSrc and msg in errMin..errMax: if optPrintSurroundingSrc and msg in errMin..errMax:
info.writeSurroundingSrc info.writeSurroundingSrc

View file

@ -89,4 +89,4 @@ condsyms.initDefines()
when not defined(selftest): when not defined(selftest):
handleCmdLine() handleCmdLine()
quit(int8(msgs.gErrorCounter > 0)) msgQuit(int8(msgs.gErrorCounter > 0))

View file

@ -0,0 +1,187 @@
#
#
# The Nim Compiler
# (c) Copyright 2015 Andreas Rumpf
#
# See the file "copying.txt", included in this
# distribution, for details about the copyright.
#
## Nimsuggest is a tool that helps to give editors IDE like capabilities.
import strutils, os, parseopt, parseUtils
import options, commands, modules, sem, passes, passaux, msgs, nimconf,
extccomp, condsyms, lists, net, rdstdin
const Usage = """
Nimsuggest - Tool to give every editor IDE like capabilities for Nim
Usage:
nimsuggest [options] projectfile.nim
Options:
--port:PORT port, by default 6000
--address:HOST binds to that address, by default ""
--stdin read commands from stdin and write results to
stdout instead of using sockets
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.
"""
var
gPort = 6000.Port
gAddress = ""
gUseStdin: bool
const
seps = {':', ';', ' ', '\t'}
Help = "usage: sug|con|def|use dirtybuffer.nim[;originalfile.nim]:line:col\n"&
"type 'quit' to quit\n" &
"type 'debug' to toggle debug mode on/off\n" &
"type 'terse' to toggle terse mode on/off"
proc parseQuoted(cmd: string; outp: var string; start: int): int =
var i = start
i += skipWhitespace(cmd, i)
if cmd[i] == '"':
i += parseUntil(cmd, outp, '"', i+1)+1
else:
i += parseUntil(cmd, outp, seps, i)
result = i
proc action(cmd: string) =
template toggle(sw) =
if sw in gGlobalOptions:
excl(gGlobalOptions, sw)
else:
incl(gGlobalOptions, sw)
return
var opc = ""
var i = parseIdent(cmd, opc, 0)
case opc.normalize
of "sug": gIdeCmd = ideSug
of "con": gIdeCmd = ideCon
of "def": gIdeCmd = ideDef
of "use":
modules.resetAllModules()
gIdeCmd = ideUse
of "quit": quit()
of "debug": toggle optIdeDebug
of "terse": toggle optIdeTerse
else:
echo Help
return
var dirtyfile = ""
var orig = ""
i = parseQuoted(cmd, dirtyfile, i)
if cmd[i] == ';':
i = parseQuoted(cmd, orig, i+1)
i += skipWhile(cmd, seps, i)
var line, col = -1
i += parseInt(cmd, line, i)
i += skipWhile(cmd, seps, i)
i += parseInt(cmd, col, i)
if dirtyfile.len != 0:
gDirtyBufferIdx = dirtyfile.fileInfoIdx
gDirtyOriginalIdx = if orig.len != 0: orig.fileInfoIdx else: gDirtyBufferIdx
else:
discard "use the same filename as in the last command"
resetModule gDirtyBufferIdx
if gDirtyBufferIdx != gProjectMainIdx:
resetModule gProjectMainIdx
gTrackPos = newLineInfo(gDirtyBufferIdx, line, col)
#echo dirtyfile, gDirtyBufferIdx, " project ", gProjectMainIdx
gErrorCounter = 0
compileProject()
proc serve() =
gDirtyBufferIdx = gProjectMainIdx
gDirtyOriginalIdx = gProjectMainIdx
# do not stop after the first error:
msgs.gErrorMax = high(int)
if gUseStdin:
echo Help
var line = ""
while readLineFromStdin("> ", line):
action line
echo ""
flushFile(stdout)
else:
var server = newSocket()
server.bindAddr(gPort, gAddress)
var inp = "".TaintedString
server.listen()
var stdoutSocket = newSocket()
msgs.writelnHook = proc (line: string) =
stdoutSocket.send(line & "\c\L")
while true:
accept(server, stdoutSocket)
stdoutSocket.readLine(inp)
action inp.string
stdoutSocket.send("\c\L")
stdoutSocket.close()
proc mainCommand =
registerPass verbosePass
registerPass semPass
gCmd = cmdIdeTools
incl gGlobalOptions, optCaasEnabled
isServing = true
wantMainModule()
appendStr(searchPaths, options.libpath)
if gProjectFull.len != 0:
# current path is always looked first for modules
prependStr(searchPaths, gProjectPath)
serve()
proc processCmdLine*(pass: TCmdLinePass, cmd: string) =
var p = parseopt.initOptParser(cmd)
while true:
parseopt.next(p)
case p.kind
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
else: processSwitch(pass, p)
of cmdArgument:
options.gProjectName = unixToNativePath(p.key)
# if processArgument(pass, p, argsCount): break
proc handleCmdLine() =
if paramCount() == 0:
stdout.writeln(Usage)
else:
processCmdLine(passCmd1, "")
if gProjectName != "":
try:
gProjectFull = canonicalizePath(gProjectName)
except OSError:
gProjectFull = gProjectName
var p = splitFile(gProjectFull)
gProjectPath = p.dir
gProjectName = p.name
else:
gProjectPath = getCurrentDir()
loadConfigs(DefaultConfig) # load all config files
# now process command line arguments again, because some options in the
# command line can overwite the config file's settings
extccomp.initVars()
processCmdLine(passCmd2, "")
mainCommand()
when false:
proc quitCalled() {.noconv.} =
writeStackTrace()
addQuitProc(quitCalled)
condsyms.initDefines()
defineSymbol "nimsuggest"
handleCmdline()

View file

@ -56,17 +56,14 @@ type # please make sure we have under 32 options
optNoMain, # do not generate a "main" proc optNoMain, # do not generate a "main" proc
optThreads, # support for multi-threading optThreads, # support for multi-threading
optStdout, # output to stdout optStdout, # output to stdout
optSuggest, # ideTools: 'suggest'
optContext, # ideTools: 'context'
optDef, # ideTools: 'def'
optUsages, # ideTools: 'usages'
optThreadAnalysis, # thread analysis pass optThreadAnalysis, # thread analysis pass
optTaintMode, # taint mode turned on optTaintMode, # taint mode turned on
optTlsEmulation, # thread var emulation turned on optTlsEmulation, # thread var emulation turned on
optGenIndex # generate index file for documentation; optGenIndex # generate index file for documentation;
optEmbedOrigSrc # embed the original source in the generated code optEmbedOrigSrc # embed the original source in the generated code
# also: generate header file # also: generate header file
optIdeDebug # idetools: debug mode
optIdeTerse # idetools: use terse descriptions
TGlobalOptions* = set[TGlobalOption] TGlobalOptions* = set[TGlobalOption]
TCommands* = enum # Nim's commands TCommands* = enum # Nim's commands
# **keep binary compatible** # **keep binary compatible**
@ -86,6 +83,12 @@ type # please make sure we have under 32 options
TGCMode* = enum # the selected GC TGCMode* = enum # the selected GC
gcNone, gcBoehm, gcMarkAndSweep, gcRefc, gcV2, gcGenerational gcNone, gcBoehm, gcMarkAndSweep, gcRefc, gcV2, gcGenerational
TIdeCmd* = enum
ideNone, ideSug, ideCon, ideDef, ideUse
var
gIdeCmd*: TIdeCmd
const const
ChecksOptions* = {optObjCheck, optFieldCheck, optRangeCheck, optNilCheck, ChecksOptions* = {optObjCheck, optFieldCheck, optRangeCheck, optNilCheck,
optOverflowCheck, optBoundsCheck, optAssert, optNaNCheck, optInfCheck} optOverflowCheck, optBoundsCheck, optAssert, optNaNCheck, optInfCheck}

View file

@ -111,6 +111,11 @@ proc rawSkipComment(p: var TParser, node: PNode) =
if p.tok.tokType == tkComment: if p.tok.tokType == tkComment:
if node != nil: if node != nil:
if node.comment == nil: node.comment = "" if node.comment == nil: node.comment = ""
if p.tok.literal == "[]":
node.flags.incl nfIsCursor
#echo "parser: "
#debug node
else:
add(node.comment, p.tok.literal) add(node.comment, p.tok.literal)
else: else:
parMessage(p, errInternal, "skipComment") parMessage(p, errInternal, "skipComment")
@ -379,7 +384,8 @@ proc dotExpr(p: var TParser, a: PNode): PNode =
#| dotExpr = expr '.' optInd ('type' | 'addr' | symbol) #| dotExpr = expr '.' optInd ('type' | 'addr' | symbol)
var info = p.parLineInfo var info = p.parLineInfo
getTok(p) getTok(p)
optInd(p, a) result = newNodeI(nkDotExpr, info)
optInd(p, result)
case p.tok.tokType case p.tok.tokType
of tkType: of tkType:
result = newNodeP(nkTypeOfExpr, p) result = newNodeP(nkTypeOfExpr, p)
@ -390,7 +396,6 @@ proc dotExpr(p: var TParser, a: PNode): PNode =
getTok(p) getTok(p)
addSon(result, a) addSon(result, a)
else: else:
result = newNodeI(nkDotExpr, info)
addSon(result, a) addSon(result, a)
addSon(result, parseSymbol(p)) addSon(result, parseSymbol(p))
@ -737,7 +742,7 @@ proc parseOperators(p: var TParser, headNode: PNode,
var a = newNodeP(nkInfix, p) var a = newNodeP(nkInfix, p)
var opNode = newIdentNodeP(p.tok.ident, p) # skip operator: var opNode = newIdentNodeP(p.tok.ident, p) # skip operator:
getTok(p) getTok(p)
optInd(p, opNode) optInd(p, a)
# read sub-expression with higher priority: # read sub-expression with higher priority:
var b = simpleExprAux(p, opPrec + leftAssoc, modeB) var b = simpleExprAux(p, opPrec + leftAssoc, modeB)
addSon(a, opNode) addSon(a, opNode)
@ -1735,8 +1740,8 @@ proc parseObject(p: var TParser): PNode =
proc parseTypeClassParam(p: var TParser): PNode = proc parseTypeClassParam(p: var TParser): PNode =
if p.tok.tokType == tkVar: if p.tok.tokType == tkVar:
result = newNodeP(nkVarTy, p)
getTok(p) getTok(p)
result = newNode(nkVarTy)
result.addSon(p.parseSymbol) result.addSon(p.parseSymbol)
else: else:
result = p.parseSymbol result = p.parseSymbol
@ -1747,7 +1752,7 @@ proc parseTypeClass(p: var TParser): PNode =
#| &IND{>} stmt #| &IND{>} stmt
result = newNodeP(nkTypeClassTy, p) result = newNodeP(nkTypeClassTy, p)
getTok(p) getTok(p)
var args = newNode(nkArgList) var args = newNodeP(nkArgList, p)
addSon(result, args) addSon(result, args)
addSon(args, p.parseTypeClassParam) addSon(args, p.parseTypeClassParam)
while p.tok.tokType == tkComma: while p.tok.tokType == tkComma:

View file

@ -411,12 +411,6 @@ proc processCommonLink(c: PContext, n: PNode, feature: TLinkFeature) =
proc pragmaBreakpoint(c: PContext, n: PNode) = proc pragmaBreakpoint(c: PContext, n: PNode) =
discard getOptionalStr(c, n, "") discard getOptionalStr(c, n, "")
proc pragmaCheckpoint(c: PContext, n: PNode) =
# checkpoints can be used to debug the compiler; they are not documented
var info = n.info
inc(info.line) # next line is affected!
msgs.addCheckpoint(info)
proc pragmaWatchpoint(c: PContext, n: PNode) = proc pragmaWatchpoint(c: PContext, n: PNode) =
if n.kind == nkExprColonExpr: if n.kind == nkExprColonExpr:
n.sons[1] = c.semExpr(c, n.sons[1]) n.sons[1] = c.semExpr(c, n.sons[1])

View file

@ -84,8 +84,9 @@ proc notFoundError*(c: PContext, n: PNode, errors: CandidateErrors) =
if c.inCompilesContext > 0: if c.inCompilesContext > 0:
# fail fast: # fail fast:
globalError(n.info, errTypeMismatch, "") globalError(n.info, errTypeMismatch, "")
if errors.len == 0: if errors.isNil or errors.len == 0:
localError(n.info, errExprXCannotBeCalled, n[0].renderTree) localError(n.info, errExprXCannotBeCalled, n[0].renderTree)
return
# to avoid confusing errors like: # to avoid confusing errors like:
# got (SslPtr, SocketHandle) # got (SslPtr, SocketHandle)

View file

@ -101,7 +101,6 @@ template checkMetaInvariants(cl: TReplTypeVars, t: PType) =
echo "UNEXPECTED META ", t.id, " ", instantiationInfo(-1) echo "UNEXPECTED META ", t.id, " ", instantiationInfo(-1)
debug t debug t
writeStackTrace() writeStackTrace()
quit 1
proc replaceTypeVarsT*(cl: var TReplTypeVars, t: PType): PType = proc replaceTypeVarsT*(cl: var TReplTypeVars, t: PType): PType =
result = replaceTypeVarsTAux(cl, t) result = replaceTypeVarsTAux(cl, t)

View file

@ -1,7 +1,7 @@
# #
# #
# The Nim Compiler # The Nim Compiler
# (c) Copyright 2012 Andreas Rumpf # (c) Copyright 2015 Andreas Rumpf
# #
# See the file "copying.txt", included in this # See the file "copying.txt", included in this
# distribution, for details about the copyright. # distribution, for details about the copyright.
@ -14,7 +14,7 @@ import
extccomp, strutils, os, platform, parseopt extccomp, strutils, os, platform, parseopt
when useCaas: when useCaas:
import sockets import net
# We cache modules and the dependency graph. However, we don't check for # We cache modules and the dependency graph. However, we don't check for
# file changes but expect the client to tell us about them, otherwise the # file changes but expect the client to tell us about them, otherwise the
@ -61,14 +61,16 @@ proc serve*(action: proc (){.nimcall.}) =
of "tcp", "": of "tcp", "":
when useCaas: when useCaas:
var server = socket() var server = newSocket()
if server == invalidSocket: raiseOSError(osLastError())
let p = getConfigVar("server.port") let p = getConfigVar("server.port")
let port = if p.len > 0: parseInt(p).Port else: 6000.Port let port = if p.len > 0: parseInt(p).Port else: 6000.Port
server.bindAddr(port, getConfigVar("server.address")) server.bindAddr(port, getConfigVar("server.address"))
var inp = "".TaintedString var inp = "".TaintedString
server.listen() server.listen()
new(stdoutSocket) var stdoutSocket = newSocket()
msgs.writelnHook = proc (line: string) =
stdoutSocket.send(line & "\c\L")
while true: while true:
accept(server, stdoutSocket) accept(server, stdoutSocket)
stdoutSocket.readLine(inp) stdoutSocket.readLine(inp)
@ -76,7 +78,7 @@ proc serve*(action: proc (){.nimcall.}) =
stdoutSocket.send("\c\L") stdoutSocket.send("\c\L")
stdoutSocket.close() stdoutSocket.close()
else: else:
quit "server.type not supported; compiler built without caas support" msgQuit "server.type not supported; compiler built without caas support"
else: else:
echo "Invalid server.type:", typ echo "Invalid server.type:", typ
quit 1 msgQuit 1

View file

@ -1,7 +1,7 @@
# #
# #
# The Nim Compiler # The Nim Compiler
# (c) Copyright 2013 Andreas Rumpf # (c) Copyright 2015 Andreas Rumpf
# #
# See the file "copying.txt", included in this # See the file "copying.txt", included in this
# distribution, for details about the copyright. # distribution, for details about the copyright.
@ -31,6 +31,19 @@ proc origModuleName(m: PSym): string =
proc symToStr(s: PSym, isLocal: bool, section: string, li: TLineInfo): string = proc symToStr(s: PSym, isLocal: bool, section: string, li: TLineInfo): string =
result = section result = section
result.add(sep) result.add(sep)
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))
else:
result.add($s.kind) result.add($s.kind)
result.add(sep) result.add(sep)
if not isLocal and s.kind != skModule: if not isLocal and s.kind != skModule:
@ -59,7 +72,11 @@ proc symToStr(s: PSym, isLocal: bool, section: string): string =
result = symToStr(s, isLocal, section, s.info) result = symToStr(s, isLocal, section, s.info)
proc filterSym(s: PSym): bool {.inline.} = proc filterSym(s: PSym): bool {.inline.} =
result = s.name.s[0] in lexer.SymChars and s.kind != skModule result = s.kind != skModule
proc filterSymNoOpr(s: PSym): bool {.inline.} =
result = s.kind != skModule and s.name.s[0] in lexer.SymChars and
not isKeyword(s.name)
proc fieldVisible*(c: PContext, f: PSym): bool {.inline.} = proc fieldVisible*(c: PContext, f: PSym): bool {.inline.} =
let fmoduleId = getModule(f).id let fmoduleId = getModule(f).id
@ -131,11 +148,20 @@ proc suggestCall(c: PContext, n, nOrig: PNode, outputs: var int) =
proc typeFits(c: PContext, s: PSym, firstArg: PType): bool {.inline.} = 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: if s.typ != nil and sonsLen(s.typ) > 1 and s.typ.sons[1] != nil:
# special rule: if system and some weird generic match via 'tyExpr'
# or 'tyGenericParam' we won't list it either to reduce the noise (nobody
# wants 'system.`-|` as suggestion
let m = s.getModule()
if m != nil and sfSystemModule in m.flags:
if s.kind == skType: return
var exp = s.typ.sons[1].skipTypes({tyGenericInst, tyVar})
if exp.kind == tyVarargs: exp = elemType(exp)
if exp.kind in {tyExpr, tyStmt, tyGenericParam, tyAnything}: return
result = sigmatch.argtypeMatches(c, s.typ.sons[1], firstArg) result = sigmatch.argtypeMatches(c, s.typ.sons[1], firstArg)
proc suggestOperations(c: PContext, n: PNode, typ: PType, outputs: var int) = proc suggestOperations(c: PContext, n: PNode, typ: PType, outputs: var int) =
assert typ != nil assert typ != nil
wholeSymTab(filterSym(it) and typeFits(c, it, typ), sectionSuggest) wholeSymTab(filterSymNoOpr(it) and typeFits(c, it, typ), sectionSuggest)
proc suggestEverything(c: PContext, n: PNode, outputs: var int) = proc suggestEverything(c: PContext, n: PNode, outputs: var int) =
# do not produce too many symbols: # do not produce too many symbols:
@ -191,19 +217,28 @@ proc suggestFieldAccess(c: PContext, n: PNode, outputs: var int) =
else: else:
suggestOperations(c, n, typ, outputs) suggestOperations(c, n, typ, outputs)
type
TCheckPointResult = enum
cpNone, cpFuzzy, cpExact
proc inCheckpoint(current: TLineInfo): TCheckPointResult =
if current.fileIndex == gTrackPos.fileIndex:
if current.line == gTrackPos.line and
abs(current.col-gTrackPos.col) < 4:
return cpExact
if current.line >= gTrackPos.line:
return cpFuzzy
proc findClosestDot(n: PNode): PNode = proc findClosestDot(n: PNode): PNode =
if n.kind == nkDotExpr and msgs.inCheckpoint(n.info) == cpExact: if n.kind == nkDotExpr and inCheckpoint(n.info) == cpExact:
result = n result = n
else: else:
for i in 0.. <safeLen(n): for i in 0.. <safeLen(n):
result = findClosestDot(n.sons[i]) result = findClosestDot(n.sons[i])
if result != nil: return if result != nil: return
const
CallNodes = {nkCall, nkInfix, nkPrefix, nkPostfix, nkCommand, nkCallStrLit}
proc findClosestCall(n: PNode): PNode = proc findClosestCall(n: PNode): PNode =
if n.kind in CallNodes and msgs.inCheckpoint(n.info) == cpExact: if n.kind in nkCallKinds and inCheckpoint(n.info) == cpExact:
result = n result = n
else: else:
for i in 0.. <safeLen(n): for i in 0.. <safeLen(n):
@ -211,15 +246,14 @@ proc findClosestCall(n: PNode): PNode =
if result != nil: return if result != nil: return
proc isTracked(current: TLineInfo, tokenLen: int): bool = proc isTracked(current: TLineInfo, tokenLen: int): bool =
for i in countup(0, high(checkPoints)): if current.fileIndex == gTrackPos.fileIndex:
if current.fileIndex == checkPoints[i].fileIndex: if current.line == gTrackPos.line:
if current.line == checkPoints[i].line: let col = gTrackPos.col
let col = checkPoints[i].col
if col >= current.col and col <= current.col+tokenLen-1: if col >= current.col and col <= current.col+tokenLen-1:
return true return true
proc findClosestSym(n: PNode): PNode = proc findClosestSym(n: PNode): PNode =
if n.kind == nkSym and msgs.inCheckpoint(n.info) == cpExact: if n.kind == nkSym and inCheckpoint(n.info) == cpExact:
result = n result = n
elif n.kind notin {nkNone..nkNilLit}: elif n.kind notin {nkNone..nkNilLit}:
for i in 0.. <sonsLen(n): for i in 0.. <sonsLen(n):
@ -258,69 +292,18 @@ proc findDefinition(info: TLineInfo; s: PSym) =
suggestWriteln(symToStr(s, isLocal=false, sectionDef)) suggestWriteln(symToStr(s, isLocal=false, sectionDef))
suggestQuit() suggestQuit()
type
TSourceMap = object
lines: seq[TLineMap]
TEntry = object
pos: int
sym: PSym
TLineMap = object
entries: seq[TEntry]
var
gSourceMaps: seq[TSourceMap] = @[]
proc ensureIdx[T](x: var T, y: int) = proc ensureIdx[T](x: var T, y: int) =
if x.len <= y: x.setLen(y+1) if x.len <= y: x.setLen(y+1)
proc ensureSeq[T](x: var seq[T]) = proc ensureSeq[T](x: var seq[T]) =
if x == nil: newSeq(x, 0) if x == nil: newSeq(x, 0)
proc resetSourceMap*(fileIdx: int32) =
ensureIdx(gSourceMaps, fileIdx)
gSourceMaps[fileIdx].lines = @[]
proc addToSourceMap(sym: PSym, info: TLineInfo) =
ensureIdx(gSourceMaps, info.fileIndex)
ensureSeq(gSourceMaps[info.fileIndex].lines)
ensureIdx(gSourceMaps[info.fileIndex].lines, info.line)
ensureSeq(gSourceMaps[info.fileIndex].lines[info.line].entries)
gSourceMaps[info.fileIndex].lines[info.line].entries.add(TEntry(pos: info.col, sym: sym))
proc defFromLine(entries: var seq[TEntry], col: int32) =
if entries == nil: return
# The sorting is done lazily here on purpose.
# No need to pay the price for it unless the user requests
# "goto definition" on a particular line
sort(entries) do (a,b: TEntry) -> int:
return cmp(a.pos, b.pos)
for e in entries:
# currently, the line-infos for most expressions point to
# one position past the end of the expression. This means
# that the first expr that ends after the cursor column is
# the one we are looking for.
if e.pos >= col:
suggestWriteln(symToStr(e.sym, isLocal=false, sectionDef))
return
proc defFromSourceMap*(i: TLineInfo) =
if not ((i.fileIndex < gSourceMaps.len) and
(gSourceMaps[i.fileIndex].lines != nil) and
(i.line < gSourceMaps[i.fileIndex].lines.len)): return
defFromLine(gSourceMaps[i.fileIndex].lines[i.line].entries, i.col)
proc suggestSym*(info: TLineInfo; s: PSym) {.inline.} = proc suggestSym*(info: TLineInfo; s: PSym) {.inline.} =
## misnamed: should be 'symDeclared' ## misnamed: should be 'symDeclared'
if optUsages in gGlobalOptions: if gIdeCmd == ideUse:
findUsages(info, s) findUsages(info, s)
if optDef in gGlobalOptions: elif gIdeCmd == ideDef:
findDefinition(info, s) findDefinition(info, s)
if isServing:
addToSourceMap(s, info)
proc markUsed(info: TLineInfo; s: PSym) = proc markUsed(info: TLineInfo; s: PSym) =
incl(s.flags, sfUsed) incl(s.flags, sfUsed)
@ -334,30 +317,28 @@ proc useSym*(sym: PSym): PNode =
markUsed(result.info, sym) markUsed(result.info, sym)
proc suggestExpr*(c: PContext, node: PNode) = proc suggestExpr*(c: PContext, node: PNode) =
var cp = msgs.inCheckpoint(node.info) if nfIsCursor notin node.flags:
if gTrackPos.line < 0: return
var cp = inCheckpoint(node.info)
if cp == cpNone: return if cp == cpNone: return
var outputs = 0 var outputs = 0
# This keeps semExpr() from coming here recursively: # This keeps semExpr() from coming here recursively:
if c.inCompilesContext > 0: return if c.inCompilesContext > 0: return
inc(c.inCompilesContext) inc(c.inCompilesContext)
if optSuggest in gGlobalOptions: if gIdeCmd == ideSug:
var n = findClosestDot(node) var n = if nfIsCursor in node.flags: node else: findClosestDot(node)
if n == nil: n = node if n == nil: n = node
else: cp = cpExact if n.kind == nkDotExpr:
if n.kind == nkDotExpr and cp == cpExact:
var obj = safeSemExpr(c, n.sons[0]) var obj = safeSemExpr(c, n.sons[0])
suggestFieldAccess(c, obj, outputs) suggestFieldAccess(c, obj, outputs)
else: else:
#debug n
suggestEverything(c, n, outputs) suggestEverything(c, n, outputs)
if optContext in gGlobalOptions: elif gIdeCmd == ideCon:
var n = findClosestCall(node) var n = if nfIsCursor in node.flags: node else: findClosestCall(node)
if n == nil: n = node if n == nil: n = node
else: cp = cpExact if n.kind in nkCallKinds:
if n.kind in CallNodes:
var a = copyNode(n) var a = copyNode(n)
var x = safeSemExpr(c, n.sons[0]) var x = safeSemExpr(c, n.sons[0])
if x.kind == nkEmpty or x.typ == nil: x = n.sons[0] if x.kind == nkEmpty or x.typ == nil: x = n.sons[0]
@ -370,13 +351,7 @@ proc suggestExpr*(c: PContext, node: PNode) =
suggestCall(c, a, n, outputs) suggestCall(c, a, n, outputs)
dec(c.inCompilesContext) dec(c.inCompilesContext)
if outputs > 0 and optUsages notin gGlobalOptions: suggestQuit() if outputs > 0 and gIdeCmd != ideUse: suggestQuit()
proc suggestStmt*(c: PContext, n: PNode) = proc suggestStmt*(c: PContext, n: PNode) =
suggestExpr(c, n) suggestExpr(c, n)
proc findSuggest*(c: PContext, n: PNode) =
if n == nil: return
suggestExpr(c, n)
for i in 0.. <safeLen(n):
findSuggest(c, n.sons[i])

View file

@ -1012,7 +1012,7 @@ proc rawExecute(c: PCtx, start: int, tos: PStackFrame): TFullReg =
of opcQuit: of opcQuit:
if c.mode in {emRepl, emStaticExpr, emStaticStmt}: if c.mode in {emRepl, emStaticExpr, emStaticStmt}:
message(c.debug[pc], hintQuitCalled) message(c.debug[pc], hintQuitCalled)
quit(int(getOrdValue(regs[ra].regToNode))) msgQuit(int8(getOrdValue(regs[ra].regToNode)))
else: else:
return TFullReg(kind: rkNone) return TFullReg(kind: rkNone)
of opcSetLenStr: of opcSetLenStr:

View file

@ -599,7 +599,7 @@ proc splitPath*(path: string): tuple[head, tail: string] {.
proc parentDirPos(path: string): int = proc parentDirPos(path: string): int =
var q = 1 var q = 1
if path[len(path)-1] in {DirSep, AltSep}: q = 2 if len(path) >= 1 and path[len(path)-1] in {DirSep, AltSep}: q = 2
for i in countdown(len(path)-q, 0): for i in countdown(len(path)-q, 0):
if path[i] in {DirSep, AltSep}: return i if path[i] in {DirSep, AltSep}: return i
result = -1 result = -1

View file

@ -9,7 +9,6 @@ version 0.10
- iterators always require a return type - iterators always require a return type
- make nimble part of the distribution - make nimble part of the distribution
- split idetools into separate tool
- split docgen into separate tool - split docgen into separate tool
- special rule for ``[]=``, items, pairs - special rule for ``[]=``, items, pairs
- BUG: echo with template `$`*(info: TLineInfo): expr = toFileLineCol(info) - BUG: echo with template `$`*(info: TLineInfo): expr = toFileLineCol(info)

View file

@ -19,6 +19,8 @@ News
- ``logging.level`` and ``logging.handlers`` are no longer exported. - ``logging.level`` and ``logging.handlers`` are no longer exported.
``addHandler``, ``getHandlers``, ``setLogFilter`` and ``getLogFilter`` ``addHandler``, ``getHandlers``, ``setLogFilter`` and ``getLogFilter``
should be used instead. should be used instead.
- ``nim idetools`` has been replaced by a separate tool `nimsuggest`_.
Language Additions Language Additions
------------------ ------------------