Source code for ebnf_compiler

# SPDX-FileCopyrightText: 2026 Filipe Casimiro Ferreira <pro.maiscommentz@gmail.com>
#
# SPDX-License-Identifier: MIT

"""
EBNF Compiler
"""

import sys
from dataclasses import dataclass
from pathlib import Path
from typing import Annotated

import typer
from loguru import logger
from rich.console import Console
from rich.panel import Panel
from rich.pretty import Pretty

from . import ast
from .analyzer import Analyzer
from .parser import Parser
from .scanner import Scanner

console = Console()
app = typer.Typer()


[docs] @dataclass class Compiler: scanner: Scanner parser: Parser
[docs] def ast(self) -> ast.Syntax | None: try: return self.parser.parse() except Exception as e: print(f"{e}") return None
[docs] @app.command(context_settings={"ignore_unknown_options": True}) def main( source: Annotated[Path, typer.Argument()], debug: bool = False, show_tree: bool = True, analyze: bool = True, ): logger.remove() if debug: logger.add(sys.stdout, level="DEBUG") else: logger.add(sys.stdout, level="INFO") scanner = Scanner() scanner.open(source) parser = Parser(scanner=scanner) compiler = Compiler(scanner=scanner, parser=parser) ast_ = compiler.ast() if ast_ is None: console.print("[red]Compilation failed[/red]") return console.print(Panel(ast_.rich(), title="Code")) if show_tree: console.print(Panel(Pretty(ast_, indent_size=2), title="Syntax Tree")) if analyze: analyzer = Analyzer(ast_) analyzer.analyze() console.print("\n[bold]Terminal Symbols[/bold]") for sym in analyzer.terminals: console.print(f"- {sym}") console.print("\n[bold]Non-terminal Symbols[/bold]") for sym in analyzer.non_terminals: console.print(f"- {sym}")
if __name__ == "__main__": app()