cl-compiler / compiler.py
GitHub Actions
Sync from GitHub: fe8c57c
e3486fe
Raw
History Blame Contribute Delete
3.96 kB
"""
Main entry point for the Mini Compiler.
Usage:
python compiler.py <source_file> [--phase lex|parse|semantic|all]
Runs the full compilation pipeline (or a single phase) on a .cl source file
and prints the result to stdout.
"""
import argparse
import sys
import os
# Make the src/ package importable regardless of working directory
sys.path.insert(0, os.path.join(os.path.dirname(os.path.abspath(__file__)), "src"))
from lexer import Lexer, LexerError
from parser import Parser, ParseError
from semantic import SemanticAnalyzer, SemanticError
def read_source(path: str) -> str:
"""Read source code from a file, with a friendly error on failure."""
try:
with open(path, "r", encoding="utf-8") as f:
return f.read()
except FileNotFoundError:
print(f"Error: File not found: '{path}'", file=sys.stderr)
sys.exit(1)
except IOError as e:
print(f"Error reading file: {e}", file=sys.stderr)
sys.exit(1)
def run_lexer(source: str) -> list:
"""Milestone 1: Tokenize source and return token list."""
lexer = Lexer(source)
try:
tokens = lexer.tokenize()
except LexerError as e:
print(f"[Lexer Error] {e}", file=sys.stderr)
sys.exit(1)
print("=== TOKEN STREAM ===")
for tok in tokens:
print(f" {tok}")
return tokens
def run_parser(source: str):
"""Milestone 2: Parse source into an AST and return the root node."""
lexer = Lexer(source)
try:
tokens = lexer.tokenize()
except LexerError as e:
print(f"[Lexer Error] {e}", file=sys.stderr)
sys.exit(1)
parser = Parser(tokens)
try:
ast = parser.parse()
except ParseError as e:
print(f"[Parse Error] {e}", file=sys.stderr)
sys.exit(1)
print("=== ABSTRACT SYNTAX TREE ===")
print(ast.pretty())
return ast
def run_semantic(source: str):
"""Milestone 3: Run semantic analysis and report errors or success."""
lexer = Lexer(source)
try:
tokens = lexer.tokenize()
except LexerError as e:
print(f"[Lexer Error] {e}", file=sys.stderr)
sys.exit(1)
parser = Parser(tokens)
try:
ast = parser.parse()
except ParseError as e:
print(f"[Parse Error] {e}", file=sys.stderr)
sys.exit(1)
analyzer = SemanticAnalyzer()
try:
analyzer.analyze(ast)
except SemanticError as e:
print(f"[Semantic Error] {e}", file=sys.stderr)
sys.exit(1)
print("=== SEMANTIC ANALYSIS ===")
print(" No errors found.")
print()
print(" Symbol Table (global scope):")
for name, info in analyzer.global_scope.symbols.items():
print(f" {name:20s} : {info['type']}")
return ast
def main():
arg_parser = argparse.ArgumentParser(
description="Mini Compiler — CL Language",
formatter_class=argparse.RawDescriptionHelpFormatter,
epilog="""
Examples:
python compiler.py samples/hello.cl
python compiler.py samples/hello.cl --phase lex
python compiler.py samples/hello.cl --phase parse
python compiler.py samples/hello.cl --phase semantic
"""
)
arg_parser.add_argument("source", help="Path to the .cl source file")
arg_parser.add_argument(
"--phase",
choices=["lex", "parse", "semantic", "all"],
default="all",
help="Which compilation phase to run (default: all)"
)
args = arg_parser.parse_args()
source = read_source(args.source)
if args.phase == "lex":
run_lexer(source)
elif args.phase == "parse":
run_parser(source)
elif args.phase == "semantic":
run_semantic(source)
else: # "all"
print(f"Compiling: {args.source}\n")
run_lexer(source)
print()
run_parser(source)
print()
run_semantic(source)
print()
print("Compilation successful.")
if __name__ == "__main__":
main()