Bayan AI commited on
Commit
b3209ae
·
1 Parent(s): 2832a7b

Fix: Enhance Jazm and Nasb logic with camel_tools for Af'al Khamsa and defective verbs

Browse files
Files changed (1) hide show
  1. src/nlp/grammar/grammar_rules.py +82 -39
src/nlp/grammar/grammar_rules.py CHANGED
@@ -94,11 +94,85 @@ class ArabicGrammarGuard:
94
 
95
  return " ".join(corrected_tokens)
96
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
97
  def fix_verbs_nasb_and_jazm(self, text):
98
  tokens = simple_word_tokenize(text)
99
  disambig_tokens = self.mle.disambiguate(tokens)
100
 
101
- nasb_particles = ['أن', 'لن', 'كي', 'لكي', 'حتى', 'إذن']
102
  jazm_particles = ['لم', 'لما', 'لا']
103
 
104
  corrected_tokens = []
@@ -120,37 +194,10 @@ class ArabicGrammarGuard:
120
 
121
  is_present_tense = word.startswith('ي') or word.startswith('ت') or word.startswith('ن') or word.startswith('أ')
122
  if (pos_tag == 'verb' or is_present_tense) and (is_nasb_context or is_jazm_context):
123
- if word.endswith('ون'):
124
- word = word[:-2] + 'وا'
125
- elif word.endswith('ان'):
126
- word = word[:-2] + 'ا'
127
- elif word.endswith('ين'):
128
- word = word[:-2] + 'ي'
129
- elif is_jazm_context:
130
- if word.endswith('و') and len(word) >= 3:
131
- word = word[:-1] + 'ُ'
132
- elif word.endswith('ي') or word.endswith('i'):
133
- stem = word[:-1]
134
- fatha_stems = {'يسع', 'تسع', 'أسع', 'نسع',
135
- 'يخش', 'تخش', 'أخش', 'نخش',
136
- 'يرض', 'ترض', 'أرض', 'نرض',
137
- 'ينس', 'تنس', 'أنس', 'ننس',
138
- 'يبق', 'تبق', 'أبق', 'نبق',
139
- 'ير', 'تر', 'أر', 'نر',
140
- 'يلق', 'تلق', 'ألق', 'نلق',
141
- 'ينه', 'تنه', 'أنه', 'ننه'}
142
- if stem in fatha_stems:
143
- word = stem + 'َ'
144
- elif len(word) > 3:
145
- word = stem + 'ِ'
146
- elif (word.endswith('ى') or word.endswith('ا')) and len(word) >= 3:
147
- if not word.endswith('وا'):
148
- word = word[:-1] + 'َ'
149
  elif is_nasb_context:
150
- if word.endswith('و') and len(word) > 3:
151
- word = word + 'َ'
152
- elif word.endswith('ي') and len(word) > 3:
153
- word = word + 'َ'
154
 
155
  corrected_tokens.append(word)
156
  return " ".join(corrected_tokens)
@@ -445,7 +492,7 @@ class ArabicGrammarGuard:
445
  return text
446
 
447
  def fix_conditional_sentences(self, text):
448
- conditional_particles = {'إن', 'من', 'ما', 'متى', 'مهما', 'أينما', 'حيثما', 'أيان', 'كيفما', 'أنى'}
449
  tokens = simple_word_tokenize(text)
450
  disambig_tokens = self.mle.disambiguate(tokens)
451
  corrected_tokens = list(tokens)
@@ -474,19 +521,15 @@ class ArabicGrammarGuard:
474
  continue
475
 
476
  if in_cond and pos_tag == 'verb':
477
- # Apply jazm to present tense verbs ending in ون, ان, ين
478
- if word.endswith('ون'):
479
- word = word[:-2] + 'وا'
480
- elif word.endswith('ان'):
481
- word = word[:-2] + 'ا'
482
- elif word.endswith('ين'):
483
- word = word[:-2] + 'ي'
484
 
485
  # Fix pronoun mismatch if 2nd person context exists
486
  if has_2nd_person_context and word.startswith('ي') and (word.endswith('وا') or word.endswith('ا') or word.endswith('ي')):
487
  word = 'ت' + word[1:]
488
 
489
  corrected_tokens[i] = word
 
490
  verbs_jazmed += 1
491
  if verbs_jazmed >= 2:
492
  in_cond = False
 
94
 
95
  return " ".join(corrected_tokens)
96
 
97
+ def _apply_jazm_to_verb(self, word, token_info):
98
+ # 1. Handle Af'al Khamsa using camel_tools analysis
99
+ if token_info and token_info.analyses:
100
+ analysis = token_info.analyses[0].analysis
101
+ num = analysis.get('num', 's')
102
+ per = analysis.get('per', '3')
103
+ gen = analysis.get('gen', 'm')
104
+
105
+ if num == 'p' and gen == 'm':
106
+ if word.endswith('ون'):
107
+ return word[:-2] + 'وا'
108
+ elif num == 'd':
109
+ if word.endswith('ان'):
110
+ return word[:-2] + 'ا'
111
+ elif num == 's' and per == '2' and gen == 'f':
112
+ if word.endswith('ين'):
113
+ return word[:-2] + 'ي'
114
+
115
+ # 2. Handle defective verbs in jazm context
116
+ match = re.search(r'^(.*?)([يوىاَُِْ]?)$', word)
117
+ if match:
118
+ stem = match.group(1)
119
+ ending = match.group(2)
120
+
121
+ fatha_bases = ['سع', 'خش', 'رض', 'نس', 'بق', 'ر', 'نه', 'حظ', 'رع', 'أب', 'تمن', 'لق', 'هو', 'سل']
122
+ damma_bases = ['دع', 'رج', 'شك', 'نم', 'غز', 'عف', 'سم', 'دن', 'بد', 'خل', 'عل']
123
+ kasra_bases = ['مش', 'جر', 'قض', 'بك', 'هد', 'رم', 'أت', 'بن', 'ق', 'وف', 'شف', 'غن', 'عط', 'تق', 'شتر', 'عتن', 'ستدع', 'نته', 'رو']
124
+
125
+ fatha_stems = {p + b for p in ['ي', 'ت', 'أ', 'ن'] for b in fatha_bases}
126
+ damma_stems = {p + b for p in ['ي', 'ت', 'أ', 'ن'] for b in damma_bases}
127
+ kasra_stems = {p + b for p in ['ي', 'ت', 'أ', 'ن'] for b in kasra_bases}
128
+
129
+ if stem in fatha_stems:
130
+ return stem + 'َ'
131
+ elif stem in damma_stems:
132
+ return stem + 'ُ'
133
+ elif stem in kasra_stems:
134
+ return stem + 'ِ'
135
+ elif ending == 'و' and len(stem) >= 2:
136
+ return stem + 'ُ'
137
+ elif ending == 'ي' and len(stem) >= 2:
138
+ return stem + 'ِ'
139
+ elif (ending == 'ى' or ending == 'ا') and len(stem) >= 2:
140
+ if not word.endswith('وا'):
141
+ return stem + 'َ'
142
+
143
+ return word
144
+
145
+ def _apply_nasb_to_verb(self, word, token_info):
146
+ # 1. Handle Af'al Khamsa using camel_tools analysis
147
+ if token_info and token_info.analyses:
148
+ analysis = token_info.analyses[0].analysis
149
+ num = analysis.get('num', 's')
150
+ per = analysis.get('per', '3')
151
+ gen = analysis.get('gen', 'm')
152
+
153
+ if num == 'p' and gen == 'm':
154
+ if word.endswith('ون'):
155
+ return word[:-2] + 'وا'
156
+ elif num == 'd':
157
+ if word.endswith('ان'):
158
+ return word[:-2] + 'ا'
159
+ elif num == 's' and per == '2' and gen == 'f':
160
+ if word.endswith('ين'):
161
+ return word[:-2] + 'ي'
162
+
163
+ # 2. Handle defective verbs in nasb
164
+ if word.endswith('و') and len(word) > 3:
165
+ return word + 'َ'
166
+ elif word.endswith('ي') and len(word) > 3:
167
+ return word + 'َ'
168
+
169
+ return word
170
+
171
  def fix_verbs_nasb_and_jazm(self, text):
172
  tokens = simple_word_tokenize(text)
173
  disambig_tokens = self.mle.disambiguate(tokens)
174
 
175
+ nasb_particles = ['أن', 'ان', 'لن', 'كي', 'لكي', 'حتى', 'حتي', 'إذن', 'اذا']
176
  jazm_particles = ['لم', 'لما', 'لا']
177
 
178
  corrected_tokens = []
 
194
 
195
  is_present_tense = word.startswith('ي') or word.startswith('ت') or word.startswith('ن') or word.startswith('أ')
196
  if (pos_tag == 'verb' or is_present_tense) and (is_nasb_context or is_jazm_context):
197
+ if is_jazm_context:
198
+ word = self._apply_jazm_to_verb(word, token_info)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
199
  elif is_nasb_context:
200
+ word = self._apply_nasb_to_verb(word, token_info)
 
 
 
201
 
202
  corrected_tokens.append(word)
203
  return " ".join(corrected_tokens)
 
492
  return text
493
 
494
  def fix_conditional_sentences(self, text):
495
+ conditional_particles = {'إن', 'ان', 'من', 'ما', 'متى', 'متي', 'مهما', 'أينما', 'حيثما', 'أيان', 'ايان', 'كيفما', 'أنى', 'اني'}
496
  tokens = simple_word_tokenize(text)
497
  disambig_tokens = self.mle.disambiguate(tokens)
498
  corrected_tokens = list(tokens)
 
521
  continue
522
 
523
  if in_cond and pos_tag == 'verb':
524
+ # Apply jazm using the comprehensive camel_tools helper
525
+ word = self._apply_jazm_to_verb(word, token_info)
 
 
 
 
 
526
 
527
  # Fix pronoun mismatch if 2nd person context exists
528
  if has_2nd_person_context and word.startswith('ي') and (word.endswith('وا') or word.endswith('ا') or word.endswith('ي')):
529
  word = 'ت' + word[1:]
530
 
531
  corrected_tokens[i] = word
532
+ # Increment jazmed verbs counter (handles both فعل الشرط and جواب الشرط)
533
  verbs_jazmed += 1
534
  if verbs_jazmed >= 2:
535
  in_cond = False