| | import re
|
| |
|
| | def process_template(input_text, keep_comments = False):
|
| | """
|
| | Process a text template with macro instructions and variable substitution.
|
| | Supports multiple values for variables to generate multiple output versions.
|
| | Each section between macro lines is treated as a separate template.
|
| |
|
| | Args:
|
| | input_text (str): The input template text
|
| |
|
| | Returns:
|
| | tuple: (output_text, error_message)
|
| | - output_text: Processed output with variables substituted, or empty string if error
|
| | - error_message: Error description and problematic line, or empty string if no error
|
| | """
|
| | lines = input_text.strip().split('\n')
|
| | current_variables = {}
|
| | current_template_lines = []
|
| | all_output_lines = []
|
| | error_message = ""
|
| |
|
| |
|
| | line_number = 0
|
| | while line_number < len(lines):
|
| | orig_line = lines[line_number]
|
| | line = orig_line.strip()
|
| | line_number += 1
|
| |
|
| |
|
| | if not line:
|
| | continue
|
| |
|
| | if line.startswith('#') and not keep_comments:
|
| | continue
|
| |
|
| |
|
| | if line.startswith('!'):
|
| |
|
| | if current_template_lines:
|
| |
|
| | template_output, err = process_current_template(current_template_lines, current_variables)
|
| | if err:
|
| | return "", err
|
| | all_output_lines.extend(template_output)
|
| | current_template_lines = []
|
| |
|
| |
|
| | current_variables = {}
|
| |
|
| |
|
| | macro_line = line[1:].strip()
|
| |
|
| |
|
| | open_braces = macro_line.count('{')
|
| | close_braces = macro_line.count('}')
|
| | if open_braces != close_braces:
|
| | error_message = f"Unmatched braces: {open_braces} opening '{{' and {close_braces} closing '}}' braces\nLine: '{orig_line}'"
|
| | return "", error_message
|
| |
|
| |
|
| | if macro_line.count('"') % 2 != 0:
|
| | error_message = f"Unclosed double quotes\nLine: '{orig_line}'"
|
| | return "", error_message
|
| |
|
| |
|
| | var_sections = re.split(r'\s*:\s*', macro_line)
|
| |
|
| | for section in var_sections:
|
| | section = section.strip()
|
| | if not section:
|
| | continue
|
| |
|
| |
|
| | var_match = re.search(r'\{([^}]+)\}', section)
|
| | if not var_match:
|
| | if '{' in section or '}' in section:
|
| | error_message = f"Malformed variable declaration\nLine: '{orig_line}'"
|
| | return "", error_message
|
| | continue
|
| |
|
| | var_name = var_match.group(1).strip()
|
| | if not var_name:
|
| | error_message = f"Empty variable name\nLine: '{orig_line}'"
|
| | return "", error_message
|
| |
|
| |
|
| | value_part = section[section.find('}')+1:].strip()
|
| | if not value_part.startswith('='):
|
| | error_message = f"Missing '=' after variable '{{{var_name}}}'\nLine: '{orig_line}'"
|
| | return "", error_message
|
| |
|
| |
|
| | var_values = re.findall(r'"([^"]*)"', value_part)
|
| |
|
| |
|
| | if not var_values:
|
| | error_message = f"No quoted values found for variable '{{{var_name}}}'\nLine: '{orig_line}'"
|
| | return "", error_message
|
| |
|
| |
|
| |
|
| | if re.search(r'"[^,]*"[^,]*"', value_part):
|
| | error_message = f"Missing comma between values for variable '{{{var_name}}}'\nLine: '{orig_line}'"
|
| | return "", error_message
|
| |
|
| |
|
| | current_variables[var_name] = var_values
|
| |
|
| |
|
| | else:
|
| | if not line.startswith('#'):
|
| |
|
| | var_references = re.findall(r'\{([^}]+)\}', line)
|
| | for var_ref in var_references:
|
| | if var_ref not in current_variables:
|
| | error_message = f"Unknown variable '{{{var_ref}}}' in template\nLine: '{orig_line}'"
|
| | return "", error_message
|
| |
|
| |
|
| | current_template_lines.append(line)
|
| |
|
| |
|
| | if current_template_lines:
|
| | template_output, err = process_current_template(current_template_lines, current_variables)
|
| | if err:
|
| | return "", err
|
| | all_output_lines.extend(template_output)
|
| |
|
| | return '\n'.join(all_output_lines), ""
|
| |
|
| | def process_current_template(template_lines, variables):
|
| | """
|
| | Process a set of template lines with the current variables.
|
| |
|
| | Args:
|
| | template_lines (list): List of template lines to process
|
| | variables (dict): Dictionary of variable names to lists of values
|
| |
|
| | Returns:
|
| | tuple: (output_lines, error_message)
|
| | """
|
| | if not variables or not template_lines:
|
| | return template_lines, ""
|
| |
|
| | output_lines = []
|
| |
|
| |
|
| | max_values = max(len(values) for values in variables.values())
|
| |
|
| |
|
| | for i in range(max_values):
|
| | for template in template_lines:
|
| | output_line = template
|
| | for var_name, var_values in variables.items():
|
| |
|
| | value_index = i % len(var_values)
|
| | var_value = var_values[value_index]
|
| | output_line = output_line.replace(f"{{{var_name}}}", var_value)
|
| | output_lines.append(output_line)
|
| |
|
| | return output_lines, ""
|
| |
|
| |
|
| | def extract_variable_names(macro_line):
|
| | """
|
| | Extract all variable names from a macro line.
|
| |
|
| | Args:
|
| | macro_line (str): A macro line (with or without the leading '!')
|
| |
|
| | Returns:
|
| | tuple: (variable_names, error_message)
|
| | - variable_names: List of variable names found in the macro
|
| | - error_message: Error description if any, empty string if no error
|
| | """
|
| |
|
| | if macro_line.startswith('!'):
|
| | macro_line = macro_line[1:].strip()
|
| |
|
| | variable_names = []
|
| |
|
| |
|
| | open_braces = macro_line.count('{')
|
| | close_braces = macro_line.count('}')
|
| | if open_braces != close_braces:
|
| | return [], f"Unmatched braces: {open_braces} opening '{{' and {close_braces} closing '}}' braces"
|
| |
|
| |
|
| | var_sections = re.split(r'\s*:\s*', macro_line)
|
| |
|
| | for section in var_sections:
|
| | section = section.strip()
|
| | if not section:
|
| | continue
|
| |
|
| |
|
| | var_matches = re.findall(r'\{([^}]+)\}', section)
|
| | for var_name in var_matches:
|
| | new_var = var_name.strip()
|
| | if not new_var in variable_names:
|
| | variable_names.append(new_var)
|
| |
|
| | return variable_names, ""
|
| |
|
| | def extract_variable_values(macro_line):
|
| | """
|
| | Extract all variable names and their values from a macro line.
|
| |
|
| | Args:
|
| | macro_line (str): A macro line (with or without the leading '!')
|
| |
|
| | Returns:
|
| | tuple: (variables_dict, error_message)
|
| | - variables_dict: Dictionary mapping variable names to their values
|
| | - error_message: Error description if any, empty string if no error
|
| | """
|
| |
|
| | if macro_line.startswith('!'):
|
| | macro_line = macro_line[1:].strip()
|
| |
|
| | variables = {}
|
| |
|
| |
|
| | open_braces = macro_line.count('{')
|
| | close_braces = macro_line.count('}')
|
| | if open_braces != close_braces:
|
| | return {}, f"Unmatched braces: {open_braces} opening '{{' and {close_braces} closing '}}' braces"
|
| |
|
| |
|
| | if macro_line.count('"') % 2 != 0:
|
| | return {}, "Unclosed double quotes"
|
| |
|
| |
|
| | var_sections = re.split(r'\s*:\s*', macro_line)
|
| |
|
| | for section in var_sections:
|
| | section = section.strip()
|
| | if not section:
|
| | continue
|
| |
|
| |
|
| | var_match = re.search(r'\{([^}]+)\}', section)
|
| | if not var_match:
|
| | if '{' in section or '}' in section:
|
| | return {}, "Malformed variable declaration"
|
| | continue
|
| |
|
| | var_name = var_match.group(1).strip()
|
| | if not var_name:
|
| | return {}, "Empty variable name"
|
| |
|
| |
|
| | value_part = section[section.find('}')+1:].strip()
|
| | if not value_part.startswith('='):
|
| | return {}, f"Missing '=' after variable '{{{var_name}}}'"
|
| |
|
| |
|
| | var_values = re.findall(r'"([^"]*)"', value_part)
|
| |
|
| |
|
| | if not var_values:
|
| | return {}, f"No quoted values found for variable '{{{var_name}}}'"
|
| |
|
| |
|
| | if re.search(r'"[^,]*"[^,]*"', value_part):
|
| | return {}, f"Missing comma between values for variable '{{{var_name}}}'"
|
| |
|
| | variables[var_name] = var_values
|
| |
|
| | return variables, ""
|
| |
|
| | def generate_macro_line(variables_dict):
|
| | """
|
| | Generate a macro line from a dictionary of variable names and their values.
|
| |
|
| | Args:
|
| | variables_dict (dict): Dictionary mapping variable names to lists of values
|
| |
|
| | Returns:
|
| | str: A formatted macro line (including the leading '!')
|
| | """
|
| | sections = []
|
| |
|
| | for var_name, values in variables_dict.items():
|
| |
|
| | quoted_values = [f'"{value}"' for value in values]
|
| |
|
| | values_str = ','.join(quoted_values)
|
| |
|
| | section = f"{{{var_name}}}={values_str}"
|
| | sections.append(section)
|
| |
|
| |
|
| | return "! " + " : ".join(sections) |