Skip to main content
Glama
output_parser.py6.89 kB
"""Parser for Delphi compiler output.""" import re from typing import Optional from src.models import CompilationError, CompilationStatistics class OutputParser: """Parses Delphi compiler output to extract errors and filter warnings/hints.""" # Pattern for Delphi compiler messages (English and German) # Format: FileName.pas(line,col): [Error/Fehler/Warning/Warnung/Hint/Hinweis] E####: Message # Example: Unit1.pas(42,15): Error: E2003 Undeclared identifier: 'Foo' # Example: Unit1.pas(42,15) Fehler: E2003 Undeklarierter Bezeichner: 'Foo' MESSAGE_PATTERN = re.compile( r"^(.+?)\((\d+)(?:,(\d+))?\)\s*(Error|Warning|Hint|Fatal|Fehler|Warnung|Hinweis|Schwerwiegend)(?:\s*:)?\s*([EWHFewh]\d+)?\s*:?\s*(.+)$", re.IGNORECASE ) # Alternative pattern for messages without file location (English and German) # Example: Fatal: F1026 File not found: 'System.pas' # Example: Schwerwiegend: F1026 Datei nicht gefunden: 'System.pas' SIMPLE_MESSAGE_PATTERN = re.compile( r"^(Error|Warning|Hint|Fatal|Fehler|Warnung|Hinweis|Schwerwiegend)\s*:?\s*([EWHFewh]\d+)?\s*:?\s*(.+)$", re.IGNORECASE ) def __init__(self): """Initialize output parser.""" self.errors: list[CompilationError] = [] self.statistics = CompilationStatistics() def parse(self, output: str) -> tuple[list[CompilationError], CompilationStatistics]: """Parse compiler output and extract errors. Args: output: Raw compiler output text Returns: Tuple of (errors list, statistics) """ lines = output.split("\n") for line in lines: line = line.strip() if not line: continue self._parse_line(line) return self.errors, self.statistics def _parse_line(self, line: str) -> None: """Parse a single line of compiler output. Args: line: Single line from compiler output """ # Try full pattern first (with file location) match = self.MESSAGE_PATTERN.match(line) if match: file_path = match.group(1) line_num = int(match.group(2)) col_num = int(match.group(3)) if match.group(3) else 0 severity = match.group(4) error_code = match.group(5) message = match.group(6) self._process_message( severity=severity, error_code=error_code, message=message, file_path=file_path, line_num=line_num, col_num=col_num, ) return # Try simple pattern (without file location) match = self.SIMPLE_MESSAGE_PATTERN.match(line) if match: severity = match.group(1) error_code = match.group(2) message = match.group(3) self._process_message( severity=severity, error_code=error_code, message=message, file_path="", line_num=0, col_num=0, ) return # Check for lines compiled info # Format: "12345 lines, 2.5 seconds" lines_match = re.search(r"(\d+)\s+lines?", line, re.IGNORECASE) if lines_match: self.statistics.lines_compiled = int(lines_match.group(1)) def _process_message( self, severity: str, error_code: Optional[str], message: str, file_path: str, line_num: int, col_num: int, ) -> None: """Process a compiler message and decide whether to include it. Args: severity: Message severity (Error, Warning, Hint, Fatal) error_code: Error code (e.g., "E2003", "W1011", "H2443") message: Error message text file_path: Source file path line_num: Line number col_num: Column number """ # Normalize error code if error_code: error_code = error_code.upper() # Determine message type based on severity and error code is_error = self._is_error(severity, error_code) is_warning = self._is_warning(severity, error_code) is_hint = self._is_hint(severity, error_code) # Update statistics if is_warning: self.statistics.warnings_filtered += 1 return # Filter out warnings if is_hint: self.statistics.hints_filtered += 1 return # Filter out hints # Only keep errors and fatal errors if is_error: error = CompilationError( file=file_path or "(unknown)", line=line_num, column=col_num, message=message.strip(), error_code=error_code, ) self.errors.append(error) def _is_error(self, severity: str, error_code: Optional[str]) -> bool: """Check if message is an error. Args: severity: Message severity (English or German) error_code: Error code Returns: True if message is an error """ severity_lower = severity.lower() # Fatal is always an error (English: "fatal", German: "schwerwiegend") if severity_lower in ("fatal", "schwerwiegend"): return True # Error severity (English: "error", German: "fehler") if severity_lower in ("error", "fehler"): return True # Error codes starting with E or F if error_code and error_code[0] in ("E", "F"): return True return False def _is_warning(self, severity: str, error_code: Optional[str]) -> bool: """Check if message is a warning. Args: severity: Message severity (English or German) error_code: Error code Returns: True if message is a warning """ # Warning severity (English: "warning", German: "warnung") if severity.lower() in ("warning", "warnung"): return True # Warning codes starting with W if error_code and error_code[0] == "W": return True return False def _is_hint(self, severity: str, error_code: Optional[str]) -> bool: """Check if message is a hint. Args: severity: Message severity (English or German) error_code: Error code Returns: True if message is a hint """ # Hint severity (English: "hint", German: "hinweis") if severity.lower() in ("hint", "hinweis"): return True # Hint codes starting with H if error_code and error_code[0] == "H": return True return False

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/Basti-Fantasti/delphi-build-mcp-server'

If you have feedback or need assistance with the MCP directory API, please join our Discord server