154 lines
4.5 KiB
Python
154 lines
4.5 KiB
Python
"""
|
|
template — One-Shot YAML Template Generator.
|
|
|
|
Converts an inferred k-ORE/SORE/CHARE expression back into
|
|
a human-readable YAML skeleton.
|
|
|
|
Generates:
|
|
- A YAML scaffold with placeholders
|
|
- Cardinality annotations:
|
|
* # REQUIRED: Exactly 1
|
|
* # REPEATED: 1 or more
|
|
* # OPTIONAL: 0 or 1
|
|
* # VARIABLE: 0 or more
|
|
* # CHOOSE: alternative module
|
|
"""
|
|
|
|
|
|
def parse_expression(expr):
|
|
"""Split a regular expression into its components."""
|
|
if not expr or expr in ('∅', 'ε', ''):
|
|
return [('empty', 'ε')]
|
|
|
|
tokens = []
|
|
i = 0
|
|
while i < len(expr):
|
|
if expr[i] == '(':
|
|
depth = 1
|
|
j = i + 1
|
|
while j < len(expr) and depth > 0:
|
|
if expr[j] == '(':
|
|
depth += 1
|
|
elif expr[j] == ')':
|
|
depth -= 1
|
|
j += 1
|
|
group = expr[i:j]
|
|
quantifier = ''
|
|
if j < len(expr) and expr[j] in '*+?':
|
|
quantifier = expr[j]
|
|
j += 1
|
|
tokens.append(('group', group, quantifier))
|
|
i = j
|
|
elif expr[i] == '|':
|
|
tokens.append(('pipe', '|'))
|
|
i += 1
|
|
elif expr[i] == '.':
|
|
if i + 1 < len(expr) and expr[i + 1] == '.':
|
|
tokens.append(('concat', '..'))
|
|
i += 2
|
|
else:
|
|
tokens.append(('concat', '.'))
|
|
i += 1
|
|
elif expr[i] in '*+?':
|
|
if tokens and tokens[-1][0] == 'name':
|
|
name, val, _ = tokens[-1]
|
|
tokens[-1] = (name, val, expr[i])
|
|
i += 1
|
|
elif expr[i].isalnum() or expr[i] in '/_-':
|
|
j = i
|
|
while j < len(expr) and (expr[j].isalnum() or expr[j] in '/_-'):
|
|
j += 1
|
|
name = expr[i:j]
|
|
tokens.append(('name', name, ''))
|
|
i = j
|
|
else:
|
|
i += 1
|
|
|
|
return tokens
|
|
|
|
|
|
def format_prompt_cardinality(quantifier):
|
|
"""Return the cardinality description for a quantifier."""
|
|
mapping = {
|
|
'': '# PFLICHT: Genau 1 mal erforderlich',
|
|
'+': '# PFLICHT: 1 oder mehrmals erforderlich',
|
|
'*': '# OPTIONAL: 0 oder mehrmals',
|
|
'?': '# OPTIONAL: 0 oder 1 mal (darf weggelassen werden)',
|
|
}
|
|
return mapping.get(quantifier, '')
|
|
|
|
|
|
def generate_template(expr, context_key=None, include_header=True):
|
|
"""
|
|
Generate a YAML one-shot template from a regular expression.
|
|
|
|
Args:
|
|
expr: Inferred expression (string)
|
|
context_key: YAML container key (e.g. 'tasks')
|
|
include_header: Whether to include header section (name, hosts)
|
|
|
|
Returns:
|
|
YAML skeleton with placeholders and cardinality comments
|
|
"""
|
|
if not expr or expr in ('∅', 'ε'):
|
|
return "# No structure inferred (empty sequences or no examples)"
|
|
|
|
if include_header:
|
|
lines = [
|
|
"- name: <Name des Plays>",
|
|
" hosts: <Ziel-Server> # PFLICHT: Genau 1 mal erforderlich",
|
|
]
|
|
if context_key:
|
|
lines.append(f" {context_key}:")
|
|
else:
|
|
lines.append(" tasks:")
|
|
indent = " "
|
|
else:
|
|
lines = []
|
|
if context_key:
|
|
lines.append(f" {context_key}: # Container-Kontext: {context_key}")
|
|
else:
|
|
lines.append(" tasks:")
|
|
indent = " "
|
|
|
|
tokens = parse_expression(expr)
|
|
task_index = 0
|
|
skip_until_pipe = False
|
|
|
|
alternatives = []
|
|
in_alternatives = False
|
|
|
|
i = 0
|
|
while i < len(tokens):
|
|
token = tokens[i]
|
|
|
|
if token[0] == 'group':
|
|
group_str = token[1]
|
|
quantifier = token[2]
|
|
card = format_prompt_cardinality(quantifier)
|
|
inner_expr = group_str[1:-1]
|
|
if '|' in inner_expr:
|
|
alts = inner_expr.split('|')
|
|
lines.append(f"{indent}# CHOOSE (pick one):")
|
|
for alt in alts:
|
|
alt_clean = alt.strip()
|
|
lines.append(f"{indent}# - {alt_clean}: <params>")
|
|
if card:
|
|
lines[-1] = f"{lines[-1]} {card}"
|
|
else:
|
|
lines.append(f"{indent}- {inner_expr}: <params> {card}")
|
|
task_index += 1
|
|
|
|
elif token[0] == 'name':
|
|
name = token[1]
|
|
quantifier = token[2]
|
|
card = format_prompt_cardinality(quantifier)
|
|
lines.append(f"{indent}- {name}: <params> {card}")
|
|
task_index += 1
|
|
|
|
elif token[0] == 'pipe':
|
|
pass
|
|
|
|
i += 1
|
|
|
|
return '\n'.join(lines) + '\n'
|