from stdint import * import stdio import memhub import sys import win32.win32console as win32cmd import win32.win32file as win32file import win32.win32base as win32base # VLogger - native logger with colored Windows console output. # Format: ├ LEVEL: message or ├ LEVEL[category]: message (aligned with TPC lib/core/VLogger.py). # Error recovery (REPL/JIT): after error, longjmp back to the REPL setjmp point. # mingw-w64 x86_64: longjmp is void longjmp(jmp_buf, int); setjmp expands to _setjmp(buf, NULL). extern def longjmp(buf: rawptr, val: int) # REPL recovery-point pointer: registered at session start, cleared at session end. # Covers the whole fragment pipeline; batch mode never sets it, so it stays None and exits via sys.exit(1). _repl_jmpbuf: rawptr = None # repl_abort - the single "terminate after diagnosis" exit in VPC. # Caller already printed the diagnostic; returns to REPL via longjmp if a recovery point is set, else sys.exit(1). def repl_abort() -> int: """Unified exit after diagnosis: longjmp to the REPL if a recovery point is set, else exit.""" if _repl_jmpbuf is not None: stdio.fflush(None) longjmp(_repl_jmpbuf, 1) sys.exit(1) return 0 def set_repl_jmpbuf(buf: rawptr) -> int: """Register the REPL jmp_buf; idempotent at each fragment entry.""" global _repl_jmpbuf _repl_jmpbuf = buf return 0 def clear_repl_jmpbuf() -> int: """Clear the registered jmp_buf at REPL session end.""" global _repl_jmpbuf _repl_jmpbuf = None return 0 # Log levels enum LogLevel: DEBUG, INFO, WARNING, ERROR, SUCCESS, CRITICAL # Win32 console foreground color attributes # 值用 WORD(...) 显式定宽为 uint16(与 _log 的 sym_color/level_color 形参一致)。 # 注意:仅写 `define X: WORD = 0x0002` 不够 —— define 的宽度由**右侧表达式的类型** # 决定(win32file.vp 的 `define STD_OUTPUT_HANDLE: DWORD = DWORD(-10)` 同款), # 否则常量仍是 i32 → 传给 WORD 形参即 32→16 隐式窄化。 define FOREGROUND_BLUE: WORD = WORD(0x0001) define FOREGROUND_GREEN: WORD = WORD(0x0002) define FOREGROUND_RED: WORD = WORD(0x0004) define FOREGROUND_INTENSITY: WORD = WORD(0x0008) define FOREGROUND_WHITE: WORD = WORD(FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE) define FOREGROUND_CYAN: WORD = WORD(FOREGROUND_BLUE | FOREGROUND_GREEN) define FOREGROUND_YELLOW: WORD = WORD(FOREGROUND_RED | FOREGROUND_GREEN) define FOREGROUND_MAGENTA: WORD = WORD(FOREGROUND_RED | FOREGROUND_BLUE) # Win32 console background color attributes define BACKGROUND_RED: WORD = WORD(0x0040) define BACKGROUND_GREEN: WORD = WORD(0x0020) define BACKGROUND_BLUE: WORD = WORD(0x0010) define BACKGROUND_INTENSITY: WORD = WORD(0x0080) define BACKGROUND_WHITE: WORD = WORD(BACKGROUND_RED | BACKGROUND_GREEN | BACKGROUND_BLUE) novirtual class Logger: _level: int _console_handle: win32base.HANDLE _use_color: int jit_flag: bool __mbuddy__: memhub.MemBuddy* def __init__(self, level: int = 1): self._level = level self._console_handle = win32file.GetStdHandle(win32file.STD_OUTPUT_HANDLE) self._use_color = 1 self.jit_flag = False self.__mbuddy__ = _mbuddy def _set_color(self, attr: WORD) -> int: uc: int = self._use_color if uc: ch: win32base.HANDLE = self._console_handle ret: int = win32cmd.SetConsoleTextAttribute(ch, attr) return ret return 0 def _reset_color(self) -> int: if self._use_color: return win32cmd.SetConsoleTextAttribute(self._console_handle, FOREGROUND_WHITE) return 0 # _log - core log output with segmented coloring (prefix symbol / level tag / message). # Format: ├ LEVEL: msg or ├ LEVEL[category]: msg. def _log(self, level: int, prefix_sym: str, level_str: str, msg: str, sym_color: WORD, level_color: WORD, category: str = "") -> int: # Color the prefix symbol self._set_color(sym_color) stdio.printf("%s ", prefix_sym) # Color the level tag self._set_color(level_color) stdio.printf("%s", level_str) self._reset_color() # Optional category if category is not None and category[0] != 0: stdio.printf(" [%s]", category) stdio.printf(": %s\n", msg) return 0 def debug(self, msg: str, category: str = "") -> int: return self._log(LogLevel.DEBUG, "├", "DEBUG", msg, FOREGROUND_INTENSITY, FOREGROUND_INTENSITY, category) def info(self, msg: str, category: str = "") -> int: ret: int = self._log(LogLevel.INFO, "├", "INFO", msg, FOREGROUND_GREEN, WORD(FOREGROUND_GREEN | FOREGROUND_INTENSITY), category) return ret def warning(self, msg: str, category: str = "") -> int: return self._log(LogLevel.WARNING, "├", "WARN", msg, FOREGROUND_YELLOW, WORD(FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_INTENSITY), category) def error(self, msg: str, category: str = "") -> int: ret: int = self._log(LogLevel.ERROR, "├", "ERROR", msg, FOREGROUND_RED, WORD(FOREGROUND_RED | FOREGROUND_INTENSITY), category) # JIT/REPL: diagnostic already printed; jump back to the REPL loop via the unified exit. if self.jit_flag: repl_abort() return ret # Batch mode: terminate immediately on error. sys.exit(1) return ret def success(self, msg: str, category: str = "") -> int: return self._log(LogLevel.SUCCESS, "├", "SUCCESS", msg, FOREGROUND_GREEN, WORD(FOREGROUND_GREEN | FOREGROUND_INTENSITY), category) def critical(self, msg: str, category: str = "") -> int: # CRITICAL uses the ╠ prefix (red-background white label, aligned with TPC style). return self._log(LogLevel.CRITICAL, "╠", "CRITICAL", msg, FOREGROUND_RED, WORD(BACKGROUND_RED | FOREGROUND_WHITE | FOREGROUND_INTENSITY), category) def set_level(self, level: int) -> int: self._level = level return 0 def set_jit_flag(self, v: bool) -> int: """Set the JIT/REPL flag: True makes error longjmp back to the REPL loop.""" self.jit_flag = v return 0 # Advanced log methods. def banner(self, msg: str) -> int: """Print a blue-highlighted section banner.""" self._set_color(FOREGROUND_BLUE | FOREGROUND_INTENSITY) stdio.printf("\n=== %s ===\n\n", msg) self._reset_color() return 0 def compile_error(self, msg: str, file: str = "", line: int = 0) -> int: """Format a compile error with a red-background header, location and message.""" self._set_color(BACKGROUND_RED | FOREGROUND_WHITE | FOREGROUND_INTENSITY) stdio.printf(" COMPILE ERROR ") self._set_color(FOREGROUND_RED | FOREGROUND_INTENSITY) stdio.printf("\n") if file is not None: if file[0] != 0 and line > 0: stdio.printf(" location: %s:%d\n", file, line) elif file[0] != 0: stdio.printf(" file: %s\n", file) stdio.printf(" error: %s\n", msg) self._reset_color() return 0 def compile_warning(self, msg: str, file: str = "", line: int = 0) -> int: """Format a compile warning with a yellow background, location and message.""" self._set_color(BACKGROUND_RED | BACKGROUND_GREEN | FOREGROUND_INTENSITY) stdio.printf(" COMPILE WARNING ") self._set_color(FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_INTENSITY) stdio.printf("\n") if file is not None: if file[0] != 0 and line > 0: stdio.printf(" location: %s:%d\n", file, line) elif file[0] != 0: stdio.printf(" file: %s\n", file) stdio.printf(" warning: %s\n", msg) self._reset_color() return 0 # Global mbuddy pointer (injected by lib.InitLib). _mbuddy: memhub.MemBuddy* # Global logger pointer logger: Logger* log: Logger* def init_logger(): """Initialize the global logger instance.""" global logger, log raw: rawptr = _mbuddy.alloc(Logger.__sizeof__()) # if raw is None: return None (memory management still unclear) logger = raw logger.__before_init__() logger.__init__(LogLevel.INFO) log = logger # if fmtbuffer is None: return None # --nowarn-register switch: suppresses Phase A dependency WARN output (output only). _nowarn_register: int = 0 # Module-level convenience functions (aligned with TPC lib.core.VLogger; no get_logger() needed). def info(msg: str, category: str = "") -> int: """Log at INFO level.""" if log is not None: return log.info(msg, category) return 0 def warning(msg: str, category: str = "") -> int: """Log at WARN level.""" if log is not None: return log.warning(msg, category) return 0 def error(msg: str, category: str = "") -> int: """Log at ERROR level: exit in batch mode, longjmp to REPL under JIT/REPL.""" if log is not None: return log.error(msg, category) # When the logger is unavailable, still use the unified exit (REPL can recover if registered) repl_abort() return 0 def success(msg: str, category: str = "") -> int: """Log at SUCCESS level.""" if log is not None: return log.success(msg, category) return 0 def critical(msg: str, category: str = "") -> int: """Log at CRITICAL level (fatal, red background).""" if log is not None: return log.critical(msg, category) return 0 def debug(msg: str, category: str = "") -> int: """Log at DEBUG level.""" if log is not None: return log.debug(msg, category) return 0