aboutsummaryrefslogtreecommitdiff
path: root/examples/tomodeps.tm
diff options
context:
space:
mode:
Diffstat (limited to 'examples/tomodeps.tm')
-rw-r--r--examples/tomodeps.tm189
1 files changed, 83 insertions, 106 deletions
diff --git a/examples/tomodeps.tm b/examples/tomodeps.tm
index 032a87b7..eeeffde6 100644
--- a/examples/tomodeps.tm
+++ b/examples/tomodeps.tm
@@ -1,5 +1,4 @@
# Show a Tomo dependency graph
-use file
_USAGE := "Usage: dependencies <files...>"
@@ -8,124 +7,101 @@ _HELP := "
$_USAGE
"
-func _get_module_imports_from_file(file:Text, imports:&{Text}, visited_files:&{Text}):
- return if visited_files:has(file)
+enum Dependency(File(path:Path), Module(name:Text))
- reader := when LineReader.from_file(file) is Failure(msg):
- !! Failed: $msg
- return
- is Open(reader): reader
-
- visited_files:add(file)
+func _get_file_dependencies(file:Path)->{Dependency}:
+ if not file:is_file():
+ !! Could not read file: $file
+ return {:Dependency}
- while when reader:next_line() is Success(line):
+ deps := {:Dependency}
+ for line in file:read():lines():
if line:matches($/use {..}.tm/):
- local_import := line:replace($/use {..}/, "\1")
- resolved := relative_path(resolve_path(local_import, file))
- if resolved != "":
- local_import = resolved
- _get_module_imports_from_file(local_import, imports, visited_files)
+ file_import := Path.from_unsafe_text(line:replace($/use {..}/, "\1")):resolved(relative_to=file)
+ deps:add(Dependency.File(file_import))
else if line:matches($/use {id}/):
- other_module := line:replace($/use {..}/, "\1")
- imports:add(other_module)
-
-func _get_module_imports_from_module(module:Text)->{Text}:
- files_path := resolve_path("~/.local/src/tomo/$module/lib$(module).files")
- if files_path == "":
- !! couldn't resolve: $files_path
- return {:Text}
-
- when read(files_path) is Failure(msg):
- !! couldn't read: $files_path $msg
- return {:Text}
- is Success(files_content):
- imports := {:Text}
- visited := {:Text}
- for line in files_content:lines():
- line_resolved := resolve_path(line, relative_to="~/.local/src/tomo/$module/")
- skip if line_resolved == ""
- _get_module_imports_from_file(line_resolved, &imports, &visited)
- return imports
-
-func _build_module_dependency_graph(module:Text, dependencies:&{Text:@{Text}}):
- return if dependencies:has(module)
-
- module_deps := @{:Text}
- dependencies:set(module, module_deps)
-
- for dep in _get_module_imports_from_module(module):
- module_deps:add(dep)
- _build_module_dependency_graph(dep, dependencies)
-
-
-func _build_file_dependency_graph(filename:Text, dependencies:&{Text:@{Text}}):
- return if dependencies:has(filename)
-
- reader := when LineReader.from_file(filename) is Failure(msg):
- !! Failed: $msg
+ module_name := line:replace($/use {..}/, "\1")
+ deps:add(Dependency.Module(module_name))
+ return deps
+
+func _build_dependency_graph(dep:Dependency, dependencies:&{Dependency:{Dependency}}):
+ return if dependencies:has(dep)
+
+ dependencies:set(dep, {:Dependency}) # Placeholder
+
+ dep_deps := when dep is File(path):
+ _get_file_dependencies(path)
+ is Module(module):
+ files_path := (~/.local/src/tomo/$module/lib$module.files):resolved()
+ if not files_path:is_file():
+ !! Could not read file: $files_path
+ return
+
+ unvisited := {:Path}
+ for line in files_path:read():lines():
+ tm_path := Path.from_unsafe_text(line):resolved(relative_to=(~/.local/src/tomo/$module/))
+ unvisited:add(tm_path)
+
+ module_deps := {:Dependency}
+ visited := {:Path}
+ while unvisited.length > 0:
+ file := unvisited.items[-1]
+ unvisited:remove(file)
+ visited:add(file)
+
+ for file_dep in _get_file_dependencies(file):
+ when file_dep is File(f):
+ if not visited:has(f):
+ unvisited:add(f)
+ is Module(m):
+ module_deps:add(file_dep)
+ module_deps
+
+ dependencies:set(dep, dep_deps)
+
+ for dep2 in dep_deps:
+ _build_dependency_graph(dep2, dependencies)
+
+func get_dependency_graph(file:Path)->{Dependency:{Dependency}}:
+ graph := {:Dependency:{Dependency}}
+ _build_dependency_graph(Dependency.File(file:resolved()), &graph)
+ return graph
+
+func _printable_name(dep:Dependency)->Text:
+ when dep is Module(module):
+ return "$(\x1b)[34;1m$module$(\x1b)[m"
+ is File(f):
+ f = f:relative()
+ if f:exists():
+ return "$(f.text_content)"
+ else:
+ return "$(\x1b)[31;1m$(f.text_content) (not found)$(\x1b)[m"
+
+func _draw_tree(dep:Dependency, dependencies:{Dependency:{Dependency}}, already_printed:&{Dependency}, prefix="", is_last=yes):
+ if already_printed:has(dep):
+ say(prefix ++ (if is_last: "└── " else: "├── ") ++ _printable_name(dep) ++ " $\x1b[2m(recursive)$\x1b[m")
return
- is Open(reader): reader
-
- file_deps := @{:Text}
- dependencies:set(filename, file_deps)
- while when reader:next_line() is Success(line):
- if line:matches($/use {..}.tm/):
- used_file := line:replace($/use {..}/, "\1")
- resolved := relative_path(resolve_path(used_file, filename))
- if resolved != "":
- used_file = resolved
-
- file_deps:add(used_file)
- _build_file_dependency_graph(used_file, dependencies)
- else if line:matches($/use {id}/):
- module := line:replace($/use {..}/, "\1")
- file_deps:add(module)
- _build_module_dependency_graph(module, dependencies)
-
-
-func get_dependency_graph(file:Text)->{Text:{Text}}:
- graph := {:Text:@{Text}}
- resolved := relative_path(file)
- _build_file_dependency_graph(resolved, &graph)
- return {f:deps[] for f,deps in graph}
-
-func _draw_tree(file:Text, dependencies:{Text:{Text}}, already_printed:&{Text}, prefix="", is_last=yes):
- color_file := if file:matches($/{id}/):
- "$\x1b[34;1m$file$\x1b[m"
- else if resolve_path(file) != "":
- file
- else:
- "$\x1b[31;1m$file (could not resolve)$\x1b[m"
-
- if already_printed:has(file):
- say(prefix ++ (if is_last: "└── " else: "├── ") ++ color_file ++ " $\x1b[2m(recursive)$\x1b[m")
- return
-
- say(prefix ++ (if is_last: "└── " else: "├── ") ++ color_file)
- already_printed:add(file)
+ say(prefix ++ (if is_last: "└── " else: "├── ") ++ _printable_name(dep))
+ already_printed:add(dep)
child_prefix := prefix ++ (if is_last: " " else: "│ ")
- children := dependencies:get(file, {:Text})
+ children := dependencies:get(dep, {:Dependency})
for i,child in children.items:
is_child_last := (i == children.length)
_draw_tree(child, dependencies, already_printed, child_prefix, is_child_last)
-func draw_tree(file:Text, dependencies:{Text:{Text}}):
- printed := {:Text}
- resolved := relative_path(file)
- if resolved != "":
- file = resolved
-
- say(file)
- printed:add(file)
- children := dependencies:get(file, {:Text})
- for i,child in children.items:
- is_child_last := (i == children.length)
+func draw_tree(dep:Dependency, dependencies:{Dependency:{Dependency}}):
+ printed := {:Dependency}
+ say(_printable_name(dep))
+ printed:add(dep)
+ deps := dependencies:get(dep, {:Dependency})
+ for i,child in deps.items:
+ is_child_last := (i == deps.length)
_draw_tree(child, dependencies, already_printed=&printed, is_last=is_child_last)
-func main(files:[Text]):
+func main(files:[Path]):
if files.length == 0:
exit(1, message="
Please provide at least one file!
@@ -133,10 +109,11 @@ func main(files:[Text]):
")
for file in files:
- if not file:matches($/{..}.tm/):
+ if not file.text_content:matches($/{..}.tm/):
say("$\x1b[2mSkipping $file$\x1b[m")
skip
+ file = file:resolved()
dependencies := get_dependency_graph(file)
- draw_tree(file, dependencies)
+ draw_tree(Dependency.File(file), dependencies)