Heading 2 Recipes class mt-12 Markdown Common patterns and recipes for working with Terrace documents in Python. Heading 3 Configuration File Parser class mt-8 mb-4 Markdown Parse a hierarchical configuration file with sections and key-value pairs. CodeBlock python from terrace import use_document, create_file_reader from typing import Dict, Any def parse_config_file(file_path: str) -> Dict[str, Any]: """Parse a Terrace configuration file into a nested dictionary""" reader = create_file_reader(file_path) doc = use_document(reader) config = {} stack = [config] for node in doc: # Adjust stack to current level while len(stack) > node.level + 1: stack.pop() current_dict = stack[-1] if node.tail: # Key-value pair current_dict[node.head] = node.tail else: # Section header current_dict[node.head] = {} stack.append(current_dict[node.head]) return config # Usage config = parse_config_file('app.tce') print(config['database']['host']) Heading 3 Document Outline Generator class mt-8 mb-4 Markdown Extract a document outline based on heading levels. CodeBlock python from terrace import use_document, create_string_reader from dataclasses import dataclass from typing import List @dataclass class Heading: level: int title: str children: List['Heading'] def extract_outline(content: str) -> List[Heading]: """Extract document outline from Terrace content""" reader = create_string_reader(content) doc = use_document(reader) headings = [] stack = [] for node in doc: if node.is_('heading') or node.is_('h1') or node.is_('h2') or node.is_('h3'): heading = Heading(level=node.level, title=node.tail, children=[]) # Find the right parent in the stack while stack and stack[-1].level >= heading.level: stack.pop() if stack: stack[-1].children.append(heading) else: headings.append(heading) stack.append(heading) return headings Heading 3 Template Engine class mt-8 mb-4 Markdown Simple template engine that processes Terrace templates with variables. CodeBlock python from terrace import use_document, create_string_reader from typing import Dict, Any import re def render_template(template: str, variables: Dict[str, Any]) -> str: """Render a Terrace template with variable substitution""" reader = create_string_reader(template) doc = use_document(reader) output = [] indent_str = " " # Two spaces per level for node in doc: # Apply indentation indentation = indent_str * node.level if node.is_('var'): # Variable substitution: var name -> value var_name = node.tail value = variables.get(var_name, f"{{undefined: {var_name}}}") output.append(f"{indentation}{value}") elif node.is_('if'): # Conditional rendering: if variable_name condition = node.tail if variables.get(condition): # Process children if condition is truthy for child in node.children(): child_line = f"{indent_str * child.level}{child.content}" # Substitute variables in child content child_line = re.sub(r'\{\{(\w+)\}\}', lambda m: str(variables.get(m.group(1), m.group(0))), child_line) output.append(child_line) elif node.is_('loop'): # Loop over array: loop items array_name = node.tail items = variables.get(array_name, []) for item in items: for child in node.children(): child_line = f"{indent_str * child.level}{child.content}" # Make item available as 'item' variable temp_vars = {**variables, 'item': item} child_line = re.sub(r'\{\{(\w+)\}\}', lambda m: str(temp_vars.get(m.group(1), m.group(0))), child_line) output.append(child_line) else: # Regular content with variable substitution content = node.content content = re.sub(r'\{\{(\w+)\}\}', lambda m: str(variables.get(m.group(1), m.group(0))), content) output.append(f"{indentation}{content}") return '\n'.join(output) # Usage template = """ title {{page_title}} if show_author author {{author_name}} content loop articles article {{item.title}} summary {{item.summary}} """ variables = { 'page_title': 'My Blog', 'show_author': True, 'author_name': 'John Doe', 'articles': [ {'title': 'First Post', 'summary': 'This is the first post'}, {'title': 'Second Post', 'summary': 'This is the second post'} ] } result = render_template(template, variables) print(result) Heading 3 Data Validation class mt-8 mb-4 Markdown Validate Terrace document structure against a schema. CodeBlock python from terrace import use_document, create_string_reader from typing import Dict, List, Set, Optional from dataclasses import dataclass @dataclass class ValidationError: line_number: int message: str class TerraceValidator: def __init__(self): self.required_fields: Dict[str, Set[str]] = {} self.allowed_fields: Dict[str, Set[str]] = {} self.field_types: Dict[str, type] = {} def require_fields(self, context: str, fields: List[str]): """Require specific fields in a context""" self.required_fields[context] = set(fields) def allow_fields(self, context: str, fields: List[str]): """Allow specific fields in a context""" self.allowed_fields[context] = set(fields) def validate(self, content: str) -> List[ValidationError]: """Validate content and return list of errors""" reader = create_string_reader(content) doc = use_document(reader) errors = [] context_stack = ['root'] found_fields = {'root': set()} for node in doc: # Update context stack based on indentation target_depth = node.level + 1 while len(context_stack) > target_depth: # Check required fields when leaving context leaving_context = context_stack.pop() required = self.required_fields.get(leaving_context, set()) found = found_fields.get(leaving_context, set()) missing = required - found if missing: errors.append(ValidationError( node.line_number, f"Missing required fields in {leaving_context}: {', '.join(missing)}" )) found_fields.pop(leaving_context, None) current_context = context_stack[-1] # Check if field is allowed allowed = self.allowed_fields.get(current_context, None) if allowed is not None and node.head not in allowed: errors.append(ValidationError( node.line_number, f"Field '{node.head}' not allowed in context '{current_context}'" )) # Track found fields found_fields.setdefault(current_context, set()).add(node.head) # If this node has children, it becomes a new context if any(True for _ in node.children()): # Check if has children context_stack.append(node.head) found_fields[node.head] = set() return errors # Usage validator = TerraceValidator() validator.require_fields('root', ['title', 'content']) validator.allow_fields('root', ['title', 'author', 'date', 'content']) validator.allow_fields('content', ['section', 'paragraph']) content = """ title My Document content section Introduction paragraph Hello world """ errors = validator.validate(content) for error in errors: print(f"Line {error.line_number}: {error.message}")