-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathcompiler.py
98 lines (80 loc) · 3.18 KB
/
compiler.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
import argparse
import os
import platform
from lexer import lex, Token
from parser.ast import Program
from parser.parser import Parser
from parser.parserprinter import ParserPrinter
from assembly.ast import AssemblyASTNode
from assembly.generator import Generator
from assembly.generatorprinter import GeneratorPrinter
from codeemitter import CodeEmitter
from typing import List
def main() -> None:
"""
Main function that orchestrates the compilation process of a C source file.
It handles command-line arguments to control the compilation steps, including
preprocessing, lexical analysis, parsing, code generation, and assembly.
The final output is an executable file generated using GCC.
"""
# Determine the operating system to handle platform-specific differences.
os_name_platform: str = platform.system()
print(f"\nOS name (platform.system()): {os_name_platform}\n")
# Set up command-line argument parsing to control the compilation process.
argparser = argparse.ArgumentParser(description="Driver program")
argparser.add_argument('path', help='Path to the source file to compile')
argparser.add_argument('--lex', action='store_true')
argparser.add_argument('--parse', action='store_true')
argparser.add_argument('--codegen', action='store_true')
argparser.add_argument('-S', action='store_true')
args = argparser.parse_args()
if len(args.path) < 1:
raise ValueError(f'Missing path to the source file to compile\n')
if not args.path.endswith('.c'):
raise ValueError(
f'File name\nFile extension must be .c not {args.path}\n')
path_i: str = args.path[:-2] + '.i' # preprocessed file name
path_s: str = args.path[:-2] + '.s' # assembly file name
path_e: str = args.path[:-2] + \
'.exe' if os_name_platform == 'Windows' else args.path[:-2]
# Use system linker to preprocss file
os.system(f"gcc -E -P {args.path} -o {path_i}")
with open(path_i, 'r', encoding='utf-8') as file:
content = file.read()
print(f"LINKED SOURCE FILE {path_i}:\n{content}\n")
# Lexer
tokens: List[Token] = lex(content)
print(f"LEXER OUTPUT:")
for token in tokens:
print(token)
if args.lex:
exit(0)
# Parser AST
parser = Parser(tokens)
ast: Program = parser.parse()
printer = ParserPrinter()
ast_string: str = printer.print_ast(ast)
print(f"\nPARSER OUTPUT:\n{ast_string}\n")
if args.parse:
exit(0)
# Assembler AST
generator = Generator(ast)
assembly_ast: AssemblyASTNode = generator.parse()
generatorprinter = GeneratorPrinter()
assembly_ast_string: str = generatorprinter.print_ast(assembly_ast)
print(f"\nASSEMBLY OUTPUT:\n {assembly_ast_string}")
if args.codegen:
exit(0)
# Code
codeemittor = CodeEmitter(os_name_platform)
code: str = codeemittor.parse(assembly_ast)
print(f"CODEEMITTOR OUTPUT:\n{code}")
with open(path_s, 'w', encoding='utf-8') as file:
file.write(code)
if args.S:
exit(0)
# Use GCC to Output Executable File
os.system(f"gcc {path_s} -o {path_e}")
print(f"GCC EXECUTABLE OUTPUT:\n{path_e}\n")
if __name__ == '__main__':
main()