RJ3vans commited on
Commit
3e456e5
·
verified ·
1 Parent(s): 532677e

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +108 -40
app.py CHANGED
@@ -1685,55 +1685,127 @@ def subject_is_singular(subj_token) -> bool:
1685
 
1686
  def fix_subject_verb_agreement(sent: str) -> str:
1687
  """
1688
- If the subject is singular and the finite verb is a plural present (VBP),
1689
- rewrite the verb to 3rd-person singular (lives, is, has, …).
1690
- Leaves past (VBD), progressives, etc. unchanged.
1691
  """
1692
- if not sent or not sent.strip():
1693
- return sent
1694
-
1695
  doc = nlp(sent)
1696
- edits = [] # (token.i, new_text)
 
1697
 
1698
- for tok in doc:
1699
- if tok.dep_ != "ROOT" and tok.dep_ not in ("ccomp", "xcomp", "advcl"):
1700
- # Prefer sentence ROOT finite verb
1701
- pass
1702
 
1703
- roots = [t for t in doc if t.dep_ == "ROOT"]
1704
- if not roots:
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1705
  return sent
1706
- verb = roots[0]
1707
 
1708
- # Only repair untreated present plural / base used as present
1709
- if verb.tag_ not in ("VBP", "VB"):
1710
  return sent
1711
- # Skip auxiliaries that are not the lexical main verb when better root exists
1712
- # (spaCy ROOT is usually the main verb or the finite aux)
1713
 
1714
- subjs = [c for c in verb.children if c.dep_ in ("nsubj", "nsubjpass")]
1715
- if not subjs:
 
1716
  return sent
1717
- subj = subjs[0]
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1718
 
1719
- if not subject_is_singular(subj):
1720
  return sent
1721
 
1722
- lemma = verb.lemma_.lower()
1723
- new_form = _singular_present_form(lemma)
 
 
 
1724
 
1725
- # Preserve capitalisation
1726
- if verb.text and verb.text[0].isupper():
1727
- new_form = new_form[0].upper() + new_form[1:]
 
 
 
 
 
 
 
 
 
 
 
 
1728
 
1729
- if new_form == verb.text:
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1730
  return sent
1731
 
1732
- pieces = [t.text_with_ws for t in doc]
1733
- # Replace only this token's text, keep its following whitespace
1734
- ws = verb.whitespace_
1735
- pieces[verb.i] = new_form + ws
1736
- return "".join(pieces).strip()
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1737
  ###############################################################################
1738
 
1739
  ###############################################################################
@@ -2559,13 +2631,8 @@ def process_text(input_text):
2559
 
2560
  # Subject–verb agreement after coordinated-NP (CMN1) splits
2561
  if simps and xml_tag == "M:Y_CMN1":
2562
- fixed = []
2563
- for s in simps:
2564
- s2 = fix_subject_verb_agreement(s)
2565
- if s2 != s:
2566
- print(f" Agreement fix: {s!r} → {s2!r}")
2567
- fixed.append(s2)
2568
- simps = fixed
2569
 
2570
  if not simps:
2571
  print(
@@ -2585,6 +2652,7 @@ def process_text(input_text):
2585
  f" No progress (same as input): "
2586
  f"{plain_s[:80]}..."
2587
  )
 
2588
  sentence_final.append(plain_s)
2589
  continue
2590
  if plain_key in seen:
 
1685
 
1686
  def fix_subject_verb_agreement(sent: str) -> str:
1687
  """
1688
+ After CMN1 (and similar) splits a plural subject into singulars,
1689
+ repair the matrix finite verb / agreeing auxiliary.
1690
+ e.g. "The man are going home." → "The man is going home."
1691
  """
 
 
 
1692
  doc = nlp(sent)
1693
+ if not doc:
1694
+ return sent
1695
 
1696
+ # --- subject: head of nsubj / nsubjpass on ROOT ---
1697
+ root = next((t for t in doc if t.dep_ == "ROOT"), None)
1698
+ if root is None:
1699
+ return sent
1700
 
1701
+ subj = None
1702
+ for t in doc:
1703
+ if t.dep_ in ("nsubj", "nsubjpass") and t.head == root:
1704
+ subj = t
1705
+ break
1706
+ # subject may attach to aux in some parses
1707
+ if t.dep_ in ("nsubj", "nsubjpass") and t.head.dep_ in ("aux", "auxpass"):
1708
+ if t.head.head == root or t.head == root:
1709
+ subj = t
1710
+ break
1711
+ if subj is None:
1712
+ for t in doc:
1713
+ if t.dep_ in ("nsubj", "nsubjpass"):
1714
+ subj = t
1715
+ break
1716
+ if subj is None:
1717
  return sent
 
1718
 
1719
+ # Still coordinated → leave plural verb alone
1720
+ if any(c.dep_ == "cc" for c in subj.subtree):
1721
  return sent
 
 
1722
 
1723
+ # Singular subject?
1724
+ plural_pronouns = {"i", "you", "we", "they"}
1725
+ if subj.lower_ in plural_pronouns:
1726
  return sent
1727
+ if subj.tag_ in ("NNS", "NNPS"):
1728
+ return sent
1729
+ singular = subj.tag_ in ("NN", "NNP", "PRP") or (
1730
+ subj.tag_ in ("DT",) and False
1731
+ )
1732
+ # "the man", "his dog": head noun is NN/NNP
1733
+ if subj.tag_ in ("NN", "NNP", "PRP"):
1734
+ singular = True
1735
+ elif subj.pos_ == "NOUN" and subj.tag_ == "NN":
1736
+ singular = True
1737
+ else:
1738
+ # walk to noun head of subject chunk
1739
+ head = subj
1740
+ for t in subj.subtree:
1741
+ if t.tag_ in ("NN", "NNP") and t.dep_ in ("nsubj", "nsubjpass", "ROOT"):
1742
+ head = t
1743
+ if head.tag_ in ("NNS", "NNPS"):
1744
+ return sent
1745
+ singular = head.tag_ in ("NN", "NNP", "PRP")
1746
 
1747
+ if not singular:
1748
  return sent
1749
 
1750
+ # --- finite form that must agree (aux or main verb) ---
1751
+ def is_finite_agreeing(t):
1752
+ return t.tag_ in ("VBP", "VBZ", "VBD", "MD") or (
1753
+ t.pos_ in ("AUX", "VERB") and t.tag_ in ("VBP", "VBZ", "VBD")
1754
+ )
1755
 
1756
+ target = None
1757
+ # Prefer aux/auxpass under ROOT (are going, has gone, …)
1758
+ for t in root.children:
1759
+ if t.dep_ in ("aux", "auxpass") and t.tag_ in ("VBP", "VBZ", "VBD"):
1760
+ target = t
1761
+ break
1762
+ if target is None and root.tag_ in ("VBP", "VBZ", "VBD", "VB"):
1763
+ target = root
1764
+ if target is None:
1765
+ for t in doc:
1766
+ if t.dep_ in ("aux", "auxpass") and t.tag_ in ("VBP", "VBZ", "VBD"):
1767
+ target = t
1768
+ break
1769
+ if target is None:
1770
+ return sent
1771
 
1772
+ # Already 3sg present or past that doesn't need change for singular
1773
+ # We only rewrite clear plural/present-base forms
1774
+ lower = target.text.lower()
1775
+ irregular_to_3sg = {
1776
+ "are": "is",
1777
+ "were": "was",
1778
+ "have": "has",
1779
+ "do": "does",
1780
+ "go": "goes",
1781
+ "'re": "'s",
1782
+ "am": "is", # rare after split
1783
+ }
1784
+ # Past "was" already singular; "were" handled above
1785
+ if lower in ("is", "was", "has", "does", "goes"):
1786
+ return sent
1787
+ if target.tag_ == "VBZ":
1788
  return sent
1789
 
1790
+ if lower in irregular_to_3sg:
1791
+ new = irregular_to_3sg[lower]
1792
+ elif target.tag_ in ("VBP", "VB"):
1793
+ lemma = target.lemma_.lower()
1794
+ if lemma in irregular_to_3sg:
1795
+ new = irregular_to_3sg[lemma]
1796
+ elif lemma.endswith(("s", "sh", "ch", "x", "z", "o")):
1797
+ new = lemma + "es"
1798
+ elif lemma.endswith("y") and len(lemma) > 1 and lemma[-2] not in "aeiou":
1799
+ new = lemma[:-1] + "ies"
1800
+ else:
1801
+ new = lemma + "s"
1802
+ else:
1803
+ return sent
1804
+
1805
+ if target.text[0].isupper():
1806
+ new = new[0].upper() + new[1:]
1807
+
1808
+ return sent[: target.idx] + new + sent[target.idx + len(target.text) :]
1809
  ###############################################################################
1810
 
1811
  ###############################################################################
 
2631
 
2632
  # Subject–verb agreement after coordinated-NP (CMN1) splits
2633
  if simps and xml_tag == "M:Y_CMN1":
2634
+ simps = [fix_subject_verb_agreement(s) for s in simps]
2635
+
 
 
 
 
 
2636
 
2637
  if not simps:
2638
  print(
 
2652
  f" No progress (same as input): "
2653
  f"{plain_s[:80]}..."
2654
  )
2655
+ plain_s = fix_subject_verb_agreement(plain_s)
2656
  sentence_final.append(plain_s)
2657
  continue
2658
  if plain_key in seen: