| 1 | import argparse
|
|---|
| 2 | import os
|
|---|
| 3 | from pprint import pprint
|
|---|
| 4 | from antlr4 import *
|
|---|
| 5 | from parser.CMODLexer import CMODLexer
|
|---|
| 6 | from parser.CMODParser import CMODParser
|
|---|
| 7 |
|
|---|
| 8 | def 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 |
|
|---|
| 15 | def main():
|
|---|
| 16 | args = parse_args()
|
|---|
| 17 | generate_code(args)
|
|---|
| 18 |
|
|---|
| 19 | def 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 |
|
|---|
| 238 | def 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 |
|
|---|
| 247 | def 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 |
|
|---|
| 265 | if __name__ == '__main__':
|
|---|
| 266 | main()
|
|---|