Spaces:
Running on Zero
Running on Zero
Update app.py
Browse files
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 |
-
|
| 1689 |
-
|
| 1690 |
-
|
| 1691 |
"""
|
| 1692 |
-
if not sent or not sent.strip():
|
| 1693 |
-
return sent
|
| 1694 |
-
|
| 1695 |
doc = nlp(sent)
|
| 1696 |
-
|
|
|
|
| 1697 |
|
| 1698 |
-
|
| 1699 |
-
|
| 1700 |
-
|
| 1701 |
-
|
| 1702 |
|
| 1703 |
-
|
| 1704 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1705 |
return sent
|
| 1706 |
-
verb = roots[0]
|
| 1707 |
|
| 1708 |
-
#
|
| 1709 |
-
if
|
| 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 |
-
|
| 1715 |
-
|
|
|
|
| 1716 |
return sent
|
| 1717 |
-
subj
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1718 |
|
| 1719 |
-
if not
|
| 1720 |
return sent
|
| 1721 |
|
| 1722 |
-
|
| 1723 |
-
|
|
|
|
|
|
|
|
|
|
| 1724 |
|
| 1725 |
-
|
| 1726 |
-
|
| 1727 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1728 |
|
| 1729 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1730 |
return sent
|
| 1731 |
|
| 1732 |
-
|
| 1733 |
-
|
| 1734 |
-
|
| 1735 |
-
|
| 1736 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 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 |
-
|
| 2563 |
-
|
| 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:
|