|
|
|
|
|
""" |
|
|
Coptic Prolog Rules - Neural-Symbolic Integration |
|
|
================================================== |
|
|
|
|
|
Integrates Prolog logic programming with neural dependency parsing |
|
|
to enhance parsing accuracy through explicit grammatical rules. |
|
|
|
|
|
Uses janus (SWI-Prolog Python interface) for bidirectional integration. |
|
|
|
|
|
Author: Coptic NLP Project |
|
|
License: CC BY-NC-SA 4.0 |
|
|
""" |
|
|
|
|
|
from pyswip import Prolog |
|
|
import warnings |
|
|
import sys |
|
|
import os |
|
|
from contextlib import contextmanager |
|
|
warnings.filterwarnings('ignore') |
|
|
|
|
|
@contextmanager |
|
|
def suppress_stderr(): |
|
|
"""Temporarily suppress stderr output from Prolog queries""" |
|
|
devnull = open(os.devnull, 'w') |
|
|
old_stderr = sys.stderr |
|
|
sys.stderr = devnull |
|
|
try: |
|
|
yield |
|
|
finally: |
|
|
sys.stderr = old_stderr |
|
|
devnull.close() |
|
|
|
|
|
|
|
|
class CopticPrologRules: |
|
|
""" |
|
|
Prolog-based grammatical rule engine for Coptic parsing validation |
|
|
and enhancement. |
|
|
""" |
|
|
|
|
|
def __init__(self): |
|
|
"""Initialize Prolog engine and load Coptic grammar rules""" |
|
|
self.prolog_initialized = False |
|
|
self.prolog = None |
|
|
self._initialize_prolog() |
|
|
|
|
|
def _initialize_prolog(self): |
|
|
"""Initialize SWI-Prolog and define Coptic grammatical rules""" |
|
|
try: |
|
|
|
|
|
self.prolog = Prolog() |
|
|
|
|
|
|
|
|
self._load_coptic_grammar() |
|
|
|
|
|
self.prolog_initialized = True |
|
|
print("✓ Prolog engine initialized successfully") |
|
|
|
|
|
except Exception as e: |
|
|
print(f"⚠️ Warning: Prolog initialization failed: {e}") |
|
|
print(" Parser will continue without Prolog validation") |
|
|
self.prolog_initialized = False |
|
|
|
|
|
def _load_dcg_grammar(self): |
|
|
""" |
|
|
Load DCG-based grammar rules from coptic_grammar.pl |
|
|
and Coptic lexicon from coptic_lexicon.pl |
|
|
|
|
|
This adds more sophisticated pattern matching using Definite Clause Grammars, |
|
|
adapted from the French DETECT5.PRO error detector. |
|
|
""" |
|
|
try: |
|
|
from pathlib import Path |
|
|
|
|
|
|
|
|
|
|
|
current_dir = Path(__file__).parent |
|
|
grammar_file = current_dir / "coptic_grammar.pl" |
|
|
|
|
|
|
|
|
if grammar_file.exists(): |
|
|
|
|
|
grammar_path = str(grammar_file.absolute()).replace('\\', '/') |
|
|
|
|
|
|
|
|
query = f"consult('{grammar_path}')" |
|
|
list(self.prolog.query(query)) |
|
|
|
|
|
print(f"✓ DCG grammar rules and lexicon loaded from {grammar_file.name}") |
|
|
self.dcg_loaded = True |
|
|
else: |
|
|
print(f"ℹ DCG grammar file not found at {grammar_file}") |
|
|
self.dcg_loaded = False |
|
|
|
|
|
except Exception as e: |
|
|
print(f"⚠️ Warning: Could not load DCG grammar: {e}") |
|
|
self.dcg_loaded = False |
|
|
|
|
|
def _load_coptic_grammar(self): |
|
|
"""Load Coptic linguistic rules into Prolog""" |
|
|
|
|
|
|
|
|
self._load_dcg_grammar() |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
self.prolog.assertz("definite_article('ⲡ')") |
|
|
self.prolog.assertz("definite_article('ⲧ')") |
|
|
self.prolog.assertz("definite_article('ⲛ')") |
|
|
self.prolog.assertz("definite_article('ⲡⲉ')") |
|
|
self.prolog.assertz("definite_article('ⲧⲉ')") |
|
|
self.prolog.assertz("definite_article('ⲛⲉ')") |
|
|
|
|
|
|
|
|
self.prolog.assertz("independent_pronoun('ⲁⲛⲟⲕ')") |
|
|
self.prolog.assertz("independent_pronoun('ⲛⲧⲟⲕ')") |
|
|
self.prolog.assertz("independent_pronoun('ⲛⲧⲟ')") |
|
|
self.prolog.assertz("independent_pronoun('ⲛⲧⲟϥ')") |
|
|
self.prolog.assertz("independent_pronoun('ⲛⲧⲟⲥ')") |
|
|
self.prolog.assertz("independent_pronoun('ⲁⲛⲟⲛ')") |
|
|
self.prolog.assertz("independent_pronoun('ⲛⲧⲱⲧⲛ')") |
|
|
self.prolog.assertz("independent_pronoun('ⲛⲧⲟⲟⲩ')") |
|
|
|
|
|
|
|
|
self.prolog.assertz("suffix_pronoun('ⲓ')") |
|
|
self.prolog.assertz("suffix_pronoun('ⲕ')") |
|
|
self.prolog.assertz("suffix_pronoun('ϥ')") |
|
|
self.prolog.assertz("suffix_pronoun('ⲥ')") |
|
|
self.prolog.assertz("suffix_pronoun('ⲛ')") |
|
|
self.prolog.assertz("suffix_pronoun('ⲧⲛ')") |
|
|
self.prolog.assertz("suffix_pronoun('ⲟⲩ')") |
|
|
|
|
|
|
|
|
self.prolog.assertz("conjugation_base('ⲁ')") |
|
|
self.prolog.assertz("conjugation_base('ⲛⲉ')") |
|
|
self.prolog.assertz("conjugation_base('ϣⲁ')") |
|
|
self.prolog.assertz("conjugation_base('ⲙⲡⲉ')") |
|
|
self.prolog.assertz("conjugation_base('ⲙⲛ')") |
|
|
self.prolog.assertz("conjugation_base('ⲉⲣϣⲁⲛ')") |
|
|
|
|
|
|
|
|
self.prolog.assertz("copula('ⲡⲉ')") |
|
|
self.prolog.assertz("copula('ⲧⲉ')") |
|
|
self.prolog.assertz("copula('ⲛⲉ')") |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
self.prolog.assertz("valid_np(Article, Noun) :- definite_article(Article), noun_compatible(Noun)") |
|
|
|
|
|
|
|
|
self.prolog.assertz("noun_compatible(_)") |
|
|
|
|
|
|
|
|
self.prolog.assertz("requires_definiteness(Noun, Article) :- definite_article(Article)") |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
self.prolog.assertz("tripartite_sentence(Subject, Copula, Predicate) :- independent_pronoun(Subject), copula(Copula), noun_compatible(Predicate)") |
|
|
|
|
|
|
|
|
|
|
|
self.prolog.assertz("verbal_sentence(Conj, Subject, Verb) :- conjugation_base(Conj), (independent_pronoun(Subject) ; definite_article(Subject)), verb_compatible(Verb)") |
|
|
|
|
|
|
|
|
self.prolog.assertz("verb_compatible(_)") |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
self.prolog.assertz("valid_subject_verb(Subject, Verb, SubjPOS, VerbPOS) :- member(SubjPOS, ['PRON', 'NOUN', 'PROPN']), member(VerbPOS, ['VERB', 'AUX'])") |
|
|
|
|
|
|
|
|
self.prolog.assertz("valid_det_noun(Det, Noun, DetPOS, NounPOS) :- DetPOS = 'DET', member(NounPOS, ['NOUN', 'PROPN'])") |
|
|
|
|
|
|
|
|
self.prolog.assertz("valid_modifier(Head, Modifier, ModPOS) :- member(ModPOS, ['ADJ', 'ADV', 'DET'])") |
|
|
|
|
|
|
|
|
|
|
|
self.prolog.assertz("invalid_punct(Word, POS, Relation) :- Relation = 'punct', member(POS, ['VERB', 'NOUN', 'PRON', 'PROPN', 'DET', 'ADJ', 'ADV', 'AUX', 'NUM'])") |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
self.prolog.assertz("suggest_correction('DET', _, 'det')") |
|
|
|
|
|
|
|
|
|
|
|
self.prolog.assertz("suggest_correction('PRON', 'VERB', 'nsubj')") |
|
|
self.prolog.assertz("suggest_correction('PRON', 'AUX', 'nsubj')") |
|
|
self.prolog.assertz("suggest_correction('PRON', _, 'nsubj')") |
|
|
|
|
|
|
|
|
self.prolog.assertz("suggest_correction('NOUN', 'VERB', 'obj')") |
|
|
self.prolog.assertz("suggest_correction('NOUN', 'AUX', 'nsubj')") |
|
|
self.prolog.assertz("suggest_correction('NOUN', _, 'obl')") |
|
|
|
|
|
|
|
|
|
|
|
self.prolog.assertz("suggest_correction('VERB', 'SCONJ', 'ccomp')") |
|
|
self.prolog.assertz("suggest_correction('VERB', 'VERB', 'ccomp')") |
|
|
self.prolog.assertz("suggest_correction('VERB', _, 'root')") |
|
|
|
|
|
|
|
|
self.prolog.assertz("suggest_correction('AUX', _, 'cop')") |
|
|
|
|
|
|
|
|
self.prolog.assertz("suggest_correction('ADJ', 'NOUN', 'amod')") |
|
|
|
|
|
|
|
|
self.prolog.assertz("suggest_correction('ADV', _, 'advmod')") |
|
|
|
|
|
|
|
|
self.prolog.assertz("suggest_correction('NUM', 'NOUN', 'nummod')") |
|
|
self.prolog.assertz("suggest_correction('NUM', _, 'obl')") |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
self.prolog.assertz("has_suffix_pronoun(Word, Base, Suffix) :- atom_concat(Base, Suffix, Word), suffix_pronoun(Suffix), atom_length(Base, BaseLen), BaseLen > 0") |
|
|
|
|
|
|
|
|
self.prolog.assertz("strip_article(Word, Lemma) :- definite_article(Article), atom_concat(Article, Lemma, Word), atom_length(Lemma, LemmaLen), LemmaLen > 0") |
|
|
|
|
|
|
|
|
self.prolog.assertz("strip_article(Word, Word) :- \\+ (definite_article(Article), atom_concat(Article, _, Word))") |
|
|
|
|
|
print("✓ Coptic grammatical rules loaded into Prolog") |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def validate_dependency(self, head_word, dep_word, head_pos, dep_pos, relation): |
|
|
""" |
|
|
Validate a dependency relation using Prolog rules |
|
|
|
|
|
Args: |
|
|
head_word: The head word text |
|
|
dep_word: The dependent word text |
|
|
head_pos: POS tag of head |
|
|
dep_pos: POS tag of dependent |
|
|
relation: Dependency relation (nsubj, obj, det, etc.) |
|
|
|
|
|
Returns: |
|
|
dict: Validation result with status and suggestions |
|
|
""" |
|
|
if not self.prolog_initialized: |
|
|
return {"valid": True, "message": "Prolog not available"} |
|
|
|
|
|
try: |
|
|
result = {"valid": True, "warnings": [], "suggestions": []} |
|
|
|
|
|
|
|
|
if relation in ['nsubj', 'csubj']: |
|
|
query = f"valid_subject_verb('{dep_word}', '{head_word}', '{dep_pos}', '{head_pos}')" |
|
|
query_result = list(self.prolog.query(query)) |
|
|
if not query_result: |
|
|
result["warnings"].append( |
|
|
f"Unusual subject-verb: {dep_word} ({dep_pos}) → {head_word} ({head_pos})" |
|
|
) |
|
|
|
|
|
|
|
|
elif relation == 'det': |
|
|
query = f"valid_det_noun('{dep_word}', '{head_word}', '{dep_pos}', '{head_pos}')" |
|
|
query_result = list(self.prolog.query(query)) |
|
|
if not query_result: |
|
|
result["warnings"].append( |
|
|
f"Unusual det-noun: {dep_word} → {head_word}" |
|
|
) |
|
|
|
|
|
|
|
|
query = f"invalid_punct('{dep_word}', '{dep_pos}', '{relation}')" |
|
|
query_result = list(self.prolog.query(query)) |
|
|
if query_result: |
|
|
|
|
|
correction_query = f"suggest_correction('{dep_pos}', '{head_pos}', Suggestion)" |
|
|
correction_result = list(self.prolog.query(correction_query)) |
|
|
|
|
|
if correction_result and 'Suggestion' in correction_result[0]: |
|
|
suggested_rel = correction_result[0]['Suggestion'] |
|
|
result["warnings"].append( |
|
|
f"⚠️ PARSER ERROR: '{dep_word}' ({dep_pos}) incorrectly labeled as 'punct' → SUGGESTED: '{suggested_rel}'" |
|
|
) |
|
|
result["suggestions"].append({ |
|
|
"word": dep_word, |
|
|
"pos": dep_pos, |
|
|
"incorrect": relation, |
|
|
"suggested": suggested_rel, |
|
|
"head_pos": head_pos |
|
|
}) |
|
|
else: |
|
|
result["warnings"].append( |
|
|
f"⚠️ PARSER ERROR: '{dep_word}' ({dep_pos}) incorrectly labeled as 'punct' - should be a content relation" |
|
|
) |
|
|
|
|
|
return result |
|
|
|
|
|
except Exception as e: |
|
|
return {"valid": True, "message": f"Validation error: {e}"} |
|
|
|
|
|
def check_tripartite_pattern(self, words, pos_tags): |
|
|
""" |
|
|
Check if a sentence follows the Coptic tripartite nominal pattern |
|
|
|
|
|
Args: |
|
|
words: List of word forms |
|
|
pos_tags: List of POS tags |
|
|
|
|
|
Returns: |
|
|
dict: Pattern analysis results |
|
|
""" |
|
|
if not self.prolog_initialized or len(words) < 3: |
|
|
return {"is_tripartite": False} |
|
|
|
|
|
try: |
|
|
|
|
|
subj, cop, pred = words[0], words[1], words[2] |
|
|
|
|
|
query = f"tripartite_sentence('{subj}', '{cop}', '{pred}')" |
|
|
query_result = list(self.prolog.query(query)) |
|
|
is_tripartite = len(query_result) > 0 |
|
|
|
|
|
return { |
|
|
"is_tripartite": is_tripartite, |
|
|
"pattern": f"{subj} - {cop} - {pred}" if is_tripartite else None, |
|
|
"description": "Tripartite nominal sentence" if is_tripartite else None |
|
|
} |
|
|
|
|
|
except Exception as e: |
|
|
return {"is_tripartite": False, "error": str(e)} |
|
|
|
|
|
def analyze_morphology(self, word): |
|
|
""" |
|
|
Analyze word morphology using Prolog rules |
|
|
|
|
|
Args: |
|
|
word: Coptic word to analyze |
|
|
|
|
|
Returns: |
|
|
dict: Morphological analysis |
|
|
""" |
|
|
if not self.prolog_initialized: |
|
|
return {"word": word, "analyzed": False} |
|
|
|
|
|
try: |
|
|
analysis = {"word": word, "components": []} |
|
|
|
|
|
|
|
|
article_query = f"strip_article('{word}', Lemma)" |
|
|
results = list(self.prolog.query(article_query)) |
|
|
if results: |
|
|
result = results[0] |
|
|
if 'Lemma' in result: |
|
|
lemma = result['Lemma'] |
|
|
if lemma != word: |
|
|
analysis["has_article"] = True |
|
|
analysis["lemma"] = lemma |
|
|
analysis["article"] = word.replace(lemma, '') |
|
|
|
|
|
|
|
|
suffix_query = f"has_suffix_pronoun('{word}', Base, Suffix)" |
|
|
results = list(self.prolog.query(suffix_query)) |
|
|
if results: |
|
|
result = results[0] |
|
|
analysis["has_suffix"] = True |
|
|
analysis["base"] = result.get('Base') |
|
|
analysis["suffix"] = result.get('Suffix') |
|
|
|
|
|
return analysis |
|
|
|
|
|
except Exception as e: |
|
|
return {"word": word, "error": str(e)} |
|
|
|
|
|
def validate_parse_tree(self, words, pos_tags, heads, deprels): |
|
|
""" |
|
|
Validate an entire parse tree using Prolog constraints |
|
|
|
|
|
Args: |
|
|
words: List of word forms |
|
|
pos_tags: List of POS tags |
|
|
heads: List of head indices |
|
|
deprels: List of dependency relations |
|
|
|
|
|
Returns: |
|
|
dict: Overall validation results with warnings and suggestions |
|
|
""" |
|
|
if not self.prolog_initialized: |
|
|
return {"validated": False, "reason": "Prolog not available"} |
|
|
|
|
|
try: |
|
|
results = { |
|
|
"validated": True, |
|
|
"warnings": [], |
|
|
"suggestions": [], |
|
|
"patterns_found": [] |
|
|
} |
|
|
|
|
|
|
|
|
tripartite = self.check_tripartite_pattern(words, pos_tags) |
|
|
if tripartite.get("is_tripartite"): |
|
|
results["patterns_found"].append(tripartite) |
|
|
|
|
|
|
|
|
if hasattr(self, 'dcg_loaded') and self.dcg_loaded: |
|
|
try: |
|
|
dcg_results = self._validate_with_dcg(words, pos_tags, heads, deprels) |
|
|
if dcg_results and isinstance(dcg_results, dict): |
|
|
|
|
|
if "patterns_found" in dcg_results and dcg_results["patterns_found"]: |
|
|
results["patterns_found"].extend(dcg_results["patterns_found"]) |
|
|
if "warnings" in dcg_results and dcg_results["warnings"]: |
|
|
results["warnings"].extend(dcg_results["warnings"]) |
|
|
except Exception as e: |
|
|
print(f"Warning: DCG validation failed: {e}") |
|
|
|
|
|
|
|
|
|
|
|
for i, (word, pos, head, rel) in enumerate(zip(words, pos_tags, heads, deprels)): |
|
|
if head > 0 and head <= len(words): |
|
|
head_word = words[head - 1] |
|
|
head_pos = pos_tags[head - 1] |
|
|
|
|
|
validation = self.validate_dependency(head_word, word, head_pos, pos, rel) |
|
|
if validation.get("warnings"): |
|
|
results["warnings"].extend(validation["warnings"]) |
|
|
|
|
|
return results |
|
|
|
|
|
except Exception as e: |
|
|
return {"validated": False, "error": str(e)} |
|
|
|
|
|
def _validate_with_dcg(self, words, pos_tags, heads, deprels): |
|
|
""" |
|
|
Validate parse tree using DCG grammar rules |
|
|
|
|
|
Args: |
|
|
words: List of word tokens |
|
|
pos_tags: List of POS tags |
|
|
heads: List of head indices |
|
|
deprels: List of dependency relations |
|
|
|
|
|
Returns: |
|
|
dict: DCG validation results |
|
|
""" |
|
|
try: |
|
|
|
|
|
words_pl = self._list_to_prolog_atoms(words) |
|
|
pos_pl = self._list_to_prolog_atoms(pos_tags) |
|
|
heads_pl = '[' + ','.join(map(str, heads)) + ']' |
|
|
deprels_pl = self._list_to_prolog_atoms(deprels) |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
query = f"coptic_dependency_rules:validate_parse_tree({words_pl}, {pos_pl}, {heads_pl}, {deprels_pl})" |
|
|
|
|
|
|
|
|
try: |
|
|
with suppress_stderr(): |
|
|
list(self.prolog.query(query)) |
|
|
except Exception: |
|
|
|
|
|
return {"patterns_found": [], "warnings": []} |
|
|
|
|
|
|
|
|
patterns = [] |
|
|
pattern_query = "coptic_grammar:pattern_found(P)" |
|
|
try: |
|
|
for result in self.prolog.query(pattern_query): |
|
|
if isinstance(result, dict) and 'P' in result: |
|
|
pattern_data = result.get('P') |
|
|
if pattern_data: |
|
|
formatted = self._format_prolog_term(pattern_data) |
|
|
patterns.append(formatted) |
|
|
except Exception as e: |
|
|
print(f"Warning: Error retrieving patterns: {e}") |
|
|
|
|
|
|
|
|
warnings = [] |
|
|
warning_query = "coptic_grammar:warning(W)" |
|
|
try: |
|
|
for result in self.prolog.query(warning_query): |
|
|
if isinstance(result, dict) and 'W' in result: |
|
|
warning_data = result.get('W') |
|
|
if warning_data: |
|
|
formatted = self._format_prolog_term(warning_data) |
|
|
warnings.append(formatted) |
|
|
except Exception as e: |
|
|
print(f"Warning: Error retrieving warnings: {e}") |
|
|
|
|
|
|
|
|
try: |
|
|
list(self.prolog.query("coptic_grammar:retractall(pattern_found(_))")) |
|
|
list(self.prolog.query("coptic_grammar:retractall(warning(_))")) |
|
|
except Exception as e: |
|
|
print(f"Warning: Error cleaning up Prolog predicates: {e}") |
|
|
|
|
|
return { |
|
|
"patterns_found": patterns, |
|
|
"warnings": warnings |
|
|
} |
|
|
|
|
|
except Exception as e: |
|
|
print(f"DCG validation error: {e}") |
|
|
import traceback |
|
|
traceback.print_exc() |
|
|
return { |
|
|
"patterns_found": [], |
|
|
"warnings": [] |
|
|
} |
|
|
|
|
|
def _list_to_prolog_atoms(self, python_list): |
|
|
""" |
|
|
Convert Python list of strings to Prolog list with properly quoted atoms |
|
|
|
|
|
Args: |
|
|
python_list: Python list of strings |
|
|
|
|
|
Returns: |
|
|
str: Prolog list syntax |
|
|
""" |
|
|
if not python_list: |
|
|
return "[]" |
|
|
|
|
|
|
|
|
items = [] |
|
|
for item in python_list: |
|
|
|
|
|
escaped = str(item).replace("'", "\\'") |
|
|
items.append(f"'{escaped}'") |
|
|
|
|
|
return '[' + ','.join(items) + ']' |
|
|
|
|
|
def _format_prolog_term(self, term): |
|
|
""" |
|
|
Format a Prolog term for Python display |
|
|
|
|
|
Args: |
|
|
term: Prolog term (can be atom, list, or compound) |
|
|
|
|
|
Returns: |
|
|
dict: Formatted representation (always a dict) |
|
|
""" |
|
|
if isinstance(term, list): |
|
|
result = {} |
|
|
for item in term: |
|
|
if hasattr(item, 'name') and hasattr(item, 'args'): |
|
|
|
|
|
key = item.name |
|
|
value = item.args[0] if len(item.args) > 0 else None |
|
|
result[key] = str(value) if value is not None else '' |
|
|
return result if result else {'data': str(term)} |
|
|
elif isinstance(term, str): |
|
|
|
|
|
return {'type': term, 'data': term} |
|
|
else: |
|
|
|
|
|
return {'data': str(term)} |
|
|
|
|
|
def query_prolog(self, query_string): |
|
|
""" |
|
|
Direct Prolog query interface for custom queries |
|
|
|
|
|
Args: |
|
|
query_string: Prolog query as string |
|
|
|
|
|
Returns: |
|
|
Query result or None |
|
|
""" |
|
|
if not self.prolog_initialized: |
|
|
return None |
|
|
|
|
|
try: |
|
|
results = list(self.prolog.query(query_string)) |
|
|
return results[0] if results else None |
|
|
except Exception as e: |
|
|
print(f"Prolog query error: {e}") |
|
|
return None |
|
|
|
|
|
def cleanup(self): |
|
|
""" |
|
|
Cleanup Prolog engine and threads properly |
|
|
""" |
|
|
if self.prolog_initialized and self.prolog is not None: |
|
|
try: |
|
|
|
|
|
|
|
|
try: |
|
|
|
|
|
list(self.prolog.query("halt")) |
|
|
except: |
|
|
|
|
|
pass |
|
|
|
|
|
|
|
|
self.prolog = None |
|
|
self.prolog_initialized = False |
|
|
print("✓ Prolog engine cleaned up successfully") |
|
|
except Exception as e: |
|
|
print(f"Warning: Error during Prolog cleanup: {e}") |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def create_prolog_engine(): |
|
|
"""Factory function to create and initialize Prolog engine""" |
|
|
return CopticPrologRules() |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if __name__ == "__main__": |
|
|
print("="*70) |
|
|
print("Coptic Prolog Rules - Test Suite") |
|
|
print("="*70) |
|
|
|
|
|
|
|
|
prolog = create_prolog_engine() |
|
|
|
|
|
if not prolog.prolog_initialized: |
|
|
print("\n⚠️ Prolog not available. Cannot run tests.") |
|
|
exit(1) |
|
|
|
|
|
print("\n" + "="*70) |
|
|
print("TEST 1: Tripartite Pattern Recognition") |
|
|
print("="*70) |
|
|
|
|
|
|
|
|
words = ['ⲁⲛⲟⲕ', 'ⲡⲉ', 'ⲡⲛⲟⲩⲧⲉ'] |
|
|
pos_tags = ['PRON', 'AUX', 'NOUN'] |
|
|
|
|
|
result = prolog.check_tripartite_pattern(words, pos_tags) |
|
|
print(f"\nInput: {' '.join(words)}") |
|
|
print(f"Result: {result}") |
|
|
|
|
|
print("\n" + "="*70) |
|
|
print("TEST 2: Morphological Analysis") |
|
|
print("="*70) |
|
|
|
|
|
|
|
|
test_words = ['ⲡⲛⲟⲩⲧⲉ', 'ⲧⲃⲁϣⲟⲣ', 'ⲛⲣⲱⲙⲉ'] |
|
|
for word in test_words: |
|
|
analysis = prolog.analyze_morphology(word) |
|
|
print(f"\nWord: {word}") |
|
|
print(f"Analysis: {analysis}") |
|
|
|
|
|
print("\n" + "="*70) |
|
|
print("TEST 3: Dependency Validation") |
|
|
print("="*70) |
|
|
|
|
|
|
|
|
validation = prolog.validate_dependency( |
|
|
head_word='ⲡⲉ', |
|
|
dep_word='ⲁⲛⲟⲕ', |
|
|
head_pos='AUX', |
|
|
dep_pos='PRON', |
|
|
relation='nsubj' |
|
|
) |
|
|
print(f"\nDependency: ⲁⲛⲟⲕ (PRON) --nsubj--> ⲡⲉ (AUX)") |
|
|
print(f"Validation: {validation}") |
|
|
|
|
|
print("\n" + "="*70) |
|
|
print("TEST 4: Custom Prolog Query") |
|
|
print("="*70) |
|
|
|
|
|
|
|
|
result = prolog.query_prolog("definite_article(X)") |
|
|
print(f"\nQuery: definite_article(X)") |
|
|
print(f"Result: {result}") |
|
|
|
|
|
print("\n" + "="*70) |
|
|
print("All tests completed!") |
|
|
print("="*70) |
|
|
|