char-fork/llama.cpp
git clone https://git.t4t.associates/char-fork/llama.cpp
eafe15a5e
char/aliceai-t5-moe
1#!/usr/bin/env python3 2""" 3align-macros.py - Inspect and align trailing backslashes in multiline C/C++ macros. 4 5Usage: 6align-macros.py [paths...] # Check and report misaligned macros 7align-macros.py --diff [paths...] # Show unified diff of fixes 8align-macros.py --fix [paths...] # Fix misaligned macros in-place 9align-macros.py --fix --mode majority ... # Align to the dominant column 10align-macros.py --fix --pad 2 ... # Align to (max_content_len + pad) 11 12Safety rules: 13- Macros that are ALREADY aligned are NEVER touched (unless --all is given). 14- Whitespace after trailing backslashes is flagged and cleaned. 15""" 16 17import argparse 18import difflib 19import logging 20import os 21import re 22import sys 23from collections import Counter 24from typing import List ,Optional ,Tuple ,NamedTuple 25 26logger = logging .getLogger ("ggml-hexagon-align-macros" ) 27 28 29class MacroLine (NamedTuple ): 30line_num :int # 1-indexed 31raw :str # Original line including newline 32content :str # Line content before trailing backslash (stripped of trailing whitespace) 33bs_col :Optional [int ]# 1-indexed column of backslash, or None if last line has no backslash 34trailing_ws :bool # True if whitespace existed after the backslash 35 36 37class MacroDef (NamedTuple ): 38name :str 39filepath :str 40start_line :int 41end_line :int 42lines :List [MacroLine ] 43 44 45def parse_macros (filepath :str )-> List [MacroDef ]: 46"""Extract all multiline macros from a C/C++ source file.""" 47try : 48with open (filepath ,"r" ,encoding = "utf-8" ,errors = "replace" )as f : 49lines = f .readlines () 50except Exception as e : 51logger .error (f"Error reading { filepath } : { e } " ) 52return [] 53 54macros :List [MacroDef ]= [] 55i = 0 56n = len (lines ) 57 58while i < n : 59line = lines [i ] 60m = re .match (r"^\s*#\s*define\s+([A-Za-z_][A-Za-z0-9_]*)" ,line ) 61if m : 62macro_name = m .group (1 ) 63macro_start = i + 1 64macro_lines :List [MacroLine ]= [] 65cur = i 66 67while cur < n : 68l_raw = lines [cur ] 69l_rstrip = l_raw .rstrip ("\r\n" ) 70 71# Check if line has a trailing backslash 72# Note: handle possible accidental spaces after backslash 73match_bs = re .search (r"\\([ \t]*)$" ,l_rstrip ) 74if match_bs : 75has_trailing_ws = len (match_bs .group (1 ))> 0 76bs_index = match_bs .start () 77content = l_rstrip [:bs_index ].rstrip () 78# 1-indexed column of the backslash 79bs_col = bs_index + 1 80macro_lines .append (MacroLine ( 81line_num = cur + 1 , 82raw = l_raw , 83content = content , 84bs_col = bs_col , 85trailing_ws = has_trailing_ws 86 )) 87cur += 1 88else : 89# Line does not end with backslash 90if cur == i : 91# Single-line macro, not multiline 92break 93else : 94# Final line of a multiline macro 95macro_lines .append (MacroLine ( 96line_num = cur + 1 , 97raw = l_raw , 98content = l_rstrip .rstrip (), 99bs_col = None , 100trailing_ws = False 101 )) 102break 103 104# Only record if it is a multiline macro (has at least one continuation line) 105continuation_lines = [ml for ml in macro_lines if ml .bs_col is not None ] 106if continuation_lines : 107macro_end = macro_lines [- 1 ].line_num 108macros .append (MacroDef ( 109name = macro_name , 110filepath = filepath , 111start_line = macro_start , 112end_line = macro_end , 113lines = macro_lines 114 )) 115i = cur 116i += 1 117 118return macros 119 120 121def is_macro_aligned (macro :MacroDef )-> bool : 122"""A macro is aligned if all continuation lines have backslashes at the same column.""" 123bs_cols = [ml .bs_col for ml in macro .lines if ml .bs_col is not None ] 124if not bs_cols : 125return True 126has_trailing_ws = any (ml .trailing_ws for ml in macro .lines ) 127return len (set (bs_cols ))== 1 and not has_trailing_ws 128 129 130def compute_target_column (macro :MacroDef ,mode :str ,pad :int ,target_col :Optional [int ])-> int : 131"""Determine the column where backslashes should be aligned.""" 132max_content_len = max (len (ml .content )for ml in macro .lines ) 133min_needed = max_content_len + pad 134 135if target_col is not None : 136return max (target_col ,min_needed ) 137 138bs_cols = [ml .bs_col for ml in macro .lines if ml .bs_col is not None ] 139if not bs_cols : 140return min_needed 141 142if mode == "min" : 143return min_needed 144elif mode == "max" : 145return max (max (bs_cols ),min_needed ) 146elif mode == "majority" : 147counts = Counter (bs_cols ) 148# Sort by frequency descending, then by column descending 149majority_col = sorted (counts .items (),key = lambda x : (- x [1 ],- x [0 ]))[0 ][0 ] 150return max (majority_col ,min_needed ) 151else : 152return min_needed 153 154 155def realign_macro_lines (macro :MacroDef ,target_col :int )-> List [str ]: 156"""Format macro lines with backslashes aligned at target_col.""" 157new_lines :List [str ]= [] 158for ml in macro .lines : 159nl = "\r\n" if ml .raw .endswith ("\r\n" )else "\n" 160if ml .bs_col is None : 161# Last line without backslash 162new_lines .append (ml .raw ) 163else : 164if not ml .content : 165spaces = " " * (target_col - 1 ) 166new_lines .append (f" { spaces } \\ { nl } " ) 167else : 168spaces_needed = max (1 ,target_col - len (ml .content )- 1 ) 169new_lines .append (f" { ml . content } { ' ' * spaces_needed } \\ { nl } " ) 170return new_lines 171 172 173def process_file (filepath :str ,args :argparse .Namespace )-> Tuple [int ,int ,Optional [str ]]: 174macros = parse_macros (filepath ) 175if not macros : 176return 0 ,0 ,None 177 178with open (filepath ,"r" ,encoding = "utf-8" ,errors = "replace" )as f : 179file_lines = f .readlines () 180 181misaligned_count = 0 182modified = False 183new_file_lines = list (file_lines ) 184 185for macro in macros : 186aligned = is_macro_aligned (macro ) 187if not aligned or args .all : 188if not aligned : 189misaligned_count += 1 190 191bs_cols = [ml .bs_col for ml in macro .lines if ml .bs_col is not None ] 192max_content = max (len (ml .content )for ml in macro .lines ) 193col_counts = Counter (bs_cols ) 194 195if not args .quiet : 196logger .info (f" { filepath } : { macro . start_line } - { macro . end_line } [ { macro . name } ]" ) 197logger .info (f" Max content width: { max_content } , Min needed column (+ { args . pad } ): { max_content + args . pad } " ) 198logger .info (f" Current backslash columns: { dict ( sorted ( col_counts . items ())) } " ) 199trailing_ws_lines = [ml .line_num for ml in macro .lines if ml .trailing_ws ] 200if trailing_ws_lines : 201logger .warning (f" Warning: Trailing whitespace after backslash on line(s): { trailing_ws_lines } " ) 202 203target_col = compute_target_column (macro ,args .mode ,args .pad ,args .target_col ) 204if not args .quiet : 205logger .info (f" -> Target alignment column: { target_col } " ) 206 207realigned = realign_macro_lines (macro ,target_col ) 208 209start_idx = macro .start_line - 1 210end_idx = start_idx + len (macro .lines ) 211if new_file_lines [start_idx :end_idx ]!= realigned : 212new_file_lines [start_idx :end_idx ]= realigned 213modified = True 214 215diff_text = None 216if modified : 217diff = difflib .unified_diff ( 218file_lines , 219new_file_lines , 220fromfile = f"a/ { filepath } " , 221tofile = f"b/ { filepath } " , 222lineterm = "" 223 ) 224diff_text = "\n" .join (diff ) 225 226if args .fix : 227with open (filepath ,"w" ,encoding = "utf-8" )as f : 228f .writelines (new_file_lines ) 229if not args .quiet : 230logger .info (f" [FIXED] Updated { filepath } " ) 231 232return len (macros ),misaligned_count ,diff_text 233 234 235def find_source_files (paths :List [str ])-> List [str ]: 236extensions = {".c" ,".cpp" ,".cc" ,".cxx" ,".h" ,".hpp" ,".inl" } 237result :List [str ]= [] 238for p in paths : 239if os .path .isfile (p ): 240result .append (p ) 241elif os .path .isdir (p ): 242for root ,_ ,files in os .walk (p ): 243for file in sorted (files ): 244_ ,ext = os .path .splitext (file ) 245if ext .lower ()in extensions : 246result .append (os .path .join (root ,file )) 247return sorted (result ) 248 249 250def main (): 251logging .basicConfig (level = logging .INFO ,format = "%(message)s" ) 252parser = argparse .ArgumentParser ( 253description = "Inspect and align backslashes in multiline C/C++ macros." 254 ) 255parser .add_argument ("paths" ,nargs = "*" ,default = ["." ],help = "Files or directories to scan (default: current dir)" ) 256parser .add_argument ("--fix" ,action = "store_true" ,help = "Fix misaligned macros in-place" ) 257parser .add_argument ("--diff" ,action = "store_true" ,help = "Display unified diff of suggested fixes" ) 258parser .add_argument ("--check" ,action = "store_true" ,help = "Exit with code 1 if misaligned macros exist" ) 259parser .add_argument ("--mode" ,choices = ["min" ,"max" ,"majority" ],default = "min" , 260help = "Alignment mode: 'min' (max_len + pad), 'max' (max existing col), 'majority' (dominant col)" ) 261parser .add_argument ("--pad" ,type = int ,default = 2 ,help = "Spaces between longest line and backslash (default: 2)" ) 262parser .add_argument ("--target-col" ,type = int ,default = None ,help = "Force alignment to an exact column" ) 263parser .add_argument ("--all" ,action = "store_true" ,help = "Realign all macros even if already aligned (default: only misaligned)" ) 264parser .add_argument ("-q" ,"--quiet" ,action = "store_true" ,help = "Only output errors and diffs/summary" ) 265 266args = parser .parse_args () 267 268files = find_source_files (args .paths ) 269if not files : 270logger .error ("No C/C++ source files found." ) 271sys .exit (0 ) 272 273total_macros = 0 274total_misaligned = 0 275diffs :List [str ]= [] 276 277for filepath in files : 278num_macros ,num_misaligned ,diff_text = process_file (filepath ,args ) 279total_macros += num_macros 280total_misaligned += num_misaligned 281if diff_text : 282diffs .append (diff_text ) 283 284if args .diff and diffs : 285logger .info ("\n--- Proposed Changes ---\n" ) 286for d in diffs : 287logger .info (d ) 288 289logger .info (f"\nSummary: scanned { len ( files ) } files, { total_macros } multiline macros, { total_misaligned } misaligned." ) 290 291if args .check and total_misaligned > 0 : 292sys .exit (1 ) 293 294 295if __name__ == "__main__" : 296main ()