File size: 11,265 Bytes
78360e7 |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 |
import re
def process_template(input_text):
"""
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 = ""
# Process the input line by line
line_number = 0
while line_number < len(lines):
orig_line = lines[line_number]
line = orig_line.strip()
line_number += 1
# Skip empty lines or comments
if not line or line.startswith('#'):
continue
# Handle macro instructions
if line.startswith('!'):
# Process any accumulated template lines before starting a new macro
if current_template_lines:
# Process the current template with current variables
template_output, err = process_current_template(current_template_lines, current_variables)
if err:
return "", err
all_output_lines.extend(template_output)
current_template_lines = [] # Reset template lines
# Reset variables for the new macro
current_variables = {}
# Parse the macro line
macro_line = line[1:].strip()
# Check for unmatched braces in the whole line
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
# Check for unclosed quotes
if macro_line.count('"') % 2 != 0:
error_message = f"Unclosed double quotes\nLine: '{orig_line}'"
return "", error_message
# Split by optional colon separator
var_sections = re.split(r'\s*:\s*', macro_line)
for section in var_sections:
section = section.strip()
if not section:
continue
# Extract variable name
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
# Check variable value format
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
# Extract all quoted values
var_values = re.findall(r'"([^"]*)"', value_part)
# Check if there are values specified
if not var_values:
error_message = f"No quoted values found for variable '{{{var_name}}}'\nLine: '{orig_line}'"
return "", error_message
# Check for missing commas between values
# Look for patterns like "value""value" (missing comma)
if re.search(r'"[^,]*"[^,]*"', value_part):
error_message = f"Missing comma between values for variable '{{{var_name}}}'\nLine: '{orig_line}'"
return "", error_message
# Store the variable values
current_variables[var_name] = var_values
# Handle template lines
else:
# Check for unknown variables in template line
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
# Add to current template lines
current_template_lines.append(line)
# Process any remaining template lines
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 = []
# Find the maximum number of values for any variable
max_values = max(len(values) for values in variables.values())
# Generate each combination
for i in range(max_values):
for template in template_lines:
output_line = template
for var_name, var_values in variables.items():
# Use modulo to cycle through values if needed
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
"""
# Remove leading '!' if present
if macro_line.startswith('!'):
macro_line = macro_line[1:].strip()
variable_names = []
# Check for unmatched braces
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"
# Split by optional colon separator
var_sections = re.split(r'\s*:\s*', macro_line)
for section in var_sections:
section = section.strip()
if not section:
continue
# Extract variable name
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
"""
# Remove leading '!' if present
if macro_line.startswith('!'):
macro_line = macro_line[1:].strip()
variables = {}
# Check for unmatched braces
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"
# Check for unclosed quotes
if macro_line.count('"') % 2 != 0:
return {}, "Unclosed double quotes"
# Split by optional colon separator
var_sections = re.split(r'\s*:\s*', macro_line)
for section in var_sections:
section = section.strip()
if not section:
continue
# Extract variable name
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"
# Check variable value format
value_part = section[section.find('}')+1:].strip()
if not value_part.startswith('='):
return {}, f"Missing '=' after variable '{{{var_name}}}'"
# Extract all quoted values
var_values = re.findall(r'"([^"]*)"', value_part)
# Check if there are values specified
if not var_values:
return {}, f"No quoted values found for variable '{{{var_name}}}'"
# Check for missing commas between values
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():
# Format each value with quotes
quoted_values = [f'"{value}"' for value in values]
# Join values with commas
values_str = ','.join(quoted_values)
# Create the variable assignment
section = f"{{{var_name}}}={values_str}"
sections.append(section)
# Join sections with a colon and space for readability
return "! " + " : ".join(sections) |