Ben
Clean deploy - only essential files for Gradio UI
4a9cdf6
Raw
History Blame Contribute Delete
16.2 kB
"""
TCL Parser - Converts TCL expressions into structured representations
The TCL parser handles:
- Symbol recognition and validation
- Causal relationship parsing
- Constraint expression parsing
- Mathematical/logical operation parsing
Grammar encodes causality, not syntax - this is fundamental to TCL design
"""
import re
from typing import List, Dict, Any, Optional, Tuple, Union
from dataclasses import dataclass
from enum import Enum
from .tcl_symbols import SymbolType
class TokenType(Enum):
"""Token types for TCL parsing"""
SYMBOL = "symbol"
OPERATOR = "operator"
CAUSALITY = "causality"
CONSTRAINT = "constraint"
NUMBER = "number"
IDENTIFIER = "identifier"
WHITESPACE = "whitespace"
END = "end"
@dataclass
class Token:
"""Represents a parsed token"""
type: TokenType
value: str
position: int
line: int
column: int
class TCLParseError(Exception):
"""Exception raised during TCL parsing"""
def __init__(self, message: str, position: int, line: int, column: int):
super().__init__(f"Parse error at line {line}, column {column}: {message}")
self.position = position
self.line = line
self.column = column
@dataclass
class ParsedExpression:
"""Represents a parsed TCL expression"""
type: str # 'symbol', 'causality', 'constraint', 'operation'
content: Any
position: Tuple[int, int] # (start_line, start_column)
dependencies: List[str] = None # Symbol IDs this expression depends on
def __post_init__(self):
if self.dependencies is None:
self.dependencies = []
@dataclass
class SymbolReference:
"""Represents a reference to a TCL symbol"""
name: str
symbol_type: SymbolType
id: Optional[str] = None
properties: Dict[str, Any] = None
def __post_init__(self):
if self.properties is None:
self.properties = {}
class TCLParser:
"""Parser for Thought-Compression Language expressions"""
def __init__(self):
self.current_line = 1
self.current_column = 1
self.input = ""
self.position = 0
# Define symbol patterns
self.symbol_patterns = [
# Mathematical symbols
(r'[∅∞∑∫∂∀∃¬]', TokenType.SYMBOL),
(r'[ΨΓΛΩΦΔ]', TokenType.SYMBOL),
(r'[→⟹≡≠⊃∪]', TokenType.SYMBOL),
# Complex symbols (combinations)
(r'[ΣΨΓΛ∞Ψ∀Γ]', TokenType.SYMBOL),
# Operators
(r'[+\-*/=<>!]', TokenType.OPERATOR),
# Causality operators
(r'→|⟹|⇒|⤴|⤵', TokenType.CAUSALITY),
# Constraint markers
(r'[{}⟂∥⊥]', TokenType.CONSTRAINT),
# Numbers
(r'\d+\.?\d*', TokenType.NUMBER),
# Identifiers (words)
(r'[a-zA-Z_][a-zA-Z0-9_]*', TokenType.IDENTIFIER),
]
# Causality patterns
self.causality_patterns = [
r'(\w+)\s*→\s*(\w+)', # A causes B
r'(\w+)\s*⟹\s*(\w+)', # A implies B
r'(\w+)\s*⇒\s*(\w+)', # A therefore B
]
# Constraint patterns
self.constraint_patterns = [
r'\{([^}]+)\}', # {constraint}
r'(\w+)\s*⊥\s*(\w+)', # A perpendicular B
r'(\w+)\s*∥\s*(\w+)', # A parallel B
]
def parse(self, tcl_input: str) -> List[ParsedExpression]:
"""
Parse TCL input into structured expressions
Args:
tcl_input: TCL expression string to parse
Returns:
List of parsed expressions
Raises:
TCLParseError: If parsing fails
"""
self.input = tcl_input
self.current_line = 1
self.current_column = 1
self.position = 0
expressions = []
# Parse line by line for better error reporting
lines = tcl_input.split('\n')
for line_num, line in enumerate(lines, 1):
line_expressions = self._parse_line(line, line_num)
expressions.extend(line_expressions)
# Post-process expressions for causality and constraints
processed_expressions = self._post_process_expressions(expressions)
return processed_expressions
def _parse_line(self, line: str, line_number: int) -> List[ParsedExpression]:
"""Parse a single line of TCL"""
expressions = []
# Skip empty lines and comments
line = line.strip()
if not line or line.startswith('//'):
return expressions
# Try to match causality patterns first
for pattern in self.causality_patterns:
matches = list(re.finditer(pattern, line))
for match in matches:
cause = match.group(1).strip()
effect = match.group(2).strip()
expr = ParsedExpression(
type="causality",
content={
'cause': cause,
'effect': effect,
'operator': match.group(0)
},
position=(line_number, match.start() + 1)
)
expressions.append(expr)
# Remove matched portion to avoid reprocessing
line = line[:match.start()] + line[match.end():]
# Try to match constraint patterns
for pattern in self.constraint_patterns:
matches = list(re.finditer(pattern, line))
for match in matches:
if len(match.groups()) == 1:
# {constraint} format
constraint = match.group(1).strip()
expr = ParsedExpression(
type="constraint",
content={
'constraint': constraint,
'operator': match.group(0)
},
position=(line_number, match.start() + 1)
)
else:
# A ⊥ B or A ∥ B format
expr = ParsedExpression(
type="constraint",
content={
'left': match.group(1).strip(),
'right': match.group(2).strip(),
'operator': match.group(0)
},
position=(line_number, match.start() + 1)
)
expressions.append(expr)
# Remove matched portion
line = line[:match.start()] + line[match.end():]
# Parse remaining symbols and operations
remaining_line = line.strip()
if remaining_line:
symbol_expressions = self._parse_symbols_and_operations(remaining_line, line_number)
expressions.extend(symbol_expressions)
return expressions
def _parse_symbols_and_operations(self, line: str, line_number: int) -> List[ParsedExpression]:
"""Parse symbols and operations from remaining line content"""
expressions = []
# Tokenize the line
tokens = self._tokenize(line)
# Parse based on token patterns
i = 0
while i < len(tokens):
token = tokens[i]
if token.type == TokenType.SYMBOL:
# Check if this is a complex operation
if i + 2 < len(tokens) and tokens[i + 1].type in [TokenType.OPERATOR, TokenType.CAUSALITY]:
# Operation or causality
operator = tokens[i + 1]
right_operand = tokens[i + 2] if i + 2 < len(tokens) else None
if right_operand:
expr = ParsedExpression(
type="operation",
content={
'left': token.value,
'operator': operator.value,
'right': right_operand.value
},
position=(line_number, token.position)
)
expressions.append(expr)
i += 3
else:
# Unary operation
expr = ParsedExpression(
type="symbol",
content={'symbol': token.value},
position=(line_number, token.position)
)
expressions.append(expr)
i += 1
else:
# Simple symbol
expr = ParsedExpression(
type="symbol",
content={'symbol': token.value},
position=(line_number, token.position)
)
expressions.append(expr)
i += 1
elif token.type == TokenType.IDENTIFIER:
# Treat identifiers as concept symbols
expr = ParsedExpression(
type="symbol",
content={'symbol': token.value, 'type': 'concept'},
position=(line_number, token.position)
)
expressions.append(expr)
i += 1
elif token.type == TokenType.OPERATOR:
# Skip standalone operators (they should be part of operations)
i += 1
else:
# Skip other token types
i += 1
return expressions
def _tokenize(self, text: str) -> List[Token]:
"""Tokenize text into TCL tokens"""
tokens = []
position = 0
while position < len(text):
matched = False
for pattern, token_type in self.symbol_patterns:
match = re.match(pattern, text[position:])
if match:
token = Token(
type=token_type,
value=match.group(0),
position=position,
line=self.current_line,
column=self.current_column + position
)
tokens.append(token)
position += len(match.group(0))
matched = True
break
if not matched:
# Skip whitespace
if text[position].isspace():
position += 1
else:
# Unknown character - treat as identifier continuation
start = position
while position < len(text) and not text[position].isspace():
position += 1
token = Token(
type=TokenType.IDENTIFIER,
value=text[start:position],
position=start,
line=self.current_line,
column=self.current_column + start
)
tokens.append(token)
# Add end token
tokens.append(Token(
type=TokenType.END,
value="",
position=len(text),
line=self.current_line,
column=self.current_column + len(text)
))
return tokens
def _post_process_expressions(self, expressions: List[ParsedExpression]) -> List[ParsedExpression]:
"""Post-process parsed expressions to resolve dependencies and relationships"""
processed = []
for expr in expressions:
# Add dependencies based on expression type
if expr.type == "causality":
expr.dependencies = [expr.content['cause'], expr.content['effect']]
elif expr.type == "operation":
expr.dependencies = [expr.content['left'], expr.content['right']]
elif expr.type == "symbol":
expr.dependencies = [expr.content['symbol']]
processed.append(expr)
# Sort expressions by dependency order
processed.sort(key=lambda x: len(x.dependencies))
return processed
def validate_expression(self, expr: ParsedExpression) -> bool:
"""Validate a parsed expression for correctness"""
try:
if expr.type == "symbol":
symbol_name = expr.content.get('symbol', '')
return len(symbol_name) > 0 and not any(c.isspace() for c in symbol_name)
elif expr.type == "causality":
cause = expr.content.get('cause', '')
effect = expr.content.get('effect', '')
operator = expr.content.get('operator', '')
return (len(cause) > 0 and len(effect) > 0 and
operator in ['→', '⟹', '⇒'])
elif expr.type == "constraint":
if 'constraint' in expr.content:
constraint = expr.content['constraint']
return len(constraint) > 0
else:
left = expr.content.get('left', '')
right = expr.content.get('right', '')
operator = expr.content.get('operator', '')
return (len(left) > 0 and len(right) > 0 and
operator in ['⊥', '∥'])
elif expr.type == "operation":
left = expr.content.get('left', '')
operator = expr.content.get('operator', '')
right = expr.content.get('right', '')
return len(left) > 0 and len(operator) > 0 and len(right) > 0
return False
except Exception:
return False
def get_expression_info(self, expr: ParsedExpression) -> Dict[str, Any]:
"""Get detailed information about a parsed expression"""
return {
'type': expr.type,
'content': expr.content,
'position': expr.position,
'dependencies': expr.dependencies,
'valid': self.validate_expression(expr),
'complexity': len(expr.dependencies)
}
# Example TCL expressions for testing
EXAMPLE_EXPRESSIONS = [
"Ψ → Γ", # Thought causes concept
"Γ ⊥ Λ", # Concept perpendicular to logic
"∀x (x → ∞Ψ)", # Universal causation to infinite thinking
"ΣΨ = Ψ₁ + Ψ₂ + Ψ₃", # Superthought as sum of thoughts
"ΓΛ ⟹ Δ", # Conceptual logic implies difference
]
def parse_example_expressions():
"""Parse and display example TCL expressions"""
parser = TCLParser()
print("TCL Parser - Example Expressions")
print("=" * 40)
for i, expr_text in enumerate(EXAMPLE_EXPRESSIONS, 1):
print(f"\nExample {i}: {expr_text}")
print("-" * 30)
try:
parsed = parser.parse(expr_text)
for j, expr in enumerate(parsed):
info = parser.get_expression_info(expr)
print(f" Expression {j + 1}:")
print(f" Type: {info['type']}")
print(f" Content: {info['content']}")
print(f" Dependencies: {info['dependencies']}")
print(f" Valid: {info['valid']}")
print(f" Complexity: {info['complexity']}")
except TCLParseError as e:
print(f" Parse Error: {e}")
if __name__ == "__main__":
parse_example_expressions()