105 lines
3.6 KiB
Python
105 lines
3.6 KiB
Python
"""template — One-Shot YAML Template Generator from AST nodes."""
|
|
|
|
from .grammar import Symbol, Concat, Alt, Plus, Optional, Star, Epsilon, Empty
|
|
|
|
|
|
def format_cardinality(node):
|
|
"""Return cardinality description for an AST node."""
|
|
if isinstance(node, Plus):
|
|
return '# PFLICHT: 1 oder mehrmals erforderlich'
|
|
if isinstance(node, Optional):
|
|
return '# OPTIONAL: 0 oder 1 mal (darf weggelassen werden)'
|
|
if isinstance(node, Star):
|
|
return '# OPTIONAL: 0 oder mehrmals'
|
|
return '# PFLICHT: Genau 1 mal erforderlich'
|
|
|
|
|
|
def generate_template(node, context_key=None, include_header=True):
|
|
"""Generate a YAML one-shot template from an AST node.
|
|
|
|
Args:
|
|
node: Grammar AST node
|
|
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 node is None or isinstance(node, Empty):
|
|
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 = " "
|
|
|
|
_generate_node(node, lines, indent)
|
|
return '\n'.join(lines) + '\n'
|
|
|
|
|
|
def _generate_node(node, lines, indent):
|
|
"""Recursively generate YAML lines from AST node."""
|
|
if isinstance(node, Symbol):
|
|
lines.append(f"{indent}- {node.value}: <params>")
|
|
elif isinstance(node, Plus):
|
|
card = format_cardinality(node)
|
|
_generate_wrapped(node.child, lines, indent, card)
|
|
elif isinstance(node, Optional):
|
|
card = format_cardinality(node)
|
|
_generate_wrapped(node.child, lines, indent, card)
|
|
elif isinstance(node, Star):
|
|
card = format_cardinality(node)
|
|
_generate_wrapped(node.child, lines, indent, card)
|
|
elif isinstance(node, Alt):
|
|
lines.append(f"{indent}# CHOOSE (pick one):")
|
|
for part in node.parts:
|
|
name = _node_name(part)
|
|
lines.append(f"{indent}# - {name}: <params>")
|
|
elif isinstance(node, Concat):
|
|
for part in node.parts:
|
|
_generate_node(part, lines, indent)
|
|
elif isinstance(node, Epsilon):
|
|
pass
|
|
elif isinstance(node, Empty):
|
|
pass
|
|
|
|
|
|
def _generate_wrapped(child, lines, indent, card):
|
|
"""Generate a child node with cardinality annotation."""
|
|
if isinstance(child, Alt):
|
|
lines.append(f"{indent}# CHOOSE (pick one):")
|
|
for part in child.parts:
|
|
name = _node_name(part)
|
|
lines.append(f"{indent}# - {name}: <params>")
|
|
lines[-1] = f"{lines[-1]} {card}"
|
|
elif isinstance(child, Symbol):
|
|
lines.append(f"{indent}- {child.value}: <params> {card}")
|
|
elif isinstance(child, Concat):
|
|
for part in child.parts:
|
|
_generate_node(part, lines, indent)
|
|
else:
|
|
_generate_node(child, lines, indent)
|
|
|
|
|
|
def _node_name(node):
|
|
"""Get a human-readable name for a node."""
|
|
if isinstance(node, Symbol):
|
|
return node.value
|
|
if isinstance(node, Concat):
|
|
return ".".join(_node_name(p) for p in node.parts)
|
|
if isinstance(node, Alt):
|
|
return "|".join(_node_name(p) for p in node.parts)
|
|
return "..."
|