RJ3vans commited on
Commit
4e01283
·
verified ·
1 Parent(s): 8d57803

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +93 -81
app.py CHANGED
@@ -1685,127 +1685,139 @@ def subject_is_singular(subj_token) -> bool:
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
  ###############################################################################
 
1685
 
1686
  def fix_subject_verb_agreement(sent: str) -> str:
1687
  """
1688
+ Repair singular subject + plural/base present verb after CMN1/SSCCV splits.
1689
+ Handles: John are moving → John is moving; John live → John lives.
 
1690
  """
1691
+ if not sent or not sent.strip():
 
1692
  return sent
1693
 
1694
+ sent = sent.strip()
1695
+ doc = nlp(sent)
1696
+ if not doc:
1697
  return sent
1698
 
1699
+ # ----- identify subject head (prefer nsubj of ROOT) -----
1700
+ root = next((t for t in doc if t.dep_ == "ROOT"), doc[0])
1701
  subj = None
1702
  for t in doc:
1703
+ if t.dep_ in ("nsubj", "nsubjpass") and (
1704
+ t.head == root or t.head.head == root
1705
+ ):
1706
  subj = t
1707
  break
1708
+ if subj is None:
1709
+ for t in doc:
1710
+ if t.dep_ in ("nsubj", "nsubjpass"):
1711
  subj = t
1712
  break
1713
+
1714
+ # Fallback: first proper/common noun or pronoun
1715
  if subj is None:
1716
  for t in doc:
1717
+ if t.tag_ in ("NNP", "NN", "PRP") and t.dep_ not in ("punct",):
1718
  subj = t
1719
  break
1720
  if subj is None:
1721
  return sent
1722
 
1723
+ # Coordinated subject → keep plural verb
1724
+ if any(t.dep_ == "cc" for t in subj.subtree):
1725
  return sent
1726
 
 
1727
  plural_pronouns = {"i", "you", "we", "they"}
1728
  if subj.lower_ in plural_pronouns:
1729
  return sent
1730
  if subj.tag_ in ("NNS", "NNPS"):
1731
  return sent
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1732
 
1733
+ # NNP / NN / singular PRP → singular
1734
+ singular = subj.tag_ in ("NNP", "NN", "PRP")
1735
  if not singular:
1736
  return sent
1737
 
1738
+ def to_3sg(word: str) -> str:
1739
+ lower = word.lower()
1740
+ irregular = {
1741
+ "are": "is",
1742
+ "were": "was",
1743
+ "have": "has",
1744
+ "do": "does",
1745
+ "go": "goes",
1746
+ "live": "lives",
1747
+ "move": "moves",
1748
+ "am": "is",
1749
+ }
1750
+ if lower in irregular:
1751
+ new = irregular[lower]
1752
+ elif lower.endswith(("s", "sh", "ch", "x", "z", "o")):
1753
+ new = lower + "es"
1754
+ elif lower.endswith("y") and len(lower) > 1 and lower[-2] not in "aeiou":
1755
+ new = lower[:-1] + "ies"
1756
+ else:
1757
+ new = lower + "s"
1758
+ if word[0].isupper():
1759
+ new = new[0].upper() + new[1:]
1760
+ return new
1761
 
1762
+ # ----- finite aux or main verb that should agree -----
1763
  target = None
1764
+ for t in doc:
1765
+ if t.dep_ in ("aux", "auxpass") and t.tag_ in ("VBP", "VB", "VBD"):
1766
+ # prefer aux of the main clause
1767
+ if t.head == root or t.head.dep_ == "ROOT":
1768
+ target = t
1769
+ break
 
1770
  if target is None:
1771
  for t in doc:
1772
+ if t.dep_ in ("aux", "auxpass") and t.lower_ in (
1773
+ "are", "were", "have", "do", "am"
1774
+ ):
1775
  target = t
1776
  break
1777
+ if target is None and root.tag_ in ("VBP", "VB"):
1778
+ target = root
1779
  if target is None:
1780
+ for t in doc:
1781
+ if t.tag_ in ("VBP", "VB") and t.lower_ in (
1782
+ "are", "were", "have", "do", "live", "go", "move"
1783
+ ):
1784
+ target = t
1785
+ break
 
 
 
 
 
 
 
 
 
 
 
 
 
1786
 
1787
+ if target is not None and target.tag_ != "VBZ":
1788
+ if target.lower_ in ("is", "was", "has", "does"):
1789
+ pass
 
 
 
 
 
 
 
1790
  else:
1791
+ new = to_3sg(target.text)
1792
+ sent = sent[: target.idx] + new + sent[target.idx + len(target.text) :]
1793
+ # re-parse if you chain multiple fixes; for one shot this is enough
1794
+ return sent
1795
 
1796
+ # ----- surface fallback (ungrammatical strings spaCy mis-parses) -----
1797
+ # "John are …" / "Mary live …"
1798
+ m = re.match(
1799
+ r"^((?:The|A|An|His|Her|Their)\s+\w+|[A-Z][\w']*)\s+"
1800
+ r"(are|were|have|do|live|move|go|want|need)\b",
1801
+ sent,
1802
+ flags=re.I,
1803
+ )
1804
+ if m:
1805
+ subj_s, verb = m.group(1), m.group(2)
1806
+ # skip if subject looks plural
1807
+ if re.search(r"\b(and|&)\b", subj_s, re.I):
1808
+ return sent
1809
+ if re.search(r"s\s*$", subj_s) and not re.match(
1810
+ r"^(His|Her|Their|The|A|An)\s", subj_s, re.I
1811
+ ):
1812
+ # weak; proper names end without s more often
1813
+ pass
1814
+ new_verb = to_3sg(verb)
1815
+ if verb[0].isupper():
1816
+ new_verb = new_verb[0].upper() + new_verb[1:]
1817
+ sent = sent[: m.start(2)] + new_verb + sent[m.end(2) :]
1818
 
1819
+ return sent
1820
+
1821
  ###############################################################################
1822
 
1823
  ###############################################################################