source: doc/proposals/modules-alvin/3_implementation_details/prototype/Driver.py@ a9049dd

Last change on this file since a9049dd was a9049dd, checked in by Alvin Zhang <alvin.zhang@…>, 8 weeks ago

Prototype code for modules

  • Property mode set to 100644
File size: 12.2 KB
Line 
1import argparse
2import os
3from pprint import pprint
4from antlr4 import *
5from parser.CMODLexer import CMODLexer
6from parser.CMODParser import CMODParser
7
8def parse_args():
9 parser = argparse.ArgumentParser()
10 parser.add_argument("input_file", help="file to parse")
11 parser.add_argument("-r", "--project_root", help="the root folder of the project, used to find imported modules. All modules, including imports, should be within this folder")
12 args = parser.parse_args()
13 return args
14
15def main():
16 args = parse_args()
17 generate_code(args)
18
19def generate_code(args):
20 # args.input_file
21 # args.project_root
22
23 # This changes the OS directory to be project_root, and removes the suffix from input_file
24 # (output: input_module)
25 input_file = os.path.relpath(args.input_file, args.project_root)
26 os.chdir(args.project_root)
27 dot_index = input_file.rfind('.')
28 if dot_index < 0 or input_file[dot_index:] != '.cmod':
29 raise RuntimeError("input_file must use .cmod suffix")
30 input_module = input_file[:dot_index]
31
32 # Parses AST of a given module
33 # (output: get_module_info())
34 module_asts = {} # dict[module_name: str, (text: str, tokens, tree)]
35 def get_module_info(module_name: str):
36 def insert_module_info(module_name):
37 input_stream = FileStream(module_name + ".cmod")
38 text = str(input_stream)
39 lexer = CMODLexer(input_stream)
40 tokens = lexer.getAllTokens() # list[token: {text, start, stop, line, column}]
41 # stop is inclusive, just like getSourceInterval
42 lexer.reset()
43 stream = CommonTokenStream(lexer)
44 parser = CMODParser(stream)
45 tree = parser.compilationUnit() # {getSourceInterval, getChildren, getChildCount, getChild, getText}
46 if parser.getNumberOfSyntaxErrors() > 0:
47 # Let it keep going with errors
48 print(f"// ERROR: syntax errors for file {module_name}")
49 module_asts[module_name] = text, tokens, tree
50
51 if module_name not in module_asts:
52 insert_module_info(module_name)
53 return module_asts[module_name]
54
55 # Gets imports of module
56 # (output: get_imports())
57 module_imports = {} # dict[module_name: str, list[module_name: str]]
58 def get_imports(module_name: str):
59 def insert_imports(module_name):
60 _, _, tree = get_module_info(module_name)
61 import_names = [] # list[module_name: str]
62 for importDeclaration in tree.translationUnit().importDeclaration():
63 import_names.append(importDeclaration.Identifier().getText())
64 module_imports[module_name] = import_names
65
66 if module_name not in module_imports:
67 insert_imports(module_name)
68 return module_imports[module_name]
69
70 # Gets info for symbols of module
71 # (output: get_symbol_list())
72 module_symbols = {} # dict[module_name: str, list[(symbol_name: str, idx: int, exported: bool)]]
73 def get_symbol_list(module_name: str):
74 def insert_symbol_list(module_name):
75 _, _, tree = get_module_info(module_name)
76 symbol_list = [] # list[(symbol_name: str, idx: int, exported: bool)]
77 for idx, externalDeclaration in enumerate(tree.translationUnit().externalDeclaration()):
78 exported = externalDeclaration.getChild(0).getText() == 'export'
79 structDefinition = externalDeclaration.structDefinition()
80 globalDefinition = externalDeclaration.globalDefinition()
81 functionDefinition = externalDeclaration.functionDefinition()
82 if structDefinition:
83 symbol_name = structDefinition.Identifier().getText()
84 elif globalDefinition:
85 symbol_name = globalDefinition.declarator().Identifier().getText()
86 elif functionDefinition:
87 symbol_name = functionDefinition.declarator().Identifier().getText()
88 symbol_list.append((symbol_name, idx, exported))
89 module_symbols[module_name] = symbol_list
90
91 if module_name not in module_symbols:
92 insert_symbol_list(module_name)
93 return module_symbols[module_name]
94
95 # Gets info for symbols via lookup
96 # (output: lookup_symbol())
97 module_lookup = {} # dict[parent_module_name: str, dict[symbol_name: str, (module_name: str, idx: int)]]
98 def lookup_symbol(parent_module_name: str, symbol_name: str) -> tuple[str, int] | None:
99 def generate_symbol_table(parent_module_name):
100 symbol_table = {} # dict[symbol_name: str, (module_name: str, idx: int)]
101 def add_to_symbol_table(symbol_name, module_name, idx):
102 if symbol_name in symbol_table:
103 # Let it keep going with errors
104 print(f"// ERROR: symbol name clash for {symbol_name}")
105 else:
106 symbol_table[symbol_name] = module_name, idx
107 for symbol_name, idx, _ in get_symbol_list(parent_module_name):
108 add_to_symbol_table(symbol_name, parent_module_name, idx)
109 for module_name in get_imports(parent_module_name):
110 for symbol_name, idx, exported in get_symbol_list(module_name):
111 if exported:
112 add_to_symbol_table(symbol_name, module_name, idx)
113 module_lookup[parent_module_name] = symbol_table
114
115 if parent_module_name not in module_lookup:
116 generate_symbol_table(parent_module_name)
117 if symbol_name in module_lookup[parent_module_name]:
118 return module_lookup[parent_module_name][symbol_name]
119 else:
120 # if symbol not found, hopefully it's fine
121 return None
122
123 # Generate the supporting code for a given symbol definition
124 # (output: codegen())
125 visited = {} # dict[(module_name: str, idx: int), is_defn: bool]
126 visiting = set() # set[(module_name: str, idx: int)]
127 def codegen(parent_module_name: str, idx: int, needs_defn: bool):
128 def codegen_type_info(typeSpecifier, uses_defn):
129 struct_identifier = typeSpecifier.Identifier()
130 if not struct_identifier:
131 return
132 res = lookup_symbol(parent_module_name, struct_identifier.getText())
133 if not res:
134 return
135 module_name, idx = res
136 codegen(module_name, idx, uses_defn)
137 def codegen_expression_info(expression):
138 for expr in expression.expression():
139 codegen_expression_info(expr)
140 typeSpecifier = expression.typeSpecifier()
141 if typeSpecifier:
142 codegen_type_info(typeSpecifier, expression.getChildCount() == 1)
143 identifier = expression.Identifier()
144 if identifier:
145 res = lookup_symbol(parent_module_name, identifier.getText())
146 if res:
147 module_name, idx = res
148 codegen(module_name, idx, False)
149
150 if (parent_module_name, idx) in visited:
151 # Unless we now need the definition, we can skip
152 if visited[(parent_module_name, idx)] == True or not needs_defn:
153 return
154 if (parent_module_name, idx) in visiting:
155 text, tokens, tree = get_module_info(parent_module_name)
156 structDefinition = tree.translationUnit().externalDeclaration(idx).structDefinition()
157 if not needs_defn and structDefinition:
158 # Special case: can revisit struct as a declaration
159 token_start = structDefinition.getSourceInterval()[0]
160 token_end = structDefinition.Identifier().getSourceInterval()[1]
161 text_start, text_end = tokens[token_start].start, tokens[token_end].stop
162 code = text[text_start:text_end+1]
163 code += ';'
164 print(code)
165 return
166 else:
167 # Let it keep going with errors
168 print(f"// ERROR: circular reference!")
169 return
170 visiting.add((parent_module_name, idx))
171
172 # get symbol definition
173 text, tokens, tree = get_module_info(parent_module_name)
174 externalDeclaration = tree.translationUnit().externalDeclaration(idx)
175 structDefinition = externalDeclaration.structDefinition()
176 globalDefinition = externalDeclaration.globalDefinition()
177 functionDefinition = externalDeclaration.functionDefinition()
178 if structDefinition:
179 # call codegen on children
180 if needs_defn:
181 for structField in structDefinition.structField():
182 codegen_type_info(structField.typeSpecifier(),
183 structField.declarator().getChildCount() == 1)
184 # print out declaration / definition
185 token_start, token_end = structDefinition.getSourceInterval()
186 if not needs_defn:
187 token_end = structDefinition.Identifier().getSourceInterval()[1]
188 text_start, text_end = tokens[token_start].start, tokens[token_end].stop
189 code = text[text_start:text_end+1]
190 if not needs_defn:
191 code += ';'
192 print(code)
193 elif globalDefinition:
194 # call codegen on children
195 codegen_type_info(globalDefinition.typeSpecifier(),
196 globalDefinition.declarator().getChildCount() == 1)
197 if needs_defn and globalDefinition.expression():
198 codegen_expression_info(globalDefinition.expression())
199 # print out declaration / definition
200 token_start, token_end = globalDefinition.getSourceInterval()
201 if not needs_defn:
202 token_end = globalDefinition.declarator().getSourceInterval()[1]
203 text_start, text_end = tokens[token_start].start, tokens[token_end].stop
204 code = text[text_start:text_end+1]
205 if not needs_defn:
206 code = 'extern ' + code + ';'
207 print(code)
208 elif functionDefinition:
209 # call codegen on children
210 codegen_type_info(functionDefinition.typeSpecifier(),
211 functionDefinition.declarator().getChildCount() == 1)
212 parameterList = functionDefinition.parameterList()
213 if parameterList:
214 for typeSpecifier, declarator in zip(parameterList.typeSpecifier(),
215 parameterList.declarator()):
216 codegen_type_info(typeSpecifier, declarator.getChildCount() == 1)
217 if needs_defn:
218 for statement in functionDefinition.compoundStatement().statement():
219 codegen_expression_info(statement.expression())
220 # print out declaration / definition
221 token_start, token_end = functionDefinition.getSourceInterval()
222 if not needs_defn:
223 token_end = functionDefinition.compoundStatement().getSourceInterval()[0] - 1
224 text_start, text_end = tokens[token_start].start, tokens[token_end].stop
225 code = text[text_start:text_end+1]
226 if not needs_defn:
227 code += ';'
228 print(code)
229
230 visiting.remove((parent_module_name, idx))
231 visited[(parent_module_name, idx)] = needs_defn
232
233 # Now drive codegen through the input module
234 _, _, tree = get_module_info(input_module)
235 for _, idx, _ in get_symbol_list(input_module):
236 codegen(input_module, idx, True)
237
238def simple_parse(input_file):
239 # Used in debugging
240 input_stream = FileStream(input_file)
241 lexer = CMODLexer(input_stream)
242 stream = CommonTokenStream(lexer)
243 parser = CMODParser(stream)
244 tree = parser.compilationUnit()
245 print(tree.getText())
246
247def details(ast):
248 # Used in debugging
249 print(f"Details for {repr(ast)}")
250 num_auto_expand = 0
251 while ast.getChildCount() == 1:
252 ast = ast.getChild(0)
253 num_auto_expand += 1
254 print(repr(ast))
255 if num_auto_expand:
256 print(f"auto expanded {num_auto_expand} times")
257 n = ast.getChildCount()
258 print(n)
259 for i in range(n):
260 print()
261 print(i)
262 print(repr(ast.getChild(i)))
263 print(ast.getChild(i).getText())
264
265if __name__ == '__main__':
266 main()
Note: See TracBrowser for help on using the repository browser.