'nimrod pretty' command: next steps

This commit is contained in:
Araq 2013-07-29 22:56:32 +02:00
commit 6810a0e3e3
2 changed files with 107 additions and 43 deletions

View file

@ -15,7 +15,8 @@ import
wordrecg, sem, semdata, idents, passes, docgen, extccomp, wordrecg, sem, semdata, idents, passes, docgen, extccomp,
cgen, jsgen, json, nversion, cgen, jsgen, json, nversion,
platform, nimconf, importer, passaux, depends, evals, types, idgen, platform, nimconf, importer, passaux, depends, evals, types, idgen,
tables, docgen2, service, parser, modules, ccgutils, sigmatch, ropes, lists tables, docgen2, service, parser, modules, ccgutils, sigmatch, ropes, lists,
pretty
from magicsys import SystemModule, resetSysTypes from magicsys import SystemModule, resetSysTypes
@ -162,12 +163,18 @@ proc commandEval(exp: string) =
var echoExp = "echo \"eval\\t\", " & "repr(" & exp & ")" var echoExp = "echo \"eval\\t\", " & "repr(" & exp & ")"
evalNim(echoExp.parseString, makeStdinModule()) evalNim(echoExp.parseString, makeStdinModule())
proc CommandPretty = proc CommandPrettyOld =
var projectFile = addFileExt(mainCommandArg(), NimExt) var projectFile = addFileExt(mainCommandArg(), NimExt)
var module = parseFile(projectFile.fileInfoIdx) var module = parseFile(projectFile.fileInfoIdx)
if module != nil: if module != nil:
renderModule(module, getOutFile(mainCommandArg(), "pretty." & NimExt)) renderModule(module, getOutFile(mainCommandArg(), "pretty." & NimExt))
proc CommandPretty =
semanticPasses()
registerPass(prettyPass)
compileProject()
pretty.overwriteFiles()
proc CommandScan = proc CommandScan =
var f = addFileExt(mainCommandArg(), nimExt) var f = addFileExt(mainCommandArg(), nimExt)
var stream = LLStreamOpen(f, fmRead) var stream = LLStreamOpen(f, fmRead)

View file

@ -11,7 +11,8 @@
## to convert Nimrod code into a consistent style. ## to convert Nimrod code into a consistent style.
import import
os, options, ast, astalgo, msgs, ropes, idents, passes, importer strutils, os, options, ast, astalgo, msgs, ropes, idents, passes, pegs,
intsets
type type
TGen = object of TPassContext TGen = object of TPassContext
@ -23,54 +24,110 @@ type
dirty: bool dirty: bool
fullpath: string fullpath: string
proc addSourceLine(fileIdx: int32, line: string) = var
fileInfos[fileIdx].lines.add line gSourceFiles: seq[TSourceFile]
proc sourceLine(i: TLineInfo): PRope = proc loadFile(info: TLineInfo) =
if i.fileIndex < 0: return nil let i = info.fileIndex
if i >= gSourceFiles.len:
gSourceFiles.setLen(i)
gSourceFiles[i].lines = @[]
let path = info.toFullPath
gSourceFiles[i].fullpath = path
# we want to die here for EIO:
for line in lines(path):
gSourceFiles[i].lines.add(line)
if not optPreserveOrigSource and fileInfos[i.fileIndex].lines.len == 0: proc overwriteFiles*() =
try: for i in 0 .. high(gSourceFiles):
for line in lines(i.toFullPath): if not gSourceFiles[i].dirty: continue
addSourceLine i.fileIndex, line.string var f = open(gSourceFiles[i].fullpath.changeFileExt(".pretty.nim"), fmWrite)
except EIO: for line in gSourceFiles[i].lines:
discard f.writeln(line)
InternalAssert i.fileIndex < fileInfos.len f.close
# can happen if the error points to EOF:
if i.line > fileInfos[i.fileIndex].lines.len: return nil
result = fileInfos[i.fileIndex].lines[i.line-1] proc beautifyName(s: string, k: TSymKind): string =
result = newStringOfCap(s.len)
var i = 0
case k
of skType, skGenericParam:
# skip leading 'T'
if s[0] == 'T' and s[1] in {'A'..'Z'}:
i = 1
result.add toUpper(s[i])
of skConst, skEnumField:
# for 'const' we keep how it's spelt; either upper case or lower case:
result.add s[0]
else:
result.add toLower(s[0])
inc i
let allUpper = allCharsInSet(s, {'A'..'Z', '0'..'9', '_'})
while i < s.len:
if s[i] == '_':
inc i
result.add toUpper(s[i])
elif allUpper:
result.add toLower(s[i])
else:
result.add s[i]
inc i
proc addDependencyAux(importing, imported: string) = const
appf(gDotGraph, "$1 -> $2;$n", [toRope(importing), toRope(imported)]) Letters = {'a'..'z', 'A'..'Z', '0'..'9', '\x80'..'\xFF', '_'}
# s1 -> s2_4[label="[0-9]"];
proc addDotDependency(c: PPassContext, n: PNode): PNode = proc identLen(line: string, start: int): int =
while start+result < line.len and line[start+result] in Letters:
inc result
proc differ(line: string, a, b: int, x: string): bool =
var j = 0
for i in a..b:
if line[i] != x[j]: return true
inc j
return false
var cannotRename = initIntSet()
proc processSym(c: PPassContext, n: PNode): PNode =
result = n result = n
var g = PGen(c) var g = PGen(c)
case n.kind case n.kind
of nkSym: of nkSym:
if n.info.fileIndex < 0: return
let s = n.sym
# operators stay as they are:
if s.kind == skTemp or s.name.s[0] notin Letters: return
of nkTypeSection: if s.id in cannotRename: return
# we need to figure out whether the PType or the TType should become
# Type. The other then is either TypePtr/TypeRef or TypeDesc.
of nkImportStmt: let newName = beautifyName(s.name.s, n.sym.kind)
for i in countup(0, sonsLen(n) - 1):
var imported = getModuleName(n.sons[i]) loadFile(n.info)
addDependencyAux(g.module.name.s, imported)
of nkFromStmt, nkImportExceptStmt: let line = gSourceFiles[n.info.fileIndex].lines[n.info.line-1]
var imported = getModuleName(n.sons[0]) var first = n.info.col.int - len(s.name.s)
addDependencyAux(g.module.name.s, imported) if line[first] == '`': inc first
of nkStmtList, nkBlockStmt, nkStmtListExpr, nkBlockExpr:
for i in countup(0, sonsLen(n) - 1): discard addDotDependency(c, n.sons[i]) if {sfImportc, sfExportc} * s.flags != {}:
# careful, we must ensure the resulting name still matches the external
# name:
if newName != s.name.s and newName != s.loc.r.ropeToStr:
Message(n.info, errGenerated,
"cannot rename $# to $# due to external name" % [s.name.s, newName])
cannotRename.incl(s.id)
return
let last = first+identLen(line, first)-1
if last-first+1 != newName.len or differ(line, first, last, newName):
var x = line.subStr(0, first-1) & newName & line.substr(last+1)
# the WinAPI module is full of 'TX = X' which after the substitution
# becomes 'X = X'. We remove those lines:
if x.match(peg"\s* {\ident} \s* '=' \s* y$1 ('#' .*)?"):
x = ""
system.shallowCopy(gSourceFiles[n.info.fileIndex].lines[n.info.line-1], x)
gSourceFiles[n.info.fileIndex].dirty = true
else: else:
nil for i in 0 .. <n.safeLen:
discard processSym(c, n.sons[i])
proc generateRefactorScript*(project: string) =
writeRope(ropef("digraph $1 {$n$2}$n", [
toRope(changeFileExt(extractFileName(project), "")), gDotGraph]),
changeFileExt(project, "dot"))
proc myOpen(module: PSym): PPassContext = proc myOpen(module: PSym): PPassContext =
var g: PGen var g: PGen
@ -78,5 +135,5 @@ proc myOpen(module: PSym): PPassContext =
g.module = module g.module = module
result = g result = g
const prettyPass* = makePass(open = myOpen, process = addDotDependency) const prettyPass* = makePass(open = myOpen, process = processSym)