@@ -229,3 +229,110 @@ def generate_output(
229229 print (error_collector .get_error_summary (), file = sys .stderr )
230230 if not args .ast and tree is None :
231231 sys .exit (1 )
232+
233+
234+ def run_main (
235+ description : str , lexer_class : type [LexerProtocol ], parser_class : type [ParserProtocol ],
236+ visitor_class : type [VisitorProtocol ], error_prefix : str , output_flag_name : str , output_flag_help : str ) -> None :
237+ """
238+ Generic main function for hrw4u and u4wrh scripts with bulk compilation support.
239+
240+ Args:
241+ description: Description for argument parser
242+ lexer_class: ANTLR lexer class to use
243+ parser_class: ANTLR parser class to use
244+ visitor_class: Visitor class to use
245+ error_prefix: Error prefix for error messages
246+ output_flag_name: Name of output flag (e.g., "hrw", "hrw4u")
247+ output_flag_help: Help text for output flag
248+ """
249+ import argparse
250+
251+ parser = argparse .ArgumentParser (
252+ description = description ,
253+ formatter_class = argparse .RawDescriptionHelpFormatter ,
254+ epilog = "For bulk compilation to files, use: input1.txt:output1.txt input2.txt:output2.txt ..." )
255+
256+ parser .add_argument (
257+ "files" , help = "Input file(s) to parse. Use input:output for bulk file output (default: stdin to stdout)" , nargs = "*" )
258+
259+ output_group = parser .add_mutually_exclusive_group ()
260+ output_group .add_argument ("--ast" , action = "store_true" , help = "Produce the ANTLR parse tree only" )
261+ output_group .add_argument (f"--{ output_flag_name } " , action = "store_true" , help = output_flag_help )
262+
263+ parser .add_argument ("--no-comments" , action = "store_true" , help = "Skip comment preservation (ignore comments in output)" )
264+ parser .add_argument ("--debug" , action = "store_true" , help = "Enable debug output" )
265+ parser .add_argument (
266+ "--stop-on-error" , action = "store_true" , help = "Stop processing on first error (default: collect and report multiple errors)" )
267+
268+ args = parser .parse_args ()
269+
270+ if not hasattr (args , output_flag_name ):
271+ setattr (args , output_flag_name , False )
272+
273+ if not (args .ast or getattr (args , output_flag_name )):
274+ setattr (args , output_flag_name , True )
275+
276+ if not args .files :
277+ content , filename = process_input (sys .stdin )
278+ tree , parser_obj , error_collector = create_parse_tree (
279+ content , filename , lexer_class , parser_class , error_prefix , not args .stop_on_error )
280+ generate_output (tree , parser_obj , visitor_class , filename , args , error_collector )
281+ return
282+
283+ has_colons = any (':' in f for f in args .files )
284+
285+ if has_colons :
286+ for pair in args .files :
287+ if ':' not in pair :
288+ print (
289+ f"Error: Mixed formats not allowed. All files must use 'input:output' format for bulk compilation." ,
290+ file = sys .stderr )
291+ sys .exit (1 )
292+
293+ input_path , output_path = pair .split (':' , 1 )
294+
295+ try :
296+ with open (input_path , 'r' , encoding = 'utf-8' ) as input_file :
297+ content = input_file .read ()
298+ filename = input_path
299+ except FileNotFoundError :
300+ print (f"Error: Input file '{ input_path } ' not found" , file = sys .stderr )
301+ sys .exit (1 )
302+ except Exception as e :
303+ print (f"Error reading '{ input_path } ': { e } " , file = sys .stderr )
304+ sys .exit (1 )
305+
306+ tree , parser_obj , error_collector = create_parse_tree (
307+ content , filename , lexer_class , parser_class , error_prefix , not args .stop_on_error )
308+
309+ try :
310+ original_stdout = sys .stdout
311+ with open (output_path , 'w' , encoding = 'utf-8' ) as output_file :
312+ sys .stdout = output_file
313+ generate_output (tree , parser_obj , visitor_class , filename , args , error_collector )
314+ sys .stdout = original_stdout
315+ except Exception as e :
316+ sys .stdout = original_stdout
317+ print (f"Error writing to '{ output_path } ': { e } " , file = sys .stderr )
318+ sys .exit (1 )
319+ else :
320+ for i , input_path in enumerate (args .files ):
321+ if i > 0 :
322+ print ("\n # ---" )
323+
324+ try :
325+ with open (input_path , 'r' , encoding = 'utf-8' ) as input_file :
326+ content = input_file .read ()
327+ filename = input_path
328+ except FileNotFoundError :
329+ print (f"Error: Input file '{ input_path } ' not found" , file = sys .stderr )
330+ sys .exit (1 )
331+ except Exception as e :
332+ print (f"Error reading '{ input_path } ': { e } " , file = sys .stderr )
333+ sys .exit (1 )
334+
335+ tree , parser_obj , error_collector = create_parse_tree (
336+ content , filename , lexer_class , parser_class , error_prefix , not args .stop_on_error )
337+
338+ generate_output (tree , parser_obj , visitor_class , filename , args , error_collector )
0 commit comments