viv-compiler 0.1.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,111 @@
1
+ """Module that handles importing between Viv files.
2
+
3
+ The entrypoint function is `integrate_imported_files()`, and everything else is only meant to be
4
+ invoked internally, i.e., within this module.
5
+ """
6
+
7
+ __all__ = ["integrate_imported_files"]
8
+
9
+ import arpeggio
10
+ import viv_compiler.types
11
+ from pathlib import Path
12
+ from .visitor import Visitor
13
+ # noinspection PyUnresolvedReferences
14
+ from arpeggio.cleanpeg import ParserPEG
15
+
16
+
17
+ def integrate_imported_files(
18
+ viv_parser: ParserPEG,
19
+ ast: viv_compiler.types.AST,
20
+ entry_point_file_path: Path,
21
+ ) -> viv_compiler.types.CombinedAST:
22
+ """Handle any include declarations in the given AST (including any recursive ones) and return a dictionary
23
+ containing trope definitions and action definitions.
24
+
25
+ Args:
26
+ viv_parser: A prepared Viv parser.
27
+ ast: An abstract syntax tree produced by the Visitor class.
28
+ entry_point_file_path: The file path for the file that is being directly compiled by the author.
29
+
30
+ Returns:
31
+ A dictionary containing trope definitions and action definitions.
32
+ """
33
+ # First, we need to recursively gather all included files. If we do this step first, we don't
34
+ # need to worry about circular dependencies (Viv allows circular imports).
35
+ all_included_file_paths = _compile_all_included_file_paths(
36
+ viv_parser=viv_parser,
37
+ ast=ast,
38
+ anchor_dir=entry_point_file_path.parent,
39
+ all_included_absolute_file_paths={entry_point_file_path}
40
+ )
41
+ # We'll start with the AST for the user's entry file. If there's no includes,
42
+ # will end up being the combined AST.
43
+ combined_ast: viv_compiler.types.CombinedAST = {"tropes": ast["tropes"], "actions": ast["actions"]}
44
+ # Now let's parse each of the included files to build up a combined AST
45
+ for included_file_path in all_included_file_paths:
46
+ # Load and parse the file
47
+ code = included_file_path.read_text(encoding="utf-8")
48
+ # Parse the file
49
+ try:
50
+ tree = viv_parser.parse(_input=code)
51
+ except arpeggio.NoMatch as parsing_error:
52
+ raise Exception(
53
+ f"Error encountered while parsing included file '{included_file_path}': "
54
+ f"{str(parsing_error)}"
55
+ )
56
+ included_file_ast = arpeggio.visit_parse_tree(tree, Visitor())
57
+ # Append its trope definitions
58
+ included_trope_definitions = included_file_ast["tropes"]
59
+ combined_ast["tropes"] += included_trope_definitions
60
+ # Append its action definitions
61
+ included_action_definitions = included_file_ast["actions"]
62
+ combined_ast["actions"] += included_action_definitions
63
+ # Return the combined AST
64
+ return combined_ast
65
+
66
+
67
+ def _compile_all_included_file_paths(
68
+ viv_parser: ParserPEG,
69
+ ast: viv_compiler.types.AST,
70
+ anchor_dir: Path,
71
+ all_included_absolute_file_paths: set[Path],
72
+ ) -> list[Path]:
73
+ """Return a list containing absolute paths for all the files to be included in the one at hand.
74
+
75
+ Critically, this method is robust to circular includes, however arcane, and it captures arbitrarily
76
+ recursive includes.
77
+
78
+ Args:
79
+ viv_parser: A prepared Viv parser.
80
+ ast: An abstract syntax tree produced by the Visitor class.
81
+ all_included_absolute_file_paths: A running list containing absolute paths for all the
82
+ files to (recursively) include.
83
+
84
+ Returns:
85
+ A list of all (recursively) included absolute file paths.
86
+ """
87
+ new_included_absolute_file_paths: list[Path] = []
88
+ for included_file_relative_path in ast["_includes"]:
89
+ included_file_absolute_path = (anchor_dir / included_file_relative_path).resolve()
90
+ if included_file_absolute_path in all_included_absolute_file_paths: # Already captured this one
91
+ continue
92
+ new_included_absolute_file_paths.append(included_file_absolute_path)
93
+ all_included_absolute_file_paths.add(included_file_absolute_path)
94
+ try:
95
+ source_code = included_file_absolute_path.read_text(encoding="utf-8")
96
+ except FileNotFoundError:
97
+ raise FileNotFoundError(f"Bad 'include' declaration (file not found): {included_file_relative_path}")
98
+ try:
99
+ tree = viv_parser.parse(_input=source_code)
100
+ except arpeggio.NoMatch as parsing_error:
101
+ raise Exception(
102
+ f"Error encountered while parsing included file '{included_file_relative_path}': {parsing_error}"
103
+ )
104
+ included_file_ast = arpeggio.visit_parse_tree(tree, Visitor())
105
+ new_included_absolute_file_paths += _compile_all_included_file_paths(
106
+ viv_parser=viv_parser,
107
+ ast=included_file_ast,
108
+ anchor_dir=included_file_absolute_path.parent,
109
+ all_included_absolute_file_paths=all_included_absolute_file_paths,
110
+ )
111
+ return new_included_absolute_file_paths