diff options
author | Araq <rumpf_a@web.de> | 2016-11-05 09:27:46 +0100 |
---|---|---|
committer | Araq <rumpf_a@web.de> | 2016-11-05 09:27:46 +0100 |
commit | 8fa3e0cb25c7fec8ea727270dec7700a2ec271e5 (patch) | |
tree | 361ddb292fc5115756f24fef9bffeab978352925 | |
parent | 9e6fb3f696b2d28f900506b531a916457b4a263c (diff) | |
download | Nim-8fa3e0cb25c7fec8ea727270dec7700a2ec271e5.tar.gz |
added missing module
-rw-r--r-- | compiler/modulegraphs.nim | 92 |
1 files changed, 92 insertions, 0 deletions
diff --git a/compiler/modulegraphs.nim b/compiler/modulegraphs.nim new file mode 100644 index 000000000..2c1671789 --- /dev/null +++ b/compiler/modulegraphs.nim @@ -0,0 +1,92 @@ +# +# +# The Nim Compiler +# (c) Copyright 2016 Andreas Rumpf +# +# See the file "copying.txt", included in this +# distribution, for details about the copyright. +# + +## This module implements the module graph data structure. The module graph +## represents a complete Nim project. Single modules can either be kept in RAM +## or stored in a ROD file. The ROD file mechanism is not yet integrated here. +## +## The caching of modules is critical for 'nimsuggest' and is tricky to get +## right. If module E is being edited, we need autocompletion (and type +## checking) for E but we don't want to recompile depending +## modules right away for faster turnaround times. Instead we mark the module's +## dependencies as 'dirty'. Let D be a dependency of E. If D is dirty, we +## need to recompile it and all of its dependencies that are marked as 'dirty'. +## 'nimsuggest sug' actually is invoked for the file being edited so we know +## its content changed and there is no need to compute any checksums. +## Instead of a recursive algorithm, we use an iterative algorithm: +## +## - If a module gets recompiled, its dependencies need to be updated. +## - Its dependent module stays the same. +## + +import ast, intsets + +type + ModuleGraph* = ref object + modules*: seq[PSym] ## indexed by int32 fileIdx + packageSyms*: TStrTable + deps*: IntSet # the dependency graph or potentially its transitive closure. + suggestMode*: bool # whether we are in nimsuggest mode or not. + invalidTransitiveClosure: bool + +{.this: g.} + +proc newModuleGraph*(): ModuleGraph = + result = ModuleGraph() + initStrTable(result.packageSyms) + result.deps = initIntSet() + result.modules = @[] + +proc resetAllModules*(g: ModuleGraph) = + initStrTable(packageSyms) + deps = initIntSet() + modules = @[] + +proc getModule*(g: ModuleGraph; fileIdx: int32): PSym = + if fileIdx >= 0 and fileIdx < modules.len: + result = modules[fileIdx] + +proc dependsOn(a, b: int): int {.inline.} = (a shl 15) + b + +proc addDep*(g: ModuleGraph; m: PSym, dep: int32) = + if suggestMode: + deps.incl m.position.dependsOn(dep) + # we compute the transitive closure later when quering the graph lazily. + # this improve efficiency quite a lot: + invalidTransitiveClosure = true + +proc transitiveClosure(g: var IntSet; n: int) = + # warshall's algorithm + for k in 0..<n: + for i in 0..<n: + for j in 0..<n: + if i != j and not g.contains(i.dependsOn(j)): + if g.contains(i.dependsOn(k)) and g.contains(k.dependsOn(j)): + g.incl i.dependsOn(j) + +proc markDirty*(g: ModuleGraph; fileIdx: int32) = + let m = getModule fileIdx + if m != nil: incl m.flags, sfDirty + +proc markClientsDirty*(g: ModuleGraph; fileIdx: int32) = + # we need to mark its dependent modules D as dirty right away because after + # nimsuggest is done with this module, the module's dirty flag will be + # cleared but D still needs to be remembered as 'dirty'. + if invalidTransitiveClosure: + invalidTransitiveClosure = false + transitiveClosure(deps, modules.len) + + # every module that *depends* on this file is also dirty: + for i in 0i32..<modules.len.int32: + let m = modules[i] + if m != nil and deps.contains(i.dependsOn(fileIdx)): + incl m.flags, sfDirty + +proc isDirty*(g: ModuleGraph; m: PSym): bool = + result = suggestMode and sfDirty in m.flags |