Spaces:
Running on Zero
Running on Zero
Update app.py
Browse files
app.py
CHANGED
|
@@ -1685,8 +1685,9 @@ def subject_is_singular(subj_token) -> bool:
|
|
| 1685 |
|
| 1686 |
def fix_subject_verb_agreement(sent: str) -> str:
|
| 1687 |
"""
|
| 1688 |
-
|
| 1689 |
-
|
|
|
|
| 1690 |
"""
|
| 1691 |
if not sent or not sent.strip():
|
| 1692 |
return sent
|
|
@@ -1696,47 +1697,25 @@ def fix_subject_verb_agreement(sent: str) -> str:
|
|
| 1696 |
if not doc:
|
| 1697 |
return sent
|
| 1698 |
|
| 1699 |
-
# ---
|
| 1700 |
-
|
| 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
|
| 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 |
-
|
| 1740 |
irregular = {
|
| 1741 |
"are": "is",
|
| 1742 |
"were": "was",
|
|
@@ -1744,80 +1723,39 @@ def fix_subject_verb_agreement(sent: str) -> str:
|
|
| 1744 |
"do": "does",
|
| 1745 |
"go": "goes",
|
| 1746 |
"live": "lives",
|
| 1747 |
-
"move": "moves",
|
| 1748 |
-
"am": "is",
|
| 1749 |
}
|
| 1750 |
-
if
|
| 1751 |
-
new = irregular[
|
| 1752 |
-
elif
|
| 1753 |
-
new =
|
| 1754 |
-
elif
|
| 1755 |
-
new =
|
| 1756 |
else:
|
| 1757 |
-
new =
|
| 1758 |
-
if word[
|
| 1759 |
new = new[0].upper() + new[1:]
|
| 1760 |
return new
|
| 1761 |
|
| 1762 |
-
#
|
| 1763 |
target = None
|
| 1764 |
for t in doc:
|
| 1765 |
-
if t.dep_ in ("aux", "auxpass") and t.
|
| 1766 |
-
|
| 1767 |
-
|
| 1768 |
-
|
| 1769 |
-
|
| 1770 |
if target is None:
|
| 1771 |
for t in doc:
|
| 1772 |
-
if t.
|
| 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 |
-
|
| 1781 |
-
|
| 1782 |
-
|
| 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 |
-
|
| 1820 |
-
|
| 1821 |
###############################################################################
|
| 1822 |
|
| 1823 |
###############################################################################
|
|
@@ -2376,78 +2314,6 @@ def build_single_sign_bracketed(tagged_list, mode="ccv"):
|
|
| 2376 |
|
| 2377 |
###############################################################################
|
| 2378 |
|
| 2379 |
-
###############################################################################
|
| 2380 |
-
def fix_subject_verb_agreement(sent: str) -> str:
|
| 2381 |
-
"""
|
| 2382 |
-
If the finite verb is present plural (VBP) but the subject is singular,
|
| 2383 |
-
rewrite the verb to singular present (lives, is, has, …).
|
| 2384 |
-
"""
|
| 2385 |
-
doc = nlp(sent)
|
| 2386 |
-
if not doc:
|
| 2387 |
-
return sent
|
| 2388 |
-
|
| 2389 |
-
# Find root verb
|
| 2390 |
-
root = None
|
| 2391 |
-
for t in doc:
|
| 2392 |
-
if t.dep_ == "ROOT" and t.pos_ in ("VERB", "AUX"):
|
| 2393 |
-
root = t
|
| 2394 |
-
break
|
| 2395 |
-
if root is None:
|
| 2396 |
-
return sent
|
| 2397 |
-
|
| 2398 |
-
# Only repair present non-3sg forms that look plural/base
|
| 2399 |
-
if root.tag_ not in ("VBP", "VB"):
|
| 2400 |
-
return sent
|
| 2401 |
-
|
| 2402 |
-
# Collect nsubj / nsubjpass text
|
| 2403 |
-
subj_tokens = [t for t in root.lefts if t.dep_ in ("nsubj", "nsubjpass")]
|
| 2404 |
-
if not subj_tokens:
|
| 2405 |
-
return sent
|
| 2406 |
-
|
| 2407 |
-
subj = subj_tokens[0]
|
| 2408 |
-
# Coordinated subject still plural → leave alone
|
| 2409 |
-
if any(t.dep_ == "cc" for t in subj.subtree):
|
| 2410 |
-
return sent
|
| 2411 |
-
|
| 2412 |
-
singular = subj.tag_ in ("NN", "NNP", "PRP") and subj.lower_ not in (
|
| 2413 |
-
"i", "you", "we", "they",
|
| 2414 |
-
)
|
| 2415 |
-
# "I/you" take VBP; don't force -s
|
| 2416 |
-
if subj.lower_ in ("i", "you", "we", "they"):
|
| 2417 |
-
return sent
|
| 2418 |
-
if not singular and subj.tag_ not in ("NN", "NNP"):
|
| 2419 |
-
# Heuristic: plural NNS / NNPS → keep verb
|
| 2420 |
-
if subj.tag_ in ("NNS", "NNPS"):
|
| 2421 |
-
return sent
|
| 2422 |
-
|
| 2423 |
-
if not singular and subj.tag_ not in ("NN", "NNP", "PRP"):
|
| 2424 |
-
return sent
|
| 2425 |
-
|
| 2426 |
-
# Map verb → 3sg present
|
| 2427 |
-
irregular = {
|
| 2428 |
-
"are": "is", "were": "was", "have": "has", "do": "does",
|
| 2429 |
-
"go": "goes", "do": "does",
|
| 2430 |
-
}
|
| 2431 |
-
lemma = root.lemma_.lower()
|
| 2432 |
-
form = root.text
|
| 2433 |
-
lower = form.lower()
|
| 2434 |
-
|
| 2435 |
-
if lower in irregular:
|
| 2436 |
-
new = irregular[lower]
|
| 2437 |
-
elif lower.endswith(("s", "sh", "ch", "x", "z", "o")):
|
| 2438 |
-
new = lower + "es"
|
| 2439 |
-
elif lower.endswith("y") and len(lower) > 1 and lower[-2] not in "aeiou":
|
| 2440 |
-
new = lower[:-1] + "ies"
|
| 2441 |
-
else:
|
| 2442 |
-
new = lower + "s"
|
| 2443 |
-
|
| 2444 |
-
if form[0].isupper():
|
| 2445 |
-
new = new[0].upper() + new[1:]
|
| 2446 |
-
|
| 2447 |
-
# Splice back into the sentence using character offsets
|
| 2448 |
-
return sent[: root.idx] + new + sent[root.idx + len(root.text) :]
|
| 2449 |
-
###############################################################################
|
| 2450 |
-
|
| 2451 |
###############################################################################
|
| 2452 |
@spaces.GPU(duration=120)
|
| 2453 |
def process_text(input_text):
|
|
|
|
| 1685 |
|
| 1686 |
def fix_subject_verb_agreement(sent: str) -> str:
|
| 1687 |
"""
|
| 1688 |
+
After CMN1 splits a plural subject, repair the finite verb / aux.
|
| 1689 |
+
The man live … → The man lives …
|
| 1690 |
+
The man are going … → The man is going …
|
| 1691 |
"""
|
| 1692 |
if not sent or not sent.strip():
|
| 1693 |
return sent
|
|
|
|
| 1697 |
if not doc:
|
| 1698 |
return sent
|
| 1699 |
|
| 1700 |
+
# --- subject: first nsubj, else first noun ---
|
| 1701 |
+
subj = next((t for t in doc if t.dep_ in ("nsubj", "nsubjpass")), None)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1702 |
if subj is None:
|
| 1703 |
+
subj = next((t for t in doc if t.tag_ in ("NN", "NNP", "PRP")), None)
|
|
|
|
|
|
|
|
|
|
| 1704 |
if subj is None:
|
| 1705 |
return sent
|
| 1706 |
|
| 1707 |
+
# Coordinated subject still plural
|
| 1708 |
if any(t.dep_ == "cc" for t in subj.subtree):
|
| 1709 |
return sent
|
| 1710 |
+
if subj.lower_ in {"i", "you", "we", "they"}:
|
|
|
|
|
|
|
| 1711 |
return sent
|
| 1712 |
if subj.tag_ in ("NNS", "NNPS"):
|
| 1713 |
return sent
|
| 1714 |
+
if subj.tag_ not in ("NN", "NNP", "PRP"):
|
|
|
|
|
|
|
|
|
|
| 1715 |
return sent
|
| 1716 |
|
| 1717 |
def to_3sg(word: str) -> str:
|
| 1718 |
+
low = word.lower()
|
| 1719 |
irregular = {
|
| 1720 |
"are": "is",
|
| 1721 |
"were": "was",
|
|
|
|
| 1723 |
"do": "does",
|
| 1724 |
"go": "goes",
|
| 1725 |
"live": "lives",
|
|
|
|
|
|
|
| 1726 |
}
|
| 1727 |
+
if low in irregular:
|
| 1728 |
+
new = irregular[low]
|
| 1729 |
+
elif low.endswith(("s", "sh", "ch", "x", "z", "o")):
|
| 1730 |
+
new = low + "es"
|
| 1731 |
+
elif low.endswith("y") and len(low) > 1 and low[-2] not in "aeiou":
|
| 1732 |
+
new = low[:-1] + "ies"
|
| 1733 |
else:
|
| 1734 |
+
new = low + "s"
|
| 1735 |
+
if word[:1].isupper():
|
| 1736 |
new = new[0].upper() + new[1:]
|
| 1737 |
return new
|
| 1738 |
|
| 1739 |
+
# Prefer agreeing AUX (are going, have gone, …)
|
| 1740 |
target = None
|
| 1741 |
for t in doc:
|
| 1742 |
+
if t.dep_ in ("aux", "auxpass") and t.lower_ in {
|
| 1743 |
+
"are", "were", "have", "do", "am"
|
| 1744 |
+
}:
|
| 1745 |
+
target = t
|
| 1746 |
+
break
|
| 1747 |
if target is None:
|
| 1748 |
for t in doc:
|
| 1749 |
+
if t.tag_ in ("VBP", "VB") and t.lower_ not in {"to"}:
|
|
|
|
|
|
|
| 1750 |
target = t
|
| 1751 |
break
|
|
|
|
|
|
|
| 1752 |
if target is None:
|
| 1753 |
+
return sent
|
| 1754 |
+
if target.lower_ in {"is", "was", "has", "does"}:
|
| 1755 |
+
return sent
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1756 |
|
| 1757 |
+
new = to_3sg(target.text)
|
| 1758 |
+
return sent[: target.idx] + new + sent[target.idx + len(target.text) :]
|
| 1759 |
###############################################################################
|
| 1760 |
|
| 1761 |
###############################################################################
|
|
|
|
| 2314 |
|
| 2315 |
###############################################################################
|
| 2316 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 2317 |
###############################################################################
|
| 2318 |
@spaces.GPU(duration=120)
|
| 2319 |
def process_text(input_text):
|