Index: compile_rules.py ================================================================== --- compile_rules.py +++ compile_rules.py @@ -110,13 +110,21 @@ def createRule (s, nIdLine, sLang, bParagraph, dOptPriority): "returns rule as list [option name, regex, bCaseInsensitive, identifier, list of actions]" global dJSREGEXES global nRULEWITHOUTNAME - #### OPTIONS sLineId = str(nIdLine) + ("p" if bParagraph else "s") sRuleId = sLineId + + #### GRAPH CALL + if s.startswith("@@@@"): + if bParagraph: + print("Error. Graph call can’t be made only after the first pass (sentence by sentence)") + exit() + return ["@@@@", s[4:], sLineId] + + #### OPTIONS sOption = False # False or [a-z0-9]+ name nPriority = 4 # Default is 4, value must be between 0 and 9 tGroups = None # code for groups positioning (only useful for JavaScript) cCaseMode = 'i' # i: case insensitive, s: case sensitive, u: uppercasing allowed cWordLimitLeft = '[' # [: word limit, <: no specific limit @@ -343,12 +351,13 @@ def _calcRulesStats (lRules): d = {'=':0, '~': 0, '-': 0, '>': 0} for aRule in lRules: - for aAction in aRule[6]: - d[aAction[1]] = d[aAction[1]] + 1 + if aRule[0] != "@@@@": + for aAction in aRule[6]: + d[aAction[1]] = d[aAction[1]] + 1 return (d, len(lRules)) def displayStats (lParagraphRules, lSentenceRules): print(" {:>18} {:>18} {:>18} {:>18}".format("DISAMBIGUATOR", "TEXT PROCESSOR", "GRAMMAR CHECKING", "REGEX")) @@ -436,47 +445,57 @@ global dDEF lLine = [] lRuleLine = [] lTest = [] lOpt = [] - zBookmark = re.compile("^!!+") - zGraphLink = re.compile(r"^@@@@GRAPHLINK>(\w+)@@@@") for i, sLine in enumerate(lRules, 1): if sLine.startswith('#END'): + # arbitrary end printBookmark(0, "BREAK BY #END", i) break elif sLine.startswith("#"): + # comment pass elif sLine.startswith("@@@@"): - m = re.match(r"^@@@@GRAPHLINK>(\w+)@@@@", sLine.strip()) + # rules graph call + m = re.match(r"@@@@GRAPH: *(\w+)@@@@", sLine.strip()) if m: #lRuleLine.append(["@GRAPHLINK", m.group(1)]) - printBookmark(1, "@GRAPHLINK: " + m.group(1), i) + printBookmark(1, "@GRAPH: " + m.group(1), i) + lRuleLine.append([i, "@@@@"+m.group(1)]) elif sLine.startswith("DEF:"): + # definition m = re.match("DEF: +([a-zA-Z_][a-zA-Z_0-9]*) +(.+)$", sLine.strip()) if m: dDEF["{"+m.group(1)+"}"] = m.group(2) else: print("Error in definition: ", end="") print(sLine.strip()) elif sLine.startswith("TEST:"): - lTest.append("{:<8}".format(i) + " " + sLine[5:].strip()) + # test + lTest.append("r{:<7}".format(i) + " " + sLine[5:].strip()) elif sLine.startswith("TODO:"): + # todo pass elif sLine.startswith(("OPTGROUP/", "OPTSOFTWARE:", "OPT/", "OPTLANG/", "OPTDEFAULTUILANG:", "OPTLABEL/", "OPTPRIORITY/")): + # options lOpt.append(sLine) elif re.match("[  \t]*$", sLine): + # empty line pass elif sLine.startswith("!!"): - m = zBookmark.search(sLine) + # bookmark + m = re.match("!!+", sLine) nExMk = len(m.group(0)) if sLine[nExMk:].strip(): printBookmark(nExMk-2, sLine[nExMk:].strip(), i) elif sLine.startswith((" ", "\t")): - lRuleLine[len(lRuleLine)-1][1] += " " + sLine.strip() + # rule (continuation) + lRuleLine[-1][1] += " " + sLine.strip() else: + # new rule lRuleLine.append([i, sLine.strip()]) # generating options files print(" parsing options...") try: @@ -542,14 +561,14 @@ print("Unnamed rules: " + str(nRULEWITHOUTNAME)) d = { "callables": sPyCallables, "callablesJS": sJSCallables, - "gctests": sGCTests, - "gctestsJS": sGCTestsJS, + "regex_gctests": sGCTests, + "regex_gctestsJS": sGCTestsJS, "paragraph_rules": mergeRulesByOption(lParagraphRules), "sentence_rules": mergeRulesByOption(lSentenceRules), "paragraph_rules_JS": jsconv.writeRulesToJSArray(mergeRulesByOption(lParagraphRulesJS)), "sentence_rules_JS": jsconv.writeRulesToJSArray(mergeRulesByOption(lSentenceRulesJS)) } d.update(dOptions) return d ADDED compile_rules_graph.py Index: compile_rules_graph.py ================================================================== --- compile_rules_graph.py +++ compile_rules_graph.py @@ -0,0 +1,370 @@ +# Create a Direct Acyclic Rule Graph (DARG) + +import re +import traceback +import json +import darg + + +dDEF = {} +dACTIONS = {} +lFUNCTIONS = [] + + +def prepareFunction (s): + s = s.replace("__also__", "bCondMemo") + s = s.replace("__else__", "not bCondMemo") + s = re.sub(r"(select|exclude|define)[(][\\](\d+)", 'g_\\1(lToken[\\2+nTokenOffset]', s) + s = re.sub(r"(morph|displayInfo)[(]\\(\d+)", 'g_\\1(lToken[\\2+nTokenOffset]', s) + s = re.sub(r"token\(\s*(\d)", 'nextToken(\\1', s) # token(n) + s = re.sub(r"token\(\s*-(\d)", 'prevToken(\\1', s) # token(-n) + s = re.sub(r"before\(\s*", 'look(s[:m.start()], ', s) # before(s) + s = re.sub(r"after\(\s*", 'look(s[m.end():], ', s) # after(s) + s = re.sub(r"textarea\(\s*", 'look(s, ', s) # textarea(s) + s = re.sub(r"before_chk1\(\s*", 'look_chk1(dDA, s[:m.start()], 0, ', s) # before_chk1(s) + s = re.sub(r"after_chk1\(\s*", 'look_chk1(dDA, s[m.end():], m.end(), ', s) # after_chk1(s) + s = re.sub(r"textarea_chk1\(\s*", 'look_chk1(dDA, s, 0, ', s) # textarea_chk1(s) + s = re.sub(r"\bspell *[(]", '_oSpellChecker.isValid(', s) + s = re.sub(r"[\\](\d+)", 'lToken[\\1]', s) + return s + + +def genTokenLines (sTokenLine): + "tokenize a string and return a list of lines of tokens" + lToken = sTokenLine.split() + lTokenLines = None + for i, sToken in enumerate(lToken): + if sToken.startswith("{") and sToken.endswith("}") and sToken in dDEF: + lToken[i] = dDEF[sToken] + if ( (sToken.startswith("[") and sToken.endswith("]")) or (sToken.startswith("([") and sToken.endswith("])")) ): + bSelectedGroup = sToken.startswith("(") and sToken.endswith(")") + if bSelectedGroup: + sToken = sToken[1:-1] + # multiple token + if not lTokenLines: + lTokenLines = [ [s] for s in sToken[1:-1].split("|") ] + else: + lNewTemp = [] + for aRule in lTokenLines: + lElem = sToken[1:-1].split("|") + sElem1 = lElem.pop(0) + if bSelectedGroup: + sElem1 = "(" + sElem1 + ")" + for sElem in lElem: + if bSelectedGroup: + sElem = "(" + sElem + ")" + aNew = list(aRule) + aNew.append(sElem) + lNewTemp.append(aNew) + aRule.append(sElem1) + lTokenLines.extend(lNewTemp) + else: + # simple token + if not lTokenLines: + lTokenLines = [[sToken]] + else: + for aRule in lTokenLines: + aRule.append(sToken) + for aRule in lTokenLines: + yield aRule + + +def createRule (iLine, sRuleName, sTokenLine, sActions, nPriority): + # print(iLine, "//", sRuleName, "//", sTokenLine, "//", sActions, "//", nPriority) + for lToken in genTokenLines(sTokenLine): + # Calculate positions + dPos = {} # key: iGroup, value: iToken + iGroup = 0 + for i, sToken in enumerate(lToken): + if sToken.startswith("(") and sToken.endswith(")"): + lToken[i] = sToken[1:-1] + iGroup += 1 + dPos[iGroup] = i + 1 # we add 1, for we count tokens from 1 to n (not from 0) + + # Parse actions + for nAction, sAction in enumerate(sActions.split(" <<- ")): + if sAction.strip(): + sActionId = sRuleName + "_a" + str(nAction) + aAction = createAction(sActionId, sAction, nPriority, len(lToken), dPos) + if aAction: + dACTIONS[sActionId] = aAction + lResult = list(lToken) + lResult.extend(["##"+str(iLine), sActionId]) + yield lResult + + +def changeReferenceToken (s, dPos): + for i in range(len(dPos), 0, -1): + s = s.replace("\\"+str(i), "\\"+str(dPos[i])) + return s + + +def createAction (sIdAction, sAction, nPriority, nToken, dPos): + m = re.search("(?P[-~=])(?P\\d+|)(?P:\\d+|)>> ", sAction) + if not m: + print(" # Error. No action found at: ", sIdAction) + print(" ==", sAction, "==") + return None + # Condition + sCondition = sAction[:m.start()].strip() + if sCondition: + sCondition = prepareFunction(sCondition) + sCondition = changeReferenceToken(sCondition, dPos) + lFUNCTIONS.append(("g_c_"+sIdAction, sCondition)) + sCondition = "g_c_"+sIdAction + else: + sCondition = "" + # Action + cAction = m.group("action") + sAction = sAction[m.end():].strip() + sAction = changeReferenceToken(sAction, dPos) + if not m.group("start"): + iStartAction = 1 + iEndAction = nToken + else: + iStartAction = int(m.group("start")) + iEndAction = int(m.group("end")[1:]) if m.group("end") else iStartAction + if dPos: + try: + iStartAction = dPos[iStartAction] + iEndAction = dPos[iEndAction] + except: + print("# Error. Wrong groups in: " + sIdAction) + + if cAction == "-": + ## error + iMsg = sAction.find(" # ") + if iMsg == -1: + sMsg = "# Error. Error message not found." + sURL = "" + print(sMsg + " Action id: " + sIdAction) + else: + sMsg = sAction[iMsg+3:].strip() + sAction = sAction[:iMsg].strip() + sURL = "" + mURL = re.search("[|] *(https?://.*)", sMsg) + if mURL: + sURL = mURL.group(1).strip() + sMsg = sMsg[:mURL.start(0)].strip() + if sMsg[0:1] == "=": + sMsg = prepareFunction(sMsg[1:]) + lFUNCTIONS.append(("g_m_"+sIdAction, sMsg)) + for x in re.finditer("group[(](\\d+)[)]", sMsg): + if int(x.group(1)) > nToken: + print("# Error in token index in message at line " + sIdAction + " ("+str(nToken)+" tokens only)") + sMsg = "=g_m_"+sIdAction + else: + for x in re.finditer(r"\\(\d+)", sMsg): + if int(x.group(1)) > nToken: + print("# Error in token index in message at line " + sIdAction + " ("+str(nToken)+" tokens only)") + if re.search("[.]\\w+[(]", sMsg): + print("# Error in message at line " + sIdAction + ": This message looks like code. Line should begin with =") + + if sAction[0:1] == "=" or cAction == "=": + if "define" in sAction and not re.search(r"define\(\\\d+ *, *\[.*\] *\)", sAction): + print("# Error in action at line " + sIdAction + ": second argument for define must be a list of strings") + sAction = prepareFunction(sAction) + for x in re.finditer("group[(](\\d+)[)]", sAction): + if int(x.group(1)) > nToken: + print("# Error in token index in replacement at line " + sIdAction + " ("+str(nToken)+" tokens only)") + else: + for x in re.finditer(r"\\(\d+)", sAction): + if int(x.group(1)) > nToken: + print("# Error in token index in replacement at line " + sIdAction + " ("+str(nToken)+" tokens only)") + if re.search("[.]\\w+[(]|sugg\\w+[(]", sAction): + print("# Error in action at line " + sIdAction + ": This action looks like code. Line should begin with =") + + if cAction == "-": + ## error detected --> suggestion + if not sAction: + print("# Error in action at line " + sIdAction + ": This action is empty.") + if sAction[0:1] == "=": + lFUNCTIONS.append(("g_s_"+sIdAction, sAction[1:])) + sAction = "=g_s_"+sIdAction + elif sAction.startswith('"') and sAction.endswith('"'): + sAction = sAction[1:-1] + if not sMsg: + print("# Error in action at line " + sIdAction + ": The message is empty.") + return [sCondition, cAction, sAction, iStartAction, iEndAction, nPriority, sMsg, sURL] + elif cAction == "~": + ## text processor + if not sAction: + print("# Error in action at line " + sIdAction + ": This action is empty.") + if sAction[0:1] == "=": + lFUNCTIONS.append(("g_p_"+sIdAction, sAction[1:])) + sAction = "=g_p_"+sIdAction + elif sAction.startswith('"') and sAction.endswith('"'): + sAction = sAction[1:-1] + return [sCondition, cAction, sAction, iStartAction, iEndAction] + elif cAction == "=": + ## disambiguator + if sAction[0:1] == "=": + sAction = sAction[1:] + if not sAction: + print("# Error in action at line " + sIdAction + ": This action is empty.") + lFUNCTIONS.append(("g_d_"+sIdAction, sAction)) + sAction = "g_d_"+sIdAction + return [sCondition, cAction, sAction] + elif cAction == ">": + ## no action, break loop if condition is False + return [sCondition, cAction, ""] + else: + print("# Unknown action at line " + sIdAction) + return None + + +def make (spLang, sLang, bJavaScript): + "compile rules, returns a dictionary of values" + # for clarity purpose, don’t create any file here + + print("> read graph rules file...") + try: + lRules = open(spLang + "/rules_graph.grx", 'r', encoding="utf-8").readlines() + except: + print("Error. Rules file in project [" + sLang + "] not found.") + exit() + + # removing comments, zeroing empty lines, creating definitions, storing tests, merging rule lines + print(" parsing rules...") + global dDEF + lTest = [] + lTokenLine = [] + sActions = "" + nPriority = 4 + dAllGraph = {} + sGraphName = "" + + for i, sLine in enumerate(lRules, 1): + sLine = sLine.rstrip() + if "\t" in sLine: + # tabulation not allowed + print("Error. Tabulation at line: ", i) + exit() + if sLine.startswith('#END'): + # arbitrary end + printBookmark(0, "BREAK BY #END", i) + break + elif sLine.startswith("#"): + # comments + pass + elif sLine.startswith("GRAPH_NAME: "): + # Graph name + m = re.match("GRAPH_NAME: +([a-zA-Z_][a-zA-Z_0-9]*)+", sLine.strip()) + if m: + sGraphName = m.group(1) + if sGraphName in dAllGraph: + print("Error. Group name " + sGraphName + " already exists.") + exit() + dAllGraph[sGraphName] = [] + else: + print("Error. Graph name not found in", sLine.strip()) + exit() + elif sLine.startswith("DEF:"): + # definition + m = re.match("DEF: +([a-zA-Z_][a-zA-Z_0-9]*) +(.+)$", sLine.strip()) + if m: + dDEF["{"+m.group(1)+"}"] = m.group(2) + else: + print("Error in definition: ", end="") + print(sLine.strip()) + elif sLine.startswith("TEST:"): + # test + lTest.append("g{:<7}".format(i) + " " + sLine[5:].strip()) + elif sLine.startswith("TODO:"): + # todo + pass + elif sLine.startswith("!!"): + # bookmarks + m = re.search("^!!+", sLine) + nExMk = len(m.group(0)) + if sLine[nExMk:].strip(): + printBookmark(nExMk-2, sLine[nExMk:].strip(), i) + elif sLine.startswith("__") and sLine.endswith("__"): + # new rule group + m = re.match("__(\\w+)(!\\d|)__", sLine) + if m: + sRuleName = m.group(1) + nPriority = int(m.group(2)[1:]) if m.group(2) else 4 + else: + print("Error at rule group: ", sLine, " -- line:", i) + break + elif re.match("[  ]*$", sLine): + # empty line to end merging + if not lTokenLine: + continue + if not sActions: + print("Error. No action found at line:", i) + exit() + if not sGraphName: + print("Error. All rules must belong to a named graph. Line: ", i) + exit() + for j, sTokenLine in lTokenLine: + dAllGraph[sGraphName].append((j, sRuleName, sTokenLine, sActions, nPriority)) + lTokenLine.clear() + sActions = "" + sRuleName = "" + nPriority = 4 + elif sLine.startswith((" ")): + # actions + sActions += " " + sLine.strip() + else: + lTokenLine.append([i, sLine.strip()]) + + # tests + print(" list tests...") + sGCTests = "\n".join(lTest) + sGCTestsJS = '{ "aData2": ' + json.dumps(lTest, ensure_ascii=False) + " }\n" + + # processing rules + print(" preparing rules...") + for sGraphName, lRuleLine in dAllGraph.items(): + lPreparedRule = [] + for i, sRuleGroup, sTokenLine, sActions, nPriority in lRuleLine: + for lRule in createRule(i, sRuleGroup, sTokenLine, sActions, nPriority): + lPreparedRule.append(lRule) + # Show rules + for e in lPreparedRule: + print(e) + # Graph creation + oDARG = darg.DARG(lPreparedRule, sLang) + dAllGraph[sGraphName] = oDARG.createGraph() + + # creating file with all functions callable by rules + print(" creating callables...") + sPyCallables = "# generated code, do not edit\n" + #sJSCallables = "// generated code, do not edit\nconst oEvalFunc = {\n" + for sFuncName, sReturn in lFUNCTIONS: + if sFuncName.startswith("g_c_"): # condition + sParams = "lToken, nTokenOffset, sCountry, bCondMemo" + elif sFuncName.startswith("g_m_"): # message + sParams = "lToken, nTokenOffset" + elif sFuncName.startswith("g_s_"): # suggestion + sParams = "lToken, nTokenOffset" + elif sFuncName.startswith("g_p_"): # preprocessor + sParams = "lToken" + elif sFuncName.startswith("g_d_"): # disambiguator + sParams = "lToken, nTokenOffset" + else: + print("# Unknown function type in [" + sFuncName + "]") + continue + sPyCallables += "def {} ({}):\n".format(sFuncName, sParams) + sPyCallables += " return " + sReturn + "\n" + #sJSCallables += " {}: function ({})".format(sFuncName, sParams) + " {\n" + #sJSCallables += " return " + jsconv.py2js(sReturn) + ";\n" + #sJSCallables += " },\n" + #sJSCallables += "}\n" + + for sActionName, aAction in dACTIONS.items(): + print(sActionName, aAction) + + # Result + d = { + "graph_callables": sPyCallables, + "graph_gctests": sGCTests, + "rules_graphs": dAllGraph, + "rules_actions": dACTIONS + } + + return d + + Index: compile_rules_js_convert.py ================================================================== --- compile_rules_js_convert.py +++ compile_rules_js_convert.py @@ -117,10 +117,13 @@ return (sRegex, lNegLookBeforeRegex) def pyRuleToJS (lRule, dJSREGEXES, sWORDLIMITLEFT): lRuleJS = copy.deepcopy(lRule) + # graph rules + if lRuleJS[0] == "@@@@": + return lRuleJS del lRule[-1] # tGroups positioning codes are useless for Python # error messages for aAction in lRuleJS[6]: if aAction[1] == "-": aAction[2] = aAction[2].replace(" ", " ") # nbsp --> nnbsp @@ -132,25 +135,31 @@ def writeRulesToJSArray (lRules): sArray = "[\n" for sOption, aRuleGroup in lRules: - sArray += ' ["' + sOption + '", [\n' if sOption else " [false, [\n" - for sRegex, bCaseInsensitive, sLineId, sRuleId, nPriority, lActions, aGroups, aNegLookBehindRegex in aRuleGroup: - sArray += ' [' + sRegex + ", " - sArray += "true, " if bCaseInsensitive else "false, " - sArray += '"' + sLineId + '", ' - sArray += '"' + sRuleId + '", ' - sArray += str(nPriority) + ", " - sArray += json.dumps(lActions, ensure_ascii=False) + ", " - sArray += json.dumps(aGroups, ensure_ascii=False) + ", " - sArray += json.dumps(aNegLookBehindRegex, ensure_ascii=False) + "],\n" - sArray += " ]],\n" + if sOption != "@@@@": + sArray += ' ["' + sOption + '", [\n' if sOption else " [false, [\n" + for sRegex, bCaseInsensitive, sLineId, sRuleId, nPriority, lActions, aGroups, aNegLookBehindRegex in aRuleGroup: + sArray += ' [' + sRegex + ", " + sArray += "true, " if bCaseInsensitive else "false, " + sArray += '"' + sLineId + '", ' + sArray += '"' + sRuleId + '", ' + sArray += str(nPriority) + ", " + sArray += json.dumps(lActions, ensure_ascii=False) + ", " + sArray += json.dumps(aGroups, ensure_ascii=False) + ", " + sArray += json.dumps(aNegLookBehindRegex, ensure_ascii=False) + "],\n" + sArray += " ]],\n" + else: + sArray += ' ["' + sOption + '", [\n' + for sGraphName, sLineId in aRuleGroup: + sArray += ' ["' + sGraphName + '", "' + sLineId + '"],\n"' + sArray += " ]],\n" sArray += "]" return sArray def groupsPositioningCodeToList (sGroupsPositioningCode): if not sGroupsPositioningCode: return None return [ int(sCode) if sCode.isdigit() or (sCode[0:1] == "-" and sCode[1:].isdigit()) else sCode \ for sCode in sGroupsPositioningCode.split(",") ] ADDED darg.py Index: darg.py ================================================================== --- darg.py +++ darg.py @@ -0,0 +1,185 @@ +#!python3 + +# RULE GRAPH BUILDER +# +# by Olivier R. +# License: MPL 2 + + +import json +import time +import traceback + +from graphspell.progressbar import ProgressBar + + + +class DARG: + """DIRECT ACYCLIC RULE GRAPH""" + # This code is inspired from Steve Hanov’s DAWG, 2011. (http://stevehanov.ca/blog/index.php?id=115) + + def __init__ (self, lRule, sLangCode): + print("===== Direct Acyclic Rule Graph - Minimal Acyclic Finite State Automaton =====") + + # Preparing DARG + print(" > Preparing list of tokens") + self.sLangCode = sLangCode + self.nRule = len(lRule) + self.aPreviousRule = [] + Node.resetNextId() + self.oRoot = Node() + self.lUncheckedNodes = [] # list of nodes that have not been checked for duplication. + self.lMinimizedNodes = {} # list of unique nodes that have been checked for duplication. + self.nNode = 0 + self.nArc = 0 + + # build + lRule.sort() + oProgBar = ProgressBar(0, len(lRule)) + for aRule in lRule: + self.insert(aRule) + oProgBar.increment(1) + oProgBar.done() + self.finish() + self.countNodes() + self.countArcs() + self.displayInfo() + + # BUILD DARG + def insert (self, aRule): + if aRule < self.aPreviousRule: + sys.exit("# Error: tokens must be inserted in order.") + + # find common prefix between word and previous word + nCommonPrefix = 0 + for i in range(min(len(aRule), len(self.aPreviousRule))): + if aRule[i] != self.aPreviousRule[i]: + break + nCommonPrefix += 1 + + # Check the lUncheckedNodes for redundant nodes, proceeding from last + # one down to the common prefix size. Then truncate the list at that point. + self._minimize(nCommonPrefix) + + # add the suffix, starting from the correct node mid-way through the graph + if len(self.lUncheckedNodes) == 0: + oNode = self.oRoot + else: + oNode = self.lUncheckedNodes[-1][2] + + iToken = nCommonPrefix + for sToken in aRule[nCommonPrefix:]: + oNextNode = Node() + oNode.dArcs[sToken] = oNextNode + self.lUncheckedNodes.append((oNode, sToken, oNextNode)) + if iToken == (len(aRule) - 2): + oNode.bFinal = True + iToken += 1 + oNode = oNextNode + oNode.bFinal = True + self.aPreviousRule = aRule + + def finish (self): + "minimize unchecked nodes" + self._minimize(0) + + def _minimize (self, downTo): + # proceed from the leaf up to a certain point + for i in range( len(self.lUncheckedNodes)-1, downTo-1, -1 ): + oNode, sToken, oChildNode = self.lUncheckedNodes[i] + if oChildNode in self.lMinimizedNodes: + # replace the child with the previously encountered one + oNode.dArcs[sToken] = self.lMinimizedNodes[oChildNode] + else: + # add the state to the minimized nodes. + self.lMinimizedNodes[oChildNode] = oChildNode + self.lUncheckedNodes.pop() + + def countNodes (self): + self.nNode = len(self.lMinimizedNodes) + + def countArcs (self): + self.nArc = 0 + for oNode in self.lMinimizedNodes: + self.nArc += len(oNode.dArcs) + + def displayInfo (self): + print(" * {:<12} {:>16,}".format("Rules:", self.nRule)) + print(" * {:<12} {:>16,}".format("Nodes:", self.nNode)) + print(" * {:<12} {:>16,}".format("Arcs:", self.nArc)) + + def createGraph (self): + dGraph = { 0: self.oRoot.getNodeAsDict() } + print(0, "\t", self.oRoot.getNodeAsDict()) + for oNode in self.lMinimizedNodes: + sHashId = oNode.__hash__() + if sHashId not in dGraph: + dGraph[sHashId] = oNode.getNodeAsDict() + print(sHashId, "\t", dGraph[sHashId]) + else: + print("Error. Double node… same id: ", sHashId) + print(str(oNode.getNodeAsDict())) + return dGraph + + + +class Node: + NextId = 0 + + def __init__ (self): + self.i = Node.NextId + Node.NextId += 1 + self.bFinal = False + self.dArcs = {} # key: arc value; value: a node + + @classmethod + def resetNextId (cls): + cls.NextId = 0 + + def __str__ (self): + # Caution! this function is used for hashing and comparison! + cFinal = "1" if self.bFinal else "0" + l = [cFinal] + for (key, oNode) in self.dArcs.items(): + l.append(str(key)) + l.append(str(oNode.i)) + return "_".join(l) + + def __hash__ (self): + # Used as a key in a python dictionary. + return self.__str__().__hash__() + + def __eq__ (self, other): + # Used as a key in a python dictionary. + # Nodes are equivalent if they have identical arcs, and each identical arc leads to identical states. + return self.__str__() == other.__str__() + + def getNodeAsDict (self): + "returns the node as a dictionary structure" + dNode = {} + dReValue = {} + dReMorph = {} + dRules = {} + dLemmas = {} + for sArc, oNode in self.dArcs.items(): + if sArc.startswith("@") and len(sArc) > 1: + dReMorph[sArc[1:]] = oNode.__hash__() + elif sArc.startswith("~") and len(sArc) > 1: + dReValue[sArc[1:]] = oNode.__hash__() + elif sArc.startswith(">") and len(sArc) > 1: + dLemmas[sArc[1:]] = oNode.__hash__() + elif sArc.startswith("##"): + dRules[sArc[1:]] = oNode.__hash__() + else: + dNode[sArc] = oNode.__hash__() + if dReValue: + dNode[""] = dReValue + if dReMorph: + dNode[""] = dReMorph + if dLemmas: + dNode[""] = dLemmas + if dRules: + dNode[""] = dRules + #if self.bFinal: + # dNode[""] = 1 + return dNode Index: gc_core/js/lang_core/gc_engine.js ================================================================== --- gc_core/js/lang_core/gc_engine.js +++ gc_core/js/lang_core/gc_engine.js @@ -37,11 +37,10 @@ // data let _sAppContext = ""; // what software is running let _dOptions = null; let _aIgnoredRules = new Set(); let _oSpellChecker = null; -let _dAnalyses = new Map(); // cache for data from dictionary var gc_engine = { //// Informations @@ -327,10 +326,11 @@ } else { _oSpellChecker = new SpellChecker("${lang}", sPath, "${dic_main_filename_js}", "${dic_extended_filename_js}", "${dic_community_filename_js}", "${dic_personal_filename_js}"); } _sAppContext = sContext; _dOptions = gc_options.getOptions(sContext).gl_shallowCopy(); // duplication necessary, to be able to reset to default + _oSpellChecker.activateStorage(); } catch (e) { helpers.logerror(e); } }, @@ -376,39 +376,30 @@ // for debugging: info of word if (!aWord) { helpers.echo("> nothing to find"); return true; } - if (!_dAnalyses.has(aWord[1]) && !_storeMorphFromFSA(aWord[1])) { - helpers.echo("> not in FSA"); + let lMorph = _oSpellChecker.getMorph(aWord[1]); + if (lMorph.length === 0) { + helpers.echo("> not in dictionary"); return true; } if (dDA.has(aWord[0])) { helpers.echo("DA: " + dDA.get(aWord[0])); } - helpers.echo("FSA: " + _dAnalyses.get(aWord[1])); + helpers.echo("FSA: " + lMorph); return true; } -function _storeMorphFromFSA (sWord) { - // retrieves morphologies list from _oSpellChecker -> _dAnalyses - //helpers.echo("register: "+sWord + " " + _oSpellChecker.getMorph(sWord).toString()) - _dAnalyses.set(sWord, _oSpellChecker.getMorph(sWord)); - return !!_dAnalyses.get(sWord); -} - function morph (dDA, aWord, sPattern, bStrict=true, bNoWord=false) { // analyse a tuple (position, word), return true if sPattern in morphologies (disambiguation on) if (!aWord) { //helpers.echo("morph: noword, returns " + bNoWord); return bNoWord; } //helpers.echo("aWord: "+aWord.toString()); - if (!_dAnalyses.has(aWord[1]) && !_storeMorphFromFSA(aWord[1])) { - return false; - } - let lMorph = dDA.has(aWord[0]) ? dDA.get(aWord[0]) : _dAnalyses.get(aWord[1]); + let lMorph = dDA.has(aWord[0]) ? dDA.get(aWord[0]) : _oSpellChecker.getMorph(aWord[1]); //helpers.echo("lMorph: "+lMorph.toString()); if (lMorph.length === 0) { return false; } //helpers.echo("***"); @@ -423,14 +414,11 @@ if (!aWord) { //helpers.echo("morph: noword, returns " + bNoWord); return bNoWord; } //helpers.echo("aWord: "+aWord.toString()); - if (!_dAnalyses.has(aWord[1]) && !_storeMorphFromFSA(aWord[1])) { - return false; - } - let lMorph = dDA.has(aWord[0]) ? dDA.get(aWord[0]) : _dAnalyses.get(aWord[1]); + let lMorph = dDA.has(aWord[0]) ? dDA.get(aWord[0]) : _oSpellChecker.getMorph(aWord[1]); //helpers.echo("lMorph: "+lMorph.toString()); if (lMorph.length === 0) { return false; } //helpers.echo("***"); @@ -442,41 +430,32 @@ return lMorph.some(s => (s.search(sPattern) !== -1)); } function analyse (sWord, sPattern, bStrict=true) { // analyse a word, return true if sPattern in morphologies (disambiguation off) - if (!_dAnalyses.has(sWord) && !_storeMorphFromFSA(sWord)) { + let lMorph = _oSpellChecker.getMorph(sWord); + if (lMorph.length === 0) { return false; } if (bStrict) { - return _dAnalyses.get(sWord).every(s => (s.search(sPattern) !== -1)); + return lMorph.every(s => (s.search(sPattern) !== -1)); } - return _dAnalyses.get(sWord).some(s => (s.search(sPattern) !== -1)); + return lMorph.some(s => (s.search(sPattern) !== -1)); } function analysex (sWord, sPattern, sNegPattern) { // analyse a word, returns True if not sNegPattern in word morphologies and sPattern in word morphologies (disambiguation off) - if (!_dAnalyses.has(sWord) && !_storeMorphFromFSA(sWord)) { + let lMorph = _oSpellChecker.getMorph(sWord); + if (lMorph.length === 0) { return false; } // check negative condition - if (_dAnalyses.get(sWord).some(s => (s.search(sNegPattern) !== -1))) { + if (lMorph.some(s => (s.search(sNegPattern) !== -1))) { return false; } // search sPattern - return _dAnalyses.get(sWord).some(s => (s.search(sPattern) !== -1)); -} - -function stem (sWord) { - // returns a list of sWord's stems - if (!sWord) { - return []; - } - if (!_dAnalyses.has(sWord) && !_storeMorphFromFSA(sWord)) { - return []; - } - return _dAnalyses.get(sWord).map( s => s.slice(1, s.indexOf(" ")) ); + return lMorph.some(s => (s.search(sPattern) !== -1)); } //// functions to get text outside pattern scope @@ -565,19 +544,17 @@ return true; } if (dDA.has(nPos)) { return true; } - if (!_dAnalyses.has(sWord) && !_storeMorphFromFSA(sWord)) { + let lMorph = _oSpellChecker.getMorph(sWord); + if (lMorph.length === 0 || lMorph.length === 1) { return true; } - if (_dAnalyses.get(sWord).length === 1) { - return true; - } - let lSelect = _dAnalyses.get(sWord).filter( sMorph => sMorph.search(sPattern) !== -1 ); + let lSelect = lMorph.filter( sMorph => sMorph.search(sPattern) !== -1 ); if (lSelect.length > 0) { - if (lSelect.length != _dAnalyses.get(sWord).length) { + if (lSelect.length != lMorph.length) { dDA.set(nPos, lSelect); } } else if (lDefault) { dDA.set(nPos, lDefaul); } @@ -589,19 +566,17 @@ return true; } if (dDA.has(nPos)) { return true; } - if (!_dAnalyses.has(sWord) && !_storeMorphFromFSA(sWord)) { + let lMorph = _oSpellChecker.getMorph(sWord); + if (lMorph.length === 0 || lMorph.length === 1) { return true; } - if (_dAnalyses.get(sWord).length === 1) { - return true; - } - let lSelect = _dAnalyses.get(sWord).filter( sMorph => sMorph.search(sPattern) === -1 ); + let lSelect = lMorph.filter( sMorph => sMorph.search(sPattern) === -1 ); if (lSelect.length > 0) { - if (lSelect.length != _dAnalyses.get(sWord).length) { + if (lSelect.length != lMorph.length) { dDA.set(nPos, lSelect); } } else if (lDefault) { dDA.set(nPos, lDefault); } Index: gc_core/py/lang_core/gc_engine.py ================================================================== --- gc_core/py/lang_core/gc_engine.py +++ gc_core/py/lang_core/gc_engine.py @@ -10,10 +10,23 @@ from ..graphspell.spellchecker import SpellChecker from ..graphspell.echo import echo from . import gc_options +from ..graphspell.tokenizer import Tokenizer +from .gc_rules_graph import dAllGraph, dRule + +try: + # LibreOffice / OpenOffice + from com.sun.star.linguistic2 import SingleProofreadingError + from com.sun.star.text.TextMarkupType import PROOFREADING + from com.sun.star.beans import PropertyValue + #import lightproof_handler_${implname} as opt + _bWriterError = True +except ImportError: + _bWriterError = False + __all__ = [ "lang", "locales", "pkg", "name", "version", "author", \ "load", "parse", "getSpellChecker", \ "setOption", "setOptions", "getOptions", "getDefaultOptions", "getOptionsLabels", "resetOptions", "displayOptions", \ "ignoreRule", "resetIgnoreRules", "reactivateRule", "listRules", "displayRules" ] @@ -31,30 +44,80 @@ _rules = None # module gc_rules # data _sAppContext = "" # what software is running _dOptions = None -_aIgnoredRules = set() _oSpellChecker = None -_dAnalyses = {} # cache for data from dictionary +_oTokenizer = None +_aIgnoredRules = set() +# functions +_createRegexError = None + + +#### Initialization + +def load (sContext="Python"): + global _oSpellChecker + global _sAppContext + global _dOptions + global _oTokenizer + global _createRegexError + global _createTokenError + try: + _oSpellChecker = SpellChecker("${lang}", "${dic_main_filename_py}", "${dic_extended_filename_py}", "${dic_community_filename_py}", "${dic_personal_filename_py}") + _sAppContext = sContext + _dOptions = dict(gc_options.getOptions(sContext)) # duplication necessary, to be able to reset to default + _oTokenizer = _oSpellChecker.getTokenizer() + _oSpellChecker.activateStorage() + _createRegexError = _createRegexWriterError if _bWriterError else _createRegexDictError + except: + traceback.print_exc() + + +def _getRules (bParagraph): + try: + if not bParagraph: + return _rules.lSentenceRules + return _rules.lParagraphRules + except: + _loadRules() + if not bParagraph: + return _rules.lSentenceRules + return _rules.lParagraphRules + + +def _loadRules (): + from . import gc_rules + global _rules + _rules = gc_rules + # compile rules regex + for sOption, lRuleGroup in chain(_rules.lParagraphRules, _rules.lSentenceRules): + if sOption != "@@@@": + for aRule in lRuleGroup: + try: + aRule[0] = re.compile(aRule[0]) + except: + echo("Bad regular expression in # " + str(aRule[2])) + aRule[0] = "(?i)" #### Parsing def parse (sText, sCountry="${country_default}", bDebug=False, dOptions=None, bContext=False): "analyses the paragraph sText and returns list of errors" #sText = unicodedata.normalize("NFC", sText) aErrors = None - sAlt = sText + sRealText = sText dDA = {} # Disambiguisator. Key = position; value = list of morphologies dPriority = {} # Key = position; value = priority dOpt = _dOptions if not dOptions else dOptions + bShowRuleId = option('idrule') # parse paragraph try: - sNew, aErrors = _proofread(sText, sAlt, 0, True, dDA, dPriority, sCountry, dOpt, bDebug, bContext) + sNew, aErrors = _proofread(None, sText, sRealText, 0, True, dDA, dPriority, sCountry, dOpt, bShowRuleId, bDebug, bContext) if sNew: sText = sNew except: raise @@ -71,31 +134,46 @@ # parse sentences for iStart, iEnd in _getSentenceBoundaries(sText): if 4 < (iEnd - iStart) < 2000: dDA.clear() try: - _, errs = _proofread(sText[iStart:iEnd], sAlt[iStart:iEnd], iStart, False, dDA, dPriority, sCountry, dOpt, bDebug, bContext) + oSentence = TokenSentence(sText[iStart:iEnd], sRealText[iStart:iEnd], iStart) + _, errs = _proofread(oSentence, sText[iStart:iEnd], sRealText[iStart:iEnd], iStart, False, dDA, dPriority, sCountry, dOpt, bShowRuleId, bDebug, bContext) aErrors.update(errs) except: raise return aErrors.values() # this is a view (iterable) + +_zEndOfSentence = re.compile(r'([.?!:;…][ .?!… »”")]*|.$)') +_zBeginOfParagraph = re.compile(r"^\W*") +_zEndOfParagraph = re.compile(r"\W*$") def _getSentenceBoundaries (sText): iStart = _zBeginOfParagraph.match(sText).end() for m in _zEndOfSentence.finditer(sText): yield (iStart, m.end()) iStart = m.end() -def _proofread (s, sx, nOffset, bParagraph, dDA, dPriority, sCountry, dOptions, bDebug, bContext): +def _proofread (oSentence, s, sx, nOffset, bParagraph, dDA, dPriority, sCountry, dOptions, bShowRuleId, bDebug, bContext): dErrs = {} bChange = False - bIdRule = option('idrule') - for sOption, lRuleGroup in _getRules(bParagraph): - if not sOption or dOptions.get(sOption, False): + if sOption == "@@@@": + # graph rules + for sGraphName, sLineId in lRuleGroup: + if bDebug: + print(sGraphName, sLineId) + bChange, errs = oSentence.parse(dAllGraph[sGraphName], dPriority, sCountry, dOptions, bShowRuleId, bDebug, bContext) + dErrs.update(errs) + if bChange: + oSentence.rewrite() + if bDebug: + print("~", oSentence.sSentence) + elif not sOption or dOptions.get(sOption, False): + # regex rules for zRegex, bUppercase, sLineId, sRuleId, nPriority, lActions in lRuleGroup: if sRuleId not in _aIgnoredRules: for m in zRegex.finditer(s): bCondMemo = None for sFuncCond, cActionType, sWhat, *eAct in lActions: @@ -105,11 +183,11 @@ if bCondMemo: if cActionType == "-": # grammar error nErrorStart = nOffset + m.start(eAct[0]) if nErrorStart not in dErrs or nPriority > dPriority[nErrorStart]: - dErrs[nErrorStart] = _createError(s, sx, sWhat, nOffset, m, eAct[0], sLineId, sRuleId, bUppercase, eAct[1], eAct[2], bIdRule, sOption, bContext) + dErrs[nErrorStart] = _createRegexError(s, sx, sWhat, nOffset, m, eAct[0], sLineId, sRuleId, bUppercase, eAct[1], eAct[2], bShowRuleId, sOption, bContext) dPriority[nErrorStart] = nPriority elif cActionType == "~": # text processor s = _rewrite(s, sWhat, eAct[0], m, bUppercase) bChange = True @@ -132,11 +210,11 @@ if bChange: return (s, dErrs) return (False, dErrs) -def _createWriterError (s, sx, sRepl, nOffset, m, iGroup, sLineId, sRuleId, bUppercase, sMsg, sURL, bIdRule, sOption, bContext): +def _createRegexWriterError (s, sx, sRepl, nOffset, m, iGroup, sLineId, sRuleId, bUppercase, sMsg, sURL, bShowRuleId, sOption, bContext): "error for Writer (LO/OO)" xErr = SingleProofreadingError() #xErr = uno.createUnoStruct( "com.sun.star.linguistic2.SingleProofreadingError" ) xErr.nErrorStart = nOffset + m.start(iGroup) xErr.nErrorLength = m.end(iGroup) - m.start(iGroup) @@ -158,17 +236,14 @@ if bUppercase and m.group(iGroup)[0:1].isupper(): xErr.aSuggestions = tuple(map(str.capitalize, m.expand(sRepl).split("|"))) else: xErr.aSuggestions = tuple(m.expand(sRepl).split("|")) # Message - if sMsg[0:1] == "=": - sMessage = globals()[sMsg[1:]](s, m) - else: - sMessage = m.expand(sMsg) + sMessage = globals()[sMsg[1:]](s, m) if sMsg[0:1] == "=" else m.expand(sMsg) xErr.aShortComment = sMessage # sMessage.split("|")[0] # in context menu xErr.aFullComment = sMessage # sMessage.split("|")[-1] # in dialog - if bIdRule: + if bShowRuleId: xErr.aShortComment += " # " + sLineId + " # " + sRuleId # URL if sURL: p = PropertyValue() p.Name = "FullCommentURL" @@ -177,11 +252,11 @@ else: xErr.aProperties = () return xErr -def _createDictError (s, sx, sRepl, nOffset, m, iGroup, sLineId, sRuleId, bUppercase, sMsg, sURL, bIdRule, sOption, bContext): +def _createRegexDictError (s, sx, sRepl, nOffset, m, iGroup, sLineId, sRuleId, bUppercase, sMsg, sURL, bShowRuleId, sOption, bContext): "error as a dictionary" dErr = {} dErr["nStart"] = nOffset + m.start(iGroup) dErr["nEnd"] = nOffset + m.end(iGroup) dErr["sLineId"] = sLineId @@ -194,25 +269,21 @@ if bUppercase and m.group(iGroup)[0:1].isupper(): dErr["aSuggestions"] = list(map(str.capitalize, sugg.split("|"))) else: dErr["aSuggestions"] = sugg.split("|") else: - dErr["aSuggestions"] = () + dErr["aSuggestions"] = [] elif sRepl == "_": - dErr["aSuggestions"] = () + dErr["aSuggestions"] = [] else: if bUppercase and m.group(iGroup)[0:1].isupper(): dErr["aSuggestions"] = list(map(str.capitalize, m.expand(sRepl).split("|"))) else: dErr["aSuggestions"] = m.expand(sRepl).split("|") # Message - if sMsg[0:1] == "=": - sMessage = globals()[sMsg[1:]](s, m) - else: - sMessage = m.expand(sMsg) - dErr["sMessage"] = sMessage - if bIdRule: + dErr["sMessage"] = globals()[sMsg[1:]](s, m) if sMsg[0:1] == "=" else m.expand(sMsg) + if bShowRuleId: dErr["sMessage"] += " # " + sLineId + " # " + sRuleId # URL dErr["URL"] = sURL if sURL else "" # Context if bContext: @@ -220,28 +291,26 @@ dErr['sBefore'] = sx[max(0,m.start(iGroup)-80):m.start(iGroup)] dErr['sAfter'] = sx[m.end(iGroup):m.end(iGroup)+80] return dErr -def _rewrite (s, sRepl, iGroup, m, bUppercase): - "text processor: write sRepl in s at iGroup position" +def _rewrite (sSentence, sRepl, iGroup, m, bUppercase): + "text processor: write in at position" nLen = m.end(iGroup) - m.start(iGroup) if sRepl == "*": sNew = " " * nLen - elif sRepl == ">" or sRepl == "_" or sRepl == "~": + elif sRepl == "_": sNew = sRepl + " " * (nLen-1) - elif sRepl == "@": - sNew = "@" * nLen elif sRepl[0:1] == "=": - sNew = globals()[sRepl[1:]](s, m) + sNew = globals()[sRepl[1:]](sSentence, m) sNew = sNew + " " * (nLen-len(sNew)) if bUppercase and m.group(iGroup)[0:1].isupper(): sNew = sNew.capitalize() else: sNew = m.expand(sRepl) sNew = sNew + " " * (nLen-len(sNew)) - return s[0:m.start(iGroup)] + sNew + s[m.end(iGroup):] + return sSentence[0:m.start(iGroup)] + sNew + sSentence[m.end(iGroup):] def ignoreRule (sRuleId): _aIgnoredRules.add(sRuleId) @@ -261,45 +330,21 @@ zFilter = re.compile(sFilter) except: echo("# Error. List rules: wrong regex.") sFilter = None for sOption, lRuleGroup in chain(_getRules(True), _getRules(False)): - for _, _, sLineId, sRuleId, _, _ in lRuleGroup: - if not sFilter or zFilter.search(sRuleId): - yield (sOption, sLineId, sRuleId) + if sOption != "@@@@": + for _, _, sLineId, sRuleId, _, _ in lRuleGroup: + if not sFilter or zFilter.search(sRuleId): + yield (sOption, sLineId, sRuleId) def displayRules (sFilter=None): echo("List of rules. Filter: << " + str(sFilter) + " >>") for sOption, sLineId, sRuleId in listRules(sFilter): echo("{:<10} {:<10} {}".format(sOption, sLineId, sRuleId)) - -#### init - -try: - # LibreOffice / OpenOffice - from com.sun.star.linguistic2 import SingleProofreadingError - from com.sun.star.text.TextMarkupType import PROOFREADING - from com.sun.star.beans import PropertyValue - #import lightproof_handler_${implname} as opt - _createError = _createWriterError -except ImportError: - _createError = _createDictError - - -def load (sContext="Python"): - global _oSpellChecker - global _sAppContext - global _dOptions - try: - _oSpellChecker = SpellChecker("${lang}", "${dic_main_filename_py}", "${dic_extended_filename_py}", "${dic_community_filename_py}", "${dic_personal_filename_py}") - _sAppContext = sContext - _dOptions = dict(gc_options.getOptions(sContext)) # duplication necessary, to be able to reset to default - except: - traceback.print_exc() - def setOption (sOpt, bVal): if sOpt in _dOptions: _dOptions[sOpt] = bVal @@ -334,51 +379,17 @@ def getSpellChecker (): return _oSpellChecker - -def _getRules (bParagraph): - try: - if not bParagraph: - return _rules.lSentenceRules - return _rules.lParagraphRules - except: - _loadRules() - if not bParagraph: - return _rules.lSentenceRules - return _rules.lParagraphRules - - -def _loadRules (): - from . import gc_rules - global _rules - _rules = gc_rules - # compile rules regex - for lRuleGroup in chain(_rules.lParagraphRules, _rules.lSentenceRules): - for rule in lRuleGroup[1]: - try: - rule[0] = re.compile(rule[0]) - except: - echo("Bad regular expression in # " + str(rule[2])) - rule[0] = "(?i)" - def _getPath (): return os.path.join(os.path.dirname(sys.modules[__name__].__file__), __name__ + ".py") #### common functions - -# common regexes -_zEndOfSentence = re.compile('([.?!:;…][ .?!… »”")]*|.$)') -_zBeginOfParagraph = re.compile("^\W*") -_zEndOfParagraph = re.compile("\W*$") -_zNextWord = re.compile(" +(\w[\w-]*)") -_zPrevWord = re.compile("(\w[\w-]*) +$") - def option (sOpt): "return True if option sOpt is active" return _dOptions.get(sOpt, False) @@ -386,33 +397,25 @@ def displayInfo (dDA, tWord): "for debugging: retrieve info of word" if not tWord: echo("> nothing to find") return True - if tWord[1] not in _dAnalyses and not _storeMorphFromFSA(tWord[1]): - echo("> not in FSA") + lMorph = _oSpellChecker.getMorph(tWord[1]) + if not lMorph: + echo("> not in dictionary") return True if tWord[0] in dDA: echo("DA: " + str(dDA[tWord[0]])) - echo("FSA: " + str(_dAnalyses[tWord[1]])) + echo("FSA: " + str(lMorph)) return True - -def _storeMorphFromFSA (sWord): - "retrieves morphologies list from _oSpellChecker -> _dAnalyses" - global _dAnalyses - _dAnalyses[sWord] = _oSpellChecker.getMorph(sWord) - return True if _dAnalyses[sWord] else False - def morph (dDA, tWord, sPattern, bStrict=True, bNoWord=False): "analyse a tuple (position, word), return True if sPattern in morphologies (disambiguation on)" if not tWord: return bNoWord - if tWord[1] not in _dAnalyses and not _storeMorphFromFSA(tWord[1]): - return False - lMorph = dDA[tWord[0]] if tWord[0] in dDA else _dAnalyses[tWord[1]] + lMorph = dDA[tWord[0]] if tWord[0] in dDA else _oSpellChecker.getMorph(tWord[1]) if not lMorph: return False p = re.compile(sPattern) if bStrict: return all(p.search(s) for s in lMorph) @@ -421,13 +424,13 @@ def morphex (dDA, tWord, sPattern, sNegPattern, bNoWord=False): "analyse a tuple (position, word), returns True if not sNegPattern in word morphologies and sPattern in word morphologies (disambiguation on)" if not tWord: return bNoWord - if tWord[1] not in _dAnalyses and not _storeMorphFromFSA(tWord[1]): + lMorph = dDA[tWord[0]] if tWord[0] in dDA else _oSpellChecker.getMorph(tWord[1]) + if not lMorph: return False - lMorph = dDA[tWord[0]] if tWord[0] in dDA else _dAnalyses[tWord[1]] # check negative condition np = re.compile(sNegPattern) if any(np.search(s) for s in lMorph): return False # search sPattern @@ -435,46 +438,41 @@ return any(p.search(s) for s in lMorph) def analyse (sWord, sPattern, bStrict=True): "analyse a word, return True if sPattern in morphologies (disambiguation off)" - if sWord not in _dAnalyses and not _storeMorphFromFSA(sWord): - return False - if not _dAnalyses[sWord]: + lMorph = _oSpellChecker.getMorph(sWord) + if not lMorph: return False p = re.compile(sPattern) if bStrict: - return all(p.search(s) for s in _dAnalyses[sWord]) - return any(p.search(s) for s in _dAnalyses[sWord]) + return all(p.search(s) for s in lMorph) + return any(p.search(s) for s in lMorph) def analysex (sWord, sPattern, sNegPattern): "analyse a word, returns True if not sNegPattern in word morphologies and sPattern in word morphologies (disambiguation off)" - if sWord not in _dAnalyses and not _storeMorphFromFSA(sWord): + lMorph = _oSpellChecker.getMorph(sWord) + if not lMorph: return False # check negative condition np = re.compile(sNegPattern) - if any(np.search(s) for s in _dAnalyses[sWord]): + if any(np.search(s) for s in lMorph): return False # search sPattern p = re.compile(sPattern) - return any(p.search(s) for s in _dAnalyses[sWord]) - - -def stem (sWord): - "returns a list of sWord's stems" - if not sWord: - return [] - if sWord not in _dAnalyses and not _storeMorphFromFSA(sWord): - return [] - return [ s[1:s.find(" ")] for s in _dAnalyses[sWord] ] + return any(p.search(s) for s in lMorph) + ## functions to get text outside pattern scope # warning: check compile_rules.py to understand how it works +_zNextWord = re.compile(r" +(\w[\w-]*)") +_zPrevWord = re.compile(r"(\w[\w-]*) +$") + def nextword (s, iStart, n): "get the nth word of the input string or empty string" m = re.match("(?: +[\\w%-]+){" + str(n-1) + "} +([\\w%-]+)", s[iStart:]) if not m: return None @@ -534,52 +532,408 @@ def select (dDA, nPos, sWord, sPattern, lDefault=None): if not sWord: return True if nPos in dDA: return True - if sWord not in _dAnalyses and not _storeMorphFromFSA(sWord): - return True - if len(_dAnalyses[sWord]) == 1: - return True - lSelect = [ sMorph for sMorph in _dAnalyses[sWord] if re.search(sPattern, sMorph) ] - if lSelect: - if len(lSelect) != len(_dAnalyses[sWord]): - dDA[nPos] = lSelect - #echo("= "+sWord+" "+str(dDA.get(nPos, "null"))) + lMorph = _oSpellChecker.getMorph(sWord) + if not lMorph or len(lMorph) == 1: + return True + lSelect = [ sMorph for sMorph in lMorph if re.search(sPattern, sMorph) ] + if lSelect: + if len(lSelect) != len(lMorph): + dDA[nPos] = lSelect elif lDefault: dDA[nPos] = lDefault - #echo("= "+sWord+" "+str(dDA.get(nPos, "null"))) return True def exclude (dDA, nPos, sWord, sPattern, lDefault=None): if not sWord: return True if nPos in dDA: return True - if sWord not in _dAnalyses and not _storeMorphFromFSA(sWord): - return True - if len(_dAnalyses[sWord]) == 1: - return True - lSelect = [ sMorph for sMorph in _dAnalyses[sWord] if not re.search(sPattern, sMorph) ] - if lSelect: - if len(lSelect) != len(_dAnalyses[sWord]): - dDA[nPos] = lSelect - #echo("= "+sWord+" "+str(dDA.get(nPos, "null"))) + lMorph = _oSpellChecker.getMorph(sWord) + if not lMorph or len(lMorph) == 1: + return True + lSelect = [ sMorph for sMorph in lMorph if not re.search(sPattern, sMorph) ] + if lSelect: + if len(lSelect) != len(lMorph): + dDA[nPos] = lSelect elif lDefault: dDA[nPos] = lDefault - #echo("= "+sWord+" "+str(dDA.get(nPos, "null"))) return True def define (dDA, nPos, lMorph): dDA[nPos] = lMorph - #echo("= "+str(nPos)+" "+str(dDA[nPos])) return True #### GRAMMAR CHECKER PLUGINS ${plugins} + +#### TOKEN SENTENCE CHECKER + +class TokenSentence: + + def __init__ (self, sSentence, sSentence0, nOffset): + self.sSentence = sSentence + self.sSentence0 = sSentence0 + self.nOffset = nOffset + self.lToken = list(_oTokenizer.genTokens(sSentence, True)) + self.createError = self._createWriterError if _bWriterError else self._createDictError + + def _getNextMatchingNodes (self, dToken, dGraph, dNode): + "generator: return nodes where “values” match arcs" + # token value + if dToken["sValue"] in dNode: + #print("value found: ", dToken["sValue"]) + yield dGraph[dNode[dToken["sValue"]]] + # token lemmas + if "" in dNode: + for sLemma in _oSpellChecker.getLemma(dToken["sValue"]): + if sLemma in dNode[""]: + #print("lemma found: ", sLemma) + yield dGraph[dNode[""][sLemma]] + # universal arc + if "*" in dNode: + #print("generic arc") + yield dGraph[dNode["*"]] + # regex value arcs + if "" in dNode: + for sRegex in dNode[""]: + if re.search(sRegex, dToken["sValue"]): + #print("value regex matching: ", sRegex) + yield dGraph[dNode[""][sRegex]] + # regex morph arcs + if "" in dNode: + for sRegex in dNode[""]: + if "¬" not in sRegex: + # no anti-pattern + if any(re.search(sRegex, sMorph) for sMorph in _oSpellChecker.getMorph(dToken["sValue"])): + yield dGraph[dNode[""][sRegex]] + else: + # there is an anti-pattern + sPattern, sNegPattern = sRegex.split("¬", 1) + if sNegPattern == "*": + # all morphologies must match with + if all(re.search(sPattern, sMorph) for sMorph in _oSpellChecker.getMorph(dToken["sValue"])): + yield dGraph[dNode[""][sRegex]] + else: + if sNegPattern and any(re.search(sNegPattern, sMorph) for sMorph in _oSpellChecker.getMorph(dToken["sValue"])): + continue + if any(re.search(sPattern, sMorph) for sMorph in _oSpellChecker.getMorph(dToken["sValue"])): + yield dGraph[dNode[""][sRegex]] + + def parse (self, dGraph, dPriority, sCountry="${country_default}", dOptions=None, bShowRuleId=False, bDebug=False, bContext=False): + dErr = {} + dPriority = {} # Key = position; value = priority + dOpt = _dOptions if not dOptions else dOptions + lPointer = [] + bChange = False + for dToken in self.lToken: + # check arcs for each existing pointer + lNextPointer = [] + for dPointer in lPointer: + for dNode in self._getNextMatchingNodes(dToken, dGraph, dPointer["dNode"]): + lNextPointer.append({"iToken": dPointer["iToken"], "dNode": dNode}) + lPointer = lNextPointer + # check arcs of first nodes + for dNode in self._getNextMatchingNodes(dToken, dGraph, dGraph[0]): + lPointer.append({"iToken": dToken["i"], "dNode": dNode}) + # check if there is rules to check for each pointer + for dPointer in lPointer: + if "" in dPointer["dNode"]: + bHasChanged, errs = self._executeActions(dGraph, dPointer["dNode"][""], dPointer["iToken"]-1, dPriority, dOpt, sCountry, bShowRuleId, bDebug, bContext) + dErr.update(errs) + if bHasChanged: + bChange = True + return (bChange, dErr) + + def _executeActions (self, dGraph, dNode, nTokenOffset, dPriority, dOpt, sCountry, bShowRuleId, bDebug, bContext): + "execute actions found in the DARG" + dErrs = {} + bChange = False + for sLineId, nextNodeKey in dNode.items(): + for sRuleId in dGraph[nextNodeKey]: + bCondMemo = None + sFuncCond, cActionType, sWhat, *eAct = dRule[sRuleId] + # action in lActions: [ condition, action type, replacement/suggestion/action[, iTokenStart, iTokenEnd[, nPriority, message, URL]] ] + try: + bCondMemo = not sFuncCond or globals()[sFuncCond](self.lToken, nTokenOffset, sCountry, bCondMemo) + if bCondMemo: + if cActionType == "-": + # grammar error + nTokenErrorStart = nTokenOffset + eAct[0] + nTokenErrorEnd = nTokenOffset + eAct[1] + nErrorStart = self.nOffset + self.lToken[nTokenErrorStart]["nStart"] + nErrorEnd = self.nOffset + self.lToken[nTokenErrorEnd]["nEnd"] + if nErrorStart not in dErrs or eAct[2] > dPriority[nErrorStart]: + dErrs[nErrorStart] = self.createError(sWhat, nTokenOffset, nTokenErrorStart, nErrorStart, nErrorEnd, sLineId, sRuleId, True, eAct[3], eAct[4], bShowRuleId, "notype", bContext) + dPriority[nErrorStart] = eAct[2] + if bDebug: + print("-", sRuleId, dErrs[nErrorStart]) + elif cActionType == "~": + # text processor + self._tagAndPrepareTokenForRewriting(sWhat, nTokenOffset + eAct[0], nTokenOffset + eAct[1]) + if bDebug: + print("~", sRuleId) + bChange = True + elif cActionType == "=": + # disambiguation + globals()[sWhat](self.lToken, nTokenOffset) + if bDebug: + print("=", sRuleId) + elif cActionType == ">": + # we do nothing, this test is just a condition to apply all following actions + if bDebug: + print(">", sRuleId) + pass + else: + print("# error: unknown action at " + sLineId) + elif cActionType == ">": + if bDebug: + print(">!", sRuleId) + break + except Exception as e: + raise Exception(str(e), sLineId) + return bChange, dErrs + + def _createWriterError (self, sSugg, nTokenOffset, iFirstToken, nStart, nEnd, sLineId, sRuleId, bUppercase, sMsg, sURL, bShowRuleId, sOption, bContext): + "error for Writer (LO/OO)" + xErr = SingleProofreadingError() + #xErr = uno.createUnoStruct( "com.sun.star.linguistic2.SingleProofreadingError" ) + xErr.nErrorStart = nStart + xErr.nErrorLength = nEnd - nStart + xErr.nErrorType = PROOFREADING + xErr.aRuleIdentifier = sRuleId + # suggestions + if sSugg[0:1] == "=": + sSugg = globals()[sSugg[1:]](self.lToken) + if sSugg: + if bUppercase and self.lToken[iFirstToken]["sValue"][0:1].isupper(): + xErr.aSuggestions = tuple(map(str.capitalize, sSugg.split("|"))) + else: + xErr.aSuggestions = tuple(sSugg.split("|")) + else: + xErr.aSuggestions = () + elif sSugg == "_": + xErr.aSuggestions = () + else: + if bUppercase and self.lToken[iFirstToken]["sValue"][0:1].isupper(): + xErr.aSuggestions = tuple(map(str.capitalize, self._expand(sSugg, nTokenOffset).split("|"))) + else: + xErr.aSuggestions = tuple(self._expand(sSugg, nTokenOffset).split("|")) + # Message + sMessage = globals()[sMsg[1:]](self.lToken) if sMsg[0:1] == "=" else self._expand(sMsg, nTokenOffset) + xErr.aShortComment = sMessage # sMessage.split("|")[0] # in context menu + xErr.aFullComment = sMessage # sMessage.split("|")[-1] # in dialog + if bShowRuleId: + xErr.aShortComment += " " + sLineId + " # " + sRuleId + # URL + if sURL: + p = PropertyValue() + p.Name = "FullCommentURL" + p.Value = sURL + xErr.aProperties = (p,) + else: + xErr.aProperties = () + return xErr + + def _createDictError (self, sSugg, nTokenOffset, iFirstToken, nStart, nEnd, sLineId, sRuleId, bUppercase, sMsg, sURL, bShowRuleId, sOption, bContext): + "error as a dictionary" + dErr = {} + dErr["nStart"] = nStart + dErr["nEnd"] = nEnd + dErr["sLineId"] = sLineId + dErr["sRuleId"] = sRuleId + dErr["sType"] = sOption if sOption else "notype" + # suggestions + if sSugg[0:1] == "=": + sSugg = globals()[sSugg[1:]](self.lToken) + if sSugg: + if bUppercase and self.lToken[iFirstToken]["sValue"][0:1].isupper(): + dErr["aSuggestions"] = list(map(str.capitalize, sSugg.split("|"))) + else: + dErr["aSuggestions"] = sSugg.split("|") + else: + dErr["aSuggestions"] = [] + elif sSugg == "_": + dErr["aSuggestions"] = [] + else: + if bUppercase and self.lToken[iFirstToken]["sValue"][0:1].isupper(): + dErr["aSuggestions"] = list(map(str.capitalize, self._expand(sSugg, nTokenOffset).split("|"))) + else: + dErr["aSuggestions"] = self._expand(sSugg, nTokenOffset).split("|") + # Message + dErr["sMessage"] = globals()[sMsg[1:]](self.lToken) if sMsg[0:1] == "=" else self._expand(sMsg, nTokenOffset) + if bShowRuleId: + dErr["sMessage"] += " " + sLineId + " # " + sRuleId + # URL + dErr["URL"] = sURL if sURL else "" + # Context + if bContext: + dErr['sUnderlined'] = self.sSentence0[dErr["nStart"]:dErr["nEnd"]] + dErr['sBefore'] = self.sSentence0[max(0,dErr["nStart"]-80):dErr["nStart"]] + dErr['sAfter'] = self.sSentence0[dErr["nEnd"]:dErr["nEnd"]+80] + return dErr + + def _expand (self, sMsg, nTokenOffset): + #print("*", sMsg) + for m in re.finditer(r"\\([0-9]+)", sMsg): + sMsg = sMsg.replace(m.group(0), self.lToken[int(m.group(1))+nTokenOffset]["sValue"]) + #print(">", sMsg) + return sMsg + + def _tagAndPrepareTokenForRewriting (self, sWhat, nTokenRewriteStart, nTokenRewriteEnd, bUppercase=True): + "text processor: rewrite tokens between and position" + if sWhat == "*": + # purge text + if nTokenRewriteEnd - nTokenRewriteStart == 0: + self.lToken[nTokenRewriteStart]["bToRemove"] = True + else: + for i in range(nTokenRewriteStart, nTokenRewriteEnd+1): + self.lToken[i]["bToRemove"] = True + else: + if sWhat.startswith("="): + sWhat = globals()[sWhat[1:]](self.lToken) + bUppercase = bUppercase and self.lToken[nTokenRewriteStart]["sValue"][0:1].isupper() + if nTokenRewriteEnd - nTokenRewriteStart == 0: + sWhat = sWhat + " " * (len(self.lToken[nTokenRewriteStart]["sValue"])-len(sWhat)) + if bUppercase: + sWhat = sWhat[0:1].upper() + sWhat[1:] + self.lToken[nTokenRewriteStart]["sNewValue"] = sWhat + else: + lTokenValue = sWhat.split("|") + if len(lTokenValue) != (nTokenRewriteEnd - nTokenRewriteStart + 1): + print("Error. Text processor: number of replacements != number of tokens.") + return + for i, sValue in zip(range(nTokenRewriteStart, nTokenRewriteEnd+1), lTokenValue): + if bUppercase: + sValue = sValue[0:1].upper() + sValue[1:] + self.lToken[i]["sNewValue"] = sValue + + def rewrite (self): + "rewrite the sentence, modify tokens, purge the token list" + lNewToken = [] + for i, dToken in enumerate(self.lToken): + if "bToRemove" in dToken: + # remove useless token + self.sSentence = self.sSentence[:self.nOffset+dToken["nStart"]] + " " * (dToken["nEnd"] - dToken["nStart"]) + self.sSentence[self.nOffset+dToken["nEnd"]:] + #print("removed:", dToken["sValue"]) + else: + lNewToken.append(dToken) + if "sNewValue" in dToken: + # rewrite token and sentence + #print(dToken["sValue"], "->", dToken["sNewValue"]) + dToken["sRealValue"] = dToken["sValue"] + dToken["sValue"] = dToken["sNewValue"] + nDiffLen = len(dToken["sRealValue"]) - len(dToken["sNewValue"]) + sNewRepl = (dToken["sNewValue"] + " " * nDiffLen) if nDiffLen >= 0 else dToken["sNewValue"][:len(dToken["sRealValue"])] + self.sSentence = self.sSentence[:self.nOffset+dToken["nStart"]] + sNewRepl + self.sSentence[self.nOffset+dToken["nEnd"]:] + del dToken["sNewValue"] + self.lToken.clear() + self.lToken = lNewToken + + + +#### Analyse tokens + +def g_morph (dToken, sPattern, sNegPattern=""): + "analyse a token, return True if not in morphologies and in morphologies" + if "lMorph" in dToken: + lMorph = dToken["lMorph"] + else: + lMorph = _oSpellChecker.getMorph(dToken["sValue"]) + if not lMorph: + return False + # check negative condition + if sNegPattern: + if sNegPattern == "*": + # all morph must match sPattern + zPattern = re.compile(sPattern) + return all(zPattern.search(sMorph) for sMorph in lMorph) + else: + zNegPattern = re.compile(sNegPattern) + if any(zNegPattern.search(sMorph) for sMorph in lMorph): + return False + # search sPattern + zPattern = re.compile(sPattern) + return any(zPattern.search(sMorph) for sMorph in lMorph) + + +def g_analyse (dToken, sPattern, sNegPattern=""): + "analyse a token, return True if not in morphologies and in morphologies (disambiguation off)" + lMorph = _oSpellChecker.getMorph(dToken["sValue"]) + if not lMorph: + return False + # check negative condition + if sNegPattern: + if sNegPattern == "*": + zPattern = re.compile(sPattern) + return all(zPattern.search(sMorph) for sMorph in lMorph) + else: + zNegPattern = re.compile(sNegPattern) + if any(zNegPattern.search(sMorph) for sMorph in lMorph): + return False + # search sPattern + zPattern = re.compile(sPattern) + return any(zPattern.search(sMorph) for sMorph in lMorph) + + + +#### Disambiguator + +def g_select (dToken, sPattern, lDefault=None): + "select morphologies for according to , always return True" + lMorph = dToken["lMorph"] if "lMorph" in dToken else _oSpellChecker.getMorph(dToken["sValue"]) + if not lMorph or len(lMorph) == 1: + if lDefault: + dToken["lMorph"] = lDefault + #print("DA:", dToken["sValue"], dToken["lMorph"]) + return True + lSelect = [ sMorph for sMorph in lMorph if re.search(sPattern, sMorph) ] + if lSelect: + if len(lSelect) != len(lMorph): + dToken["lMorph"] = lSelect + elif lDefault: + dToken["lMorph"] = lDefault + #print("DA:", dToken["sValue"], dToken["lMorph"]) + return True + + +def g_exclude (dToken, sPattern, lDefault=None): + "select morphologies for according to , always return True" + lMorph = dToken["lMorph"] if "lMorph" in dToken else _oSpellChecker.getMorph(dToken["sValue"]) + if not lMorph or len(lMorph) == 1: + if lDefault: + dToken["lMorph"] = lDefault + #print("DA:", dToken["sValue"], dToken["lMorph"]) + return True + lSelect = [ sMorph for sMorph in lMorph if not re.search(sPattern, sMorph) ] + if lSelect: + if len(lSelect) != len(lMorph): + dToken["lMorph"] = lSelect + elif lDefault: + dToken["lMorph"] = lDefault + #print("DA:", dToken["sValue"], dToken["lMorph"]) + return True + + +def g_define (dToken, lMorph): + "set morphologies of , always return True" + dToken["lMorph"] = lMorph + #print("DA:", dToken["sValue"], lMorph) + return True + + +#### CALLABLES FOR REGEX RULES (generated code) + ${callables} + + +#### CALLABLES FOR GRAPH RULES (generated code) + +${graph_callables} ADDED gc_core/py/lang_core/gc_rules_graph.py Index: gc_core/py/lang_core/gc_rules_graph.py ================================================================== --- gc_core/py/lang_core/gc_rules_graph.py +++ gc_core/py/lang_core/gc_rules_graph.py @@ -0,0 +1,5 @@ +# generated code, do not edit + +dAllGraph = ${rules_graphs} + +dRule = ${rules_actions} ADDED gc_core/py/lang_core/gc_sentence.py Index: gc_core/py/lang_core/gc_sentence.py ================================================================== --- gc_core/py/lang_core/gc_sentence.py +++ gc_core/py/lang_core/gc_sentence.py @@ -0,0 +1,237 @@ +# Sentence checker + +from ..graphspell.tokenizer import Tokenizer +from .gc_rules_graph import dGraph + + +oTokenizer = Tokenizer("${lang}") + + +class TokenSentence: + + def __init__ (self, sSentence, sSentence0, nOffset): + self.sSentence = sSentence + self.sSentence0 = sSentence0 + self.nOffset = nOffset + self.lToken = list(oTokenizer.genTokens()) + + def parse (self): + dErr = {} + lPointer = [] + for dToken in self.lToken: + for i, dPointer in enumerate(lPointer): + bValid = False + for dNode in self._getNextMatchingNodes(dToken, dPointer["dNode"]): + dPointer["nOffset"] = dToken["i"] + dPointer["dNode"] = dNode + bValid = True + if not bValid: + del lPointer[i] + for dNode in self._getNextMatchingNodes(dToken, dGraph): + lPointer.append({"nOffset": 0, "dNode": dNode}) + for dPointer in lPointer: + if "" in dPointer["dNode"]: + for dNode in dGraph[dPointer["dNode"][""]]: + dErr = self._executeActions(dNode, nOffset) + return dErr + + def _getNextMatchingNodes (self, dToken, dNode): + # token value + if dToken["sValue"] in dNode: + yield dGraph[dNode[dToken["sValue"]]] + # token lemmas + for sLemma in dToken["lLemma"]: + if sLemma in dNode: + yield dGraph[dNode[sLemma]] + # universal arc + if "*" in dNode: + yield dGraph[dNode["*"]] + # regex arcs + if "~" in dNode: + for sRegex in dNode["~"]: + for sMorph in dToken["lMorph"]: + if re.search(sRegex, sMorph): + yield dGraph[dNode["~"][sRegex]] + + def _executeActions (self, dNode, nOffset): + for sLineId, nextNodeKey in dNode.items(): + for sArc in dGraph[nextNodeKey]: + bCondMemo = None + sFuncCond, cActionType, sWhat, *eAct = dRule[sArc] + # action in lActions: [ condition, action type, replacement/suggestion/action[, iGroupStart, iGroupEnd[, message, URL]] ] + try: + bCondMemo = not sFuncCond or globals()[sFuncCond](self, sCountry, bCondMemo) + if bCondMemo: + if cActionType == "-": + # grammar error + nErrorStart = nSentenceOffset + m.start(eAct[0]) + nErrorEnd = nSentenceOffset + m.start(eAct[1]) + if nErrorStart not in dErrs or nPriority > dPriority[nErrorStart]: + dErrs[nErrorStart] = _createError(self, sWhat, nErrorStart, nErrorEnd, sLineId, bUppercase, eAct[2], eAct[3], bIdRule, sOption, bContext) + dPriority[nErrorStart] = nPriority + elif cActionType == "~": + # text processor + self._rewrite(sWhat, nErrorStart, nErrorEnd) + elif cActionType == "@": + # jump + self._jump(sWhat) + elif cActionType == "=": + # disambiguation + globals()[sWhat](self.lToken) + elif cActionType == ">": + # we do nothing, this test is just a condition to apply all following actions + pass + else: + print("# error: unknown action at " + sLineId) + elif cActionType == ">": + break + except Exception as e: + raise Exception(str(e), "# " + sLineId + " # " + sRuleId) + + def _createWriterError (self): + d = {} + return d + + def _createDictError (self): + d = {} + return d + + def _rewrite (self, sWhat, nErrorStart, nErrorEnd): + "text processor: rewrite tokens between and position" + lTokenValue = sWhat.split("|") + if len(lTokenValue) != (nErrorEnd - nErrorStart + 1): + print("Error. Text processor: number of replacements != number of tokens.") + return + for i, sValue in zip(range(nErrorStart, nErrorEnd+1), lTokenValue): + self.lToken[i]["sValue"] = sValue + + def _jump (self, sWhat): + try: + nFrom, nTo = sWhat.split(">") + self.lToken[int(nFrom)]["iJump"] = int(nTo) + except: + print("# Error. Jump failed: ", sWhat) + traceback.print_exc() + return + + +#### Analyse tokens + +def g_morph (dToken, sPattern, bStrict=True): + "analyse a token, return True if in morphologies" + if "lMorph" in dToken: + lMorph = dToken["lMorph"] + else: + if dToken["sValue"] not in _dAnalyses and not _storeMorphFromFSA(dToken["sValue"]): + return False + if not _dAnalyses[dToken["sValue"]]: + return False + lMorph = _dAnalyses[dToken["sValue"]] + zPattern = re.compile(sPattern) + if bStrict: + return all(zPattern.search(sMorph) for sMorph in lMorph) + return any(zPattern.search(sMorph) for sMorph in lMorph) + +def g_morphex (dToken, sPattern, sNegPattern): + "analyse a token, return True if not in morphologies and in morphologies" + if "lMorph" in dToken: + lMorph = dToken["lMorph"] + else: + if dToken["sValue"] not in _dAnalyses and not _storeMorphFromFSA(dToken["sValue"]): + return False + if not _dAnalyses[dToken["sValue"]]: + return False + lMorph = _dAnalyses[dToken["sValue"]] + # check negative condition + zNegPattern = re.compile(sNegPattern) + if any(zNegPattern.search(sMorph) for sMorph in lMorph): + return False + # search sPattern + zPattern = re.compile(sPattern) + return any(zPattern.search(sMorph) for sMorph in lMorph) + +def g_analyse (dToken, sPattern, bStrict=True): + "analyse a token, return True if in morphologies (disambiguation off)" + if dToken["sValue"] not in _dAnalyses and not _storeMorphFromFSA(dToken["sValue"]): + return False + if not _dAnalyses[dToken["sValue"]]: + return False + zPattern = re.compile(sPattern) + if bStrict: + return all(zPattern.search(sMorph) for sMorph in _dAnalyses[dToken["sValue"]]) + return any(zPattern.search(sMorph) for sMorph in _dAnalyses[dToken["sValue"]]) + + +def g_analysex (dToken, sPattern, sNegPattern): + "analyse a token, return True if not in morphologies and in morphologies (disambiguation off)" + if dToken["sValue"] not in _dAnalyses and not _storeMorphFromFSA(dToken["sValue"]): + return False + if not _dAnalyses[dToken["sValue"]]: + return False + # check negative condition + zNegPattern = re.compile(sNegPattern) + if any(zNegPattern.search(sMorph) for sMorph in _dAnalyses[dToken["sValue"]]): + return False + # search sPattern + zPattern = re.compile(sPattern) + return any(zPattern.search(sMorph) for sMorph in _dAnalyses[dToken["sValue"]]) + + +#### Go outside the rule scope + +def g_nextToken (i): + pass + +def g_prevToken (i): + pass + +def g_look (): + pass + +def g_lookAndCheck (): + pass + + +#### Disambiguator + +def g_select (dToken, sPattern, lDefault=None): + "select morphologies for according to , always return True" + if dToken["sValue"] not in _dAnalyses and not _storeMorphFromFSA(dToken["sValue"]): + return True + if len(_dAnalyses[dToken["sValue"]]) == 1: + return True + lMorph = dToken["lMorph"] or _dAnalyses[dToken["sValue"]] + lSelect = [ sMorph for sMorph in lMorph if re.search(sPattern, sMorph) ] + if lSelect: + if len(lSelect) != len(lMorph): + dToken["lMorph"] = lSelect + elif lDefault: + dToken["lMorph"] = lDefault + return True + + +def g_exclude (dToken, sPattern, lDefault=None): + "select morphologies for according to , always return True" + if dToken["sValue"] not in _dAnalyses and not _storeMorphFromFSA(dToken["sValue"]): + return True + if len(_dAnalyses[dToken["sValue"]]) == 1: + return True + lMorph = dToken["lMorph"] or _dAnalyses[dToken["sValue"]] + lSelect = [ sMorph for sMorph in lMorph if not re.search(sPattern, sMorph) ] + if lSelect: + if len(lSelect) != len(lMorph): + dToken["lMorph"] = lSelect + elif lDefault: + dToken["lMorph"] = lDefault + return True + + +def g_define (dToken, lMorph): + "set morphologies of , always return True" + dToken["lMorph"] = lMorph + return True + + +#### CALLABLES (generated code) + +${graph_callables} Index: gc_lang/fr/modules-js/conj.js ================================================================== --- gc_lang/fr/modules-js/conj.js +++ gc_lang/fr/modules-js/conj.js @@ -85,11 +85,11 @@ getSimil: function (sWord, sMorph, bSubst=false) { if (!sMorph.includes(":V")) { return new Set(); } - let sInfi = sMorph.slice(1, sMorph.indexOf(" ")); + let sInfi = sMorph.slice(1, sMorph.indexOf("/")); let aSugg = new Set(); let tTags = this._getTags(sInfi); if (tTags) { if (!bSubst) { // we suggest conjugated forms Index: gc_lang/fr/modules-js/gce_analyseur.js ================================================================== --- gc_lang/fr/modules-js/gce_analyseur.js +++ gc_lang/fr/modules-js/gce_analyseur.js @@ -20,12 +20,11 @@ } if (s2 == "eux") { return "ils"; } if (s2 == "elle" || s2 == "elles") { - // We don’t check if word exists in _dAnalyses, for it is assumed it has been done before - if (cregex.mbNprMasNotFem(_dAnalyses.gl_get(s1, ""))) { + if (cregex.mbNprMasNotFem(_oSpellChecker.getMorph(s1))) { return "ils"; } // si épicène, indéterminable, mais OSEF, le féminin l’emporte return "elles"; } @@ -32,41 +31,40 @@ return s1 + " et " + s2; } function apposition (sWord1, sWord2) { // returns true if nom + nom (no agreement required) - // We don’t check if word exists in _dAnalyses, for it is assumed it has been done before - return cregex.mbNomNotAdj(_dAnalyses.gl_get(sWord2, "")) && cregex.mbPpasNomNotAdj(_dAnalyses.gl_get(sWord1, "")); + return cregex.mbNomNotAdj(_oSpellChecker.getMorph(sWord2)) && cregex.mbPpasNomNotAdj(_oSpellChecker.getMorph(sWord1)); } function isAmbiguousNAV (sWord) { // words which are nom|adj and verb are ambiguous (except être and avoir) - if (!_dAnalyses.has(sWord) && !_storeMorphFromFSA(sWord)) { + let lMorph = _oSpellChecker.getMorph(sWord); + if (lMorph.length === 0) { return false; } - if (!cregex.mbNomAdj(_dAnalyses.gl_get(sWord, "")) || sWord == "est") { + if (!cregex.mbNomAdj(lMorph) || sWord == "est") { return false; } - if (cregex.mbVconj(_dAnalyses.gl_get(sWord, "")) && !cregex.mbMG(_dAnalyses.gl_get(sWord, ""))) { + if (cregex.mbVconj(lMorph) && !cregex.mbMG(lMorph)) { return true; } return false; } function isAmbiguousAndWrong (sWord1, sWord2, sReqMorphNA, sReqMorphConj) { //// use it if sWord1 won’t be a verb; word2 is assumed to be true via isAmbiguousNAV - // We don’t check if word exists in _dAnalyses, for it is assumed it has been done before - let a2 = _dAnalyses.gl_get(sWord2, null); - if (!a2 || a2.length === 0) { + let a2 = _oSpellChecker.getMorph(sWord2); + if (a2.length === 0) { return false; } if (cregex.checkConjVerb(a2, sReqMorphConj)) { // verb word2 is ok return false; } - let a1 = _dAnalyses.gl_get(sWord1, null); - if (!a1 || a1.length === 0) { + let a1 = _oSpellChecker.getMorph(sWord1); + if (a1.length === 0) { return false; } if (cregex.checkAgreement(a1, a2) && (cregex.mbAdj(a2) || cregex.mbAdj(a1))) { return false; } @@ -73,21 +71,20 @@ return true; } function isVeryAmbiguousAndWrong (sWord1, sWord2, sReqMorphNA, sReqMorphConj, bLastHopeCond) { //// use it if sWord1 can be also a verb; word2 is assumed to be true via isAmbiguousNAV - // We don’t check if word exists in _dAnalyses, for it is assumed it has been done before - let a2 = _dAnalyses.gl_get(sWord2, null); - if (!a2 || a2.length === 0) { + let a2 = _oSpellChecker.getMorph(sWord2); + if (a2.length === 0) { return false; } if (cregex.checkConjVerb(a2, sReqMorphConj)) { // verb word2 is ok return false; } - let a1 = _dAnalyses.gl_get(sWord1, null); - if (!a1 || a1.length === 0) { + let a1 = _oSpellChecker.getMorph(sWord1); + if (a1.length === 0) { return false; } if (cregex.checkAgreement(a1, a2) && (cregex.mbAdj(a2) || cregex.mbAdjNb(a1))) { return false; } @@ -101,17 +98,16 @@ } return false; } function checkAgreement (sWord1, sWord2) { - // We don’t check if word exists in _dAnalyses, for it is assumed it has been done before - let a2 = _dAnalyses.gl_get(sWord2, null); - if (!a2 || a2.length === 0) { + let a2 = _oSpellChecker.getMorph(sWord2); + if (a2.length === 0) { return true; } - let a1 = _dAnalyses.gl_get(sWord1, null); - if (!a1 || a1.length === 0) { + let a1 = _oSpellChecker.getMorph(sWord1); + if (a1.length === 0) { return true; } return cregex.checkAgreement(a1, a2); } Index: gc_lang/fr/modules-js/gce_suggestions.js ================================================================== --- gc_lang/fr/modules-js/gce_suggestions.js +++ gc_lang/fr/modules-js/gce_suggestions.js @@ -10,20 +10,19 @@ //// verbs function suggVerb (sFlex, sWho, funcSugg2=null) { - // we don’t check if word exists in _dAnalyses, for it is assumed it has been done before let aSugg = new Set(); - for (let sStem of stem(sFlex)) { + for (let sStem of _oSpellChecker.getLemma(sFlex)) { let tTags = conj._getTags(sStem); if (tTags) { // we get the tense let aTense = new Set(); - for (let sMorph of _dAnalyses.gl_get(sFlex, [])) { + for (let sMorph of _oSpellChecker.getMorph(sFlex)) { let m; - let zVerb = new RegExp (">"+sStem+" .*?(:(?:Y|I[pqsf]|S[pq]|K))", "g"); + let zVerb = new RegExp (">"+sStem+"/.*?(:(?:Y|I[pqsf]|S[pq]|K))", "g"); while ((m = zVerb.exec(sMorph)) !== null) { // stem must be used in regex to prevent confusion between different verbs (e.g. sauras has 2 stems: savoir and saurer) if (m) { if (m[1] === ":Y") { aTense.add(":Ip"); @@ -59,11 +58,11 @@ return ""; } function suggVerbPpas (sFlex, sWhat=null) { let aSugg = new Set(); - for (let sStem of stem(sFlex)) { + for (let sStem of _oSpellChecker.getLemma(sFlex)) { let tTags = conj._getTags(sStem); if (tTags) { if (!sWhat) { aSugg.add(conj._getConjWithTags(sStem, tTags, ":PQ", ":Q1")); aSugg.add(conj._getConjWithTags(sStem, tTags, ":PQ", ":Q2")); @@ -109,11 +108,11 @@ return ""; } function suggVerbTense (sFlex, sTense, sWho) { let aSugg = new Set(); - for (let sStem of stem(sFlex)) { + for (let sStem of _oSpellChecker.getLemma(sFlex)) { if (conj.hasConj(sStem, sTense, sWho)) { aSugg.add(conj.getConj(sStem, sTense, sWho)); } } if (aSugg.size > 0) { @@ -122,11 +121,11 @@ return ""; } function suggVerbImpe (sFlex) { let aSugg = new Set(); - for (let sStem of stem(sFlex)) { + for (let sStem of _oSpellChecker.getLemma(sFlex)) { let tTags = conj._getTags(sStem); if (tTags) { if (conj._hasConjWithTags(tTags, ":E", ":2s")) { aSugg.add(conj._getConjWithTags(sStem, tTags, ":E", ":2s")); } @@ -143,11 +142,11 @@ } return ""; } function suggVerbInfi (sFlex) { - return stem(sFlex).filter(sStem => conj.isVerb(sStem)).join("|"); + return _oSpellChecker.getLemma(sFlex).filter(sStem => conj.isVerb(sStem)).join("|"); } const _dQuiEst = new Map ([ ["je", ":1s"], ["j’", ":1s"], ["j’en", ":1s"], ["j’y", ":1s"], @@ -174,11 +173,11 @@ return ""; } sWho = ":3s"; } let aSugg = new Set(); - for (let sStem of stem(sFlex)) { + for (let sStem of _oSpellChecker.getLemma(sFlex)) { let tTags = conj._getTags(sStem); if (tTags) { for (let sTense of lMode) { if (conj._hasConjWithTags(tTags, sTense, sWho)) { aSugg.add(conj._getConjWithTags(sStem, tTags, sTense, sWho)); @@ -195,14 +194,15 @@ //// Nouns and adjectives function suggPlur (sFlex, sWordToAgree=null) { // returns plural forms assuming sFlex is singular if (sWordToAgree) { - if (!_dAnalyses.has(sWordToAgree) && !_storeMorphFromFSA(sWordToAgree)) { + let lMorph = _oSpellChecker.getMorph(sWordToAgree); + if (lMorph.length === 0) { return ""; } - let sGender = cregex.getGender(_dAnalyses.gl_get(sWordToAgree, [])); + let sGender = cregex.getGender(lMorph); if (sGender == ":m") { return suggMasPlur(sFlex); } else if (sGender == ":f") { return suggFemPlur(sFlex); } @@ -256,13 +256,12 @@ return ""; } function suggMasSing (sFlex, bSuggSimil=false) { // returns masculine singular forms - // we don’t check if word exists in _dAnalyses, for it is assumed it has been done before let aSugg = new Set(); - for (let sMorph of _dAnalyses.gl_get(sFlex, [])) { + for (let sMorph of _oSpellChecker.getMorph(sFlex)) { if (!sMorph.includes(":V")) { // not a verb if (sMorph.includes(":m") || sMorph.includes(":e")) { aSugg.add(suggSing(sFlex)); } else { @@ -292,13 +291,12 @@ return ""; } function suggMasPlur (sFlex, bSuggSimil=false) { // returns masculine plural forms - // we don’t check if word exists in _dAnalyses, for it is assumed it has been done before let aSugg = new Set(); - for (let sMorph of _dAnalyses.gl_get(sFlex, [])) { + for (let sMorph of _oSpellChecker.getMorph(sFlex)) { if (!sMorph.includes(":V")) { // not a verb if (sMorph.includes(":m") || sMorph.includes(":e")) { aSugg.add(suggPlur(sFlex)); } else { @@ -333,13 +331,12 @@ } function suggFemSing (sFlex, bSuggSimil=false) { // returns feminine singular forms - // we don’t check if word exists in _dAnalyses, for it is assumed it has been done before let aSugg = new Set(); - for (let sMorph of _dAnalyses.gl_get(sFlex, [])) { + for (let sMorph of _oSpellChecker.getMorph(sFlex)) { if (!sMorph.includes(":V")) { // not a verb if (sMorph.includes(":f") || sMorph.includes(":e")) { aSugg.add(suggSing(sFlex)); } else { @@ -367,13 +364,12 @@ return ""; } function suggFemPlur (sFlex, bSuggSimil=false) { // returns feminine plural forms - // we don’t check if word exists in _dAnalyses, for it is assumed it has been done before let aSugg = new Set(); - for (let sMorph of _dAnalyses.gl_get(sFlex, [])) { + for (let sMorph of _oSpellChecker.getMorph(sFlex)) { if (!sMorph.includes(":V")) { // not a verb if (sMorph.includes(":f") || sMorph.includes(":e")) { aSugg.add(suggPlur(sFlex)); } else { @@ -400,11 +396,11 @@ } return ""; } function hasFemForm (sFlex) { - for (let sStem of stem(sFlex)) { + for (let sStem of _oSpellChecker.getLemma(sFlex)) { if (mfsp.isFemForm(sStem) || conj.hasConj(sStem, ":PQ", ":Q3")) { return true; } } if (phonet.hasSimil(sFlex, ":f")) { @@ -412,11 +408,11 @@ } return false; } function hasMasForm (sFlex) { - for (let sStem of stem(sFlex)) { + for (let sStem of _oSpellChecker.getLemma(sFlex)) { if (mfsp.isFemForm(sStem) || conj.hasConj(sStem, ":PQ", ":Q1")) { // what has a feminine form also has a masculine form return true; } } @@ -425,14 +421,13 @@ } return false; } function switchGender (sFlex, bPlur=null) { - // we don’t check if word exists in _dAnalyses, for it is assumed it has been done before let aSugg = new Set(); if (bPlur === null) { - for (let sMorph of _dAnalyses.gl_get(sFlex, [])) { + for (let sMorph of _oSpellChecker.getMorph(sFlex)) { if (sMorph.includes(":f")) { if (sMorph.includes(":s")) { aSugg.add(suggMasSing(sFlex)); } else if (sMorph.includes(":p")) { aSugg.add(suggMasPlur(sFlex)); @@ -447,19 +442,19 @@ aSugg.add(suggFemPlur(sFlex)); } } } } else if (bPlur) { - for (let sMorph of _dAnalyses.gl_get(sFlex, [])) { + for (let sMorph of _oSpellChecker.getMorph(sFlex)) { if (sMorph.includes(":f")) { aSugg.add(suggMasPlur(sFlex)); } else if (sMorph.includes(":m")) { aSugg.add(suggFemPlur(sFlex)); } } } else { - for (let sMorph of _dAnalyses.gl_get(sFlex, [])) { + for (let sMorph of _oSpellChecker.getMorph(sFlex)) { if (sMorph.includes(":f")) { aSugg.add(suggMasSing(sFlex)); } else if (sMorph.includes(":m")) { aSugg.add(suggFemSing(sFlex)); } @@ -471,11 +466,11 @@ return ""; } function switchPlural (sFlex) { let aSugg = new Set(); - for (let sMorph of _dAnalyses.gl_get(sFlex, [])) { // we don’t check if word exists in _dAnalyses, for it is assumed it has been done before + for (let sMorph of _oSpellChecker.getMorph(sFlex)) { if (sMorph.includes(":s")) { aSugg.add(suggPlur(sFlex)); } else if (sMorph.includes(":p")) { aSugg.add(suggSing(sFlex)); } @@ -491,11 +486,11 @@ } function suggSimil (sWord, sPattern=null, bSubst=false) { // return list of words phonetically similar to sWord and whom POS is matching sPattern let aSugg = phonet.selectSimil(sWord, sPattern); - for (let sMorph of _dAnalyses.gl_get(sWord, [])) { + for (let sMorph of _oSpellChecker.getMorph(sWord)) { for (let e of conj.getSimil(sWord, sMorph, bSubst)) { aSugg.add(e); } } if (aSugg.size > 0) { @@ -513,12 +508,11 @@ } return "ce"; } function suggLesLa (sWord) { - // we don’t check if word exists in _dAnalyses, for it is assumed it has been done before - if (_dAnalyses.gl_get(sWord, []).some(s => s.includes(":p"))) { + if (_oSpellChecker.getMorph(sWord).some(s => s.includes(":p"))) { return "les|la"; } return "la"; } Index: gc_lang/fr/modules-js/tests_data.json ================================================================== --- gc_lang/fr/modules-js/tests_data.json +++ gc_lang/fr/modules-js/tests_data.json @@ -1,1 +1,1 @@ -${gctestsJS} +${regex_gctestsJS} Index: gc_lang/fr/modules/conj.py ================================================================== --- gc_lang/fr/modules/conj.py +++ gc_lang/fr/modules/conj.py @@ -56,11 +56,11 @@ def getSimil (sWord, sMorph, bSubst=False): if ":V" not in sMorph: return set() - sInfi = sMorph[1:sMorph.find(" ")] + sInfi = sMorph[1:sMorph.find("/")] aSugg = set() tTags = _getTags(sInfi) if tTags: if not bSubst: # we suggest conjugated forms Index: gc_lang/fr/modules/gce_analyseur.py ================================================================== --- gc_lang/fr/modules/gce_analyseur.py +++ gc_lang/fr/modules/gce_analyseur.py @@ -15,63 +15,58 @@ return "nous" if s2 == "vous": return "vous" if s2 == "eux": return "ils" - if s2 == "elle" or s2 == "elles": - # We don’t check if word exists in _dAnalyses, for it is assumed it has been done before - if cr.mbNprMasNotFem(_dAnalyses.get(s1, False)): + if s2 == "elle" or s2 == "elles": + if cr.mbNprMasNotFem(_oSpellChecker.getMorph(s1)): return "ils" # si épicène, indéterminable, mais OSEF, le féminin l’emporte return "elles" return s1 + " et " + s2 def apposition (sWord1, sWord2): "returns True if nom + nom (no agreement required)" - # We don’t check if word exists in _dAnalyses, for it is assumed it has been done before - return cr.mbNomNotAdj(_dAnalyses.get(sWord2, False)) and cr.mbPpasNomNotAdj(_dAnalyses.get(sWord1, False)) + return cr.mbNomNotAdj(_oSpellChecker.getMorph(sWord2)) and cr.mbPpasNomNotAdj(_oSpellChecker.getMorph(sWord1)) def isAmbiguousNAV (sWord): "words which are nom|adj and verb are ambiguous (except être and avoir)" - if sWord not in _dAnalyses and not _storeMorphFromFSA(sWord): + lMorph = _oSpellChecker.getMorph(sWord) + if not cr.mbNomAdj(lMorph) or sWord == "est": return False - if not cr.mbNomAdj(_dAnalyses[sWord]) or sWord == "est": - return False - if cr.mbVconj(_dAnalyses[sWord]) and not cr.mbMG(_dAnalyses[sWord]): + if cr.mbVconj(lMorph) and not cr.mbMG(lMorph): return True return False def isAmbiguousAndWrong (sWord1, sWord2, sReqMorphNA, sReqMorphConj): "use it if sWord1 won’t be a verb; word2 is assumed to be True via isAmbiguousNAV" - # We don’t check if word exists in _dAnalyses, for it is assumed it has been done before - a2 = _dAnalyses.get(sWord2, None) + a2 = _oSpellChecker.getMorph(sWord2) if not a2: return False if cr.checkConjVerb(a2, sReqMorphConj): # verb word2 is ok return False - a1 = _dAnalyses.get(sWord1, None) + a1 = _oSpellChecker.getMorph(sWord1) if not a1: return False if cr.checkAgreement(a1, a2) and (cr.mbAdj(a2) or cr.mbAdj(a1)): return False return True def isVeryAmbiguousAndWrong (sWord1, sWord2, sReqMorphNA, sReqMorphConj, bLastHopeCond): "use it if sWord1 can be also a verb; word2 is assumed to be True via isAmbiguousNAV" - # We don’t check if word exists in _dAnalyses, for it is assumed it has been done before - a2 = _dAnalyses.get(sWord2, None) + a2 = _oSpellChecker.getMorph(sWord2) if not a2: return False if cr.checkConjVerb(a2, sReqMorphConj): # verb word2 is ok return False - a1 = _dAnalyses.get(sWord1, None) + a1 = _oSpellChecker.getMorph(sWord1) if not a1: return False if cr.checkAgreement(a1, a2) and (cr.mbAdj(a2) or cr.mbAdjNb(a1)): return False # now, we know there no agreement, and conjugation is also wrong @@ -82,15 +77,14 @@ return True return False def checkAgreement (sWord1, sWord2): - # We don’t check if word exists in _dAnalyses, for it is assumed it has been done before - a2 = _dAnalyses.get(sWord2, None) + a2 = _oSpellChecker.getMorph(sWord2) if not a2: return True - a1 = _dAnalyses.get(sWord1, None) + a1 = _oSpellChecker.getMorph(sWord1) if not a1: return True return cr.checkAgreement(a1, a2) Index: gc_lang/fr/modules/gce_suggestions.py ================================================================== --- gc_lang/fr/modules/gce_suggestions.py +++ gc_lang/fr/modules/gce_suggestions.py @@ -7,17 +7,17 @@ ## Verbs def suggVerb (sFlex, sWho, funcSugg2=None): aSugg = set() - for sStem in stem(sFlex): + for sStem in _oSpellChecker.getLemma(sFlex): tTags = conj._getTags(sStem) if tTags: # we get the tense aTense = set() - for sMorph in _dAnalyses.get(sFlex, []): # we don’t check if word exists in _dAnalyses, for it is assumed it has been done before - for m in re.finditer(">"+sStem+" .*?(:(?:Y|I[pqsf]|S[pq]|K|P))", sMorph): + for sMorph in _oSpellChecker.getMorph(sFlex): + for m in re.finditer(">"+sStem+"/.*?(:(?:Y|I[pqsf]|S[pq]|K|P))", sMorph): # stem must be used in regex to prevent confusion between different verbs (e.g. sauras has 2 stems: savoir and saurer) if m: if m.group(1) == ":Y": aTense.add(":Ip") aTense.add(":Iq") @@ -40,11 +40,11 @@ return "" def suggVerbPpas (sFlex, sWhat=None): aSugg = set() - for sStem in stem(sFlex): + for sStem in _oSpellChecker.getLemma(sFlex): tTags = conj._getTags(sStem) if tTags: if not sWhat: aSugg.add(conj._getConjWithTags(sStem, tTags, ":PQ", ":Q1")) aSugg.add(conj._getConjWithTags(sStem, tTags, ":PQ", ":Q2")) @@ -83,21 +83,21 @@ return "" def suggVerbTense (sFlex, sTense, sWho): aSugg = set() - for sStem in stem(sFlex): + for sStem in _oSpellChecker.getLemma(sFlex): if conj.hasConj(sStem, sTense, sWho): aSugg.add(conj.getConj(sStem, sTense, sWho)) if aSugg: return "|".join(aSugg) return "" def suggVerbImpe (sFlex): aSugg = set() - for sStem in stem(sFlex): + for sStem in _oSpellChecker.getLemma(sFlex): tTags = conj._getTags(sStem) if tTags: if conj._hasConjWithTags(tTags, ":E", ":2s"): aSugg.add(conj._getConjWithTags(sStem, tTags, ":E", ":2s")) if conj._hasConjWithTags(tTags, ":E", ":1p"): @@ -108,11 +108,11 @@ return "|".join(aSugg) return "" def suggVerbInfi (sFlex): - return "|".join([ sStem for sStem in stem(sFlex) if conj.isVerb(sStem) ]) + return "|".join([ sStem for sStem in _oSpellChecker.getLemma(sFlex) if conj.isVerb(sStem) ]) _dQuiEst = { "je": ":1s", "j’": ":1s", "j’en": ":1s", "j’y": ":1s", \ "tu": ":2s", "il": ":3s", "on": ":3s", "elle": ":3s", "nous": ":1p", "vous": ":2p", "ils": ":3p", "elles": ":3p" } _lIndicatif = [":Ip", ":Iq", ":Is", ":If"] @@ -131,11 +131,11 @@ if not sWho: if sSuj[0:1].islower(): # pas un pronom, ni un nom propre return "" sWho = ":3s" aSugg = set() - for sStem in stem(sFlex): + for sStem in _oSpellChecker.getLemma(sFlex): tTags = conj._getTags(sStem) if tTags: for sTense in lMode: if conj._hasConjWithTags(tTags, sTense, sWho): aSugg.add(conj._getConjWithTags(sStem, tTags, sTense, sWho)) @@ -147,13 +147,14 @@ ## Nouns and adjectives def suggPlur (sFlex, sWordToAgree=None): "returns plural forms assuming sFlex is singular" if sWordToAgree: - if sWordToAgree not in _dAnalyses and not _storeMorphFromFSA(sWordToAgree): + lMorph = _oSpellChecker.getMorph(sFlex) + if not lMorph: return "" - sGender = cr.getGender(_dAnalyses.get(sWordToAgree, [])) + sGender = cr.getGender(lMorph) if sGender == ":m": return suggMasPlur(sFlex) elif sGender == ":f": return suggFemPlur(sFlex) aSugg = set() @@ -191,13 +192,12 @@ return "" def suggMasSing (sFlex, bSuggSimil=False): "returns masculine singular forms" - # we don’t check if word exists in _dAnalyses, for it is assumed it has been done before aSugg = set() - for sMorph in _dAnalyses.get(sFlex, []): + for sMorph in _oSpellChecker.getMorph(sFlex): if not ":V" in sMorph: # not a verb if ":m" in sMorph or ":e" in sMorph: aSugg.add(suggSing(sFlex)) else: @@ -219,13 +219,12 @@ return "" def suggMasPlur (sFlex, bSuggSimil=False): "returns masculine plural forms" - # we don’t check if word exists in _dAnalyses, for it is assumed it has been done before aSugg = set() - for sMorph in _dAnalyses.get(sFlex, []): + for sMorph in _oSpellChecker.getMorph(sFlex): if not ":V" in sMorph: # not a verb if ":m" in sMorph or ":e" in sMorph: aSugg.add(suggPlur(sFlex)) else: @@ -250,13 +249,12 @@ return "" def suggFemSing (sFlex, bSuggSimil=False): "returns feminine singular forms" - # we don’t check if word exists in _dAnalyses, for it is assumed it has been done before aSugg = set() - for sMorph in _dAnalyses.get(sFlex, []): + for sMorph in _oSpellChecker.getMorph(sFlex): if not ":V" in sMorph: # not a verb if ":f" in sMorph or ":e" in sMorph: aSugg.add(suggSing(sFlex)) else: @@ -276,13 +274,12 @@ return "" def suggFemPlur (sFlex, bSuggSimil=False): "returns feminine plural forms" - # we don’t check if word exists in _dAnalyses, for it is assumed it has been done before aSugg = set() - for sMorph in _dAnalyses.get(sFlex, []): + for sMorph in _oSpellChecker.getMorph(sFlex): if not ":V" in sMorph: # not a verb if ":f" in sMorph or ":e" in sMorph: aSugg.add(suggPlur(sFlex)) else: @@ -301,33 +298,32 @@ return "|".join(aSugg) return "" def hasFemForm (sFlex): - for sStem in stem(sFlex): + for sStem in _oSpellChecker.getLemma(sFlex): if mfsp.isFemForm(sStem) or conj.hasConj(sStem, ":PQ", ":Q3"): return True if phonet.hasSimil(sFlex, ":f"): return True return False def hasMasForm (sFlex): - for sStem in stem(sFlex): + for sStem in _oSpellChecker.getLemma(sFlex): if mfsp.isFemForm(sStem) or conj.hasConj(sStem, ":PQ", ":Q1"): # what has a feminine form also has a masculine form return True if phonet.hasSimil(sFlex, ":m"): return True return False def switchGender (sFlex, bPlur=None): - # we don’t check if word exists in _dAnalyses, for it is assumed it has been done before aSugg = set() if bPlur == None: - for sMorph in _dAnalyses.get(sFlex, []): + for sMorph in _oSpellChecker.getMorph(sFlex): if ":f" in sMorph: if ":s" in sMorph: aSugg.add(suggMasSing(sFlex)) elif ":p" in sMorph: aSugg.add(suggMasPlur(sFlex)) @@ -338,17 +334,17 @@ aSugg.add(suggFemPlur(sFlex)) else: aSugg.add(suggFemSing(sFlex)) aSugg.add(suggFemPlur(sFlex)) elif bPlur: - for sMorph in _dAnalyses.get(sFlex, []): + for sMorph in _oSpellChecker.getMorph(sFlex): if ":f" in sMorph: aSugg.add(suggMasPlur(sFlex)) elif ":m" in sMorph: aSugg.add(suggFemPlur(sFlex)) else: - for sMorph in _dAnalyses.get(sFlex, []): + for sMorph in _oSpellChecker.getMorph(sFlex): if ":f" in sMorph: aSugg.add(suggMasSing(sFlex)) elif ":m" in sMorph: aSugg.add(suggFemSing(sFlex)) if aSugg: @@ -355,13 +351,12 @@ return "|".join(aSugg) return "" def switchPlural (sFlex): - # we don’t check if word exists in _dAnalyses, for it is assumed it has been done before aSugg = set() - for sMorph in _dAnalyses.get(sFlex, []): + for sMorph in _oSpellChecker.getMorph(sFlex): if ":s" in sMorph: aSugg.add(suggPlur(sFlex)) elif ":p" in sMorph: aSugg.add(suggSing(sFlex)) if aSugg: @@ -373,13 +368,12 @@ return phonet.hasSimil(sWord, sPattern) def suggSimil (sWord, sPattern=None, bSubst=False): "return list of words phonetically similar to sWord and whom POS is matching sPattern" - # we don’t check if word exists in _dAnalyses, for it is assumed it has been done before aSugg = phonet.selectSimil(sWord, sPattern) - for sMorph in _dAnalyses.get(sWord, []): + for sMorph in _oSpellChecker.getMorph(sWord): aSugg.update(conj.getSimil(sWord, sMorph, bSubst)) break if aSugg: return "|".join(aSugg) return "" @@ -392,12 +386,11 @@ return "ce|cet" return "ce" def suggLesLa (sWord): - # we don’t check if word exists in _dAnalyses, for it is assumed it has been done before - if any( ":p" in sMorph for sMorph in _dAnalyses.get(sWord, []) ): + if any( ":p" in sMorph for sMorph in _oSpellChecker.getMorph(sWord) ): return "les|la" return "la" _zBinary = re.compile("^[01]+$") Index: gc_lang/fr/rules.grx ================================================================== --- gc_lang/fr/rules.grx +++ gc_lang/fr/rules.grx @@ -682,11 +682,11 @@ ({w_1})( car)(?= (?:j[e’]|tu|ils?|nous|vous|elles?|on|les?|l[a’]|ces?|des?|cette|[mts](?:on|a|es))\b) @@0,$ <<- not morph(\1, ":[DR]", False) -2>> , car # Si « car » est la conjonction de coordination, une virgule est peut-être souhaitable.|http://bdl.oqlf.gouv.qc.ca/bdl/gabarit_bdl.asp?id=3447 __[i>/virg(virgule_manquante_avant_mais)__ ({w_1})( mais)(?= (?:j[e’]|tu|ils?|nous|vous|elles?|on)\b) @@0,$ - <<- not morph(\1, ">(?:[mtscl]es|[nv]os|quels) ", False) -2>> , mais + <<- not morph(\1, ">(?:[mtscl]es|[nv]os|quels)/", False) -2>> , mais # Si « mais » est la conjonction de coordination, une virgule est souhaitable si elle introduit une nouvelle proposition.|http://bdl.oqlf.gouv.qc.ca/bdl/gabarit_bdl.asp?id=3445 __[i>/virg(virgule_manquante_avant_donc)__ ({w_1})( donc)(?= (?:j[e’]|tu|ils?|elles?|on)\b) @@0,$ <<- not morph(\1, ":V", False) -2>> , donc # Si « mais » est la conjonction de coordination, une virgule est souhaitable si elle introduit une nouvelle proposition.|http://bdl.oqlf.gouv.qc.ca/bdl/gabarit_bdl.asp?id=3448 @@ -1232,11 +1232,11 @@ !!!! Redondances !! !! __[i]/redon1(redondances_paragraphe)__ ({w_4})[  ,.;!?:].*[  ](\1) @@0,$ - <<- not morph(\1, ":(?:G|V0)|>(?:t(?:antôt|emps|rès)|loin|souvent|parfois|quelquefois|côte|petit|même) ", False) and not \1[0].isupper() + <<- not morph(\1, ":(?:G|V0)|>(?:t(?:antôt|emps|rès)|loin|souvent|parfois|quelquefois|côte|petit|même)/", False) and not \1[0].isupper() -2>> _ # Dans ce paragraphe, répétition de « \1 » (à gauche). <<- __also__ -1>> _ # Dans ce paragraphe, répétition de « \1 » (à droite). TEST: __redon1__ Tu es son {{avenir}}. Et lui aussi est ton {{avenir}}. TEST: __redon1__ Car parfois il y en a. Mais parfois il n’y en a pas. @@ -1751,11 +1751,11 @@ # est-ce … ? __[i]/tu(tu_est_ce)__ (?qui ") and morph(word(-1), ":Cs", False, True) + <<- morphex(\2, ":", ":N.*:[me]:[si]|>qui/") and morph(word(-1), ":Cs", False, True) -1>> est-ce # S’il s’agit d’une interrogation, il manque un trait d’union. TEST: {{est ce}} que c’est grave ? ->> est-ce TEST: qu’{{est ce}} que c’est ? ->> est-ce TEST: elles reviendront, {{n’est ce pas}} ? @@ -1890,11 +1890,11 @@ TEST: Peu {{d’entre-nous}} savent ce dont il s’agit. __[i]/tu(tu_y_attaché)__ (y[’-])({avoir_etre})(?:-(?:t-|)(?:ils?|elles?|je|tu|on|nous|vous)|) @@0,2 - <<- morph(\2, ":V0|>en ", False) -1>> "y " # Ici, ni apostrophe, ni trait d’union. + <<- morph(\2, ":V0|>en/", False) -1>> "y " # Ici, ni apostrophe, ni trait d’union. TEST: {{Y’}}a trop de malheureux sur Terre. TEST: {{Y’}}en a marre, de ces conneries. TEST: {{y-}}a-t-il des beignets ? ->> "y " @@ -1933,11 +1933,11 @@ TEST: Tape-toi Patrick. __[u]/virg(virgule_après_verbe_COD)__ l(?:es?|a) ({w_2}(?:[ei]r|re)) ([A-ZÉÂÔÈ][\w-]+) @@w,$ - <<- morph(\1, ":Y", False) and morph(\2, ":M", False) and not morph(word(-1), ">à ", False, False) + <<- morph(\1, ":Y", False) and morph(\2, ":M", False) and not morph(word(-1), ">à/", False, False) -1>> \1, # Une virgule est probablement souhaitable. TEST: Tu vas les {{donner}} Rachel. TEST: Il va la {{tuer}} Paul. TEST: Cependant les promesses n’engagent que ceux qui les croient, comme aimait à le dire Jacques Chirac. @@ -1956,11 +1956,11 @@ !!!! A / À: accentuation la préposition en début de phrase __(?:priori|post[eé]riori|contrario|capella|fortiori) ") + <<- morphex(\2, ":[GNAY]", ":(?:Q|3s)|>(?:priori|post[eé]riori|contrario|capella|fortiori)/") or (\2 == "bientôt" and isEnd()) -1>> À # S’il s’agit de la préposition « à », il faut accentuer la majuscule. __/typo(typo_À_début_phrase2)__ ^ *(A) [ldnms]’ @@* <<- -1>> À # S’il s’agit de la préposition « à », il faut accentuer la majuscule. __/typo(typo_À_début_phrase3)__ @@ -1994,15 +1994,15 @@ !!! # mots grammaticaux __[i](d_dans)__ dans - <<- not morph(word(-1), ":D.*:p|>[a-z]+ièmes ", False, False) =>> select(\0, ":R") + <<- not morph(word(-1), ":D.*:p|>[a-z]+ièmes/", False, False) =>> select(\0, ":R") __[i](d_ton_son)__ (\w+) ([ts]on) @@0,$ - <<- morph(\1, ">(?:le|ce[st]?|ton|mon|son|quel(?:que|)s?|[nv]otre|un|leur|ledit|dudit) ") =>> exclude(\2, ":D") + <<- morph(\1, ">(?:le|ce[st]?|ton|mon|son|quel(?:que|)s?|[nv]otre|un|leur|ledit|dudit)/") =>> exclude(\2, ":D") # Pronoms le/la/les __[i](d_je_le_la_les)__ je (l(?:e(?:ur|s|)|a)) @@$ <<- not morph(word(-1), ":1s", False, False) =>> select(\1, ":Oo") __[i](d_tu_le_la_les)__ @@ -2044,11 +2044,11 @@ <<- morph(word(-1), ":Cs", False, True) and not morph(\1, ":(?:Oo|X)", False) =>> select(\1, ":[123][sp]") __[s](d_nom_propre_verbe)__ ([A-ZÉÈ]{w_1}) +({w_1}) @@0,$ <<- morph(\1, ":M") and \2.islower() and morphex(\2, ":[123][sg]", ":Q") and morph(\2, ":N", False) and morph(word(-1), ":Cs", False, True) =>> select(\2, ":[123][sp]") - <<- morph(\1, ":M", False) and morphex(\2, ":[123]s|>(?:[nmts]e|nous|vous) ", ":A") and isStart() =>> =select(\1, ":M") + <<- morph(\1, ":M", False) and morphex(\2, ":[123]s|>(?:[nmts]e|nous|vous)/", ":A") and isStart() =>> =select(\1, ":M") __[i](d_que_combien_pourquoi_en_y_verbe)__ (?:que?|combien|pourquoi) +(?:en +|y +|)({w_3}) @@$ <<- =>> exclude(\1, ":E") # groupe nominal @@ -2196,11 +2196,11 @@ TEST: __ocr__ on poirautait, {{cotte}} mariée n’arrivait pas à se décider. # Comme / Gomme -__[s]/ocr(ocr_comme)__ Gomme <<- not morph(word(1), ">(?:et|o[uù]) ") ->> Comme # Erreur de numérisation ? +__[s]/ocr(ocr_comme)__ Gomme <<- not morph(word(1), ">(?:et|o[uù])/") ->> Comme # Erreur de numérisation ? TEST: __ocr__ {{Gomme}} il était sage à cette époque-là ! # Comment / Gomment @@ -2459,11 +2459,11 @@ # Mais / Hais / Mats / niais __[u]/ocr(ocr_mais1)__ Hais <<- ->> Mais # Erreur de numérisation ? __[i]/ocr(ocr_mais2)__ mats <<- not morph(word(-1), ":D:[me]:p", False, False) ->> mais # Erreur de numérisation ? __[i]/ocr(ocr_mais3)__ maïs <<- not morph(word(-1), ":D:(?:m:s|e:p)", False, False) ->> mais # Erreur de numérisation ? __[s]/ocr(ocr_mais4)__ - niais <<- not morph(word(-1), ">(?:homme|ce|quel|être) ", False, False) ->> mais # Erreur de numérisation ? + niais <<- not morph(word(-1), ">(?:homme|ce|quel|être)/", False, False) ->> mais # Erreur de numérisation ? TEST: __ocr__ {{Hais}} il en sait trop. TEST: __ocr__ c’était bien, {{mats}} quelle journée TEST: __ocr__ c’est bien, {{niais}} trop subtil. TEST: __ocr__ c’est parfait, {{maïs}} trop subtil. @@ -2687,11 +2687,11 @@ ## Casse __[s]/ocr(ocr_casse1)__ [A-ZÉÈÂÊÎÔ]{w_1} <<- \0.istitle() and before(r"(?i)\w") >>> <<- morphex(\0, ":G", ":M") ->> =\0.lower() # Erreur de numérisation ? Casse improbable. - <<- __else__ and morphex(\0, ":[123][sp]", ":[MNA]|>Est ") ->> =\0.lower() # Erreur de numérisation ? Casse improbable. + <<- __else__ and morphex(\0, ":[123][sp]", ":[MNA]|>Est/") ->> =\0.lower() # Erreur de numérisation ? Casse improbable. TEST: __ocr__ votre ami la regarde, {{Vous}} ne l’avez pas achetée TEST: __ocr__ pour accommoder son regard, {{La}} lourde forme demeure TEST: __ocr__ parler de Nicole, {{Le}} sommeil ne vient pas. TEST: __ocr__ a fait de toi, Charles, {{Tu}} étais beau quand @@ -2772,13 +2772,13 @@ __[s](incohérence_globale_au_qqch)__ ([aA]u) ({w2}) @@0,$ <<- not \2.isupper() >>> - <<- morph(\2, ">(?:[cdlmst]es|[nv]os|cettes?|[mts]a|mon|je|tu|ils?|elle?|[vn]ous|on|parce) ", False) + <<- morph(\2, ">(?:[cdlmst]es|[nv]os|cettes?|[mts]a|mon|je|tu|ils?|elle?|[vn]ous|on|parce)/", False) -2>> =suggSimil(\2, ":[NA].*:[si]", True) # Incohérence : les mots “\1” et “\2” ne devraient pas se succéder. - <<- __else__ and morph(\2, ">quelle ", False) ->> auquel|auxquels|auxquelles # Incohérence. Soudez les deux mots.|https://fr.wiktionary.org/wiki/auquel + <<- __else__ and morph(\2, ">quelle/", False) ->> auquel|auxquels|auxquelles # Incohérence. Soudez les deux mots.|https://fr.wiktionary.org/wiki/auquel <<- __else__ and \2 == "combien" and morph(word(1), ":[AY]", False) -1>> ô # Incohérence probable.|https://fr.wiktionary.org/wiki/%C3%B4_combien TEST: au {{nos}} enfants. TEST: {{Au quel}} faut-il s’adresser ? TEST: Au MES, rien de nouveau. @@ -2785,13 +2785,13 @@ __[s](incohérence_globale_aux_qqch)__ ([aA]ux) ({w2}) @@0,$ <<- not \2.isupper() >>> - <<- morph(\2, ">(?:[cdlmst]es|[nv]os|cettes?|[mts]a|mon|je|tu|ils?|elle?|[vn]ous|on|parce) ", False) + <<- morph(\2, ">(?:[cdlmst]es|[nv]os|cettes?|[mts]a|mon|je|tu|ils?|elle?|[vn]ous|on|parce)/", False) -2>> =suggSimil(\2, ":[NA].*:[pi]", True) # Incohérence : les mots “\1” et “\2” ne devraient pas se succéder. - <<- __else__ and morph(\2, ">quelle ", False) ->> auxquels|auxquelles # Incohérence. Soudez les deux mots.|https://fr.wiktionary.org/wiki/auquel + <<- __else__ and morph(\2, ">quelle/", False) ->> auxquels|auxquelles # Incohérence. Soudez les deux mots.|https://fr.wiktionary.org/wiki/auquel <<- __else__ and \2 == "combien" and morph(word(1), ":[AY]", False) -1>> ô # Incohérence probable.|https://fr.wiktionary.org/wiki/%C3%B4_combien TEST: ils jouent aux {{des}}. TEST: {{Aux quels}} a-t-il adressé sa requête. ? TEST: Des individus {{aux}} combien sensibles aux usages. @@ -2824,11 +2824,11 @@ # avoir été __[i]/bs(bs_avoir_été_chez)__ (?avoir ", False) + <<- not re.search("(?i)^avoir$", \1) and morph(\1, ">avoir/", False) ->> _ # Tournure familière. Utilisez « être allé ». TEST: J’{{ai été chez}} le coiffeur. TEST: Chez les intellectuels, le mot utopie n’a jamais été synonyme de folie, mais il l’a été pour l’homme de la rue. @@ -2841,11 +2841,11 @@ TEST: La mise en {{abîme}}. # à date / jusqu’à date __[i]/bs(bs_à_date)__ - ({etre}|m\w+) ([aà] date) @@0,$ <<- morph(\1, ">(?:être|mettre) ", False) -2>> à jour # Anglicisme incompris hors du Québec. + ({etre}|m\w+) ([aà] date) @@0,$ <<- morph(\1, ">(?:être|mettre)/", False) -2>> à jour # Anglicisme incompris hors du Québec. __[i]/bs(bs_jusquà_date)__ jusqu [àa] date <<- ->> jusqu’ici|jusqu’à maintenant|jusqu’à ce jour|à ce jour # Anglicisme incompris hors du Québec. TEST: être {{à date}} TEST: mettre {{a date}} @@ -2921,49 +2921,49 @@ !! !!!! Pléonasmes !! !! -__[i]/pleo(pleo_abolir)__ (abol\w+) (?:absolument|entièrement|compl[èé]tement|totalement) @@0 <<- morph(\1, ">abolir ", False) ->> \1 # Pléonasme. -__[i]/pleo(pleo_acculer)__ (accul\w+) aux? pieds? du mur @@0 <<- morph(\1, ">acculer ", False) ->> \1 # Pléonasme. -__[i]/pleo(pleo_achever)__ (ach[eè]v\w+) (?:absolument|entièrement|compl[èé]tement|totalement) @@0 <<- morph(\1, ">achever ", False) ->> \1 # Pléonasme. +__[i]/pleo(pleo_abolir)__ (abol\w+) (?:absolument|entièrement|compl[èé]tement|totalement) @@0 <<- morph(\1, ">abolir/", False) ->> \1 # Pléonasme. +__[i]/pleo(pleo_acculer)__ (accul\w+) aux? pieds? du mur @@0 <<- morph(\1, ">acculer/", False) ->> \1 # Pléonasme. +__[i]/pleo(pleo_achever)__ (ach[eè]v\w+) (?:absolument|entièrement|compl[èé]tement|totalement) @@0 <<- morph(\1, ">achever/", False) ->> \1 # Pléonasme. __[i]/pleo(pleo_en_cours)__ actuellement en cours <<- not after(r" +de?\b") ->> en cours # Pléonasme. __[i]/pleo(pleo_en_train_de)__ (actuellement en train) d(?:e(?! nuit)|’{w_2}) @@0 <<- -1>> en train # Pléonasme. __[i]/pleo(pleo_ajouter)__ (ajout\w+) en plus @@0 <<- ->> \1 # Pléonasme. __[i]/pleo(pleo_apanage)__ (apanages?) exclusifs? @@0 <<- ->> \1 # Pléonasme. __[i]/pleo(pleo_applaudir)__ (applaudi\w+) des deux mains @@0 <<- ->> \1 # Pléonasme. __[i]/pleo(pleo_aujourd_hui)__ au jour d’aujourd’hui <<- ->> aujourd’hui # Pléonasme. -__[i]/pleo(pleo_avancer)__ (avan[cç]\w+) en avant @@0 <<- morph(\1, ">avancer ", False) ->> \1 # Pléonasme. +__[i]/pleo(pleo_avancer)__ (avan[cç]\w+) en avant @@0 <<- morph(\1, ">avancer/", False) ->> \1 # Pléonasme. __[i]/pleo(pleo_s_avérer)__ s’av([éè]r\w+) vrai(e?s?) @@4,$ <<- ->> s’av\1 exact\2 # Pléonasme. __[i]/pleo(pleo_avéré)__ (avérée?s?) vraie?s? @@0 <<- ->> \1 # Pléonasme. __[i]/pleo(pleo_avenir)__ avenir devant (?:lui|[mts]oi|eux|[nv]ous) <<- morph(word(-1), ":A|>un", False) ->> avenir # Pléonasme. __[i]/pleo(pleo_bourrasque)__ (bourrasques?) de vent @@0 <<- ->> \1 # Pléonasme. __[i]/pleo(pleo_car_en_effet)__ car en effet <<- ->> car|en effet # Pléonasme. __[i]/pleo(pleo_cirrhose)__ (cirrhoses?) du foie @@0 <<- ->> \1 # Pléonasme. -__[i]/pleo(pleo_collaborer)__ (collabor\w+) ensemble @@0 <<- morph(\1, ">collaborer ", False) ->> \1 # Pléonasme. +__[i]/pleo(pleo_collaborer)__ (collabor\w+) ensemble @@0 <<- morph(\1, ">collaborer/", False) ->> \1 # Pléonasme. __[i]/pleo(pleo_comme_par_exemple)__ comme par exemple <<- ->> comme|par exemple # Pléonasme. -__[i]/pleo(pleo_comparer)__ (compar\w+) entre (?:eux|elles) @@0 <<- morph(\1, ">comparer ", False) ->> \1 # Pléonasme. -__[i]/pleo(pleo_contraindre)__ (contrai\w+) malgré (?:soi|eux|lui|moi|elle|toi) @@0 <<- morph(\1, ">contraindre ", False) ->> \1 # Pléonasme. +__[i]/pleo(pleo_comparer)__ (compar\w+) entre (?:eux|elles) @@0 <<- morph(\1, ">comparer/", False) ->> \1 # Pléonasme. +__[i]/pleo(pleo_contraindre)__ (contrai\w+) malgré (?:soi|eux|lui|moi|elle|toi) @@0 <<- morph(\1, ">contraindre/", False) ->> \1 # Pléonasme. __[i]/pleo(pleo_descendre)__ (descend\w+) en bas(?! de) @@0 <<- ->> \1 # Pléonasme. __[i]/pleo(pleo_dessiner)__ (dessin\w+) un dessin @@0 <<- ->> \1 # Pléonasme. __[i]/pleo(pleo_dorénavant)__ à (?:partir|compter) de dorénavant <<- ->> dorénavant|à partir de maintenant # Pléonasme. __[i]/pleo(pleo_donc_par_conséquent)__ donc par conséquent <<- ->> donc|par conséquent|c’est pourquoi # Pléonasme. -__[i]/pleo(pleo_enchevêtrer)__ (enchevêtr\w+) les uns dans les autres @@0 <<- morph(\1, ">enchevêtrer ", False) ->> \1 # Pléonasme. -__[i]/pleo(pleo_entraider)__ (entraid\w+) (?:mutuellement|les uns les autres) @@0 <<- morph(\1, ">entraider ", False) ->> \1 # Pléonasme. +__[i]/pleo(pleo_enchevêtrer)__ (enchevêtr\w+) les uns dans les autres @@0 <<- morph(\1, ">enchevêtrer/", False) ->> \1 # Pléonasme. +__[i]/pleo(pleo_entraider)__ (entraid\w+) (?:mutuellement|les uns les autres) @@0 <<- morph(\1, ">entraider/", False) ->> \1 # Pléonasme. __[i]/pleo(pleo_entraide)__ (entraides?) mutuelles? @@0 <<- ->> \1 # Pléonasme. __[i]/pleo(pleo_erreur)__ (erreurs?) involontaires? @@0 <<- ->> \1 # Pléonasme. __[i]/pleo(pleo_étape)__ (étapes?) intermédiaires? @@0 <<- ->> \1 # Pléonasme. __[i]/pleo(pleo_hasard)__ (hasards?) imprévus? @@0 <<- ->> \1 # Pléonasme. __[i]/pleo(pleo_hémorragie)__ (hémorragies?) de sang @@0 <<- ->> \1 # Pléonasme. -__[i]/pleo(pleo_joindre)__ (join\w+) ensemble @@0 <<- morph(\1, ">joindre ") ->> \1|mettre ensemble # Pléonasme. +__[i]/pleo(pleo_joindre)__ (join\w+) ensemble @@0 <<- morph(\1, ">joindre/") ->> \1|mettre ensemble # Pléonasme. __[i]/pleo(pleo_lever)__ lever debout <<- ->> lever # Pléonasme. __[i]/pleo(pleo_mais_qqch)__ mais (cependant|pourtant|toutefois) @@5 <<- ->> mais|cependant|pourtant|toutefois # Pléonasme. __[i]/pleo(pleo_marche)__ (marches?) à pieds? @@0 <<- ->> \1 # Pléonasme. __[i]/pleo(pleo_méandre)__ (méandres?) sinueux @@0 <<- ->> \1 # Pléonasme. __[i]/pleo(pleo_media)__ (m[eé]dias?) d’informations? @@0 <<- ->> \1 # Pléonasme. __[i]/pleo(pleo_monopole)__ (monopoles?) exclusifs? @@0 <<- ->> \1 # Pléonasme. -__[i]/pleo(pleo_monter)__ (mont\w+) en haut(?! d[eu’]) @@0 <<- morph(\1, ">monter ", False) ->> \1 # Pléonasme. +__[i]/pleo(pleo_monter)__ (mont\w+) en haut(?! d[eu’]) @@0 <<- morph(\1, ">monter/", False) ->> \1 # Pléonasme. __[i]/pleo(pleo_opportunité)__ (opportunités?) à saisir @@0 <<- ->> \1 # Pléonasme. __[i]/pleo(pleo_orage)__ (orages?) électriques? @@0 <<- ->> \1 # Pléonasme. __[i]/pleo(pleo_jumelles)__ paires? de jumelles? <<- ->> jumelles # Pléonasme. __[i]/pleo(pleo_panacée)__ (panacées?) universelles? @@0 <<- ->> \1|remède universel # Pléonasme. __[i]/pleo(pleo_perspective)__ (perspectives?) d’avenir @@0 <<- ->> \1 # Pléonasme. @@ -2970,19 +2970,19 @@ __[i]/pleo(pleo_balbutiement)__ premiers? (balbutiements?) @@$ <<- ->> \1 # Pléonasme. __[i]/pleo(pleo_priorité)__ premières? (priorités?) @@$ <<- ->> \1 # Pléonasme. __[i]/pleo(pleo_projet1)__ (projets?) futurs? @@0 <<- ->> \1 # Pléonasme. __[i]/pleo(pleo_projet2)__ futurs? (projets?) @@$ <<- ->> \1 # Pléonasme. __[i]/pleo(pleo_prototype)__ (prototypes?) expérimenta(?:l|ux) @@0 <<- ->> \1 # Pléonasme. -__[i]/pleo(pleo_rénover)__ (rénov\w+) à neuf @@0 <<- morph(\1, ">rénov(?:er|ation) ", False) ->> \1 # Pléonasme. +__[i]/pleo(pleo_rénover)__ (rénov\w+) à neuf @@0 <<- morph(\1, ">rénov(?:er|ation)/", False) ->> \1 # Pléonasme. __[i]/pleo(pleo_puis_qqch)__ puis (?:après|ensuite|alors) <<- ->> puis|après|ensuite|alors # Pléonasme. -__[i]/pleo(pleo_réunir)__ (réuni\w*) ensemble @@0 <<- morph(\1, ">réunir ", False) ->> \1 # Pléonasme. -__[i]/pleo(pleo_reculer)__ (recul\w*) en arrière @@0 <<- morph(\1, ">recul(?:er|) ", False) ->> \1 # Pléonasme. +__[i]/pleo(pleo_réunir)__ (réuni\w*) ensemble @@0 <<- morph(\1, ">réunir/", False) ->> \1 # Pléonasme. +__[i]/pleo(pleo_reculer)__ (recul\w*) en arrière @@0 <<- morph(\1, ">recul(?:er|)/", False) ->> \1 # Pléonasme. __[i]/pleo(pleo_risque)__ (risques?) (?:potentiels?|de menaces?) @@0 <<- ->> \1 # Pléonasme. __[i]/pleo(pleo_secousse)__ (secousses?) sé?ismiques? @@0 <<- ->> secousse tellurique|secousses telluriques|tremblement de terre # Pléonasme. __[i]/pleo(pleo_solidaire)__ (solidaires?) les uns des autres @@0 <<- ->> \1 # Pléonasme. -__[i]/pleo(pleo_suffire)__ (suffi\w+) simplement @@0 <<- morph(\1, ">suffire ", False) ->> \1 # Pléonasme. -__[i]/pleo(pleo_talonner)__ (talonn\w+) de près @@0 <<- morph(\1, ">talonner ", False) ->> \1 # Pléonasme. +__[i]/pleo(pleo_suffire)__ (suffi\w+) simplement @@0 <<- morph(\1, ">suffire/", False) ->> \1 # Pléonasme. +__[i]/pleo(pleo_talonner)__ (talonn\w+) de près @@0 <<- morph(\1, ">talonner/", False) ->> \1 # Pléonasme. __[i]/pleo(pleo_taux_alcoolémie)__ taux d’alcoolémies? @@7 <<- ->> taux d’alcool|alcoolémie # Pléonasme. L’alcoolémie est le taux d’alcool dans le sang. __[i]/pleo(pleo_tunnel)__ (tunnels?) souterrains? @@0 <<- ->> \1 # Pléonasme. __[i]/pleo(pleo_hardes)__ vieilles hardes <<- ->> hardes # Pléonasme. __[i]/pleo(pleo_voire_même)__ voire même <<- ->> voire|même # Pléonasme.|https://fr.wiktionary.org/wiki/voire_m%C3%AAme @@ -3053,11 +3053,11 @@ # d’avance / à l’avance __[i]/pleo(pleo_verbe_à_l_avance)__ ((?:pré[pvds]|pressen|pronostiqu|réserv|dev(?:an[cç]|in)|avert)\w+) (?:d’avance|à l’avance) @@0 - <<- morph(\1, ">(?:prévenir|prévoir|prédire|présager|préparer|pressentir|pronostiquer|avertir|devancer|deviner|réserver) ", False) + <<- morph(\1, ">(?:prévenir|prévoir|prédire|présager|préparer|pressentir|pronostiquer|avertir|devancer|deviner|réserver)/", False) ->> \1 # Pléonasme. TEST: {{prédire à l’avance}} ->> prédire TEST: {{pronostiquer d’avance}} ->> pronostiquer TEST: {{réserver d’avance}} ->> réserver @@ -3064,11 +3064,11 @@ # plus tard / à une date ultérieure __[i]/pleo(pleo_différer_ajourner_reporter)__ ((?:diff|ajourn|report)\w+) à (?:plus tard|date ultérieure|une date ultérieure) @@0 - <<- morph(\1, ">(?:ajourner|différer|reporter) ", False) + <<- morph(\1, ">(?:ajourner|différer|reporter)/", False) ->> \1 # Pléonasme. TEST: {{Ajourner à une date ultérieure}} ->> Ajourner TEST: {{différer à une date ultérieure}} ->> différer TEST: {{reporter à plus tard}} ->> reporter @@ -3273,19 +3273,19 @@ TEST: plus d’une sont parties aussi vite qu’elles étaient venues __[i]/conf(conf_il_on_pas_verbe)__ (?ou/") and morphex(word(-1), ":", ":3s", True) -1>> =suggSimil(\1, ":(?:3s|Oo)", False) # Incohérence : « \1 » devrait être un verbe, un pronom objet, un adverbe de négation, etc. TEST: il {{et}} parti. __[i]/conf(conf_ils_pas_verbe)__ (?ou/") and morphex(word(-1), ":", ":3p", True) -1>> =suggSimil(\1, ":(?:3p|Oo)", False) # Incohérence avec « ils » : « \1 » devrait être un verbe, un pronom objet, un adverbe de négation, etc. TEST: ils {{son}} du même bois. TEST: Ils {{étai}} partie au {{restaurent}} @@ -3331,11 +3331,11 @@ __[i]/conf(conf_très_verbe)__ très +(?!envie)({w_2}) @@$ <<- morphex(\1, ":(?:Y|[123][sp])", ":[AQW]") -1>> =suggSimil(\1, ":[AW]", True) # Incohérence avec « très » : « \1 » n’est ni un adjectif, ni un participe passé, ni un adverbe. - <<- morph(\1, ">jeûne ", False) -1>> =\1.replace("û", "u") # Confusion. Le jeûne est une privation de nourriture.|https://fr.wiktionary.org/wiki/jeune + <<- morph(\1, ">jeûne/", False) -1>> =\1.replace("û", "u") # Confusion. Le jeûne est une privation de nourriture.|https://fr.wiktionary.org/wiki/jeune TEST: Il est très {{cite}}. TEST: très {{suivit}} par ce détective TEST: il était très {{habille}} TEST: Très {{jeûne}}, elle a su qu’elle ne voulait pas d’une vie ordinaire. @@ -3385,11 +3385,11 @@ TEST: Ça ira mieux demain, surtout si émerge une demande forte de la part des consommateurs. __[i]/conf(conf_de_plus_en_plus_verbe)__ de plus en plus +({w_2}) @@$ - <<- morphex(\1, ":(?:[123][sp]|Y)", ":(?:[GAQW]|3p)") and not morph(word(-1), ":V[123].*:[123][sp]|>(?:pouvoir|vouloir|falloir) ", False, False) + <<- morphex(\1, ":(?:[123][sp]|Y)", ":(?:[GAQW]|3p)") and not morph(word(-1), ":V[123].*:[123][sp]|>(?:pouvoir|vouloir|falloir)/", False, False) -1>> =suggVerbPpas(@) # Incohérence avec « de plus en plus » : « \1 » n’est ni un adjectif, ni un participe passé, ni un adverbe. TEST: de plus en plus {{gagnait}} par la folie. TEST: de plus en plus {{concerner}} par ce problème @@ -3407,11 +3407,11 @@ __[i]/conf(conf_a_à_face_à)__ face (a) @@5 <<- not before(r"(?i)\b(?:[lmts]a|leur|une|en) +$") -1>> à # Confusion. __[i]/conf(conf_a_à_pas_à_pas)__ pas (a) pas @@4 <<- -1>> à # Confusion. __[i]/conf(conf_a_à_par_rapport)__ par rapport (a) ({w_2}) @@12,$ <<- morph(\2, ":(?:D|Oo|M)", False) -1>> à # Confusion. __[i]/conf(conf_a_à_être_à)__ ({etre}) (a)(?! priori| posteriori| fortiori) @@0,$ - <<- morph(\1, ">être :V") and not before(r"(?i)\bce que? ") -2>> à # Confusion. Utilisez la préposition « à ». + <<- morph(\1, ">être/:V") and not before(r"(?i)\bce que? ") -2>> à # Confusion. Utilisez la préposition « à ». __[i]/conf(conf_a_à_peu_près)__ (?:a peu[tx]? (?:près|prés?|prêts?)|à peu[tx] (?:près|prés?|prêts?)|à peu (?:prés?|prêts?)) <<- ->> à peu près # Confusion. <<- ~>> * __[i]/conf(conf_a_à_pronoms1)__ ne +l(?:es?|a) +(?:l(?:eur|ui) +|)(à) @@$ <<- -1>> a # Confusion : “à” est une préposition. Pour le verbe avoir, écrivez : @@ -3433,11 +3433,11 @@ __[i]/conf(conf_a_à_il_on_à)__ (?:il|on) +(?:l(?:es +|’)|en +|y +(?:en +|)|[vn]ous +|)(à) @@$ <<- not morph(word(-1), ":3s", False, False) -1>> a # Confusion probable : “à” est une préposition. Pour le verbe avoir, écrivez : __[i]/conf(conf_a_à_elle_à)__ elle +(?:l(?:es +|’)|en +|y +(?:en |)|[vn]ous +|)(à) @@$ - <<- not morph(word(-1), ":(?:3s|R)", False, False) and not morph(word(1), ":Oo|>qui ", False, False) + <<- not morph(word(-1), ":(?:3s|R)", False, False) and not morph(word(1), ":Oo|>qui/", False, False) -1>> a # Confusion probable : “à” est une préposition. Pour le verbe avoir, écrivez : __[i]/conf(conf_a_à_qui_pronom_à)__ qui (?:l(?:ui|eur)(?: en|)|nous|vous|en|y) +(à) @@$ <<- -1>> a # Confusion : “à” est une préposition. Pour le verbe avoir, écrivez : __[i]/conf(conf_a_à_qui_a)__ qui (à) +({w_2}) @@4,$ <<- morphex(\2, ":Q", ":M[12P]") -1>> a # Confusion : “à” est une préposition. Pour le verbe avoir, écrivez : @@ -3570,11 +3570,11 @@ TEST: Il y a qui au dîner ce soir ? __[i]/conf(conf_mener_à_bien)__ (m[eèé]n\w+) (a) bien @@0,w - <<- morph(\1, ">mener ", False) and ( not before(r"\bque? ") or morph(word(-1), ">(?:falloir|aller|pouvoir) ", False, True) ) + <<- morph(\1, ">mener/", False) and ( not before(r"\bque? ") or morph(word(-1), ">(?:falloir|aller|pouvoir)/", False, True) ) -2>> à # Confusion probable. Dans cette locution, utilisez la préposition « à ».|https://fr.wiktionary.org/wiki/mener_%C3%A0_bien <<- __also__ ~>> \1 TEST: Mener {{a}} bien cette guerre sera plus difficile qu’on le pense. TEST: Je peux mener {{a}} bien cette opération. @@ -3581,11 +3581,11 @@ TEST: Cette coalition que tu penses mener a bien l’intention de te trahir. __[i]/conf(conf_mettre_à_profit)__ (m(?:i[st]|ett)\w*).* (a) profit @@0,w - <<- morph(\1, ">mettre ", False) -2>> à # Confusion probable. Dans « mettre à profit », utilisez la préposition « à ».|https://fr.wiktionary.org/wiki/mettre_%C3%A0_profit + <<- morph(\1, ">mettre/", False) -2>> à # Confusion probable. Dans « mettre à profit », utilisez la préposition « à ».|https://fr.wiktionary.org/wiki/mettre_%C3%A0_profit TEST: Mettre {{a}} profit ses compétences TEST: Il a mis son talent {{a}} profit. @@ -3630,11 +3630,11 @@ # ça / çà / sa __[i]/conf(conf_ça_sa)__ (ça) ({w_2}) @@0,3 <<- morph(\2, ":[NAQ].*:f") and not re.search("^seule?s?", \2) -1>> sa # Confusion : “sa” (sa maison, sa passion) ≠ “ça” (ça vient, ça heurte). __[i]/conf(conf_sa_ça1)__ (sa) +({w_2}) @@0,$ - <<- morphex(\2, ":G", ">(?:tr(?:ès|op)|peu|bien|plus|moins|toute) |:[NAQ].*:f") -1>> ça # Confusion : “sa” (sa maison, sa passion) ≠ “ça” (ça vient, ça heurte). + <<- morphex(\2, ":G", ">(?:tr(?:ès|op)|peu|bien|plus|moins|toute)/|:[NAQ].*:f") -1>> ça # Confusion : “sa” (sa maison, sa passion) ≠ “ça” (ça vient, ça heurte). __[i>/conf(conf_sa_ça2)__ (sa) +(?:[dnmtsjl]’|lorsqu |qu |puisqu ) @@0 <<- -1>> ça # Confusion : “sa” (sa maison, sa passion) ≠ “ça” (ça vient, ça heurte). __[i]/conf(conf_çà_ça)__ çà(?! et là) <<- not before(r"\b(?:[oO]h|[aA]h) +$") ->> ça # Confusion : « çà » ne s’emploie plus guère que dans l’expression « çà et là ». __[i]/conf(conf_çà_et_là)__ ça et là <<- not morph(word(-1), ":R") ->> çà et là # Confusion : « ça » équivaut à « cela ». Dans l’expression « çà et là », « çà » équivaut à « ici ». __[s]/conf(conf_sa_fin)__ (sa) *$ @@0 <<- -1>> ça # Confusion probable : “sa” est un déterminant féminin singulier. Pour l’équivalent de “cela” ou “ceci”, écrivez : @@ -3653,11 +3653,11 @@ # ce / se / ceux __[s]/conf(conf_se_verbe)__ ([cC]e) ({w_2}) @@0,3 <<- \2[0].islower() and \2 != "faire" - and ( morphex(\2, ":V[123].*:(?:Y|[123][sp])", ":[NAGM]|>(?:devoir|pouvoir|sembler) ") or re.search("-(?:ils?|elles?|on)$", \2) ) + and ( morphex(\2, ":V[123].*:(?:Y|[123][sp])", ":[NAGM]|>(?:devoir|pouvoir|sembler)/") or re.search("-(?:ils?|elles?|on)$", \2) ) -1>> se # Confusion : « \2 » est un verbe. Exemples : ce bâtiment, se perdre. __[i]/conf(conf_pour_ce_faire)__ pour (se) faire,? ({w_2}) @@5,$ <<- (\0.find(",") >= 0 or morphex(\2, ":G", ":[AYD]")) -1>> ce # Confusion probable. Dans cette locution, il faut employer “ce”.|http://fr.wiktionary.org/wiki/pour_ce_faire @@ -3674,21 +3674,21 @@ __[i]/conf(conf_ceux_ce_être)__ (ceux) (?:ne |)(?:sont|serai(?:en|)[ts]?|f[uû](?:ren|)t|n’(?!ayant|étant)\w+) @@0 <<- -1>> ce # Confusion.|http://www.intellego.fr/soutien-scolaire-6eme/aide-scolaire-francais/ce-ceux-ou-se/3829 __[s]/conf(conf_ce_ne_être_doit)__ ([sS]e) n(?:e |’)({être}|d[eouû]\w+|p[oeuû]\w+) @@0,$ - <<- morph(\2, ">(?:être|pouvoir|devoir) .*:3s", False) + <<- morph(\2, ">(?:être|pouvoir|devoir)/.*:3s", False) -1>> ce # Confusion probable.|http://bdl.oqlf.gouv.qc.ca/bdl/gabarit_bdl.asp?id=2440 __[i]/conf(conf_ce_ne)__ (ceux) ne ({w_2}) @@0,$ <<- morphex(\2, ":[123]s", ":P") -1>> ce # Confusion.|http://www.intellego.fr/soutien-scolaire-6eme/aide-scolaire-francais/ce-ceux-ou-se/3829 __[i]/conf(conf_ce_nom1)__ (se) ({w1}) @@0,3 - <<- morphex(\2, ":[NAQ]", ":([123][sp]|Y|P|Q)|>l[ea]? ") -1>> ce # Confusion. Ce chien, ce chat… Se demander, se croire… + <<- morphex(\2, ":[NAQ]", ":([123][sp]|Y|P|Q)|>l[ea]?/") -1>> ce # Confusion. Ce chien, ce chat… Se demander, se croire… __[i]/conf(conf_ce_nom2)__ (ceux) (?!l[aà] |qu[ie]? )({w_2}) @@0,$ - <<- morphex(\2, ":N.*:s", ":(?:A.*:[pi]|P|R)|>autour ") -1>> ce # Confusion probable.|http://www.intellego.fr/soutien-scolaire-6eme/aide-scolaire-francais/ce-ceux-ou-se/3829 + <<- morphex(\2, ":N.*:s", ":(?:A.*:[pi]|P|R)|>autour/") -1>> ce # Confusion probable.|http://www.intellego.fr/soutien-scolaire-6eme/aide-scolaire-francais/ce-ceux-ou-se/3829 TEST: il ne {{ce}} compte pas parmi eux TEST: il ne {{ç’}}avançait jamais sans avoir pesé toutes les conséquences TEST: {{Se}} seraient des histoires. TEST: {{se}} seraient des jours heureux. @@ -3733,11 +3733,11 @@ __[s]/conf(conf_c_est3)__ ([scSC]es) (?:qu(?:lle|el?|)|comme|ce(?:t|tte|)|[nv]os|les?|eux|elles) @@0 <<- -1>> c’est # Confusion probable. Écrivez « c’est » pour dire « ceci est… ». __[s]/conf(conf_c_est4)__ ([scSC]es) ({w_1}) ({w_1}) @@0,w,$ - <<- morph(\2, ":[WX]", ":N:.*:[pi]") and morph(\3, ":[RD]|>pire ", False) -1>> c’est # Confusion probable. Écrivez « c’est » pour dire « ceci est… ». + <<- morph(\2, ":[WX]", ":N:.*:[pi]") and morph(\3, ":[RD]|>pire/", False) -1>> c’est # Confusion probable. Écrivez « c’est » pour dire « ceci est… ». __[i]/conf(conf_ces_ses)__ (c’est) ({w_2}) @@0,6 <<- morphex(\2, ":N.*:p", ":(?:G|W|M|A.*:[si])") -1>> ces|ses # Confusion. Exemples : c’est facile ; ces chats (désignation) ; ses chats (possession)… TEST: {{ses}} au-dessus de ses forces. TEST: {{ces}} comme la peste @@ -3772,11 +3772,11 @@ __[i]/conf(règlement_de_comptes)__ r[éè]glements? de (co[mn]tes?) @@$ <<- -1>> comptes # Confusion.|https://fr.wiktionary.org/wiki/r%C3%A8glement_de_comptes __[i]/conf(régler_son_compte)__ (r[éè]gl\w+) +(?:[mts]on|leurs?|[vn]otre) (co[mn]tes?) @@0,$ - <<- morph(\1, ">régler ", False) -2>> compte # Confusion. Un conte est un récit fictif, “comte” est un titre de noblesse. Pour un état chiffré, un calcul… écrivez :|https://fr.wiktionary.org/wiki/r%C3%A9gler_son_compte + <<- morph(\1, ">régler/", False) -2>> compte # Confusion. Un conte est un récit fictif, “comte” est un titre de noblesse. Pour un état chiffré, un calcul… écrivez :|https://fr.wiktionary.org/wiki/r%C3%A9gler_son_compte __[i]/conf(conf_tout_compte_fait)__ tout (co[mn]te) fait @@w <<- -1>> compte # Confusion. Locution “tout compte fait”.|https://fr.wiktionary.org/wiki/tout_compte_fait TEST: il s’en est tiré à bon {{conte}}. @@ -3837,15 +3837,15 @@ __[i]/conf(conf_être_davantage_ppas)__ ({etre}) (d’avantages?) ({w_2}) @@0,w,$ <<- morph(\1, ":V0e", False) and morphex(\3, ":[NAQ]", ":G") -2>> davantage # Confusion possible : “davantage” signifie “plus” ; un “avantage” signifie “faveur”, “bénéfice”, “profit”… __[i]/conf(conf_davantage1)__ ({w1}) (d’avantages?) @@0,$ - <<- morphex(\1, ":V", ":Q|>(?:profiter|bénéficier) ") and not morph(word(1), ">(?:financi[eè]re?|pécuni(?:er|aire))s? ", False, False) + <<- morphex(\1, ":V", ":Q|>(?:profiter|bénéficier)/") and not morph(word(1), ">(?:financi[eè]re?|pécuni(?:er|aire))s?/", False, False) -2>> davantage # Confusion probable : “davantage” signifie “plus” ; un “avantage” signifie “faveur”, “bénéfice”, “profit”… __[i]/conf(conf_davantage2)__ ({w_1})-(?:je|tu|ils?|elles?|[nv]ous|on) +(d’avantages?) @@0,$ - <<- not morph(\1, ">(?:profiter|bénéficier) ", False) and not morph(word(1), ">(?:financi[eè]re?|pécuni(?:er|aire))s? ", False, False) + <<- not morph(\1, ">(?:profiter|bénéficier)/", False) and not morph(word(1), ">(?:financi[eè]re?|pécuni(?:er|aire))s?/", False, False) -2>> davantage # Confusion probable : “davantage” signifie “plus” ; un “avantage” signifie “faveur”, “bénéfice”, “profit”… __[i>/conf(conf_davantage3)__ (d’avantages?) d(?:e +|’) @@0 <<- -1>> davantage # Confusion possible : “davantage” signifie “plus” ; un “avantage” signifie “faveur”, “bénéfice”, “profit”… @@ -3921,11 +3921,11 @@ # faut / faux __[i]/conf(conf_faux)__ faut - <<- not morph(word(-1), ">(?:ils?|ne|en|y|leur|lui|nous|vous|[mtsl]e|la|les) ", False, True) and morphex(word(1), ":", ":(?:Y|Oo|X|M)", True) + <<- not morph(word(-1), ">(?:ils?|ne|en|y|leur|lui|nous|vous|[mtsl]e|la|les)/", False, True) and morphex(word(1), ":", ":(?:Y|Oo|X|M)", True) ->> faux # Confusion probable : “faut” est une conjugaison de “falloir”. Pour indiquer la fausseté d’une chose, écrivez : TEST: un homme {{faut}} TEST: c’est {{faut}} TEST: il m’en faut plus. @@ -3956,15 +3956,15 @@ __[i]/conf(conf_flanc)__ (flans?) (?:des? (?:la |)(?:colline|montagne)s?|gauches?|droites?|nord|sud|ouest) @@0 <<- -1>> =\0.replace("an", "anc").replace("AN", "ANC") # Confusion probable. Le flan est une pâtisserie.|https://fr.wiktionary.org/wiki/flanc __[i]/conf(conf_sur_le_flanc)__ ((?:attaqu|allong|bless|couch|étend|touch)\w+) +sur (?:les?|[mts](?:on|es)|[nv]o(?:tre|s)) (flans?) @@0,$ - <<- morph(\1, ">(?:attaquer|allonger|blesser|coucher|étendre|toucher) ", False) + <<- morph(\1, ">(?:attaquer|allonger|blesser|coucher|étendre|toucher)/", False) -2>> =\0.replace("an", "anc").replace("AN", "ANC") # Confusion probable. Le flan est une pâtisserie.|https://fr.wiktionary.org/wiki/flanc __[i]/conf(conf_tirer_au_flanc)__ (tir\w*)[ -]+aux?[ -](flans?) @@0,$ - <<- morph(\1, ">tir(?:er|) ", False) -2>> =\0.replace("an", "anc").replace("AN", "ANC") # Confusion. Le flan est une pâtisserie.|https://fr.wiktionary.org/wiki/flanc + <<- morph(\1, ">tir(?:er|)/", False) -2>> =\0.replace("an", "anc").replace("AN", "ANC") # Confusion. Le flan est une pâtisserie.|https://fr.wiktionary.org/wiki/flanc TEST: attaqué sur son {{flan}} droit TEST: elle possède une maison à {{flan}} de colline. TEST: étendu sur son {{flan}}. TEST: Ce sale tir-au-{{flan}} le paiera cher. @@ -4019,11 +4019,11 @@ # la / là __[s]/conf(conf_la_là)__ ([lL]a) (?:a(?:fin|lors|près|uprès|ux?|vant|vec)|au(?:-de(?:dans|hors|là|sso?us|vant)|x|)|c(?:e(?:t|te|s|)|ar|hez|omme)|ça|d(?:ans|evant|es?|ès|onc|urant|’{w_1})|e(?:lles?|n|t)|ils?|je?|l(?:es?|a|orsque?|’{w_1})|m(?:algré|es|on|a|e)|n(?:e|ous)|o[uùn]|par(?:ce|fois|mi|)|p(?:arce|endant|our|uisque)|qu(?:e?|and)|s(?:on|a|es?|ouvent|ur)|t(?:andis|on|a|es?|u)|un|vous) @@0 - <<- not morph(word(-1), ":E|>le ", False, False) + <<- not morph(word(-1), ":E|>le/", False, False) -1>> là # Confusion probable. Écrivez “là” si vous voulez dire “ici”. TEST: nous serions encore {{la}} l’année prochaine TEST: en reprenant le chandail de John {{la}} où elle l’avait abandonné. TEST: Qui serait la ou le plus à même à occuper ce poste selon vous ? @@ -4071,11 +4071,11 @@ # loin s’en faut __[i]/conf(conf_loin_s_en_faut)__ loins? +(?:[sc]ens|san[gs]?s?|s[’ ]en) +fau[xt] - <<- not re.search("(?i)loin s’en faut", \0) and morph(word(-1), ":N", ">(?:aller|venir|partir) ", True) + <<- not re.search("(?i)loin s’en faut", \0) and morph(word(-1), ":N", ">(?:aller|venir|partir)/", True) ->> loin s’en faut # Confusion probable. Cette locution s’écrit :|https://fr.wiktionary.org/wiki/loin_s%E2%80%99en_faut TEST: Ils n’étaient guère prêts à ça, {{loins sans faux}}. TEST: Et les intellectuels ? En France comme ailleurs, tous n’ont pas, loin s’en faut, une pleine lucidité sur cette précarité galopante. @@ -4205,11 +4205,11 @@ # par-dessus / pardessus __[i]/conf(conf_par_dessus)__ (pardessus) +({w1}) @@0,$ - <<- morph(\2, ":D|>bord ", False) and not morph(word(-1), ":D.*:[me]|>(?:grande|petite) ", False, False) + <<- morph(\2, ":D|>bord/", False) and not morph(word(-1), ":D.*:[me]|>(?:grande|petite)/", False, False) -1>> par-dessus # Confusion probable. Un pardessus est un vêtement. Pour la préposition, écrivez : TEST: {{Pardessus}} les montagnes. TEST: Il passa {{pardessus}} les collines. TEST: Mets ton pardessus ce matin. @@ -4224,14 +4224,14 @@ # prêt / près / pré __[i]/conf(conf_prêt_à)__ (près) à ({w_2}) @@0,$ - <<- not before("(?i)(?:peu|de|au plus) $") and morph(\2, ":Y|>(?:tout|les?|la) ") -1>> prêt|prêts # Confusion. Être près de (faire) quelque chose. Prêt à faire quelque chose. + <<- not before("(?i)(?:peu|de|au plus) $") and morph(\2, ":Y|>(?:tout|les?|la)/") -1>> prêt|prêts # Confusion. Être près de (faire) quelque chose. Prêt à faire quelque chose. __[i]/conf(conf_près_de)__ (prêts?) d(?:e +|’)({w_1}) @@0,$ - <<- morph(\2, ":(?:Y|M[12P])|>(?:en|y|les?) ", False) -1>> près # Confusion. Être près de (faire) quelque chose. Prêt à faire quelque chose. + <<- morph(\2, ":(?:Y|M[12P])|>(?:en|y|les?)/", False) -1>> près # Confusion. Être près de (faire) quelque chose. Prêt à faire quelque chose. __[i]/conf(conf_près)__ de(?: plus|puis) (prêts?) @@$ <<- -1>> près # Confusion. Être prêt(e) à faire quelque chose. Être près de quelque chose. __[i]/conf(conf_très_près)__ très (pr(?:êt|é)s?) @@$ <<- -1>> près # Confusion probable. Pour évoquer la proximité, utilisez : TEST: ils se sont approchés très {{prêts}}. TEST: Je suis si {{prêt}} d’y arriver. @@ -4240,11 +4240,11 @@ # quand / quant / qu’en __[i]/conf(conf_quant_à)__ (?(?:arriver|venir|à|revenir|partir|aller) ") + <<- not morph(word(-1), ">(?:arriver|venir|à|revenir|partir|aller)/") and not(\0.endswith("à") and after("^ +[mts]on tour[, ]")) -1>> quant # Confusion probable. Quand = à quel moment. Quant à = à propos de. __[i]/conf(conf_quand1)__ quant(?! à| aux?| est[ -]il d(?:es?|u) ) <<- ->> quand # Confusion. Quand = à quel moment. Quant à = à propos de. __[i]/conf(conf_qu_en1)__ (quan[dt]) est[ -]il d(?:es?|u) @@0 <<- -1>> qu’en # Confusion. Ce qu’il en est de… → Qu’en est-il de… ? __[i]/conf(conf_qu_en2)__ (quan[dt]) ({w_2}ant) @@0,$ <<- morph(\2, ":P", False) -1>> qu’en # Confusion probable. __[i]/conf(conf_quand2)__ @@ -4281,12 +4281,12 @@ TEST: {{qu’elle}} emmerdeuse. __[i]/conf(conf_qu_elle_verbe)__ (quelles?) +({w_1}) @@0,$ - <<- \2.islower() and (morphex(\2, ":V|>(?:ne?|me?|te?|se?|[nv]ous|l(?:e|a|es|ui|leur|)|en|y) ", ":[NA].*:[fe]|>(?:plus|moins)") or \2 == "t" or \2 == "s") - and not (morph(\2, ">(?:pouvoir|devoir|en)", False) and morph(word(1), ":V0e", False)) >>> + <<- \2.islower() and (morphex(\2, ":V|>(?:ne?|me?|te?|se?|[nv]ous|l(?:e|a|es|ui|leur|)|en|y)/", ":[NA].*:[fe]|>(?:plus|moins)") or \2 == "t" or \2 == "s") + and not (morph(\2, ">(?:pouvoir|devoir|en)/", False) and morph(word(1), ":V0e", False)) >>> <<- \1.endswith("e") and not morph(\2, ":V0e", False) -1>> qu’elle # Confusion. Le sujet “elle” doit être séparée de la conjonction “que”. 1 <<- __else__ and \1.endswith("s") and not morph(\2, ":V0e", False) -1>> qu’elles # Confusion. Le sujet “elles” doit être séparée de la conjonction “que”. 2 <<- __else__ and morph(\2, ":V0e", False) and morphex(word(1), ":[QA]", ":G", False) >>> <<- \1.endswith("e") -1>> qu’elle # Confusion. Le sujet “elle” doit être séparée de la conjonction “que”. 3 <<- __else__ and \1.endswith("s") -1>> qu’elles # Confusion. Le sujet “elles” doit être séparée de la conjonction “que”. 4 @@ -4342,12 +4342,12 @@ __[i]/conf(conf_me_te_se_son)!6__ [mts]e (son) @@3 <<- -1>> sont # Confusion : “son” est un déterminant ou un nom masculin. Le verbe “être” à la 3ᵉ personne du pluriel s’écrit “sont”. __[i]/conf(conf_son_qqch)__ (sont) ({w_2}) @@0,$ - <<- morphex(\2, ":[NA].*:[me]:s|>[aeéiîou].* :[NA].*:f:s", ":[GW]") - and morphex(word(-1), ":V|>(?:à|avec|chez|dès|contre|devant|derrière|en|par|pour|sans|sur) ", ":[NA].*:[pi]|>(?:ils|elles|vous|nous|leur|lui|[nmts]e) ", True) + <<- morphex(\2, ":[NA].*:[me]:s|>[aeéiîou].*/:[NA].*:f:s", ":[GW]") + and morphex(word(-1), ":V|>(?:à|avec|chez|dès|contre|devant|derrière|en|par|pour|sans|sur)/", ":[NA].*:[pi]|>(?:ils|elles|vous|nous|leur|lui|[nmts]e)/", True) and not before(r"(?i)\bce que? |[mts]’en +$") -1>> son # Confusion : “sont” est le verbe “être” à la 3ᵉ personne du pluriel. Pour le déterminant, écrivez “son”. __[i]/conf(conf_qui_sont_les)__ (?:qu[ie]|comment|pourquoi) +(son) @@$ <<- morph(word(1), ":[DR]", False, True) -1>> sont # Confusion probable : “son” est un déterminant ou un nom masculin. Le verbe “être” à la 3ᵉ personne du pluriel s’écrit “sont”. @@ -4423,11 +4423,11 @@ __[i]/conf(conf_sur_la_bonne_voie)__ sur la bonne (voix) @@$ <<- -1>> voie # Confusion.|http://fr.wiktionary.org/wiki/voix __[i]/conf(conf_en_voie_de)__ en (voix) d(?:e (?:développement|disparition|guérison|résorption)|’(?:acquisition|achèvement|extinction|obtention)) @@3 <<- -1>> voie # Confusion.|http://fr.wiktionary.org/wiki/voie __[i]/conf(conf_ouvrir_la_voix)__ - (ouv\w+) +la (voix) (?:à|aux?) @@0,w <<- morph(\1, ">ouvrir ", False) -2>> voie # Confusion.|http://fr.wiktionary.org/wiki/voie + (ouv\w+) +la (voix) (?:à|aux?) @@0,w <<- morph(\1, ">ouvrir/", False) -2>> voie # Confusion.|http://fr.wiktionary.org/wiki/voie __[i]/conf(conf_par_voie_de_conséquence)__ par (voix) de conséquence @@4 <<- -1>> voie # Confusion.|http://fr.wiktionary.org/wiki/voie __[i]/conf(conf_voie_adj)__ (voix) (?:abdominale|anale|biliaire|carrossable|communale|express|interdite|intramusculaire|intraveineuse|piétonne|principale|prioritaire|privée|publique|déserte|romaine|appienne|flaminienne|ferrée|ferroviaire|lactée|lacrymale|aérienne|maritime|fluviale|terrestre|navigable|détournée|déviée|buccale|digestive|urinaire|respiratoire|parallèle|administrative|diplomatique|gouvernementale|législative|hiérarchique|rectiligne|sinueuse|souterraine|urbaine)s? @@0 <<- -1>> voie # Confusion.|http://fr.wiktionary.org/wiki/voie @@ -4440,17 +4440,17 @@ # voire / voir __[i]/conf(conf_voir_voire)__ (voir) ({w_2}) @@0,$ <<- not re.search("^(?:grand|petit|rouge)$", \2) and morphex(\2, ":A", ":[NGM]") and not \2.istitle() - and not before(r"(?i)\b[ndmts](?:e |’(?:en |y ))(?:pas |jamais |) *$") and not morph(word(-1), ":O[os]|>(?:[ndmts]e|falloir|pouvoir|savoir|de) ", False) + and not before(r"(?i)\b[ndmts](?:e |’(?:en |y ))(?:pas |jamais |) *$") and not morph(word(-1), ":O[os]|>(?:[ndmts]e|falloir|pouvoir|savoir|de)/", False) -1>> voire # Confusion probable : “voir” est un verbe concernant la perception visuelle. Pour signifier “et même possiblement”, écrivez :|https://fr.wiktionary.org/wiki/voire __[i]/conf(conf_voire_voir)__ voire - <<- morph(word(-1), ":Cs|>(?:ni|et|sans|pour|falloir|[pv]ouvoir|aller) ", True, False) ->> voir + <<- morph(word(-1), ":Cs|>(?:ni|et|sans|pour|falloir|[pv]ouvoir|aller)/", True, False) ->> voir # Confusion probable : “voire” signifie “et même possiblement”. Pour le verbe, écrivez “voir”.|https://fr.wiktionary.org/wiki/voire TEST: Elles sont fatiguées, {{voir}} épuisées. TEST: Ce serait pour aider, ainsi que {{voire}} l’avancement du projet. TEST: Elles vont voir rouge en apprenant cet échec. @@ -4482,11 +4482,11 @@ (j’(?:en +|y +|))({w_1}) @@0,$ <<- morphex(\2, ":", ":(?:[123][sp]|O[onw])") -2>> =suggSimil(\2, ":1s", False) # Incohérence avec « \1 » : « \2 » devrait être un verbe. __[i]/conf(conf_ne_qqch)__ (n(?:e +|’))({w_1}) @@0,$ - <<- morphex(\2, ":", ":(?:[123][sp]|Y|P|O[onw]|X)|>(?:[lmtsn]|surtout|guère|presque|même|tout|parfois|vraiment|réellement|justement) ") and not re.search("(?i)-(?:ils?|elles?|[nv]ous|je|tu|on|ce)$", \2) + <<- morphex(\2, ":", ":(?:[123][sp]|Y|P|O[onw]|X)|>(?:[lmtsn]|surtout|guère|presque|même|tout|parfois|vraiment|réellement|justement)/") and not re.search("(?i)-(?:ils?|elles?|[nv]ous|je|tu|on|ce)$", \2) -2>> =suggSimil(\2, ":(?:[123][sp]|Oo|Y)", False) # Incohérence avec « \1 » : « \2 » devrait être un verbe ou un pronom personnel objet. __[i]/conf(conf_n_y_en_qqch)__ (n’(?:en|y)) ({w_1}) @@0,$ <<- morphex(\2, ":", ":(?:[123][sp]|Y|P|O[onw]|X)") and not re.search("(?i)-(?:ils?|elles?|[nv]ous|je|tu|on|ce)$", \2) -2>> =suggSimil(\2, ":(?:[123][sp]|Y)", False) # Incohérence avec « \1 » : « \2 » devrait être un verbe. @@ -4495,27 +4495,27 @@ <<- morphex(\2, ":", ":(?:[123][sp]|Y|P|O[onw]|X)") and not re.search("(?i)-(?:ils?|elles?|[nv]ous|je|tu|on|ce)$", \2) -2>> =suggSimil(\2, ":(?:[123][sp]|Y)", False) # Incohérence avec « \1 » : « \2 » devrait être un verbe. __[i]/conf(conf_me_te_se_qqch)__ ([mts]e +(?:les? |la |l’|))(?!voi(?:là|ci))({w_1}) @@0,$ <<- not re.search("(?i)^se que?", \0) - and morphex(\2, ":", ":(?:[123][sp]|Y|P|Oo)|>[lmts] ") and not re.search("(?i)-(?:ils?|elles?|[nv]ous|je|tu|on|ce)$", \2) + and morphex(\2, ":", ":(?:[123][sp]|Y|P|Oo)|>[lmts]/") and not re.search("(?i)-(?:ils?|elles?|[nv]ous|je|tu|on|ce)$", \2) -2>> =suggSimil(\2, ":(?:[123][sp]|Oo|Y)", False) # Incohérence avec « \1 » : « \2 » devrait être un verbe ou un pronom personnel objet. __[i]/conf(conf_m_t_s_y_en_qqch)__ ([mts]’(?:en|y)) (?!voilà)({w_1}) @@0,$ - <<- morphex(\2, ":", ":(?:[123][sp]|Y|P|X|Oo)|rien ") and not re.search("(?i)-(?:ils?|elles?|[nv]ous|je|tu|on|ce)$", \2) + <<- morphex(\2, ":", ":(?:[123][sp]|Y|P|X|Oo)|>rien/") and not re.search("(?i)-(?:ils?|elles?|[nv]ous|je|tu|on|ce)$", \2) -2>> =suggSimil(\2, ":(?:[123][sp]|Y)", False) # Incohérence avec « \1 » : « \2 » devrait être un verbe. __[i]/conf(conf_m_s_qqch)__ ([ms]’)({w_1}) @@0,2 - <<- morphex(\2, ":", ":(?:[123][sp]|Y|P)|>(?:en|y|ils?) ") and not re.search("(?i)-(?:ils?|elles?|[nv]ous|je|tu|on|ce)$", \2) + <<- morphex(\2, ":", ":(?:[123][sp]|Y|P)|>(?:en|y|ils?)/") and not re.search("(?i)-(?:ils?|elles?|[nv]ous|je|tu|on|ce)$", \2) -2>> =suggSimil(\2, ":(?:[123][sp]|Y)", False) # Incohérence avec « \1 » : « \2 » devrait être un verbe. __[i]/conf(conf_t_qqch)__ (t’)({w_1}) @@0,2 - <<- morphex(\2, ":", ":(?:[123][sp]|Y|P)|>(?:en|y|ils?|elles?) ") and not re.search("(?i)-(?:ils?|elles?|[nv]ous|je|tu|on|ce)$", \2) + <<- morphex(\2, ":", ":(?:[123][sp]|Y|P)|>(?:en|y|ils?|elles?)/") and not re.search("(?i)-(?:ils?|elles?|[nv]ous|je|tu|on|ce)$", \2) -2>> =suggSimil(\2, ":(?:[123][sp]|Y)", False) # Incohérence avec « \1 » : « \2 » devrait être un verbe. __[i]/conf(conf_c_ç_qqch)__ ([cç]’)({w_1}) @@0,2 - <<- morphex(\2, ":", ":[123][sp]|>(?:en|y|que?) ") and not re.search("(?i)-(?:ils?|elles?|[nv]ous|je|tu|on|dire)$", \2) + <<- morphex(\2, ":", ":[123][sp]|>(?:en|y|que?)/") and not re.search("(?i)-(?:ils?|elles?|[nv]ous|je|tu|on|dire)$", \2) -2>> =suggSimil(\2, ":3s", False) # Incohérence avec « \1 » : « \2 » devrait être un verbe. TEST: ne l’{{oubli}} pas TEST: elle ne la {{croix}} pas TEST: ils me les {{laissés}}. @@ -4700,41 +4700,41 @@ TEST: car {{toute}} mon savoir vient d’elle __[i]/gn(gn_toutes_déterminant_nom_fem_plur)__ (tous) +(?:[lcmtsd]es) +({w_2}) @@0,$ - <<- morphex(\2, ":f", ":(?:[123][sp]|[me])") and morphex(word(-1), ":", ":(?:R|[123][sp]|Q)|>(?:[nv]ous|eux) ", True) + <<- morphex(\2, ":f", ":(?:[123][sp]|[me])") and morphex(word(-1), ":", ":(?:R|[123][sp]|Q)|>(?:[nv]ous|eux)/", True) -1>> toutes # Erreur d’accord probable. « \2 » est féminin. <<- __also__ and hasFemForm(\2) -2>> =suggMasPlur(@, True) # Erreur d’accord probable. « \1 » est masculin. __[i]/gn(gn_tous_déterminant_nom_mas_plur)__ (toutes) +(?:[lcmtsd]es) +({w_2}) @@0,$ - <<- morphex(\2, ":m", ":(?:[123][sp]|[fe])") and morphex(word(-1), ":", ":(?:R|[123][sp]|Q)|>(?:[nv]ous|eux) ", True) + <<- morphex(\2, ":m", ":(?:[123][sp]|[fe])") and morphex(word(-1), ":", ":(?:R|[123][sp]|Q)|>(?:[nv]ous|eux)/", True) -1>> tous # Erreur d’accord probable. « \2 » est masculin. <<- __also__ and hasFemForm(\2) -2>> =suggFemPlur(@, True) # Erreur d’accord probable. « \1 » est féminin. TEST: {{tous}} ces {{idiotes}} TEST: {{toutes}} mes {{bars}} __[i]/gn(gn_tout_nom_mas_sing)__ tout ({w3}) @@5 - <<- morphex(\1, ":N.*:[fp]", ":(?:A|W|G|M[12P]|Y|[me]:i|3s)") and morph(word(-1), ":R|>de ", False, True) + <<- morphex(\1, ":N.*:[fp]", ":(?:A|W|G|M[12P]|Y|[me]:i|3s)") and morph(word(-1), ":R|>de/", False, True) -1>> =suggMasSing(@, True) # “\1” devrait être au masculin singulier. __[i]/gn(gn_toute_nom_fem_sing)__ toute ({w3}) @@6 - <<- morph(\1, ":[NAQ].*:[mp]") and morph(word(-1), ":R|>de ", False, True) + <<- morph(\1, ":[NAQ].*:[mp]") and morph(word(-1), ":R|>de/", False, True) -1>> =suggFemSing(@, True) # “\1” devrait être au féminin singulier. __[i]/gn(gn_tous_nom_mas_plur)__ tous ({w3}) @@5 - <<- morph(\1, ":[NAQ].*:[fs]") and morph(word(-1), ":R|>de ", False, True) + <<- morph(\1, ":[NAQ].*:[fs]") and morph(word(-1), ":R|>de/", False, True) -1>> =suggMasPlur(@, True) # “\1” devrait être au masculin pluriel. __[i]/gn(gn_toutes_nom_fem_plur)__ toutes ({w3}) @@7 - <<- morph(\1, ":[NAQ].*:[ms]") and morph(word(-1), ":R|>de ", False, True) + <<- morph(\1, ":[NAQ].*:[ms]") and morph(word(-1), ":R|>de/", False, True) -1>> =suggFemPlur(@, True) # “\1” devrait être au féminin pluriel. TEST: Tout {{hommes}} TEST: De tous {{âge}} ! TEST: avec toutes {{femme}} ->> femmes @@ -4793,11 +4793,11 @@ ne (?:pas|plus|jamais) +(beaucoup|trop|rien) @@$ <<- ~1>> * __[i]/infi(infi_ne)__ ne (?:pas|rien|jamais(?: rien| plus|)|plus(?: jamais| rien| guère|)|guère|point) (?:non plus |)(?:l(?:e(?:ur|s|)|a|ui) |nous |vous |[mtsl]’(?:en |y |)|[mts]e |en |y |)({w_1}) @@$ - <<- not morph(\1, ":(?:Y|W|O[ow])|>que? ", False) and spell(\1) + <<- not morph(\1, ":(?:Y|W|O[ow])|>que?/", False) and spell(\1) -1>> =suggVerbInfi(@) # Le verbe devrait être à l’infinitif. TEST: ne jamais {{cédé}} TEST: ne rien {{finit}} TEST: ne jamais plus s’y {{frottait}} @@ -5273,11 +5273,11 @@ __[i](p_motion_de)__ motions? (de (?:blâme|censure|défiance)) @@$ <<- ~1>> * __[i](p_noix_de)__ noix (de (?:cajou|p[ée]can|coco|lavage|muscade|veau|macadamia)) @@$ <<- ~1>> * __[i](p_nu_comme_un_ver)__ nue?s? (comme (?:un ver|des vers)) @@$ <<- ~1>> * __[i](p_numéro)__ numéro (un|deux|trois|quatre|cinq|six|sept|huit|neuf|dix(?:-sept|-huit|-neuf|)|onze|douze|treize|quatorze|quinze|seize|vingt|trente|quarante|cinquante|soixante(?:-dix|)|quatre-vingt(?:-dix|)|cent|mille|\d+) @@$ - <<- before(r"\b[lL]a +$") =>> define(\0, [">numéro :N:f:s"]) + <<- before(r"\b[lL]a +$") =>> define(\0, [">numéro/:N:f:s"]) <<- ~1>> * __[i](p_oiseau_de)__ oiseaux? (de (?:malheur|nuit|proie|mauvais augure)) @@$ <<- ~1>> * __[i](p_onde_de_choc)__ ondes? (de choc) @@$ <<- ~1>> * __[i](p_orge)__ orge (perlé|mondé|carré) @@$ <<- ~1>> * __[i](p_noire_comme)__ noire?s? (comme (?:la nuit|une nuit sans lune)) @@$ <<- ~1>> * @@ -5420,15 +5420,15 @@ ## Conditionnel __[i](p_à_xxx_pour_cent)__ à ({w_2}) pour cent @@2 <<- morph(\1, ":B", False) ~>> * __[i](p_au_moins)__ (au moins) +({w_1}) @@0,$ <<- not morph(\2, ":[AQ].*:[me]:[si]", False) ~1>> * __[i](p_au_hasard)__ au hasard <<- isEndOfNG() ~>> * __[i](p_aussi_adv_que_possible)__ aussi ({w_2}) que (?:nécessaire|possible) @@6 <<- morph(\1, ":W", False) ~>> * -__[i](p_au_sens_adj_du_terme)__ au sens (?:le (?:plus|moins) |)({w_2}) du terme @@w <<- morph(\1, ":A .*:m:s", False) ~>> * +__[i](p_au_sens_adj_du_terme)__ au sens (?:le (?:plus|moins) |)({w_2}) du terme @@w <<- morph(\1, ":A.*:m:s", False) ~>> * #__[i](p_aussi_xxx_que_ce_soit)__ aussi ({w_2}) que ce soit __[i](p_nombre_de)__ (nombre) des? @@0 <<- morph(word(-1), ":(?:R|C[sc])", False, True) ~1>> * __[i](p_à_xxx_reprises)__ à ({w_2}) reprises @@2 <<- morph(\1, ":B", False) or re.search("(?i)^(?:plusieurs|maintes)", \1) ~>> * -__[i](p_bien_entendu)__ bien entendu <<- morph(word(1), ":[NAQR]|>que? ", False, True) ~>> * +__[i](p_bien_entendu)__ bien entendu <<- morph(word(1), ":[NAQR]|>que?/", False, True) ~>> * __[i](p_comme_pronom)__ ({w_2}) (comme (?:eux|elles?|lui|ça|celui-(?:ci|là)|celles?-(?:ci|là)|ceux(?:ci|là)|l[ea] [nv]ôtre|le [mts]ien|la [mts]ienne|les (?:[nv]ôtres|sien(?:ne|)s))) @@0,$ <<- morphex(\1, ":[NAQ]", ":V0") ~2>> * __[i](p_pêle_mêle)__ ({w_2}) (pêle-mêle) @@0,$ <<- not morph(\1, ":D", False) ~2>> * __[i](p_droit_devant)__ ({w_2}) (droit) devant @@0,w <<- not morph(\1, ":D.*:[me]:[si]", False) ~2>> * @@ -5438,11 +5438,11 @@ __[i](p_du_coup)__ (du coup) ({w_1}) @@0,$ <<- not morph(\2, ":A", False) ~1>> * __[i](p_verbe_pronom_être)__ (d[eouû]\w+|cr[ouû]\w+|pens\w+|imagin\w+|estim\w+) (l(?:eur|ui)|nous|vous) être @@0,w - <<- morph(\1, ">(?:croire|devoir|estimer|imaginer|penser) ") ~2>> * + <<- morph(\1, ">(?:croire|devoir|estimer|imaginer|penser)/") ~2>> * __[i](p_en_partie)__ (en partie) ({w_2}) @@0,$ <<- morph(\1, ":(?:R|D|[123]s|X)", False) ~1>> * __[i](p_en_plus)__ en plus @@ -5528,11 +5528,11 @@ __[i](p_avoir_pronom_loc_adv)__ ({avoir})-(?:je|tu|ils?|elles?|nous|vous|on) +(besoin|bon (?:dos|pied,? bon œil)|carte blanche|confiance|conscience|crainte|faim|forme humaine|honte|partie (?:gagnée|liée)|peur|soif|voix au chapitre) @@0,$ <<- morph(\1, ":V0a", False) ~2>> * __[i](p_avoir_tous_toutes_les)__ ({avoir}) +(tou(?:te|)s les ({w_2})) +({w_2}) @@0,w,>3:$,$ - <<- morph(\1, ":V0a", False) and morph(\3, ":B", False) and morph(\4, ">besoin |:(?:Q|V1.*:Y)", False) ~2>> * + <<- morph(\1, ":V0a", False) and morph(\3, ":B", False) and morph(\4, ">besoin/|:(?:Q|V1.*:Y)", False) ~2>> * # elle aussi + adj __[i](p_elle_aussi)__ (elle aussi) +({w_3}) @@0,$ <<- morph(\2, ":A:[fe]:s", False) ~1>> * @@ -5561,11 +5561,11 @@ __[i](p_les_xxx_les_plus_adj)__ (?:[lmts]es|nos|vos|leurs) ({w_2}) (les plus) ({w_2}) @@w,w,$ <<- morphex(\1, ":[NAQ].*:[pi]", ":[123][sp]") and morph(\3, ":A.*:[pi]", False) ~2>> * __[i](p_le_plus_le_moins)__ (le (?:plus|moins)) ({w_2}) @@0,$ - <<- morph(\2, ":A", ":([me]:[si]|G)") and morph(word(-1), ">(?:avoir|être) :V", False) ~1>> * + <<- morph(\2, ":A", ":([me]:[si]|G)") and morph(word(-1), ">(?:avoir|être)/:V", False) ~1>> * __[i](p_bien_sûr)__ bien sûr(?! de) <<- ~>> * __[i](p_bien_mal_fort_adj_adv)__ (bien|mal|(?:fort|super) (?:bien|mal)|fort) +({w_2}) @@0,$ <<- morph(\2, ":[AW]") ~1>> * @@ -5692,94 +5692,94 @@ ## Simplication des locutions verbales __[i](loc_arriver)__ (arriv\w+) (([aà]) (?:échéance|point nommé)) @@0,$,w - <<- morph(\1, ">arriver ", False) >>> + <<- morph(\1, ">arriver/", False) >>> <<- \3 == "a" -3>> à # Confusion : “a” est une conjugaison du verbe “avoir”. Pour la préposition, écrivez “à”. <<- ~2>> * __[i](p_donner_sens)__ ((?:re|)donn\w+) +(sens) @@0,$ - <<- morph(\1, ">(?:re|)donner ", False) ~2>> * + <<- morph(\1, ">(?:re|)donner/", False) ~2>> * __[i](p_faire_qqch)__ (f[aiîeo]\w*) +(tous(?: deux| trois|) +|)(allusion|amende honorable|assaut|bande à part|bonne figure|chaud|confiance|compliqué|copain[- ]copain|de (?:[mts]on|leur|[nv]otre) mieux|dé(?:bat|faut)|demi-tour|envie|fausse route|figure|froid|front commun|gr(?:ise mine|and (?:bruit|cas))|h(?:alte|onte)|illusion|long feu|ma(?:chine|rche) arrière|main basse|mouche|office|p(?:art(?:ie(?: intégrante|)|)|eur|laisir|rofil bas)|rage|salle comble|scandale|sens|signe|table rase|volte-face|ce que bon (?:me|te|lui|leur|nous|vous) semble) @@0,*,$ - <<- morph(\1, ">faire ", False) ~2>> * + <<- morph(\1, ">faire/", False) ~2>> * <<- __also__ ~3>> * __[i](loc_laisser_pour_compte)__ (laiss\w+) +(pour (co[mnp]tes?)) @@0,$,$ - <<- morph(\1, ">laisser ", False) >>> + <<- morph(\1, ">laisser/", False) >>> <<- \2 != "compte" -3>> compte # Confusion. Locution “laisser pour compte”.|https://fr.wiktionary.org/wiki/laisser_pour_compte <<- ~2>> * __[i](loc_mettre_à_qqch)__ (m(?:et|[iî][mst])\w*) +(([àa]) (?:bas|jour|niveau|plat|l’(?:écart|épreuve)|terre)) @@0,$,w - <<- morph(\1, ">mettre ", False) >>> + <<- morph(\1, ">mettre/", False) >>> <<- \3 == "a" -3>> à # Confusion : “a” est une conjugaison du verbe “avoir”. Pour la préposition, écrivez “à”. <<- ~2>> * __[i](p_mettre_qqch)__ (m(?:et|[iî][mst])\w*) +(au p(?:oint|as)|en (?:avant|bouche|demeure|garde|jeu|lumière|œuvre|place|scène|terre)) @@0,$ - <<- morph(\1, ">mettre ", False) ~2>> * + <<- morph(\1, ">mettre/", False) ~2>> * __[i](loc_mourir_qqch)__ (m[oe]\w+) +(jeûne) @@0,$ - <<- morph(\1, ">mourir ", False) -2>> =\2.replace("û", "u") # Confusion. Le jeûne est une privation de nourriture.|https://fr.wiktionary.org/wiki/jeune + <<- morph(\1, ">mourir/", False) -2>> =\2.replace("û", "u") # Confusion. Le jeûne est une privation de nourriture.|https://fr.wiktionary.org/wiki/jeune __[i](p_paraitre_qqch)__ (par\w+) +(jeûnes?) @@0,$ - <<- morph(\1, ">para[îi]tre ", False) -2>> =\2.replace("û", "u") # Confusion. Le jeûne est une privation de nourriture.|https://fr.wiktionary.org/wiki/jeune + <<- morph(\1, ">para[îi]tre/", False) -2>> =\2.replace("û", "u") # Confusion. Le jeûne est une privation de nourriture.|https://fr.wiktionary.org/wiki/jeune __[i](p_porter_qqch)__ (port\w+) +(atteinte|bonheur|caution|chance|malheur|plainte|préjudice|secours) @@0,$ - <<- morph(\1, ">porter ", False) ~2>> * + <<- morph(\1, ">porter/", False) ~2>> * __[i](loc_prendre_à_la_légère)__ (pr[eiî]\w+) +(([àa]) la légère) @@0,$,w - <<- morph(\1, ">prendre ", False) >>> + <<- morph(\1, ">prendre/", False) >>> <<- \3 == "a" -3>> à # Confusion : “a” est une conjugaison du verbe “avoir”. Pour la préposition, écrivez “à”. <<- ~2>> * __[i](p_prendre)__ (pr[eiî]\w+) +(au (?:dépourvu|sérieux)|congé|conscience|contact|de court|en charge|ombrage|pour argent comptant|par surprise|racine|soin|vie) @@0,$ - <<- morph(\1, ">prendre ", False) ~2>> * + <<- morph(\1, ">prendre/", False) ~2>> * __[i](loc_rendre_compte)__ (rend\w+) +(co[mn]tes?) @@0,$ - <<- morph(\1, ">rendre ", False) -2>> compte # Confusion probable. Locution “rendre compte”.|https://fr.wiktionary.org/wiki/rendre_compte + <<- morph(\1, ">rendre/", False) -2>> compte # Confusion probable. Locution “rendre compte”.|https://fr.wiktionary.org/wiki/rendre_compte <<- ~1>> * __[i](loc_rester_qqch)__ (rest\w+) +(lettre morte|jeûnes?) @@0,$ - <<- morph(\1, ">rester ", False) >>> - <<- morph(\2, ">jeûne ", False) -2>> =\2.replace("û", "u") # Confusion. Le jeûne est une privation de nourriture.|https://fr.wiktionary.org/wiki/jeune + <<- morph(\1, ">rester/", False) >>> + <<- morph(\2, ">jeûne/", False) -2>> =\2.replace("û", "u") # Confusion. Le jeûne est une privation de nourriture.|https://fr.wiktionary.org/wiki/jeune <<- __else__ ~2>> * __[i](loc_semble_qqch)__ (sembl\w+) +(jeûnes?) @@0,$ - <<- morph(\1, ">sembler ", False) -2>> =\2.replace("û", "u") # Confusion. Le jeûne est une privation de nourriture.|https://fr.wiktionary.org/wiki/jeune + <<- morph(\1, ">sembler/", False) -2>> =\2.replace("û", "u") # Confusion. Le jeûne est une privation de nourriture.|https://fr.wiktionary.org/wiki/jeune __[i](p_sembler_paraitre_être)__ (sembl\w+|par[au]\w+) +(être|avoir été) +({w_2}) @@0,w,$ - <<- morph(\1, ">(?:sembler|para[îi]tre) ") and morphex(\3, ":A", ":G") ~2>> * + <<- morph(\1, ">(?:sembler|para[îi]tre)/") and morphex(\3, ":A", ":G") ~2>> * __[i](loc_suivre_de_près)__ (suiv\w+) +((?:ça +|ce(?:ci|la) +|)de (pr[èé]s?|prêts?)) @@0,$,$ - <<- morph(\1, ">suivre ", False) >>> + <<- morph(\1, ">suivre/", False) >>> <<- \3 != "près" -3>> près # Confusion : écrivez “près” pour dire “proche de quelque chose”.|https://fr.wiktionary.org/wiki/pr%C3%A8s <<- ~2>> * __[i](loc_tenir_à_distance)__ (t[eiî]\w+) +(([àa]) distance +(?:respectable +|))d(?:es?|u) @@0,*,w - <<- morph(\1, ">tenir ", False) >>> + <<- morph(\1, ">tenir/", False) >>> <<- \3 == "a" -3>> à # Confusion : “a” est une conjugaison du verbe “avoir”. Pour la préposition, écrivez “à”. <<- ~2>> * __[i](loc_tenir_compte)__ (t[eiî]\w+) +(co(?:mp?|n)tes?|au courant) @@0,$ - <<- morph(\1, ">tenir ", False) >>> - <<- morph(\2, ">co[mn]te(?:sse|) ", False) -2>> compte # Confusion. Dans la locution “tenir compte”, écrivez “compte” au singulier.|https://fr.wiktionary.org/wiki/tenir_compte + <<- morph(\1, ">tenir/", False) >>> + <<- morph(\2, ">co[mn]te(?:sse|)/", False) -2>> compte # Confusion. Dans la locution “tenir compte”, écrivez “compte” au singulier.|https://fr.wiktionary.org/wiki/tenir_compte <<- ~2>> * __[i](p_tirer_profit)__ (tir\w+) +(avantage|profit) d(?:es?|u) @@0,w - <<- morph(\1, ">tirer ", False) ~2>> * + <<- morph(\1, ">tirer/", False) ~2>> * __[i](loc_tourner_court)__ (tourn\w+) +(cour(?:re|t|s|)) @@0,$ - <<- morph(\1, ">tourner ", False) >>> + <<- morph(\1, ">tourner/", False) >>> <<- \2 != "court" -2>> court # Locution : tourner court.|https://fr.wiktionary.org/wiki/tourner_court <<- ~2>> * __[i](p_trier_sur_le_volet)__ (tri\w+) (sur le volet) @@0,$ - <<- morph(\1, ">trier ", False) ~2>> * + <<- morph(\1, ">trier/", False) ~2>> * __[i](p_venir)__ (v[eiî]\w+) ((?:on ne sait|je ne sais) (?:pas |)(?:trop |)d’où) @@0,$ - <<- morph(\1, ">venir ", False) ~2>> * + <<- morph(\1, ">venir/", False) ~2>> * TEST: ce contrat arrive {{a}} échéance. TEST: il faut tenir {{contes}} des faits au lieu de nos impressions. TEST: prendre {{a}} la légère ce test serait une erreur. TEST: on va suivre ça de {{prêt}}. @@ -5873,11 +5873,11 @@ !!!! Redondances dans la phrase __[i]/redon2(redondances_phrase)__ ({w_4})[ ,].* (\1) @@0,$ - <<- not morph(\1, ":(?:G|V0)|>même ", False) -2>> _ # Dans cette phrase, répétition de « \1 » (à gauche). + <<- not morph(\1, ":(?:G|V0)|>même/", False) -2>> _ # Dans cette phrase, répétition de « \1 » (à gauche). <<- __also__ -1>> _ # Dans cette phrase, répétition de « \1 » (à droite). TEST: __redon2__ Quelle {{imposture}}, c’est d’un ennui, c’est une {{imposture}}. TEST: __redon2__ ils sont là côte à côte. TEST: __redon2__ Tu avances petit à petit, et tu réussis. @@ -5928,15 +5928,15 @@ <<- __also__ -1>> les # Accord de nombre erroné : « \2 » est au pluriel. __[i]/gn(gn_le_accord2)__ ({w_1}) +(le) +({w_2}) @@0,w,$ <<- morph(\2, ":D", False) >>> <<- morphex(\3, ":[NAQ].*:f", ":(?:e|m|P|G|W|[123][sp]|Y)") - or ( morphex(\3, ":[NAQ].*:f", ":[me]") and morphex(\1, ":R", ">(?:e[tn]|ou) ") and not (morph(\1, ":Rv", False) and morph(\3, ":Y", False)) ) + or ( morphex(\3, ":[NAQ].*:f", ":[me]") and morphex(\1, ":R", ">(?:e[tn]|ou)/") and not (morph(\1, ":Rv", False) and morph(\3, ":Y", False)) ) -2>> =suggLesLa(\3) # Accord de genre erroné : « \3 » est féminin. <<- __also__ and hasMasForm(\3) -3>> =suggMasSing(@, True) # Accord de genre erroné : « \2 » est un déterminant masculin. <<- __else__ and morph(\3, ":[NAQ].*:p") - or ( morphex(\3, ":[NAQ].*:p", ":[si]") and morphex(\1, ":[RC]", ">(?:e[tn]|ou)") and not (morph(\1, ":Rv", False) and morph(\3, ":Y", False)) ) + or ( morphex(\3, ":[NAQ].*:p", ":[si]") and morphex(\1, ":[RC]", ">(?:e[tn]|ou)/") and not (morph(\1, ":Rv", False) and morph(\3, ":Y", False)) ) -3>> =suggMasSing(@) # Accord de nombre erroné : « \3 » devrait être au singulier. <<- __also__ -2>> les # Accord de nombre erroné : « \3 » est au pluriel. __[i]/gn(gn_le_accord3)__ ^ *(le) +({w_2}) @@*,$ <<- morphex(\2, ":[NAQ].*:f", ":(?:e|m|P|G|W|Y)") -1>> =suggLesLa(\2) # Accord de genre erroné : « \2 » est féminin. @@ -6019,15 +6019,15 @@ <<- __else__ and morph(\2, ":[NAQ].*:p") -2>> =suggFemSing(@) # Accord de nombre erroné : « \2 » devrait être au singulier. __[i]/gn(gn_la_accord2)__ ({w_1}) +(la) +({w_2}) @@0,w,$ <<- morph(\2, ":D", False) >>> <<- morphex(\3, ":[NAQ].*:m", ":(?:e|f|P|G|W|[1-3][sp]|Y)") - or ( morphex(\3, ":[NAQ].*:m", ":[fe]") and morphex(\1, ":[RC]", ">(?:e[tn]|ou) ") and not (morph(\1, ":(?:Rv|C)", False) and morph(\3, ":Y", False)) ) + or ( morphex(\3, ":[NAQ].*:m", ":[fe]") and morphex(\1, ":[RC]", ">(?:e[tn]|ou)/") and not (morph(\1, ":(?:Rv|C)", False) and morph(\3, ":Y", False)) ) -2>> le # Accord de genre erroné : « \3 » est masculin. <<- __also__ and hasFemForm(\3) -3>> =suggFemSing(@, True) # Accord de genre erroné : « \2 » est un déterminant féminin. <<- __else__ and morph(\3, ":[NAQ].*:p") - or ( morphex(\3, ":[NAQ].*:p", ":[si]") and morphex(\1, ":[RC]", ">(?:e[tn]|ou)") and not (morph(\1, ":Rv", False) and morph(\3, ":Y", False)) ) + or ( morphex(\3, ":[NAQ].*:p", ":[si]") and morphex(\1, ":[RC]", ">(?:e[tn]|ou)/") and not (morph(\1, ":Rv", False) and morph(\3, ":Y", False)) ) -3>> =suggFemSing(@) # Accord de nombre erroné : « \3 » devrait être au singulier. __[i]/gn(gn_la_accord3)__ ^ *(la) +({w_2}) @@*,$ <<- morphex(\2, ":[NAQ].*:m", ":[efPGWY]") -1>> le # Accord de genre erroné : « \2 » est masculin. <<- __also__ and hasFemForm(\2) -2>> =suggFemSing(@, True) # Accord de genre erroné : « \1 » est un déterminant féminin. @@ -6129,11 +6129,11 @@ <<- morph(\2, ":[NAQ].*:p") -1>> leurs # Accord de nombre erroné avec « \2 ». <<- __also__ -2>> =suggSing(@) # Accord de nombre erroné : « \2 » devrait être au singulier. __[i]/gn(gn_leur_accord2)__ ({w_1}) +(leur) +({w_2}) @@0,w,$ <<- morph(\3, ":[NAQ].*:p") - or ( morphex(\3, ":[NAQ].*:p", ":[si]") and morphex(\1, ":[RC]|>de ", ">(?:e[tn]|ou)") and not (morph(\1, ":Rv", False) and morph(\3, ":Y", False)) ) + or ( morphex(\3, ":[NAQ].*:p", ":[si]") and morphex(\1, ":[RC]|>de/", ">(?:e[tn]|ou)/") and not (morph(\1, ":Rv", False) and morph(\3, ":Y", False)) ) -2>> leurs # Accord de nombre erroné avec « \3 ». <<- __also__ -3>> =suggSing(@) # Accord de nombre erroné : « \3 » devrait être au singulier. __> leurs # Accord de nombre erroné avec « \1 ». @@ -6168,11 +6168,11 @@ -2>> =suggPlur(@) # Accord de nombre erroné : « \2 » devrait être au pluriel. __[i]/gn(gn_les_accord2)__ ({w_1}) +(les) +({w_2}) @@0,w,$ <<- morph(\2, ":D", False) >>> <<- ( morph(\3, ":[NAQ].*:s") - or (morphex(\3, ":[NAQ].*:s", ":[pi]|>avoir") and morphex(\1, ":[RC]", ">(?:e[tn]|ou) ") and not (morph(\1, ":Rv", False) and morph(\3, ":Y", False))) ) + or (morphex(\3, ":[NAQ].*:s", ":[pi]|>avoir") and morphex(\1, ":[RC]", ">(?:e[tn]|ou)/") and not (morph(\1, ":Rv", False) and morph(\3, ":Y", False))) ) and not (after("^ +(?:et|ou) ") and morph(word(2), ":[NAQ]", True, False)) -3>> =suggPlur(@) # Accord de nombre erroné : « \3 » devrait être au pluriel. __[i]/gn(gn_les_accord3)__ ^ *(les) +({w_2}) @@w,$ <<- (morphex(\2, ":[NAQ].*:s", ":[ipYPGW]") @@ -6457,11 +6457,11 @@ ## trouver ça/ceci/cela + adj __[i]/gn(gn_trouver_ça_adj)__ (trouv\w+) +(ça|ce(?:ci|la)) +({w_2}) @@0,w,$ - <<- morph(\1, ">trouver ", False) and morphex(\3, ":A.*:(?:f|m:p)", ":(?:G|3[sp]|M[12P])") + <<- morph(\1, ">trouver/", False) and morphex(\3, ":A.*:(?:f|m:p)", ":(?:G|3[sp]|M[12P])") -3>> =suggMasSing(@) # Trouver \2 + [adjectif] : l’adjectif s’accorde avec “\2” (au masculin singulier). TEST: ils trouvent ça de plus en plus {{idiots}} ->> idiot @@ -6594,15 +6594,15 @@ __[i]/gn(gn_2m_un_après_et_ou_de)__ (?:et +|ou +|d’)un +({w_2}) +({w_2}) @@w,$ <<- not \2.startswith("seul") and morphex(\1, ":[NAQ].*:[me]", ":(?:B|G|V0|f)") and morph(\2, ":[NAQ].*:f") and not apposition(\1, \2) - and not morph(word(-1), ":[NAQ]|>(?:et|ou) ", False, False) + and not morph(word(-1), ":[NAQ]|>(?:et|ou)/", False, False) -2>> =suggMasSing(@, True) # Accord de genre erroné : « \1 » est masculin, « \2 » est féminin. <<- morphex(\1, ":[NAQ].*:[si]", ":G") and morph(\2, ":[NAQ].*:p") and not \2.startswith("seul") and not apposition(\1, \2) - and not morph(word(-1), ":[NAQB]|>(?:et|ou) ", False, False) + and not morph(word(-1), ":[NAQB]|>(?:et|ou)/", False, False) -2>> =suggMasSing(@) # Accord de nombre erroné avec « \1 » : « \2 » devrait être au singulier. TEST: un exercice pas très {{utiles}}. ->> utile TEST: un homme {{grands}} ->> grand TEST: un homme {{futiles}} ->> futile @@ -6623,15 +6623,15 @@ __[i]/gn(gn_2m_une_après_et_ou_de)__ (?:et +|ou +|d’)une +({w_2}) +({w_2}) @@w,$ <<- not \2.startswith("seul") and morphex(\1, ":[NAQ].*:[fe]", ":(?:B|G|V0|m)") and morph(\2, ":[NAQ].*:m") and not apposition(\1, \2) - and not morph(word(-1), ":[NAQ]|>(?:et|ou) ", False, False) + and not morph(word(-1), ":[NAQ]|>(?:et|ou)/", False, False) -2>> =suggFemSing(@, True) # Accord de genre erroné : « \1 » est féminin, « \2 » est masculin. <<- \1 != "fois" and morph(\1, ":[NAQ].*:[si]", False) and morph(\2, ":[NAQ].*:p") and not \2.startswith("seul") and not apposition(\1, \2) - and not morph(word(-1), ":[NAQB]|>(?:et|ou) ", False, False) + and not morph(word(-1), ":[NAQB]|>(?:et|ou)/", False, False) -2>> =suggFemSing(@) # Accord de nombre erroné avec « \1 » : « \2 » devrait être au singulier. TEST: Une grande {{homme}}. TEST: une géologue {{intelligents}} TEST: Et une femme {{déterminées}} @@ -6655,15 +6655,15 @@ __[i]/gn(gn_2m_le_après_et_ou_de)__ (?:et|ou) +(le) +({w_2}) +({w_2}) @@w,w,$ <<- morph(\1, ":D", False) >>> <<- not \3.startswith("seul") and morphex(\2, ":[NAQ].*:[me]", ":(?:B|G|V0|f)") and morph(\3, ":[NAQ].*:f") - and not apposition(\2, \3) and not morph(word(-1), ":[NAQ]|>(?:et|ou) ", False, False) + and not apposition(\2, \3) and not morph(word(-1), ":[NAQ]|>(?:et|ou)/", False, False) -3>> =suggMasSing(@, True) # Accord de genre erroné : « \2 » est masculin, « \3 » est féminin. <<- not \3.startswith("seul") and morphex(\2, ":[NAQ].*:[si]", ":G") and morphex(\3, ":[NAQ].*:p", ":[GWsi]") - and not apposition(\2, \3) and not morph(word(-1), ":[NAQ]|>(?:et|ou) ", False, False) + and not apposition(\2, \3) and not morph(word(-1), ":[NAQ]|>(?:et|ou)/", False, False) -3>> =suggMasSing(@) # Accord de nombre erroné avec « \2 » : « \3 » devrait être au singulier. TEST: le test très {{cons}} qu’on a passé hier. TEST: c’était le chien {{perdue}} des voisins. TEST: viens vite ou le pari {{imperdables}} sera moins facile… @@ -6681,15 +6681,15 @@ -2>> =suggMasSing(@) # Accord de nombre erroné avec « \1 » : « \2 » devrait être au singulier. __[i]/gn(gn_2m_det_mas_sing_après_et_ou_de)__ (?:et|ou|de) +(?:cet?|quel|au|ledit) +({w_2}) +({w_2}) @@w,$ <<- not \2.startswith("seul") and morphex(\1, ":[NAQ].*:[me]", ":(?:B|G|V0|f)") and morph(\2, ":[NAQ].*:f") - and not apposition(\1, \2) and not morph(word(-1), ":[NAQ]|>(?:et|ou) ", False, False) + and not apposition(\1, \2) and not morph(word(-1), ":[NAQ]|>(?:et|ou)/", False, False) -2>> =suggMasSing(@, True) # Accord de genre erroné : « \1 » est masculin, « \2 » est féminin. <<- not \2.startswith("seul") and morphex(\1, ":[NAQ].*:[si]", ":G") and morphex(\2, ":[NAQ].*:p", ":[GWsi]") - and not apposition(\1, \2) and not morph(word(-1), ":[NAQ]|>(?:et|ou) ", False, False) + and not apposition(\1, \2) and not morph(word(-1), ":[NAQ]|>(?:et|ou)/", False, False) -2>> =suggMasSing(@) # Accord de nombre erroné avec « \1 » : « \2 » devrait être au singulier. TEST: cet outil {{terribles}} qu’il a dans les mains TEST: J’aimerais connaître de quel parti {{gauchistes}} on parle. @@ -6706,16 +6706,16 @@ -2>> =suggMasSing(@) # Accord de nombre erroné avec « \1 » : « \2 » devrait être au singulier. __[i]/gn(gn_2m_mon_ton_son_après_et_ou_de)__ (?:et|ou|de) +[mts]on +({w_2}) +({w_2}) @@w,$ <<- not \2.startswith("seul") and morphex(\1, ":[NAQ].*:m", ":(?:B|G|e|V0|f)") and morph(\2, ":[NAQ].*:f") - and not apposition(\1, \2) and not morph(word(-1), ":[NAQ]|>(?:et|ou) ", False, False) + and not apposition(\1, \2) and not morph(word(-1), ":[NAQ]|>(?:et|ou)/", False, False) -2>> =suggMasSing(@, True) # Accord de genre erroné : « \1 » est masculin, « \2 » est féminin. <<- not \2.startswith("seul") and morphex(\1, ":[NAQ].*:[si]", ":G") and morphex(\2, ":[NAQ].*:p", ":[GWsi]") and not apposition(\1, \2) - and not morph(word(-1), ":[NAQ]|>(?:et|ou) ", False, False) + and not morph(word(-1), ":[NAQ]|>(?:et|ou)/", False, False) -2>> =suggMasSing(@) # Accord de nombre erroné avec « \1 » : « \2 » devrait être au singulier. TEST: il brandissait avec fougue son drapeau {{déchirés}} TEST: comment osez-vous médire de mon héritage {{glorieuse}} @@ -6734,15 +6734,15 @@ __[i]/gn(gn_2m_la_après_et_ou_de)__ (?:et|ou|de) +(la) +({w_2}) +({w_2}) @@w,w,$ <<- morph(\1, ":D", False) >>> <<- \2 != "fois" and not \3.startswith("seul") and morphex(\2, ":[NAQ].*:[fe]", ":(?:B|G|V0|m)") and morph(\3, ":[NAQ].*:m") - and not apposition(\2, \3) and not morph(word(-1), ":[NAQ]|>(?:et|ou) ", False, False) + and not apposition(\2, \3) and not morph(word(-1), ":[NAQ]|>(?:et|ou)/", False, False) -3>> =suggFemSing(@, True) # Accord de genre erroné : « \2 » est féminin, « \3 » est masculin. <<- not \3.startswith("seul") and morphex(\2, ":[NAQ].*:[si]", ":G") and morphex(\3, ":[NAQ].*:p", ":[GWsi]") - and not apposition(\2, \3) and not morph(word(-1), ":[NAQ]|>(?:et|ou) ", False, False) + and not apposition(\2, \3) and not morph(word(-1), ":[NAQ]|>(?:et|ou)/", False, False) -3>> =suggFemSing(@) # Accord de nombre erroné avec « \2 » : « \3 » devrait être au singulier. TEST: La plus grande {{cinglé}}. TEST: il imaginait de la pluie {{noir}} tombant sur une terre dévastée. @@ -6759,15 +6759,15 @@ -2>> =suggFemSing(@) # Accord de nombre erroné avec « \1 » : « \2 » devrait être au singulier. __[i]/gn(gn_2m_det_fem_sing_après_et_ou_de)__ (?:et|ou|de) +(?:[mts]a|cette|quelle|ladite) +({w_2}) +({w_2}) @@w,$ <<- \1 != "fois" and not \2.startswith("seul") and morphex(\1, ":[NAQ].*:[fe]", ":(?:B|G|V0|m)") and morph(\2, ":[NAQ].*:m") - and not apposition(\1, \2) and not morph(word(-1), ":[NAQ]|>(?:et|ou) ", False, False) + and not apposition(\1, \2) and not morph(word(-1), ":[NAQ]|>(?:et|ou)/", False, False) -2>> =suggFemSing(@, True) # Accord de genre erroné : « \1 » est féminin, « \2 » est masculin. <<- not \2.startswith("seul") and morphex(\1, ":[NAQ].*:[si]", ":G") and morphex(\2, ":[NAQ].*:p", ":[GWsi]") - and not apposition(\1, \2) and not morph(word(-1), ":[NAQ]|>(?:et|ou) ", False, False) + and not apposition(\1, \2) and not morph(word(-1), ":[NAQ]|>(?:et|ou)/", False, False) -2>> =suggFemSing(@) # Accord de nombre erroné avec « \1 » : « \2 » devrait être au singulier. TEST: quelle belle {{étourdi}}, cette gamine TEST: j’en ai assez de cette ville {{stressées}} en permanence. TEST: Peut-on imaginer de plus {{beaux}} {{enfant}} ? @@ -6789,16 +6789,16 @@ (?:et|ou|de) +(leur) +({w_2}) +({w_2}) @@w,w,$ <<- morph(\1, ":D", False) >>> <<- \2 != "fois" and not \3.startswith("seul") and ((morphex(\2, ":[NAQ].*:m", ":(?:B|e|G|V0|f)") and morph(\3, ":[NAQ].*:f")) or (morphex(\2, ":[NAQ].*:f", ":(?:B|e|G|V0|m)") and morph(\3, ":[NAQ].*:m"))) and not apposition(\2, \3) - and not morph(word(-1), ":[NAQ]|>(?:et|ou) ", False, False) + and not morph(word(-1), ":[NAQ]|>(?:et|ou)/", False, False) -3>> =switchGender(@, False) # Accord de genre erroné entre « \2 » et « \3 ». <<- __also__ and hasFemForm(\2) -1>> =switchGender(@, False) # Accord de genre erroné avec « \3 ». <<- not \3.startswith("seul") and morphex(\2, ":[NAQ].*:[si]", ":G") and morphex(\3, ":[NAQ].*:p", ":[GWsi]") - and not apposition(\2, \3) and not morph(word(-1), ":[NAQ]|>(?:et|ou) ", False, False) + and not apposition(\2, \3) and not morph(word(-1), ":[NAQ]|>(?:et|ou)/", False, False) -3>> =suggSing(@) # Accord de nombre erroné avec « \2 » : « \3 » devrait être au singulier. TEST: leur puissance {{perdues}} TEST: leur arbre {{élaguée}} TEST: je me souviens de leur verve {{décalé}} @@ -6819,16 +6819,16 @@ __[i]/gn(gn_2m_det_epi_sing_après_et_ou_de)__ (?:et|ou|de) +(?:chaque|quelque|[nv]otre) +({w_2}) +({w_2}) @@w,$ <<- \1 != "fois" and not \2.startswith("seul") and not re.search("(?i)quelque chose", \0) and ((morphex(\1, ":[NAQ].*:m", ":(?:B|e|G|V0|f)") and morph(\2, ":[NAQ].*:f")) or (morphex(\1, ":[NAQ].*:f", ":(?:B|e|G|V0|m)") and morph(\2, ":[NAQ].*:m"))) and not apposition(\1, \2) - and not morph(word(-1), ":[NAQ]|>(?:et|ou) ", False, False) + and not morph(word(-1), ":[NAQ]|>(?:et|ou)/", False, False) -2>> =switchGender(@, False) # Accord de genre erroné entre « \1 » et « \2 ». <<- __also__ and hasFemForm(\1) -1>> =switchGender(@, False) # Accord de genre erroné avec « \2 ». <<- not \2.startswith("seul") and morphex(\1, ":[NAQ].*:[si]", ":G") and morphex(\2, ":[NAQ].*:p", ":[GWsi]") - and not apposition(\1, \2) and not morph(word(-1), ":[NAQ]|>(?:et|ou) ", False, False) + and not apposition(\1, \2) and not morph(word(-1), ":[NAQ]|>(?:et|ou)/", False, False) -2>> =suggSing(@) # Accord de nombre erroné avec « \1 » : « \2 » devrait être au singulier. TEST: chaque élément {{terrestres}} TEST: ils viennent de chaque coin {{ignorée}} du pays. @@ -6847,11 +6847,11 @@ __[i]/gn(gn_2m_det_mas_plur_après_et_ou_de)__ (?:et|ou|de) +(?:certains|quels|lesdits) +({w_2}) +({w_2}) @@w,$ <<- not \2.startswith("seul") and morphex(\1, ":[NAQ].*:[me]", ":(?:B|G|V0|f)") and morph(\2, ":[NAQ].*:f") and not apposition(\1, \2) - and not morph(word(-1), ":[NAQ]|>(?:et|ou) ", False, False) + and not morph(word(-1), ":[NAQ]|>(?:et|ou)/", False, False) -2>> =suggMasPlur(@, True) # Accord de genre erroné : « \1 » est masculin, « \2 » est féminin. <<- not \2.startswith("seul") and morphex(\1, ":[NAQ].*:[pi]", ":G") and morph(\2, ":[NAQ].*:s") and not apposition(\1, \2) and not (after_chk1(r"^ +et +(\w[\w-]+)", ":A") or after_chk1(r"^ *, +(\w[\w-]+)", ":A.*:[si]")) and not ( before(r"(?i)\bune? de ") or (\0.startswith("de") and before(r"(?i)\bune? +$")) ) @@ -6877,11 +6877,11 @@ __[i]/gn(gn_2m_det_fem_plur_après_et_ou_de)__ (?:et|ou|de) +(?:certaines|quelles|lesdites) +({w_2}) +({w_2}) @@w,$ <<- \1 != "fois" and not \2.startswith("seul") and morphex(\1, ":[NAQ].*:[fe]", ":(?:B|G|V0|m)") and morph(\2, ":[NAQ].*:m") and not apposition(\1, \2) - and not morph(word(-1), ":[NAQ]|>(?:et|ou) ", False, False) + and not morph(word(-1), ":[NAQ]|>(?:et|ou)/", False, False) -2>> =suggFemPlur(@, True) # Accord de genre erroné : « \1 » est féminin, « \2 » est masculin. <<- not \2.startswith("seul") and morph(\1, ":[NAQ].*:[pi]", False) and morph(\2, ":[NAQ].*:s") and not apposition(\1, \2) and not (after_chk1(r"^ +et +(\w[\w-]+)", ":A") or after_chk1(r"^ *, +(\w[\w-]+)", ":A.*:[si]")) and not ( before(r"(?i)\bune? de ") or (\0.startswith("de") and before(r"(?i)\bune? +$")) ) @@ -6907,11 +6907,11 @@ (?:et|ou) +(les) +({w_2}) +({w_2}) @@w,w,$ <<- morph(\1, ":D", False) >>> <<- \2 != "fois" and not \3.startswith("seul") and ((morphex(\2, ":[NAQ].*:m", ":(?:B|e|G|V0|f)") and morph(\3, ":[NAQ].*:f")) or (morphex(\2, ":[NAQ].*:f", ":(?:B|e|G|V0|m)") and morph(\3, ":[NAQ].*:m"))) and not apposition(\2, \3) - and not morph(word(-1), ":[NAQ]|>(?:et|ou) ", False, False) + and not morph(word(-1), ":[NAQ]|>(?:et|ou)/", False, False) -3>> =switchGender(@, True) # Accord de genre erroné entre « \2 » et « \3 ». <<- __also__ and hasFemForm(\2) -2>> =switchGender(@, True) # Accord de genre erroné avec « \3 ». <<- \2 != "fois" and not \3.startswith("seul") and morph(\2, ":[NAQ].*:[pi]", False) and morph(\3, ":[NAQ].*:s") and not apposition(\2, \3) and not (after_chk1(r"^ +et +(\w[\w-]+)", ":A") or after_chk1(r"^ *, +(\w[\w-]+)", ":A.*:[si]")) @@ -6937,11 +6937,11 @@ __[i]/gn(gn_2m_det_epi_plur_après_et_ou_de)__ (?:et|ou|de) +(?:[cmts]es|[nv]os|leurs|quelques|plusieurs|aux|moult) +({w_2}) +({w_2}) @@w,$ <<- \1 != "fois" and not \2.startswith("seul") and ((morphex(\1, ":[NAQ].*:m", ":(?:B|e|G|V0|f)") and morph(\2, ":[NAQ].*:f")) or (morphex(\1, ":[NAQ].*:f", ":(?:B|e|G|V0|m)") and morph(\2, ":[NAQ].*:m"))) and not apposition(\1, \2) - and not morph(word(-1), ":[NAQ]|>(?:et|ou) ", False, False) + and not morph(word(-1), ":[NAQ]|>(?:et|ou)/", False, False) -2>> =switchGender(@, True) # Accord de genre erroné entre « \1 » et « \2 ». <<- __also__ and hasFemForm(\1) -1>> =switchGender(@, True) # Accord de genre erroné avec « \2 ». <<- \1 != "fois" and not \2.startswith("seul") and morph(\1, ":[NAQ].*:[pi]", False) and morph(\2, ":[NAQ].*:s") and not apposition(\1, \2) and not (after_chk1(r"^ +et +(\w[\w-]+)", ":A") or after_chk1(r"^ *, +(\w[\w-]+)", ":A.*:[si]")) @@ -6958,16 +6958,16 @@ __[i]/gn(gn_2m_des)__ des +({w_2}) +({w_2}) @@w,$ <<- \1 != "fois" and not \2.startswith("seul") and ( (morphex(\1, ":[NAQ].*:m", ":[fe]") and morph(\2, ":[NAQ].*:f")) or (morphex(\1, ":[NAQ].*:f", ":[me]") and morph(\2, ":[NAQ].*:m")) ) and not apposition(\1, \2) and not (after_chk1(r"^ +et +(\w[\w-]+)", ":A") or after_chk1(r"^ *, +(\w[\w-]+)", ":A.*:[si]")) - and morph(word(-1), ":[VRBX]|>comme ", True, True) + and morph(word(-1), ":[VRBX]|>comme/", True, True) -2>> =switchGender(@, True) # Accord de genre erroné avec « \1 ». <<- __also__ and hasFemForm(\1) -1>> =switchGender(@) # Accord de genre erroné avec « \2 ». <<- morph(\1, ":[NAQ].*:[pi]", False) and morph(\2, ":[NAQ].*:s") and not apposition(\1, \2) and not (after_chk1(r"^ +et +(\w[\w-]+)", ":A") or after_chk1(r"^ *, +(\w[\w-]+)", ":A.*:[si]")) - and (morphex(\2, ":N", ":[AQ]") or morph(word(-1), ":[VRBX]|>comme ", True, True)) + and (morphex(\2, ":N", ":[AQ]") or morph(word(-1), ":[VRBX]|>comme/", True, True)) -2>> =suggPlur(@) # Accord de nombre erroné avec « \1 » : « \2 » devrait être au pluriel. <<- checkAgreement(\1, \2) =>> =exclude(\2, ":V") TEST: faire table rase des passions {{inutile}} ->> inutiles TEST: à bonne distance des {{chiens}} {{méchante}} @@ -7231,11 +7231,11 @@ #### Locutions # à __[i]/sgpl(sgpl_à_nu)__ (m[eiî]\w+) +([aà] nu(?:es?|s)) @@0,$ - <<- morph(\1, ">(?:mettre|mise) ", False) -2>> à nu # « nu » est invariable dans cette locution. + <<- morph(\1, ">(?:mettre|mise)/", False) -2>> à nu # « nu » est invariable dans cette locution. TEST: Mettre {{à nus}} les hommes. __[i]/sgpl(sgpl_à_part_égales)__ à part? égale? <<- ->> à parts égales # Il y a plusieurs parts. @@ -7251,14 +7251,14 @@ # affaires __[i]/sgpl(sgpl_chiffre_d_affaires)__ chiffres? d’(affaire) @@$ <<- -1>> affaires # Le chiffre d’affaires. Toujours un “s” final. __[i]/sgpl(sgpl_faire_affaire_avec)__ (f[aieî]\w+) (affaires) avec @@0,w - <<- morph(\1, ">faire ", False) -2>> affaire # « Faire affaire avec ». Pas de “s”. + <<- morph(\1, ">faire/", False) -2>> affaire # « Faire affaire avec ». Pas de “s”. __[u]/sgpl(sgpl_faire_affaire_à_en)__ (f[aieî]\w+) (affaire) (?:à|en) ([A-ZÉÈÂ][\w-]+) @@0,w,$ - <<- morph(\1, ">faire ", False) and morph(\3, ":(?:N|MP)") + <<- morph(\1, ">faire/", False) and morph(\3, ":(?:N|MP)") -2>> affaires # Ajoutez un “s” à « affaire ». TEST: Quel est son chiffre d’{{affaire}} ? TEST: Allez-vous faire {{affaires}} avec ces connards ? TEST: Faire {{affaire}} à Paris. @@ -7313,20 +7313,20 @@ # coûter cher __[i]/sgpl(sgpl_coûter_cher)__ ((?:co[uû]t|pa)\w+) +(chers|chères?|chaire?s?) @@0,$ - <<- morph(\1, ">(?:co[ûu]ter|payer) ", False) + <<- morph(\1, ">(?:co[ûu]ter|payer)/", False) -2>> cher # Ici, « cher » est un adverbe, invariable. TEST: ces saloperies coûtent vraiment {{chères}} ! # donner lieu __[i]/sgpl(sgpl_donner_lieu)__ (donn\w+) +(lieux) @@0,$ - <<- morph(\1, ">donner ", False) + <<- morph(\1, ">donner/", False) -2>> lieu # « Donner lieu » : “lieu” est invariable dans cette locution verbale. TEST: ces conneries donneront {{lieux}} à une enquête approfondie. @@ -7338,11 +7338,11 @@ # ensemble __[i]/sgpl(sgpl_ensemble)__ ({w_1}) +(ensembles) @@0,$ - <<- morphex(\1, ":V.*:[123]p|>(?:tou(?:te|)s|pas|rien|guère|jamais|toujours|souvent) ", ":[DRB]") + <<- morphex(\1, ":V.*:[123]p|>(?:tou(?:te|)s|pas|rien|guère|jamais|toujours|souvent)/", ":[DRB]") -2>> ensemble # S’il s’agit bien de l’adverbe “ensemble”, il est invariable.|https://fr.wiktionary.org/wiki/ensemble TEST: Elles viendront {{ensembles}}. @@ -7369,11 +7369,11 @@ # pied __[i]/sgpl(sgpl_avoir_pied)__ ([aeop]\w*) +(?:pas |)(pieds) @@0,$ - <<- morph(\1, ">(?:avoir|perdre) ", False) -2>> pied # Pas de “s” final. + <<- morph(\1, ">(?:avoir|perdre)/", False) -2>> pied # Pas de “s” final. __[i]/sgpl(sgpl_à_pied)__ à (pieds) @@2 <<- not before(r"(?i)\b(?:lit|fauteuil|armoire|commode|guéridon|tabouret|chaise)s?\b") -1>> pied # Pas de “s” final. __[i]/sgpl(sgpl_au_pied_levé)__ @@ -7465,11 +7465,11 @@ # vacances __[i]/sgpl(sgpl_bonnes_vacances)__ bonne vacance <<- not morph(word(-1), ":D.*:f:s", False, False) ->> bonnes vacances # Au pluriel. __[i]/sgpl(sgpl_en_vacances)__ ({w1}) +en (vacance) @@0,$ - <<- morph(\1, ">(?:aller|partir) ", False) -2>> vacances # Si vous parlez des congés, « vacance » doit être au pluriel. + <<- morph(\1, ">(?:aller|partir)/", False) -2>> vacances # Si vous parlez des congés, « vacance » doit être au pluriel. TEST: Je pars en {{vacance}}. TEST: {{Bonne vacance}} ! TEST: Il nous reste un poste en vacance. TEST: Cette place est en vacance. @@ -7503,19 +7503,19 @@ !! # à / a __[i]/conf(conf_suite_à)__ suite (a) ({w1}) @@w,$ - <<- morph(\2, ":D|>[ld] ", False) and isStart() -1>> à # Confusion : “a” est une forme conjuguée du verbe “avoir”. Pour la préposition, écrivez “à”. + <<- morph(\2, ":D|>[ld]/", False) and isStart() -1>> à # Confusion : “a” est une forme conjuguée du verbe “avoir”. Pour la préposition, écrivez “à”. TEST: Suite {{a}} ces folies, nous rentrâmes chez nous. TEST: il s’avère que, suite {{a}} d’horribles complications, nous renonçâmes. __[i]/conf(conf_pronom_à_l_air)__ (?:tout|ça|ce(?:ci|la)) (à) l’air +({w_2}) @@w,$ - <<- morphex(\2, ":[AR]", ">libre ") and morph(word(-1), ":Cs", False, True) + <<- morphex(\2, ":[AR]", ">libre/") and morph(word(-1), ":Cs", False, True) -1>> a # Confusion probable : “à” est une préposition. Pour le verbe “avoir”, écrivez “a”. TEST: lorsque tout {{à}} l’air fini, c’est trompeur. TEST: Tout {{à}} l’air complètement foutu… TEST: Ça {{à}} l’air génial. @@ -7550,21 +7550,21 @@ TEST: un terrain de 3 {{âcres}}. __[i]/conf(conf_âcre)__ acres? - <<- morph(word(-1), ">(?:être|go[ûu]t|humeur|odeur|parole|parfum|remarque|reproche|réponse|saveur|senteur|sensation|vin)", False, False) + <<- morph(word(-1), ">(?:être|go[ûu]t|humeur|odeur|parole|parfum|remarque|reproche|réponse|saveur|senteur|sensation|vin)/", False, False) ->> =\0.replace("a", "â").replace("A", "Â") # Confusion probable : “acre” est une unité de surface agraire. Pour l’adjectif signifiant “irritant”, écrivez :|https://fr.wiktionary.org/wiki/%C3%A2cre TEST: Il avait ce goût {{acre}} dans la bouche qui ne passait pas. # accro / accroc __[i]/conf(conf_être_accro)__ ({etre}|dev\w+|sembl\w+|par\w+|rend\w+) +(accrocs?) @@0,$ - <<- morph(\1, ">(?:être|devenir|para[îi]tre|rendre|sembler) ", False) + <<- morph(\1, ">(?:être|devenir|para[îi]tre|rendre|sembler)/", False) -2>> =\2.replace("oc", "o") # Confusion : “accroc” signifie “déchirure”, “incident”, etc. tandis que “accro” est un terme familier qui signifie “dépendant”. __[i]/conf(conf_accro_à)__ (accrocs?) (?:[àa] (?:la (?:bouffe|cocaïne|cod[ée]ine|course|drogue|coke|meth|méthamphétamine|morphine|nicotine|nourriture|télé(?:vision|)|clope|cigarette|came|poudre|baise|musique)|cette (?:came|émission|merde|poudre|femme|meuf|gonzesse|conne|salope|garce)|ce (?:mec|keum|type|con(?:nard|)|fils de pute)|cet (?:homme|enculé|imbécile|enfoiré)|l’(?:alcool|amour|argent|ecstasy|herbe|héro(?:ïne|)|opium|ordi(?:nateur|))|Facebook|Internet|Twitter|lui|elle)|[ad]u (?:chocolat|cul|jeu|poker|sexe|shopping|smartphone|sport|sucre|tabac|téléphone|travail|LSD|crack)|aux (?:anti-?dépresseurs|bonbons|hommes|mecs|femmes|gonzesses|méd(?:icaments|ocs)|jeux|séries|sucreries)) @@0 @@ -7584,11 +7584,11 @@ __[i]/conf(conf_par_acquit_de_conscience)__ par (acquis) de conscience @@4 <<- -1>> acquit # Confusion. On écrit « par acquit de conscience ». <<- ~>> * __[i]/conf(conf_tenir_pour_acquit)__ - (t\w+) +pour (acquits?) @@0,$ <<- morph(\1, ">tenir ") -2>> acquis # Confusion. On écrit « tenir pour acquis ». + (t\w+) +pour (acquits?) @@0,$ <<- morph(\1, ">tenir/") -2>> acquis # Confusion. On écrit « tenir pour acquis ». TEST: Je le tenais pour {{acquit}}. TEST: Par {{acquis}} de conscience. @@ -7608,14 +7608,14 @@ # amende / amande __[i]/conf(conf_yeux_en_amande)__ yeux en (amendes?) @@$ <<- -1>> amande # Confusion. Une amende est une peine.|http://www.cnrtl.fr/lexicographie/amende __[i]/conf(conf_à_l_amende)__ - (m\w+) à (l’amande) @@0,$ <<- morph(\1, ">mettre ", False) -2>> l’amende # Confusion. L’amande est un fruit. + (m\w+) à (l’amande) @@0,$ <<- morph(\1, ">mettre/", False) -2>> l’amende # Confusion. L’amande est un fruit. __[i]/conf(conf_faire_amende_honorable)__ (f\w+)(?:-(?:je|tu|ils?|[nv]ous|elles?)|) +(amandes? honorables?) @@0,$ - <<- morph(\1, ">faire ", False) -2>> amende honorable # Confusion. L’amande est un fruit. + <<- morph(\1, ">faire/", False) -2>> amende honorable # Confusion. L’amande est un fruit. TEST: Avec ses beaux yeux en {{amendes}} nul ne peut lui résister. TEST: Nous avons déconné, nous avons été mis à {{l’amande}}. TEST: Ces gens-là ne feront jamais {{amande honorable}}. @@ -7651,11 +7651,11 @@ # Confusion probable. L’hospice est un centre de soins.|https://fr.wiktionary.org/wiki/auspice __[i]/conf(conf_sous_les_auspices2)__ sous (?:les \w+|d’\w+|des? \w+) +(hospices) @@$ <<- -1>> auspices # Confusion probable. L’hospice est un centre de soins.|https://fr.wiktionary.org/wiki/auspice __[i]/conf(conf_hospice1)__ - ({etre}|{aller}) +(?:à|dans) l’(auspice) @@0,$ <<- morph(\1, ">(?:être|aller) ", False) -2>> hospice + ({etre}|{aller}) +(?:à|dans) l’(auspice) @@0,$ <<- morph(\1, ">(?:être|aller)/", False) -2>> hospice # Confusion. Les auspices sont des présages, des devins ou, au sens moderne, les circonstances.|https://fr.wiktionary.org/wiki/auspice __[i]/conf(conf_hospice2)__ dans (?:un|cet|[ldc]es) +(auspices?) @@$ <<- -1>> =\1.replace("auspice", "hospice") # Confusion. Les auspices sont des présages, des devins ou, au sens moderne, les circonstances.|https://fr.wiktionary.org/wiki/auspice __[i]/conf(conf_hospice3)__ @@ -7698,15 +7698,15 @@ __[i]/conf(conf_en_rupture_de_ban)__ en ruptures? de (bancs?) @@$ <<- -1>> ban # Confusion. Locution “en rupture de ban”.|https://fr.wiktionary.org/wiki/en_rupture_de_ban __[i]/conf(conf_mettre_au_ban)__ (m[eiî]\w+) au (banc) @@0,$ - <<- morph(\1, ">mettre ", False) and not after("^ +des accusés") + <<- morph(\1, ">mettre/", False) and not after("^ +des accusés") -2>> ban # Confusion : « mettre au ban » signifie « faire déchoir ».|https://fr.wiktionary.org/wiki/mettre_au_ban __[i]/conf(conf_publier_les_bans)__ (publi\w+) [dlcmts]es (bancs) @@0,$ - <<- morph(\1, ">publi(?:er|cation) ", False) -2>> bans # Confusion.|https://fr.wikipedia.org/wiki/Publication_des_bans + <<- morph(\1, ">publi(?:er|cation)/", False) -2>> bans # Confusion.|https://fr.wikipedia.org/wiki/Publication_des_bans TEST: Convoquons le ban et l’{{arrière-banc}}. TEST: il faut publier les {{bancs}} avant qu’il ne soit trop tard. TEST: Les {{bancs}} de mariage sont prêts. TEST: des hommes en rupture de {{banc}} @@ -7782,11 +7782,11 @@ __[i]/conf(conf_nom_de_cane)__ (?:œuf|filet)s? de (cannes?) @@$ <<- -1>> cane # Confusion. La canne est un bâton ou un roseau. Pour la femelle du canard, écrivez|https://fr.wiktionary.org/wiki/canne __[i]/conf(conf_verbe_canne)__ ((?:appu|batt|frapp|l[eè]v|march)\w+) (?:avec|sur) (?:[dl]es|[mts](?:a|es)|une) (canes?) @@0,$ - <<- morph(\1, ">(?:appuyer|battre|frapper|lever|marcher) ", False) + <<- morph(\1, ">(?:appuyer|battre|frapper|lever|marcher)/", False) -2>> =\2.replace("cane", "canne") # Confusion. La cane est la femelle du canard.|https://fr.wiktionary.org/wiki/cane __[i]/conf(conf_bec_de_cane)__ becs?-de-(cannes?) @@$ <<- -1>> cane # Confusion. Le bec-de-cane se somme ainsi à cause de la ressemblance avec le bec de l’animal.|https://fr.wiktionary.org/wiki/bec-de-cane @@ -7810,11 +7810,11 @@ # chair / chère __[i]/conf(conf_faire_bonne_chère)__ (f[aioîe]\w+) +(bonnes? ch(?:ai|e)re?) @@0,$ - <<- morph(\1, ">faire ", False) + <<- morph(\1, ">faire/", False) -2>> bonne chère # Confusion. « Faire bonne chère » signifie bien manger, ripailler. TEST: ils ont fait {{bonne chaire}}. @@ -7928,15 +7928,15 @@ TEST: Merci de calculer le {{coup}} de production avant d’établir une facture. TEST: Elle a un {{coût}} si gracile. __[i]/conf(conf_tordre_le_cou)__ - (tord\w*) +le (co[uû][pt]s?) @@0,$ <<- morph(\1, ">tordre ", False) -2>> cou + (tord\w*) +le (co[uû][pt]s?) @@0,$ <<- morph(\1, ">tordre/", False) -2>> cou # Confusion. Le coût indique ce que ça coûte. Un coup, c’est quelque chose qui frappe. La partie séparant la tête du corps s’écrit “cou”. __[i]/conf(conf_rendre_coup_pour_coup)__ (rend\w*) +(co[uû]t?s? pour co[uû]t?s?) @@0,$ - <<- morph(\1, ">rendre ", False) -2>> coup pour coup + <<- morph(\1, ">rendre/", False) -2>> coup pour coup # Confusion. Le coût indique ce que ça coûte. Un cou est la partie séparant la tête du corps. Pour ce qui frappe, écrivez “coup”. TEST: Je vais tordre le {{coup}} à toutes ces idées stupides, une par une. TEST: Implacable, elle a rendu {{cout pour cout}} sans se départir de son calme. @@ -7945,14 +7945,14 @@ __[i]/conf(conf_au_cours_de)__ au (court?) (?:des?|du?) @@3 <<- -1>> cours # Confusion probable. Une cour… Un cours… Adjectif : court(e). __[i]/conf(conf_en_cours)__ en cour(?! martiale?| de (?:cassation|justice)| d’assises) <<- ->> en cours # Confusion probable. Une cour… Un cours… Adjectif : court(e). __[i]/conf(conf_couper_court)__ - (coup\w+) (cours?) @@0,$ <<- morph(\1, ">couper ") -2>> court # « Couper court ». Écourter. Une cour… Un cours… Adjectif : court(e). + (coup\w+) (cours?) @@0,$ <<- morph(\1, ">couper/") -2>> court # « Couper court ». Écourter. Une cour… Un cours… Adjectif : court(e). __[i]/conf(conf_laisser_libre_cours)__ ({w1}) +libre (court?) @@0,$ - <<- morph(\1, ">(?:avoir|donner|laisser) ", False) -2>> cours # Confusion probable. Ce qui a « libre cours ».|https://fr.wiktionary.org/wiki/donner_libre_cours + <<- morph(\1, ">(?:avoir|donner|laisser)/", False) -2>> cours # Confusion probable. Ce qui a « libre cours ».|https://fr.wiktionary.org/wiki/donner_libre_cours __[i]/conf(conf_à_court_de)__ à (cours?) de? @@2 <<- -1>> court # Confusion probable : écrivez « à court de … » pour « manquer de … » __[i]/conf(conf_à_court_terme)__ à cour(?:s|ts|) termes? <<- ->> à court terme # Confusion. Une cour… Un cours… Adjectif : court(e). @@ -7982,16 +7982,16 @@ # desceller / déceler / desseller __[i]/conf(conf_erreur_problème_decelé)__ (erreur|faute|incohérence|problème|bug|bogue|faille|maladie|défaut|défaillance|perturbation|irrégularité)s? .*(des[cs]ell\w+) @@0,$ - <<- morph(\2, ">(?:desceller|desseller) ", False) + <<- morph(\2, ">(?:desceller|desseller)/", False) -2>> =\2.replace("escell", "écel").replace("essell", "écel") # Confusion probable si ce mot se rapporte à « \1 ». Desceller signifie briser un sceau, un cachet… Desseller signifie ôter une selle. Si vous voulez dire “remarquer”, “dévoiler”, “découvrir”, écrivez :|http://fr.wiktionary.org/wiki/déceler __[i]/conf(conf_deceler_qqch)__ (des[cs]ell\w+) +(?:(?:une?|l[ae]|des?|ce(?:tte|t|s|)|[mts](?:on|a|es)|[nv]os|leurs?|plusieurs|quelques|deux|trois|quatre|cinq|six|sept|huit|neuf|dix|onze|douze) +|l’)(?:(?:petite?|grande?|énorme|dangeureu(?:x|se)|formidable|forte?|lég(?:er|ère)|merveilleu(?:x|se)|nouv(?:el|elle|eaux?)|vraie?|réel(?:le|)|sévère|véritable)s? +|)(acidité|activité|allergie|anévrisme|anomalie|arnaque|appendicite|atrophie|baisse|bébé|blessure|bug|bogue|carie|cancer|cause|changement|complot|comète|concentration|corrélation|croissance|défaut|défaillance|demande|dépression|diabète|différence|diminution|effluve|épilepsie|erreur|essai|existence|grossesse|grosseur|faille|faute|fuite|fraude|grippe|handicap|hausse|hémorragie|hostilité|hypertrophie|incompatibilité|incohérence|infection|infraction|indice|infidélité|insuffisance|intrigue|irrégularité|leucémie|lésion|lueur|lumière|maladie|malformation|manœuvre|manipulation|molécule|mensonge|mutation|once|perturbation|personnalité|piste|perte|planète|exoplanète|présence|qualité|odeur|opportunité|otite|problème|surdité|talent|tendance|tentative|tumeur|utilisation|hoax|variation|vie|virus)s? @@0,$ - <<- morph(\1, ">(?:desceller|desseller) ", False) + <<- morph(\1, ">(?:desceller|desseller)/", False) -1>> =\1.replace("escell", "écel").replace("essell", "écel") # Confusion probable si ce mot se rapporte à « \2 ». Desceller signifie briser un sceau, un cachet… Desseller signifie ôter une selle.|http://fr.wiktionary.org/wiki/déceler TEST: il y a une erreur qu’on peut {{desceller}} dans ses analyses. TEST: elle a {{dessellé}} une forte hostilité dans ses propos. @@ -7998,21 +7998,21 @@ # en train / entrain __[i]/conf(conf_en_train)__ entrain - <<- morph(word(-1), ">(?:être|voyager|surprendre|venir|arriver|partir|aller) ", False, False) or before("-(?:ils?|elles?|on|je|tu|nous|vous) +$") + <<- morph(word(-1), ">(?:être|voyager|surprendre|venir|arriver|partir|aller)/", False, False) or before("-(?:ils?|elles?|on|je|tu|nous|vous) +$") ->> en train # Confusion. L’entrain est une fougue, une ardeur à accomplir quelque chose.|https://fr.wiktionary.org/wiki/entrain TEST: Vous êtes {{entrain}} de vaincre. TEST: Viennent-ils {{entrain}} ? TEST: ces idiots sont en train de tout foutre en l’air. __[i]/conf(conf_entrain)__ en train - <<- morph(word(-1), ">(?:avec|sans|quel(?:le|)|cet|votre|notre|mon|leur) ", False, False) or before(" [dlDL]’$") + <<- morph(word(-1), ">(?:avec|sans|quel(?:le|)|cet|votre|notre|mon|leur)/", False, False) or before(" [dlDL]’$") ->> entrain # Confusion. Soudez les deux mots. L’entrain est une fougue, une ardeur à accomplir quelque chose.|https://fr.wiktionary.org/wiki/entrain TEST: Avec quel {{en train}}, ils nous ont mené jusque là-haut. TEST: Son manque d’{{en train}} était contagieux. TEST: c’est l’{{en train}} de cette jeune femme qui force l’admiration de tout le monde. @@ -8020,11 +8020,11 @@ # envi / envie __[i]/conf(conf_à_l_envi)__ à l’(envie) @@4 - <<- not morph(word(-1), ">(?:abandonner|céder|résister) ", False) and not after("^ d(?:e |’)") + <<- not morph(word(-1), ">(?:abandonner|céder|résister)/", False) and not after("^ d(?:e |’)") -1>> envi # Locution adverbiale « à l’envi », signifiant « autant que possible ». TEST: Ils s’amusèrent à l’{{envie}} et oublièrent tous leurs soucis. TEST: Je résiste à l’envie de manger du chocolat. TEST: On ne s’intéresse pas à l’école ni à l’âge, mais aux compétences et à l’envie de partager. @@ -8097,11 +8097,11 @@ (mauvaise|bonne) (fois) @@0,$ <<- not ( \1 == "bonne" and before(r"(?i)\bune +$") and after("(?i)^ +pour toute") ) -2>> foi # Confusion probable.|http://fr.wiktionary.org/wiki/foi __[i]/conf(conf_faire_perdre_donner_foi)__ ((?:f[aieî]|perd|donn|[ae])\w*) (fois) @@0,$ - <<- morph(\1, ">(?:faire|perdre|donner|avoir) ", False) -2>> foi # Confusion probable.|http://fr.wiktionary.org/wiki/foi + <<- morph(\1, ">(?:faire|perdre|donner|avoir)/", False) -2>> foi # Confusion probable.|http://fr.wiktionary.org/wiki/foi TEST: C’est une personne de bonne {{fois}}. TEST: Mais il a perdu {{fois}} en l’avenir. @@ -8251,17 +8251,17 @@ TEST: elle se {{l’a}} {{réserve}} pour elle-même. __[i]/conf(conf_il_elle_on_l_a)__ (?:il|elle|on) (?:vous |nous |)(la)[ @]+({w_2}) @@*,$ - <<- morphex(\2, ":Q", ":(?:[123][sp]|V[123]......e)|>lui ") -1>> l’a # Confusion probable : “\2” est un participe passé. Il faut donc employer l’auxiliaire “avoir”. + <<- morphex(\2, ":Q", ":(?:[123][sp]|V[123]......e)|>lui/") -1>> l’a # Confusion probable : “\2” est un participe passé. Il faut donc employer l’auxiliaire “avoir”. __[i]/conf(conf_ne_l_a)__ ne (?:vous |nous |)(la)[ @]+({w_2}) @@*,$ - <<- morphex(\2, ":Q", ":(?:[123][sp]|V[123]......e)|>lui ") -1>> l’a # Confusion probable : “\2” est un participe passé. Il faut donc employer l’auxiliaire “avoir”. + <<- morphex(\2, ":Q", ":(?:[123][sp]|V[123]......e)|>lui/") -1>> l’a # Confusion probable : “\2” est un participe passé. Il faut donc employer l’auxiliaire “avoir”. __[i]/conf(conf_me_te_l_a)__ [mt]e (la)[ @]+({w_2}) @@*,$ - <<- morphex(\2, ":Q", ":(?:[123][sp]|V[123]......e)|>lui ") -1>> l’a # Confusion probable : “\2” est un participe passé. Il faut donc employer l’auxiliaire “avoir”. + <<- morphex(\2, ":Q", ":(?:[123][sp]|V[123]......e)|>lui/") -1>> l’a # Confusion probable : “\2” est un participe passé. Il faut donc employer l’auxiliaire “avoir”. TEST: il {{la}} {{donnée}}. TEST: ne {{la}} {{donné}} que contraint et forcé… TEST: celle-là, il me {{la}} {{commandée}} ? @@ -8284,11 +8284,11 @@ # lever un lièvre / soulever __[i]/conf(conf_lever_un_lièvre)__ (soul\w+) +(?:un|le) lièvre @@0 - <<- morph(\1, ">soulever ", False) -1>> =\1[3:] + <<- morph(\1, ">soulever/", False) -1>> =\1[3:] # Expression impropre. On écrit « lever un lièvre ».|http://fr.wiktionary.org/wiki/lever_le_li%C3%A8vre TEST: j’ai {{soulevé}} un lièvre, là ! @@ -8302,16 +8302,16 @@ @@0 <<- -1>> lieux # Confusion probable. Pour désigner un endroit, utilisez “lieu(x)”.|http://fr.wiktionary.org/wiki/lieu __[i]/conf(conf_être_à_xxx_lieues)__ ((?:[eêsf]|demeur|habit|trouv|situ|rest)\w+) à (?:quelques|dix|douze|quinze|seize|vingt|cent|mille|des|\d+) (lieu[sx]) @@0,$ - <<- morph(\1, ">(?:être|habiter|trouver|situer|rester|demeurer?) ", False) + <<- morph(\1, ">(?:être|habiter|trouver|situer|rester|demeurer?)/", False) -2>> lieues # Confusion probable. Pour désigner une distance, utilisez “lieues”.|http://fr.wiktionary.org/wiki/lieue __[i]/conf(conf_avoir_eu_lieu)__ ({avoir}) +(?:eue?s? +|)(lieu(?:es?|x)) @@0,$ - <<- morph(\1, ">avoir ", False) -2>> lieu # Confusion. Dans l’expression « avoir lieu », “lieu” est invariable. + <<- morph(\1, ">avoir/", False) -2>> lieu # Confusion. Dans l’expression « avoir lieu », “lieu” est invariable. TEST: qui est le responsable des {{lieues}} ? TEST: ce sont des {{lieus}} mythiques TEST: elle habitait à quelques {{lieux}} d’ici TEST: Cette réunion ayant eu {{lieue}} loin d’ici @@ -8474,11 +8474,11 @@ __[i]/conf(conf_pain_qqch)__ (pins?) (?:d’épices?|perdus?|sans glutens?) @@0 <<- -1>> =\1.replace("pin", "pain") # Confusion. Le pin est un arbre résineux à aiguilles persistantes. Pour parler la pâte de farine et d’eau cuite au four, écrivez : __[i]/conf(conf_manger_pain)__ ((?:mang|dévor|aval|englout)\w+) +(?:les?|d(?:u|es)|un|[mts](?:on|es)|leurs?|[nv]o(?:s|tre)) +(pins?) @@0,$ - <<- morph(\1, ">(?:manger|dévorer|avaler|engloutir) ") -2>> =\2.replace("pin", "pain") + <<- morph(\1, ">(?:manger|dévorer|avaler|engloutir)/") -2>> =\2.replace("pin", "pain") # Confusion probable. Le pin est un arbre résineux à aiguilles persistantes. Pour parler la pâte de farine et d’eau cuite au four, écrivez : __[i]/conf(conf_pomme_de_pin)__ pommes? de (pains?) @@$ <<- -1>> pin # Le pain est une pâte de farine et d’eau cuite au four. La pomme de pin est le fruit du pin.|https://fr.wiktionary.org/wiki/pomme_de_pin @@ -8488,11 +8488,11 @@ # pair / paire __[i]/conf(conf_aller_de_pair)__ ((?:all|v|ir)\w+) de (pair(?:es?|s)|perd?s?) @@0,$ - <<- morph(\1, ">aller ", False) -2>> pair # Confusion. On écrit « aller de pair ». + <<- morph(\1, ">aller/", False) -2>> pair # Confusion. On écrit « aller de pair ». TEST: Ils vont de {{paires}}. # pâle / pale @@ -8509,19 +8509,19 @@ TEST: Sous une lumière {{pale}}, # parti / partie __[i]/conf(conf_prendre_parti)__ - (pr\w+) +(parti(?:s|es?)) @@0,$ <<- morph(\1, ">prendre ", False) -2>> parti # Confusion. On écrit « prendre parti ». + (pr\w+) +(parti(?:s|es?)) @@0,$ <<- morph(\1, ">prendre/", False) -2>> parti # Confusion. On écrit « prendre parti ». __[i]/conf(conf_tirer_parti)__ - (tir\w+) +(parti(?:s|es?)) @@0,$ <<- morph(\1, ">tirer ", False) -2>> parti # Confusion. On écrit « tirer parti ». + (tir\w+) +(parti(?:s|es?)) @@0,$ <<- morph(\1, ">tirer/", False) -2>> parti # Confusion. On écrit « tirer parti ». __[i]/conf(conf_faire_partie)__ - (f[aieoî]\w+) +(parti(?:s|es|)) @@0,$ <<- morph(\1, ">faire ", False) -2>> partie # Confusion. On écrit « faire partie ». + (f[aieoî]\w+) +(parti(?:s|es|)) @@0,$ <<- morph(\1, ">faire/", False) -2>> partie # Confusion. On écrit « faire partie ». __[i]/conf(conf_juge_et_partie)__ juges? et partis? <<- ->> juge et partie|juges et parties # Confusion. On écrit « être juge et partie ». __[i]/conf(conf_prendre_à_partie)__ - (pr\w+) +(?:{w_2} +|)([àa] partis?) @@0,$ <<- morph(\1, ">prendre ", False) -2>> à partie # Confusion. On écrit « prendre à partie ». + (pr\w+) +(?:{w_2} +|)([àa] partis?) @@0,$ <<- morph(\1, ">prendre/", False) -2>> à partie # Confusion. On écrit « prendre à partie ». TEST: Elle prend toujours {{partie}} aux réunions. TEST: Il faut savoir tirer {{partis}} de ces atouts-là. TEST: Tu fais {{parti}} de l’élite, enfin, façon de parler. TEST: Nous sommes tous d’une manière ou d’une autre {{juge et parti}}. @@ -8666,11 +8666,11 @@ # raisonner / résonner __[i]/conf(conf_raisonner)__ (?:la|les?|[mts]e|[nv]ous) (résonn\w+) @@$ - <<- morph(\1, ">résonner ", False) -1>> =\1.replace("réso", "raiso") # Confusion probable. Vous utilisez la raison, mais vous ne « sonnez » pas. + <<- morph(\1, ">résonner/", False) -1>> =\1.replace("réso", "raiso") # Confusion probable. Vous utilisez la raison, mais vous ne « sonnez » pas. TEST: Vous {{résonnez}} comme un sot. TEST: Nous allons le {{résonner}}. @@ -8728,20 +8728,20 @@ __[i]/conf(conf_qqch_septique)__ (?:fosse|installation|choc|chirurgie|maladie|plaie|blessure|embolie|arthrite|isolement|pneumo-entérite)s? (sceptiques?) @@$ <<- -1>> =\1.replace("scep","sep") # Confusion possible. Septique = corrompu, infecté. Sceptique = ayant des doutes. __[i]/conf(conf_être_sceptique)__ ({etre}|demeur\w+) +(septiques?) @@0,$ - <<- morph(\1, ">(?:être|demeurer) ", False) -2>> =\2.replace("sep", "scep") + <<- morph(\1, ">(?:être|demeurer)/", False) -2>> =\2.replace("sep", "scep") # Confusion possible. Septique = corrompu, infecté. Sceptique = ayant des doutes. TEST: cette fosse {{sceptique}} est pleine. TEST: Je suis {{septique}} ! # s’ensuivre __[i]/conf(conf_s_ensuivre)__ - s’en (sui\w+) @@$ <<- morph(\1, ">suivre ", False) ->> s’en\1 # Verbe « s’ensuivre ». + s’en (sui\w+) @@$ <<- morph(\1, ">suivre/", False) ->> s’en\1 # Verbe « s’ensuivre ». TEST: {{S’en suivit}} une guerre de tous les instants. TEST: {{S’en suivre}}. @@ -8762,11 +8762,11 @@ -1>> soi # Confusion probable. __[i]/conf(conf_quel_que_soit2)__ quel(?:le|)s? que (soi(?:es?|)) @@$ <<- -1>> soit|soient # Confusion probable. __[i]/conf(conf_soi_même1)__ (soi[tes]s? mêmes?) @@$ - <<- morph(word(-1), ":[YQ]|>(?:avec|contre|par|pour|sur) ", False, True) -1>> soi-même # Confusion probable : moi-même, toi-même, lui-même, elle-même, soi-même, elles-mêmes, eux-mêmes. + <<- morph(word(-1), ":[YQ]|>(?:avec|contre|par|pour|sur)/", False, True) -1>> soi-même # Confusion probable : moi-même, toi-même, lui-même, elle-même, soi-même, elles-mêmes, eux-mêmes. __[i]/conf(conf_soi_même2)__ soi[tes]s?-mêmes? <<- ->> soi-même # Confusion : moi-même, toi-même, lui-même, elle-même, soi-même, elles-mêmes, eux-mêmes. TEST: chez {{soit}}, c’est presque toujours mieux. TEST: ce n’est pas la philosophie en {{soit}} qui est problématique @@ -8819,11 +8819,11 @@ # tâche / tache (de chocolat / rousseur / vin / sang / café / gras / graisse / huile / etc.) __[i]/conf(conf_tache_de_qqch)__ (tâches?) d(?:e +|’)({w_2}) @@0,$ - <<- morphex(\2, ":N", ":[GMY]|>(?:fonds?|grande (?:envergure|ampleur|importance)|envergure|ampleur|importance|départ|surveillance) ") and not before("accompl|dél[éè]gu") + <<- morphex(\2, ":N", ":[GMY]|>(?:fonds?|grande (?:envergure|ampleur|importance)|envergure|ampleur|importance|départ|surveillance)/") and not before("accompl|dél[éè]gu") -1>> =\1.replace("â", "a") # Confusion probable. Une tâche est un travail à accomplir. Pour une salissure, une altération, une marque, une coloration… employez “tache”. __[i]/conf(conf_tache_adjectif)__ (tâches?) +(?:indélébile|rouge|verte|noire|bleue|jaune|grise|blanche|brune|pourpre|chocolat|mauve|fushia|violette|rose|claire|sombre)s? @@0 <<- -1>> =\1.replace("â", "a") @@ -8839,14 +8839,14 @@ # taule / tôle __[i]/conf(conf_aller_en_taule)__ ({aller}) +en (t[ôo]les?) @@0,$ - <<- morph(\1, ">aller ", False) -2>> taule # Confusion. La tôle est une plaque de métal laminé. Pour la prison, écrivez : + <<- morph(\1, ">aller/", False) -2>> taule # Confusion. La tôle est une plaque de métal laminé. Pour la prison, écrivez : __[i]/conf(conf_faire_de_la_taule)__ (f[aiî]\w+) +de la (t[ôo]les?) @@0,$ - <<- morph(\1, ">faire ", False) -2>> taule # Confusion. La tôle est une plaque de métal laminé. Pour la prison, écrivez : + <<- morph(\1, ">faire/", False) -2>> taule # Confusion. La tôle est une plaque de métal laminé. Pour la prison, écrivez : __[i]/conf(conf_tôle_qqch)__ (taules?) (?:(?:boulonné|cintré|émaillé|embouti|galvanisé|gaufré|nervuré|ondulé|perforé|soudé|translucide)e?s?|(?:d(?:e |’)|en )(?:acier|alu|aluminium|bardage|cuivre|étanchéité|fer|festonnage|inox|laiton|métal|trapèze|zinc|éverite|fibro-?ciment|plastique|polycarbonate|PVC)s?) @@0 <<- -1>> =\1.replace("au", "ô") # Confusion. La taule est la forme argotique pour évoquer la prison, le bordel ou toute forme d’habitation. TEST: Demain, il va aller en {{tôle}}. @@ -8857,11 +8857,11 @@ # tant / temps (2e partie) __[i]/conf(conf_en_tant_que)__ en (temps|tan) que? @@3 <<- -1>> tant # Confusion. Écrivez « en tant que ».|http://fr.wiktionary.org/wiki/en_tant_que __[i]/conf(conf_il_être_tant_de)__ il ({etre}) +(tant?) d(?:e |’)({infi}|ne|en|y) @@3,w,$ - <<- morph(\1, ":V0e", False) and morph(\3, ":Y|>(?:ne|en|y) ", False) + <<- morph(\1, ":V0e", False) and morph(\3, ":Y|>(?:ne|en|y)/", False) -2>> temps # Confusion. TEST: en {{tan}} que meneuse intrépide, elle a toujours fait preuve d’une grande imagination. TEST: il est bien évidemment {{tant}} d’en finir avec ça. @@ -8879,11 +8879,11 @@ # tort / tord __[i]/conf(conf_à_tort)__ à (tor[de]?s?) @@2 <<- -1>> tort # Confusion : “tord” est une conjugaison du verbe tordre. __[i]/conf(conf_avoir_tort)__ ({avoir}|donn\w+) +(tor[ed]?s?) @@0,$ - <<- morph(\1, ">(?:avoir|donner) ", False) -2>> tort # Confusion : “tord” est une conjugaison du verbe tordre. + <<- morph(\1, ">(?:avoir|donner)/", False) -2>> tort # Confusion : “tord” est une conjugaison du verbe tordre. TEST: elles seront à {{tord}} accusées. TEST: ils ont {{tords}}… TEST: ils ont {{tord}}. TEST: ils n’ont pas {{tord}}. @@ -9048,11 +9048,11 @@ # les langues __[s]/maj(maj_langues)__ ((?:parl|cours|leçon|appr|étud|tradu|enseign|professeur|enseignant|dictionnaire|méthode)\w*) (?:le |d[eu] |l’|d’|qu |)(Afrikaans|Albanais|Allemand|Alsacien|Anglais|Arabe|Aragonais|Arménien|Asturien|Basque|Bengali|Biélorusse|Birman|Bosniaque|Breton|Bulgare|Cantonais|Catalan|Cherokee|Chinois|Corse|Cornique|Coréen|Croate|Danois|Écossais|Espagnol|Espéranto|Estonien|Féroïen|Farsi|Finnois|Flamand|Français|Frison|Galicien|Gallois|Gaulois|Géorgien|Grec|Gujarati|Hakka|Hawaïen|Hébreu|Hindi|Hollandais|Hongrois|Javanais|Ido|Indonésien|Interlingua|Islandais|Italien|Irlandais|Japonais|Kazakh|Khmer|Kurde|Ladino|Laotien|Latin|Ligurien|Limbourgeois|Lituanien|Lombard|Luxembourgeois|Macédonien|Malais|Maldivien|Malgache|Maltais|Mandarin|Maori|Marathi|Marwari|Moldave|Mongol|Napolitain|Néerlandais|Norvégien|Occitan|Ourdou|Ouzbek|Persan|Peul|Piémontais|Polonais|Portugais|Provençal|Quichua|Romanche|Roumain|Russe|Sans[ck]rit|Sarde|Serbe|Sicilien|Sindhi|Slovaque|Slovène|Soudanais|Sorabe|Suédois|Swahili|Tagalog|Tahitien|Tamoul|Tatar|Tchèque|Thaï|Turc|Ukrainien|Vénitien|Vietnamien|Volapük|Wallon|Wo?u|Yiddish|Xhosa|Xiang|Zoulou) @@0,$ - <<- morph(\1, ">(?:parler|cours|leçon|apprendre|étudier|traduire|enseigner|professeur|enseignant|dictionnaire|méthode) ", False) + <<- morph(\1, ">(?:parler|cours|leçon|apprendre|étudier|traduire|enseigner|professeur|enseignant|dictionnaire|méthode)/", False) -2>> =\2.lower() # Si vous parlez de la langue, pas de majuscule. __[s]/maj(maj_en_langue)__ (?:[Ee]n )(Afrikaans|Albanais|Allemand|Alsacien|Anglais|Arabe|Aragonais|Arménien|Asturien|Basque|Bengali|Biélorusse|Birman|Bosniaque|Breton|Bulgare|Cantonais|Catalan|Cherokee|Chinois|Cornique|Coréen|Croate|Danois|Écossais|Espagnol|Espéranto|Estonien|Féroïen|Farsi|Finnois|Flamand|Français|Frison|Galicien|Gallois|Gaulois|Géorgien|Grec|Gujarati|Hakka|Hawaïen|Hébreu|Hindi|Hollandais|Hongrois|Javanais|Ido|Indonésien|Interlingua|Islandais|Italien|Irlandais|Japonais|Kazakh|Khmer|Kurde|Ladino|Laotien|Latin|Ligurien|Limbourgeois|Lituanien|Lombard|Luxembourgeois|Macédonien|Malais|Maldivien|Malgache|Maltais|Mandarin|Maori|Marathi|Marwari|Moldave|Mongol|Napolitain|Néerlandais|Norvégien|Occitan|Ourdou|Ouzbek|Persan|Peul|Piémontais|Polonais|Portugais|Provençal|Quichua|Romanche|Roumain|Russe|Sans[ck]rit|Sarde|Serbe|Sicilien|Sindhi|Slovaque|Slovène|Soudanais|Sorabe|Suédois|Swahili|Tagalog|Tahitien|Tamoul|Tatar|Tchèque|Thaï|Turc|Ukrainien|Vénitien|Vietnamien|Volapük|Wallon|Wo?u|Yiddish|Xhosa|Xiang|Zoulou) @@3 <<- -1>> =\1.lower() # Si vous parlez de la langue, pas de majuscule. @@ -9187,11 +9187,11 @@ TEST: y {{mangée}} était un supplice __[i]/infi(infi_pour)__ pour ({w_2}(?:ée?s?|ez)) @@5 - <<- morphex(\1, ":V1", ":[NM]") and not morph(word(-1), ">(?:tenir|passer) ", False) + <<- morphex(\1, ":V1", ":[NM]") and not morph(word(-1), ">(?:tenir|passer)/", False) -1>> =suggVerbInfi(@) # Le verbe devrait être à l’infinitif. TEST: pour {{mangé}} à sa faim, il faudra chasser. TEST: C’est pour {{attaqué}} la journée. @@ -9219,11 +9219,11 @@ TEST: vous {{mangé}} __[i]/infi(infi_devoir_savoir_pouvoir_interrogatif)__ (d[eouû]\w+|s[auû]\w+|p[eouû]\w+|v[eo]u\w+)-(?:ils?|elles?|on|je|tu|nous|vous) +(?:pas +|)(?:[mts](?:e +|’)|lui +|[nv]ous +|)({w_2}) @@0,$ - <<- morph(\1, ">(?:devoir|savoir|pouvoir|vouloir) ", False) and morphex(\2, ":(?:Q|A|[123][sp])", ":[GYW]") + <<- morph(\1, ">(?:devoir|savoir|pouvoir|vouloir)/", False) and morphex(\2, ":(?:Q|A|[123][sp])", ":[GYW]") -2>> =suggVerbInfi(@) # Après « \1 » , le verbe devrait être à l’infinitif. TEST: Peuvent-elles s’{{installaient}} ici ? TEST: Peut-il {{chassé}} ces intrus ? TEST: Ne veux-tu pas {{gardé}} ton boulot ? @@ -9239,41 +9239,41 @@ TEST: Est-ce que Pierre Xazzz va bien ? TEST: Qu’est-ce que rapporte réellement Dassault & Co au budget __[i]/infi(infi_commencer_finir_par)__ ((?:commen[cç]|fin[iî])\w+) +par ({w_2}(?:ée?s?|ai[st])) @@0,$ - <<- morph(\1, ">(?:commencer|finir) ", False) and morphex(\2, ":V", ":[NGM]") and not \2[0:1].isupper() + <<- morph(\1, ">(?:commencer|finir)/", False) and morphex(\2, ":V", ":[NGM]") and not \2[0:1].isupper() -2>> =suggVerbInfi(@) # Le verbe devrait être à l’infinitif. TEST: commence par {{mangé}} le poulet. TEST: enfin la petite finit par {{pleuré}} à chaudes larmes. TEST: sa tournée, elle la finit par Rodez. __[i]/infi(infi_verbe_de)__ ((?:cess|dé[cf]|sugg[éè]r|command|essa|tent|chois|perm[eiî]t|interd)\w*) +(?:pas |plus |point |guère |jamais |peu |rien |) *(?:de +|d’)({w_2}(?:ée?s?|ez)) @@0,$ - <<- morph(\1, ">(?:cesser|décider|défendre|suggérer|commander|essayer|tenter|choisir|permettre|interdire) ", False) and analysex(\2, ":(?:Q|2p)", ":M") + <<- morph(\1, ">(?:cesser|décider|défendre|suggérer|commander|essayer|tenter|choisir|permettre|interdire)/", False) and analysex(\2, ":(?:Q|2p)", ":M") -2>> =suggVerbInfi(@) # Le verbe devrait être à l’infinitif. TEST: cessez d’{{anesthésié}} ces gens ! ## INFINITIFS ERRONÉS __[i]/infi(infi_adjectifs_masculins_singuliers)__ ^ *(?:le|un|cet?|[mts]on|quel) (?!verbe)({w_2}) +({w_2}er) @@w,$ - <<- morphex(\1, ":N.*:m:[si]", ":G") and morphex(\2, ":Y", ">aller |:(?:M|N.*:m:s)") and isNextVerb() + <<- morphex(\1, ":N.*:m:[si]", ":G") and morphex(\2, ":Y", ">aller/|:(?:M|N.*:m:s)") and isNextVerb() -2>> =suggVerbPpas(\2, ":m:s") # Confusion probable : “\2” est à verbe à l’infinitif. Pour l’adjectif, écrivez : __[i]/infi(infi_adjectifs_féminins_singuliers)__ ^ *(?:la|une|cette|[mts]a|quelle) ({w_2}) +({w_2}er) @@w,$ - <<- morphex(\1, ":N.*:f:[si]", ":G") and morphex(\2, ":Y", ">aller |:M") and isNextVerb() + <<- morphex(\1, ":N.*:f:[si]", ":G") and morphex(\2, ":Y", ">aller/|:M") and isNextVerb() -2>> =suggVerbPpas(\2, ":f:s") # Confusion probable : “\2” est à verbe à l’infinitif. Pour l’adjectif, écrivez : __[i]/infi(infi_adjectifs_singuliers)__ ^ *(?:leur|[nv]otre) ({w_2}) +({w_2}er) @@w,$ - <<- morphex(\1, ":N.*:[si]", ":G") and morphex(\2, ":Y", ">aller |:M") and isNextVerb() + <<- morphex(\1, ":N.*:[si]", ":G") and morphex(\2, ":Y", ">aller/|:M") and isNextVerb() -2>> =suggVerbPpas(\2, ":s") # Confusion probable : “\2” est à verbe à l’infinitif. Pour l’adjectif, écrivez : TEST: ce tableau {{voler}} coûte très cher. TEST: la difficulté {{passer}} t’aidera par la suite TEST: leur compte {{épurer}} servira encore. @@ -9280,11 +9280,11 @@ TEST: Le vieux cocher avait mission __[i]/infi(infi_adjectifs_pluriels)__ ^ *(?:[lmtsc]es|[nv]os|leurs|quel(?:le|)s) ({w_1}[sxz]) +({w_2}er) @@w,$ - <<- morphex(\1, ":N.*:[pi]", ":G") and morphex(\2, ":Y", ">aller |:M") and isNextVerb() + <<- morphex(\1, ":N.*:[pi]", ":G") and morphex(\2, ":Y", ">aller/|:M") and isNextVerb() -2>> =suggVerbPpas(\2, ":p") # Confusion probable : “\2” est à verbe à l’infinitif. Pour l’adjectif, écrivez : TEST: les documents {{scanner}} ne sont pas lisibles. TEST: tes doutes {{remâcher}} deviennent difficiles à vivre. @@ -9320,11 +9320,11 @@ \w+-(?:je|tu|ils?|elles?|on|[nv]ous) (pas|point|rien|bien|ensemble) @@$ <<- ~1>> * # sembler le croire/penser/présumer/supposer/envisager/imaginer __[i](p_que_semble_le_penser)__ que +(sembl\w+) +(l(?:e (?:penser|croire|présumer|supposer)|’(?:envisager|imaginer))) @@w,$ - <<- morph(\1, ">sembler ", False) ~2>> * + <<- morph(\1, ">sembler/", False) ~2>> * ### tous / tout / toute / toutes __[i](p_tout_det_mas)__ (tout) (?:le|ce|[mts]on|leur) @@0 <<- ~1>> * __[i](p_toute_det_fem)__ (toute) (?:la|cette|[mts](?:a|on)|leur) @@0 <<- ~1>> * __[i](p_tous_det_plur)__ (tou(?:te|)s) (?:[csmlt]es|[vn]os|leurs) @@0 <<- ~1>> * @@ -9341,11 +9341,11 @@ __[i](p_en_tant_que_tel)__ en tant que tel(?:s|lles?|) <<- ~>> * # de + __[i](p_de_vinfi)__ d(?:e |’)({infi}) @@$ - <<- morphex(\1, ":V[123]_i", ">(?:devenir|rester|demeurer) ") and isNextNotCOD() ~>> * + <<- morphex(\1, ":V[123]_i", ">(?:devenir|rester|demeurer)/") and isNextNotCOD() ~>> * __[i](p_de_manière_façon_xxx_et_xxx)__ de (?:manière|façon) +(?:non +|)({w_2}) +et +(?:non +|)({w_2}) @@w,$ <<- morph(\1, ":A", False) and morphex(\2, ":A", ":[GM]") ~>> * __[i](p_de_manière_façon)__ de (?:manière|façon) +(?:non +|)({w_2}) @@$ @@ -9395,11 +9395,11 @@ ## doute que __[i](p_nul_doute_que)__ nul doute qu <<- isStart() ~>> * __[i](p_douter_que)__ - (dout\w+)( ) *que? @@0,* <<- morph(\1, ">douter ", False) and before(r"(?i)\b(?:[mts]e|[nv]ous) +$") ~2>> , + (dout\w+)( ) *que? @@0,* <<- morph(\1, ">douter/", False) and before(r"(?i)\b(?:[mts]e|[nv]ous) +$") ~2>> , ## de + __[i](p_de_nom)__ d(?:e +|’)(?!autres)({w_2}) @@$ <<- morphex(\1, ":N", ":[GY]") and isEndOfNG() ~>> * @@ -9439,11 +9439,11 @@ -2>> =suggVerbPpas(\2, ":m:p") # Erreur de numérisation ? __[i]/ocr(ocr_avoir_participes_passés)__ ({avoir}) +({w_2}es?) @@0,$ <<- morph(\1, ":V0a", False) >>> - <<- \2.endswith("e") and morphex(\2, ":V1.*:Ip.*:[13]s", ":[GM]|>envie ") + <<- \2.endswith("e") and morphex(\2, ":V1.*:Ip.*:[13]s", ":[GM]|>envie/") -2>> =suggVerbPpas(\2, ":m:s") # Erreur de numérisation ? <<- __else__ and \2.endswith("s") and morphex(\2, ":V1.*:Ip.*:2s", ":[GM]") -2>> =suggVerbPpas(\2, ":m:p") # Erreur de numérisation ? TEST: __ocr__ vous serez {{couche}} en terre. @@ -9518,11 +9518,11 @@ ## soit / soie / soi __[i]/conf(conf_aller_de_soi)__ ({aller}) +de (soi[tes]) @@0,$ - <<- morph(\1, ">aller", False) and not after(" soit ") -2>> soi # Confusion.|https://fr.wiktionary.org/wiki/aller_de_soi + <<- morph(\1, ">aller/", False) and not after(" soit ") -2>> soi # Confusion.|https://fr.wiktionary.org/wiki/aller_de_soi TEST: cela ne va pas de {{soit}}. @@ -9529,11 +9529,11 @@ !!!! Adverbes après verbe # fort __[i]/sgpl(sgpl_verbe_fort)__ ({w_2}) +(forts) @@0,$ - <<- morphex(\1, ":V", ":[AN].*:[me]:[pi]|>(?:être|sembler|devenir|re(?:ster|devenir)|para[îi]tre|appara[îi]tre) .*:(?:[123]p|P|Q)|>(?:affirmer|trouver|croire|désirer|estime|préférer|penser|imaginer|voir|vouloir|aimer|adorer|souhaiter) ") + <<- morphex(\1, ":V", ":[AN].*:[me]:[pi]|>(?:être|sembler|devenir|re(?:ster|devenir)|para[îi]tre|appara[îi]tre)/.*:(?:[123]p|P|Q)|>(?:affirmer|trouver|croire|désirer|estime|préférer|penser|imaginer|voir|vouloir|aimer|adorer|souhaiter)/") and not morph(word(1), ":A.*:[me]:[pi]", False) -2>> fort # Confusion probable. S’il s’agit ici de l’adverbe “fort” (équivalent de “fortement”), écrivez-le au singulier. TEST: ces emmerdeurs crient bien trop {{forts}} TEST: ces animaux paraissent forts, mais ils sont faibles. @@ -9596,11 +9596,11 @@ TEST: de me le {{facturez}} __[i]/infi(infi_faire_vouloir)__ ((?:fai|f[iî]|fer|fon|v[oe]u)\w+) +({w_2}(?:ée?s?|ez)) @@0,$ - <<- morph(\1, ">(?:faire|vouloir) ", False) and not before(r"(?i)\b(?:en|[mtsld]es?|[nv]ous|un) +$") and morphex(\2, ":V", ":M") + <<- morph(\1, ">(?:faire|vouloir)/", False) and not before(r"(?i)\b(?:en|[mtsld]es?|[nv]ous|un) +$") and morphex(\2, ":V", ":M") and not (re.search("(?i)^(?:fait|vouloir)$", \1) and \2.endswith("é")) and not (re.search("(?i)^(?:fait|vouloir)s$", \1) and \2.endswith("és")) -2>> =suggVerbInfi(@) # Le verbe devrait être à l’infinitif. TEST: Tu fais {{décoloré}} tes cheveux ? @@ -9613,11 +9613,11 @@ TEST: fait pourtant avéré et corroboré par le même sondage. __[i]/infi(infi_me_te_se_faire)__ [mts]e (f(?:ai|[iî]|er|on)\w+) +({w_2}(?:ée?s?|ez)) @@0,$ - <<- morph(\1, ">faire ", False) and morphex(\2, ":V", ":M") + <<- morph(\1, ">faire/", False) and morphex(\2, ":V", ":M") -2>> =suggVerbInfi(@) # Le verbe devrait être à l’infinitif. TEST: me faire constamment {{laminé}} au jeu, ça finit par me fâcher. @@ -9629,11 +9629,11 @@ TEST: Je suis fatigué de vouloir {{essayé}} d’y remédier. __[i]/infi(infi_savoir)__ (s[auû]\w+) +({w_2}(?:ée?s?|ez)) @@0,$ - <<- morph(\1, ">savoir :V", False) and morph(\2, ":V", False) and not before(r"(?i)\b(?:[mts]e|[vn]ous|les?|la|un) +$") + <<- morph(\1, ">savoir/:V", False) and morph(\2, ":V", False) and not before(r"(?i)\b(?:[mts]e|[vn]ous|les?|la|un) +$") -2>> =suggVerbInfi(@) # Le verbe devrait être à l’infinitif. TEST: Il faut savoir {{arrêté}} les frais. TEST: un certain nombre de savoirs spécialisés @@ -9667,34 +9667,34 @@ !! !! __[i]/conj(conj_se_conf_être_avoir)__ (s’)(?:en +|y+ |)({avoir}) @@0,$ - <<- morph(\2, ">avoir ", False) >>> + <<- morph(\2, ">avoir/", False) >>> <<- morph(\2, ":3p", False) -2>> sont|étaient|seront|seraient # Confusion. Sous sa forme pronominale, un verbe s’emploie avec l’auxilaire “être”, non “avoir”. <<- __else__ -2>> est|était|sera|serait # Confusion. Sous sa forme pronominale, un verbe s’emploie avec l’auxilaire “être”, non “avoir”. TEST: s’en {{ait}} trop __[i]/conj(conj_je_me_conf_être_avoir)__ je m’(?:en +|y+ |)({avoir}) @@$ - <<- morph(\1, ">avoir ", False) -1>> suis|étais|serai|serais # Confusion. Sous sa forme pronominale, un verbe s’emploie avec l’auxilaire “être”, non “avoir”. + <<- morph(\1, ">avoir/", False) -1>> suis|étais|serai|serais # Confusion. Sous sa forme pronominale, un verbe s’emploie avec l’auxilaire “être”, non “avoir”. __[i]/conj(conj_tu_te_conf_être_avoir)__ tu t’(?:en +|y+ |)({avoir}) @@$ - <<- morph(\1, ">avoir ", False) and not morph(word(-1), ":V0", False, False) + <<- morph(\1, ">avoir/", False) and not morph(word(-1), ":V0", False, False) -1>> es|étais|seras|serais # Confusion. Sous sa forme pronominale, un verbe s’emploie avec l’auxilaire “être”, non “avoir”. __[i]/conj(conj_nous_nous_conf_être_avoir)__ (nous) nous (?:en +|y+ |)({avoir}) @@0,$ - <<- morph(\2, ">avoir ", False) and isStart() -2>> sommes|étions|serons|serions # Confusion possible. Sous sa forme pronominale, un verbe s’emploie avec l’auxilaire “être”, non “avoir”. + <<- morph(\2, ">avoir/", False) and isStart() -2>> sommes|étions|serons|serions # Confusion possible. Sous sa forme pronominale, un verbe s’emploie avec l’auxilaire “être”, non “avoir”. <<- __also__ -1>> nous, # S’il ne s’agit pas d’une locution pronominale, mettez une virgule pour séparer les personnes que vous désignez du sujet. __[i]/conj(conj_vous_vous_conf_être_avoir)__ (vous) vous (?:en +|y+ |)({avoir}) @@0,$ - <<- morph(\2, ">avoir ", False) and isStart() -2>> êtes|étiez|serez|seriez # Confusion possible. Sous sa forme pronominale, un verbe s’emploie avec l’auxilaire “être”, non “avoir”. + <<- morph(\2, ">avoir/", False) and isStart() -2>> êtes|étiez|serez|seriez # Confusion possible. Sous sa forme pronominale, un verbe s’emploie avec l’auxilaire “être”, non “avoir”. <<- __also__ -1>> vous, # S’il ne s’agit pas d’une locution pronominale, mettez une virgule pour séparer les personnes que vous désignez du sujet. TEST: je m’y {{avais}} habitué. TEST: tu t’{{avais}} donné du temps pour finir ton mémoire. TEST: Ce qu’il a tu t’a donné la nausée. @@ -9729,54 +9729,54 @@ __[i]/ppas(ppas_il_se_être_verbe)__ il +(?:ne +|)s(?:e +|’(?:y +|))(?:est?|soi[st]|étai[st]|fu(?:sses?|s|t)|serai?[st]?) +({w_3}) @@$ - <<- morphex(\1, ":Q.*:(?:f|m:p)", ":(?:G|Q.*:m:[si])|>dire ") and ( morph(\1, ":V[123]_.__p_e_") or (isRealEnd() and not before(r"\b[qQ]ue? +$")) ) + <<- morphex(\1, ":Q.*:(?:f|m:p)", ":(?:G|Q.*:m:[si])|>dire/") and ( morph(\1, ":V[123]_.__p_e_") or (isRealEnd() and not before(r"\b[qQ]ue? +$")) ) -1>> =suggVerbPpas(\1, ":m:s") # Si ce participe passé se rapporte bien à “il”, il devrait être au masculin singulier. TEST: le dédale dans lequel il se serait {{perdue}} TEST: il s’était perdu dans la forêt. __[i]/ppas(ppas_elle_se_être_verbe)__ elle +(?:ne +|)s(?:e +|’(?:y +|))(?:est?|soi[st]|étai[st]|fu(?:sses?|s|t)|serai?[st]?) +({w_3}) @@$ - <<- morphex(\1, ":Q.*:(?:m|f:p)", ":(?:G|Q.*:f:[si])|>dire ") and ( morph(\1, ":V[123]_.__p_e_") or (isRealEnd() and not morph(word(-1), ":R|>que ", False, False)) ) + <<- morphex(\1, ":Q.*:(?:m|f:p)", ":(?:G|Q.*:f:[si])|>dire/") and ( morph(\1, ":V[123]_.__p_e_") or (isRealEnd() and not morph(word(-1), ":R|>que/", False, False)) ) -1>> =suggVerbPpas(\1, ":f:s") # Si ce participe passé se rapporte bien à “elle”, il devrait être au féminin singulier. TEST: elle s’y était {{préparé}}. TEST: elle s’était trouvé un mari. __[i]/ppas(ppas_nous_nous_être_verbe)__ nous +(?:ne +|)nous +(?:y +|)(?:sommes|étions|fûmes|fussions|seri?ons) +({w_3}) @@$ - <<- morphex(\1, ":Q.*:s", ":(?:G|Q.*:[pi])|>dire ") and ( morph(\1, ":V[123]_.__p_e_") or (isRealEnd() and not morph(word(-1), ":R|>que ", False, False)) ) + <<- morphex(\1, ":Q.*:s", ":(?:G|Q.*:[pi])|>dire/") and ( morph(\1, ":V[123]_.__p_e_") or (isRealEnd() and not morph(word(-1), ":R|>que/", False, False)) ) -1>> =suggVerbPpas(\1, ":p") # Si ce participe passé se rapporte bien à “nous”, il devrait être au pluriel. TEST: Nous nous étions {{cru}} au paradis. __[i]/ppas(ppas_ils_se_être_verbe)__ ils +(?:ne +|)s(?:e +|’(?:y +|))(?:so(?:ie|)nt|étaient|fu(?:r|ss)ent|ser(?:aie|o)nt) +({w_3}) @@$ - <<- morphex(\1, ":Q.*:(?:f|m:s)", ":(?:G|Q.*:m:[pi])|>dire ") and ( morph(\1, ":V[123]_.__p_e_") or (isRealEnd() and not before(r"\b[qQ]ue? +$")) ) + <<- morphex(\1, ":Q.*:(?:f|m:s)", ":(?:G|Q.*:m:[pi])|>dire/") and ( morph(\1, ":V[123]_.__p_e_") or (isRealEnd() and not before(r"\b[qQ]ue? +$")) ) -1>> =suggVerbPpas(\1, ":m:p") # Si ce participe passé se rapporte bien à “ils”, il devrait être au masculin pluriel. TEST: ils s’y étaient {{abandonné}} avec ferveur __[i]/ppas(ppas_elles_se_être_verbe)__ elles +(?:ne +|)s(?:e +|’(?:y +|))(?:so(?:ie|)nt|étaient|fu(?:r|ss)ent|ser(?:aie|o)nt) +({w_3}) @@$ - <<- morphex(\1, ":Q.*:(?:m|f:s)", ":(?:G|Q.*:f:[pi])|>dire ") and ( morph(\1, ":V[123]_.__p_e_") or (isRealEnd() and not morph(word(-1), ":R|>que ", False, False)) ) + <<- morphex(\1, ":Q.*:(?:m|f:s)", ":(?:G|Q.*:f:[pi])|>dire/") and ( morph(\1, ":V[123]_.__p_e_") or (isRealEnd() and not morph(word(-1), ":R|>que/", False, False)) ) -1>> =suggVerbPpas(\1, ":f:p") # Si ce participe passé se rapporte bien à “elles”, il devrait être au féminin pluriel. TEST: elles ne s’y étaient pas {{donnée}}. TEST: sans fin elles se sont succédé __[i]/ppas(ppas_se_être)__ [mts](?:e +|’(?:y +|en +|))({etre}) +({w_2}) @@w,$ - <<- morph(\1, ">être ", False) >>> + <<- morph(\1, ">être/", False) >>> <<- morphex(\2, ":(?:Y|[123][sp])", ":Q") and not re.search(r"(?i)^t’(?:es|étais)", \0) -2>> =suggVerbPpas(@) # Incohérence. Après « s’être », le verbe doit être un participe passé. <<- __else__ and morph(\1, ":[123]s", False) and morph(\2, ":Q.*:p", False) and not before(r"(?i)\bque?[, ]|\bon (?:ne |)$") and not re.search(r"(?i)^t’(?:es|étais)", \0) -2>> =suggSing(@) # Le participe passé devrait être au singulier. @@ -9802,11 +9802,11 @@ !! !! __[i]/ppas(ppas_me_te_laisser_adj)__ ([mt]e|l[ae]) +(laiss\w*) +({w_3}) @@0,w,$ - <<- morph(\2, ">laisser ", False) and morphex(\3, ":[AQ].*:p", ":(?:[YG]|[AQ].*:[is])") + <<- morph(\2, ">laisser/", False) and morphex(\3, ":[AQ].*:p", ":(?:[YG]|[AQ].*:[is])") -3>> =suggSing(@) # Accord avec « \1 » : « \3 » devrait être au singulier. TEST: Elle te laisse {{épuisés}} par la tâche. TEST: Ils la laissèrent {{malades}}. TEST: Ils la laissent prendre le train. @@ -9815,11 +9815,11 @@ TEST: Il te laisse trois jours de délai. __[i]/ppas(ppas_nous_les_laisser_adj)__ (nous|les) +(laiss\w*) +({w_3}) @@0,w,$ - <<- morph(\2, ">laisser ", False) and morphex(\3, ":[AQ].*:s", ":(?:[YG]|[AQ].*:[ip])") + <<- morph(\2, ">laisser/", False) and morphex(\3, ":[AQ].*:s", ":(?:[YG]|[AQ].*:[ip])") and (\1.endswith("es") or ( \1.endswith("us") and not \2.endswith("ons") )) -3>> =suggPlur(@) # Accord avec « \1 » : « \3 » devrait être au pluriel. TEST: je les laisse {{indifférent}}. TEST: elle nous laissera {{perdu}} dans nos délires. @@ -9835,11 +9835,11 @@ !! !! __[i]/ppas(ppas_je_verbe)__ j(?:e +|’(?:y +|en +|))(?:ne +|n’|)((?:s[oue]|étai|fus|dev|re(?:dev|st)|par)\w*|a(?:ie?|vais|urais?) +été|eus(?:se|) +été) +({w_2}) @@w,$ - <<- (morph(\1, ">(?:être|sembler|devenir|re(?:ster|devenir)|para[îi]tre) ", False) or \1.endswith(" été")) and morphex(\2, ":[NAQ].*:p", ":[GWYsi]") + <<- (morph(\1, ">(?:être|sembler|devenir|re(?:ster|devenir)|para[îi]tre)/", False) or \1.endswith(" été")) and morphex(\2, ":[NAQ].*:p", ":[GWYsi]") -2>> =suggSing(@) # Accord avec le sujet « je » : « \2 » devrait être au singulier. TEST: j’étais {{perdus}} ->> perdu TEST: j’aurais été {{perdus}} sans toi ->> perdu TEST: je n’étais pas {{perdus}} ->> perdu @@ -9854,21 +9854,21 @@ TEST: J’y semble être {{perdus}}. __[i]/ppas(ppas_tu_verbe)__ tu +(?:ne +|n’|)((?:es|étai|fus|se[rm]|soi|dev|re(?:dev|st)|par)\w*|a(?:s|ies|vais|urai?s) +été|eus(?:ses|) +été) +({w_2}) @@w,$ - <<- (morph(\1, ">(?:être|sembler|devenir|re(?:ster|devenir)|para[îi]tre) ", False) or \1.endswith(" été")) and morphex(\2, ":[NAQ].*:p", ":[GWYsi]") + <<- (morph(\1, ">(?:être|sembler|devenir|re(?:ster|devenir)|para[îi]tre)/", False) or \1.endswith(" été")) and morphex(\2, ":[NAQ].*:p", ":[GWYsi]") -2>> =suggSing(@) # Accord avec le sujet « tu » : « \2 » devrait être au singulier. TEST: tu n’es pas {{petites}} TEST: tu es {{insignifiants}} TEST: tu deviens vraiment très {{forts}} à ce jeu. __[i]/ppas(ppas_il_verbe)__ (il|ce|ce qui|celui +qui|ça +qui|lui +qui|celui-(?:ci|là) +(?:qui +|)|quiconque) +(?:ne +|n’|)((?:es|étai|f[uû]|se[mr]|soi|dev|re(?:dev|st)|par)\w*|a(?:it|vait|ura(?:it|)|) +été|e[uû]t +été) +({w_2}) @@0,w,$ - <<- (morph(\2, ">(?:être|sembler|devenir|re(?:ster|devenir)|para[îi]tre) ", False) or \2.endswith(" été")) + <<- (morph(\2, ">(?:être|sembler|devenir|re(?:ster|devenir)|para[îi]tre)/", False) or \2.endswith(" été")) and (morphex(\3, ":[NAQ].*:p", ":[GWYsi]") or morphex(\3, ":[AQ].*:f", ":[GWYme]")) -3>> =suggMasSing(@) # Accord avec le sujet « \1 » : « \3 » devrait être au masculin singulier. TEST: Il semble être {{partis}} pour toujours. ->> parti TEST: Il est {{demander}} à chacun de participer. @@ -9878,11 +9878,11 @@ TEST: c’est ça qui paraît {{stupides}} TEST: celui-là semble {{perdus}} dans ses pensées. __[i]/ppas(ppas_c_être)__ c’(?:est|était|e[uû]t +été) +({w_2}) @@$ - <<- not (morph(\1, ">seule ", False) and after("^ +que? ")) + <<- not (morph(\1, ">seule/", False) and after("^ +que? ")) and ( morphex(\1, ":[NAQ].*:p", ":[GWYsi]") or ( morphex(\1, ":[AQ].*:f", ":[GWYme]") and not morph(word(1), ":N.*:f", False, False) ) ) -1>> =suggMasSing(@) # Accord avec le sujet « c’ » : « \1 » devrait être au masculin singulier. TEST: c’est {{condescendants}}. ->> condescendant TEST: C’est {{finis}}. @@ -9897,13 +9897,13 @@ TEST: Ç’avait été {{horribles}} __[i]/ppas(ppas_ça_verbe)__ (ça|ce(?:la|ci)|celui-(?:ci|là)) +(?:ne +|n’|)((?:es|étai|f[uû]|se[mr]|soi|par|dev|re(?:dev|st))\w+|a(?:it|vait|ura(?:it|)|) +été|e[uû]t +été) +({w_2}) @@0,w,$ - <<- (morph(\2, ">(?:être|sembler|devenir|re(?:ster|devenir)|para[îi]tre) ", False) or \2.endswith(" été")) + <<- (morph(\2, ">(?:être|sembler|devenir|re(?:ster|devenir)|para[îi]tre)/", False) or \2.endswith(" été")) and ( morphex(\3, ":[NAQ].*:p", ":[GWYsi]") or ( morphex(\3, ":[AQ].*:f", ":[GWYme]") and not morph(word(1), ":N.*:f", False, False) ) ) - and not morph(word(-1), ":(?:R|V...t)|>de ", False, False) + and not morph(word(-1), ":(?:R|V...t)|>de/", False, False) -3>> =suggMasSing(@) # Accord avec le sujet « \1 » : « \3 » devrait être au masculin singulier. TEST: ça semble {{perdus}} TEST: cela paraît {{incroyables}} TEST: Je n’arrêtais pas de me répéter que tout cela était peut-être pure imagination @@ -9911,23 +9911,23 @@ TEST: De cela a toujours été faite notre vie __[i]/ppas(ppas_lequel_verbe)__ (lequel) +(?:ne +|n’|)((?:es|étai|f[uû]|se[mr]|soi|par|dev|re(?:dev|st))\w+|a(?:it|vait|ura(?:it|)|) +été|e[uû]t +été) +({w_2}) @@0,w,$ - <<- (morph(\2, ">(?:être|sembler|devenir|re(?:ster|devenir)|para[îi]tre) ", False) or \2.endswith(" été")) + <<- (morph(\2, ">(?:être|sembler|devenir|re(?:ster|devenir)|para[îi]tre)/", False) or \2.endswith(" été")) and ( morphex(\3, ":[NAQ].*:p", ":[GWYsi]") or ( morphex(\3, ":[AQ].*:f", ":[GWYme]") and not morph(word(1), ":N.*:f", False, False) ) ) and not morph(word(-1), ":R", False, False) -3>> =suggMasSing(@) # Accord avec le sujet « \1 » : « \3 » devrait être au masculin singulier. TEST: elle avait accompagné cet homme, lequel était {{revenue}} de l’enfer. __[i]/ppas(ppas_elle_verbe)__ (elle|celle-(?:ci|là)|laquelle) +(?:ne +|n’|)((?:es|étai|f[uû]|se[rm]|soi|dev|re(?:dev|st)|par)\w*|a(?:it|vait|ura(?:it|)|) +été|e[uû]t +été) +({w_2}) @@0,w,$ - <<- (morph(\2, ">(?:être|sembler|devenir|re(?:ster|devenir)|para[îi]tre) ", False) or \2.endswith(" été")) + <<- (morph(\2, ">(?:être|sembler|devenir|re(?:ster|devenir)|para[îi]tre)/", False) or \2.endswith(" été")) and (morphex(\3, ":[NAQ].*:p", ":[GWYsi]") or morphex(\3, ":[AQ].*:m", ":[GWYfe]")) - and not morph(word(-1), ":R|>de ", False, False) + and not morph(word(-1), ":R|>de/", False, False) -3>> =suggFemSing(@) # Accord avec le sujet « \1 » : « \3 » devrait être au féminin singulier. TEST: elle a été {{perdu}} sans toi ->> perdue TEST: Elle semble être totalement {{ruiné}}. ->> ruinée TEST: Elle est complètement {{fol}}. ->> folle @@ -9935,31 +9935,31 @@ TEST: Elle est de plus en plus {{belles}}. ->> belle __[i]/ppas(ppas_elle_qui_verbe)__ (c?elle +qui) +(?:ne +|n’|)((?:es|étai|f[uû]|se[rm]|soi|dev|re(?:dev|st)|par)\w*|a(?:it|vait|ura(?:it|)|) +été|e[uû]t +été) +({w_2}) @@0,w,$ - <<- (morph(\2, ">(?:être|sembler|devenir|re(?:ster|devenir)|para[îi]tre) ", False) or \2.endswith(" été")) + <<- (morph(\2, ">(?:être|sembler|devenir|re(?:ster|devenir)|para[îi]tre)/", False) or \2.endswith(" été")) and (morphex(\3, ":[NAQ].*:p", ":[GWYsi]") or morphex(\3, ":[AQ].*:m", ":[GWYfe]")) -3>> =suggFemSing(@) # Accord avec le sujet « \1 » : « \3 » devrait être au féminin singulier. TEST: celle qui paraît {{dingues}} de toi __[i]/ppas(ppas_nous_verbe)__ nous +(?:ne +|n’|)((?:sommes|étions|fûmes|fussions|seri?ons|soyons|sembl|dev|re(?:dev|st)|par)\w*|a(?:vi?ons|uri?ons|yions) +été|e(?:ûme|ussion)s +été) +({w_2}) @@w,$ <<- not re.search("(?i)^légion$", \2) and not before(r"(?i)\b(?:nous|ne) +$") - and ((morph(\1, ">(?:être|sembler|devenir|re(?:ster|devenir)|para[îi]tre) ", False) and morph(\1, ":1p", False)) or \1.endswith(" été")) + and ((morph(\1, ">(?:être|sembler|devenir|re(?:ster|devenir)|para[îi]tre)/", False) and morph(\1, ":1p", False)) or \1.endswith(" été")) and morphex(\2, ":[NAQ].*:s", ":[GWYpi]") -2>> =suggPlur(@) # Accord avec le sujet « nous » : « \2 » devrait être au pluriel. TEST: nous paraissons {{faible}} TEST: Nous paraissons avoir été complètement {{prise}} de panique. ->> prises __[i]/ppas(ppas_ils_verbe)__ (ils|c?eux +qui|ceux-ci|ceux-là|lesquels) +(?:ne +|n’|)((?:sont|étaient|fu[rs]|se[rm]|soient|dev|re(?:dev|st)|par)\w*|ont +été|a(?:ient|vaient|ur(?:ont|aient)) +été|eu(?:r|ss)ent +été) +({w_2}) @@0,w,$ - <<- not re.search("(?i)^légion$", \3) and (morph(\2, ">(?:être|sembler|devenir|re(?:ster|devenir)|para[îi]tre) ", False) or \2.endswith(" été")) + <<- not re.search("(?i)^légion$", \3) and (morph(\2, ">(?:être|sembler|devenir|re(?:ster|devenir)|para[îi]tre)/", False) or \2.endswith(" été")) and (morphex(\3, ":[NAQ].*:s", ":[GWYpi]") or morphex(\3, ":[AQ].*:f", ":[GWYme]")) and not before("(?i)ce que? +$") and (not re.search("^(?:ceux-(?:ci|là)|lesquels)$", \1) or not morph(word(-1), ":R", False, False)) -3>> =suggMasPlur(@) # Accord avec le sujet « \1 » : « \3 » devrait être au masculin pluriel. TEST: ils sont {{parti}}. ->> partis @@ -9974,11 +9974,11 @@ TEST: ils sont très loin d’être {{idiot}}. __[i]/ppas(ppas_elles_verbe)__ (elles|c?elles +qui|celles-(?:ci|là)|lesquelles) +(?:ne +|n’|)((?:sont|étai|fu[rs]|se[rm]|soi|dev|re(?:dev|st)|par)\w*|ont +été|a(?:ient|vaient|ur(?:ont|aient)) +été|eu(?:r|ss)ent +été) +({w_2}) @@0,w,$ - <<- not re.search("(?i)^légion$", \3) and (morph(\2, ">(?:être|sembler|devenir|re(?:ster|devenir)|para[îi]tre) ", False) or \2.endswith(" été")) + <<- not re.search("(?i)^légion$", \3) and (morph(\2, ">(?:être|sembler|devenir|re(?:ster|devenir)|para[îi]tre)/", False) or \2.endswith(" été")) and (morphex(\3, ":[NAQ].*:s", ":[GWYpi]") or morphex(\3, ":[AQ].*:m", ":[GWYfe]")) and (not re.search("(?i)^(?:elles|celles-(?:ci|là)|lesquelles)$", \1) or not morph(word(-1), ":R", False, False)) -3>> =suggFemPlur(@) # Accord avec le sujet « \1 » : « \3 » devrait être au féminin pluriel. TEST: elles n’ont tout de même pas été {{attaqué}} ->> attaquées @@ -10018,11 +10018,11 @@ __[i](p_risque_d_être)__ risqu\w+ +(d’)être @@* <<- ~1>> * __[i]/ppas(ppas_je_verbe_être)__ j(?:e|’(?:y|en)) +(?:ne +|n’|)((?:p[aeouûr]|s(?:embl|ouhait)|cr[ouû]|d[eouûéiî]|estim|i(?:magin|r)|v(?:[eo]u|a)|a(?:ffirm|im|dor|ll)|risqu)\w*) +(?:être|avoir été) +({w_2}) @@w,$ - <<- morph(\1, ">(?:sembler|para[îi]tre|pouvoir|penser|préférer|croire|d(?:evoir|éclarer|ésirer|étester|ire)|vouloir|affirmer|aimer|adorer|souhaiter|estimer|imaginer|risquer|aller) ", False) + <<- morph(\1, ">(?:sembler|para[îi]tre|pouvoir|penser|préférer|croire|d(?:evoir|éclarer|ésirer|étester|ire)|vouloir|affirmer|aimer|adorer|souhaiter|estimer|imaginer|risquer|aller)/", False) and morphex(\2, ":[NAQ].*:p", ":[GWYsi]") -2>> =suggSing(@) # Accord avec le sujet « je » : « \2 » devrait être au singulier. TEST: Je ne peux pas être {{méchants}}. TEST: j’aurais vraiment été {{tentés}} @@ -10031,11 +10031,11 @@ TEST: je voudrais bien être dans ses souliers __[i]/ppas(ppas_tu_verbe_être)__ tu +(?:ne +|n’|)((?:p[aeouûr]|s(?:embl|ouhait)|cr[ouû]|d[eouûéiî]|estim|i(?:magin|r)|v(?:[eo]u|a)|a(?:ffirm|im|dor|ll)|risqu)\w*) +(?:être|avoir été) +({w_2}) @@w,$ - <<- morph(\1, ">(?:sembler|para[îi]tre|pouvoir|penser|préférer|croire|d(?:evoir|éclarer|ésirer|étester|ire)|vouloir|affirmer|aimer|adorer|souhaiter|estimer|imaginer|risquer|aller) ", False) + <<- morph(\1, ">(?:sembler|para[îi]tre|pouvoir|penser|préférer|croire|d(?:evoir|éclarer|ésirer|étester|ire)|vouloir|affirmer|aimer|adorer|souhaiter|estimer|imaginer|risquer|aller)/", False) and morphex(\2, ":[NAQ].*:p", ":[GWYsi]") -2>> =suggSing(@) # Accord avec le sujet « tu » : « \2 » devrait être au singulier. TEST: tu ne crois pas être {{meilleurs}}. TEST: tu ne crois pas avoir été {{découvertes}} @@ -10042,11 +10042,11 @@ TEST: tu vas être {{payées}} __[i]/ppas(ppas_il_verbe_être)__ (il|ce|ce qui|celui +qui|ça +qui|lui +qui|celui-(?:ci|là) +qui|quiconque) +(?:ne +|n’|)((?:p[aeouûr]|s(?:embl|ouhait)|cr[ouû]|d[eouûéiî]|estim|i(?:magin|r)|v(?:[eo]u|a)|a(?:ffirm|im|dor|ll)|risqu)\w*) +(?:être|avoir été) +({w_2}) @@0,w,$ - <<- morph(\2, ">(?:sembler|para[îi]tre|pouvoir|penser|préférer|croire|d(?:evoir|éclarer|ésirer|étester|ire)|vouloir|affirmer|aimer|adorer|souhaiter|estimer|imaginer|risquer|aller) ", False) + <<- morph(\2, ">(?:sembler|para[îi]tre|pouvoir|penser|préférer|croire|d(?:evoir|éclarer|ésirer|étester|ire)|vouloir|affirmer|aimer|adorer|souhaiter|estimer|imaginer|risquer|aller)/", False) and (morphex(\3, ":[NAQ].*:p", ":[GWYsi]") or morphex(\3, ":[AQ].*:f", ":[GWYme]")) -3>> =suggMasSing(@) # Accord avec le sujet « \1 » : « \3 » devrait être au masculin singulier. TEST: Il peut être {{observée}}. TEST: celui-là pensait être {{perdue}} @@ -10055,11 +10055,11 @@ TEST: lui qui ne pensait jamais être {{reconnus}}. __[i]/ppas(ppas_ça_verbe_être)__ (ça|ce(?:la|ci)|celui-(?:ci|là)|lequel) +(?:ne +|n’|)((?:p[aeouûr]|s(?:embl|ouhait)|cr[ouû]|d[eouûéiî]|estim|i(?:magin|r)|v(?:[eo]u|a)|a(?:ffirm|im|dor|ll)|risqu)\w*) +(?:être|avoir été) +({w_2}) @@0,w,$ - <<- morph(\2, ">(?:sembler|para[îi]tre|pouvoir|penser|préférer|croire|d(?:evoir|éclarer|ésirer|étester|ire)|vouloir|affirmer|aimer|adorer|souhaiter|estimer|imaginer|risquer|aller) ", False) + <<- morph(\2, ">(?:sembler|para[îi]tre|pouvoir|penser|préférer|croire|d(?:evoir|éclarer|ésirer|étester|ire)|vouloir|affirmer|aimer|adorer|souhaiter|estimer|imaginer|risquer|aller)/", False) and (morphex(\3, ":[NAQ].*:p", ":[MWYsi]") or morphex(\3, ":[AQ].*:f", ":[GWYme]")) and not morph(word(-1), ":R", False, False) -3>> =suggMasSing(@) # Accord avec le sujet « \1 » : « \3 » devrait être au masculin singulier. TEST: ça ne semble pas avoir été {{conçus}} pour ça. @@ -10066,11 +10066,11 @@ TEST: lequel allait être {{renvoyée}} de l’établissement. __[i]/ppas(ppas_elle_verbe_être)__ (elle|celle-(?:ci|là)|laquelle) +(?:ne +|n’|)((?:p[aeouûr]|s(?:embl|ouhait)|cr[ouû]|d[eouûéiî]|estim|i(?:magin|r)|v(?:[eo]u|a)|a(?:ffirm|im|dor|ll)|risqu)\w*) +(?:être|avoir été) +({w_2}) @@0,w,$ - <<- morph(\2, ">(?:sembler|para[îi]tre|pouvoir|penser|préférer|croire|d(?:evoir|éclarer|ésirer|étester|ire)|vouloir|affirmer|aimer|adorer|souhaiter|estimer|imaginer|risquer|aller) ", False) + <<- morph(\2, ">(?:sembler|para[îi]tre|pouvoir|penser|préférer|croire|d(?:evoir|éclarer|ésirer|étester|ire)|vouloir|affirmer|aimer|adorer|souhaiter|estimer|imaginer|risquer|aller)/", False) and (morphex(\3, ":[NAQ].*:p", ":[GWYsi]") or morphex(\3, ":[AQ].*:m", ":[GWYfe]")) and not morph(word(-1), ":R", False, False) -3>> =suggFemSing(@) # Accord avec le sujet « \1 » : « \3 » devrait être au féminin singulier. TEST: elle ne croit pas être {{trompé}} @@ -10077,40 +10077,40 @@ TEST: ici, elle ne risque pas d’être {{attaquées}} __[i]/ppas(ppas_elle_qui_verbe_être)__ (c?elle +qui) +(?:ne +|n’|)((?:p[aeouûr]|s(?:embl|ouhait)|cr[ouû]|d[eouûéiî]|estim|i(?:magin|r)|v(?:[eo]u|a)|a(?:ffirm|im|dor|ll)|risqu)\w*) +(?:être|avoir été) +({w_2}) @@0,w,$ - <<- morph(\2, ">(?:sembler|para[îi]tre|pouvoir|penser|préférer|croire|d(?:evoir|éclarer|ésirer|étester|ire)|vouloir|affirmer|aimer|adorer|souhaiter|estimer|imaginer|risquer|aller) ", False) + <<- morph(\2, ">(?:sembler|para[îi]tre|pouvoir|penser|préférer|croire|d(?:evoir|éclarer|ésirer|étester|ire)|vouloir|affirmer|aimer|adorer|souhaiter|estimer|imaginer|risquer|aller)/", False) and (morphex(\3, ":[NAQ].*:p", ":[MWYsi]") or morphex(\3, ":[AQ].*:m", ":[GWYfe]")) -3>> =suggFemSing(@) # Accord avec le sujet « \1 » : « \3 » devrait être au féminin singulier. TEST: celle qui pense être {{découvert}} __[i]/ppas(ppas_nous_verbe_être)__ (?(?:sembler|para[îi]tre|pouvoir|penser|préférer|croire|d(?:evoir|éclarer|ésirer|étester|ire)|vouloir|affirmer|aimer|adorer|souhaiter|estimer|imaginer|risquer|aller) ", False) + <<- not re.search("(?i)^légion$", \2) and morph(\1, ">(?:sembler|para[îi]tre|pouvoir|penser|préférer|croire|d(?:evoir|éclarer|ésirer|étester|ire)|vouloir|affirmer|aimer|adorer|souhaiter|estimer|imaginer|risquer|aller)/", False) and morph(\1, ":1p", False) and morphex(\2, ":[NAQ].*:s", ":[GWYpi]") -2>> =suggPlur(@) # Accord avec le sujet « nous » : « \2 » devrait être au pluriel. TEST: nous pensons être {{désiré}} TEST: nous ne devons pas être {{instruit}} __[i]/ppas(ppas_ils_verbe_être)__ (ils|c?eux +qui|ceux-(?:ci|là)|lesquels) +(?:ne +|n’|)((?:p[aeouûr]|s(?:embl|ouhait)|cr[ouû]|d[eouûéiî]|estim|i(?:magin|r)|v(?:[eo]u|a)|a(?:ffirm|im|dor|ll)|risqu)\w*) +(?:être|avoir été) +({w_2}) @@0,w,$ - <<- not re.search("(?i)^légion$", \3) and morph(\2, ">(?:sembler|para[îi]tre|pouvoir|penser|préférer|croire|d(?:evoir|éclarer|ésirer|étester|ire)|vouloir|affirmer|aimer|adorer|souhaiter|estimer|imaginer|risquer|aller) ", False) + <<- not re.search("(?i)^légion$", \3) and morph(\2, ">(?:sembler|para[îi]tre|pouvoir|penser|préférer|croire|d(?:evoir|éclarer|ésirer|étester|ire)|vouloir|affirmer|aimer|adorer|souhaiter|estimer|imaginer|risquer|aller)/", False) and (morphex(\3, ":[NAQ].*:s", ":[GWYpi]") or morphex(\3, ":[AQ].*:f", ":[GWYme]")) and (not re.search("^(?:ceux-(?:ci|là)|lesquels)$", \1) or not morph(word(-1), ":R", False, False)) -3>> =suggMasPlur(@) # Accord avec le sujet « \1 » : « \3 » devrait être au masculin pluriel. TEST: ils croient être {{perdu}} __[i]/ppas(ppas_elles_verbe_être)__ (elles|c?elles +qui|celles-(?:ci|là)|lesquelles) +(?:ne +|n’|)((?:p[aeouûr]|s(?:embl|ouhait)|cr[ouû]|d[eouûéiî]|estim|i(?:magin|r)|v(?:[eo]u|a)|a(?:ffirm|im|dor|ll)|risqu)\w*) +(?:être|avoir été) +({w_2}) @@0,w,$ - <<- not re.search("(?i)^légion$", \3) and morph(\2, ">(?:sembler|para[îi]tre|pouvoir|penser|préférer|croire|d(?:evoir|éclarer|ésirer|étester|ire)|vouloir|affirmer|aimer|adorer|souhaiter|estimer|imaginer|risquer|aller) ", False) + <<- not re.search("(?i)^légion$", \3) and morph(\2, ">(?:sembler|para[îi]tre|pouvoir|penser|préférer|croire|d(?:evoir|éclarer|ésirer|étester|ire)|vouloir|affirmer|aimer|adorer|souhaiter|estimer|imaginer|risquer|aller)/", False) and (morphex(\3, ":[NAQ].*:s", ":[GWYpi]") or morphex(\3, ":[AQ].*:m", ":[GWYfe]")) and (not re.search("^(?:elles|celles-(?:ci|là)|lesquelles)$", \1) or not morph(word(-1), ":R", False, False)) -3>> =suggFemPlur(@) # Accord avec le sujet « \1 » : « \3 » devrait être au féminin pluriel. TEST: elles veulent être {{différente}} @@ -10196,19 +10196,19 @@ TEST: — {{Déçue}}, il s’en est allé. __[i]/ppas(ppas_adj_accord_elle)__ ^ *({w_2}[éuitsx]),? elle @@* - <<- morphex(\1, ":A.*:[mp]", ":(?:G|E|M1|W|f:[si])|>(?:désoler|pire) ") + <<- morphex(\1, ":A.*:[mp]", ":(?:G|E|M1|W|f:[si])|>(?:désoler|pire)/") -1>> =suggFemSing(@) # Si cet adjectif se réfère au pronom « elle », l’adjectif devrait être au féminin singulier. TEST: — {{Déçu}}, elle s’en est allée. __[i]/ppas(ppas_adj_accord_ils)__ ^ *({w_2}[eiuéts]),? ils @@* - <<- morphex(\1, ":A.*:[fs]", ":(?:G|E|M1|W|m:[pi])|>(?:désoler|pire) ") + <<- morphex(\1, ":A.*:[fs]", ":(?:G|E|M1|W|m:[pi])|>(?:désoler|pire)/") -1>> =suggMasPlur(@) # Si cet adjectif se réfère au pronom « ils », l’adjectif devrait être au masculin pluriel. TEST: Très vite, ils sont partis TEST: Une fois terminé, ils sont revenus. TEST: Vraiment {{soucieuse}}, ils sont. @@ -10216,11 +10216,11 @@ TEST: Pire, ils piétinent parfois les droits humains. __[i]/ppas(ppas_adj_accord_elles)__ ^ *({w_2}[eiuétsx]),? elles @@* - <<- morphex(\1, ":A.*:[ms]", ":(?:G|E|M1|W|f:[pi])|>(?:désoler|pire) ") + <<- morphex(\1, ":A.*:[ms]", ":(?:G|E|M1|W|f:[pi])|>(?:désoler|pire)/") -1>> =suggFemPlur(@) # Si cet adjectif se réfère au pronom « elles », l’adjectif devrait être au féminin pluriel. TEST: Absolument {{heureux}}, elles exultèrent de joie. @@ -10249,27 +10249,27 @@ (?:es|étais|fus(?:ses|)|serai?s)-tu +({w_2}) @@$ <<- morphex(\1, ":(?:[123][sp]|Y|[NAQ].*:p)", ":[GWsi]") -1>> =suggSing(@) # Accord avec le sujet « tu » : « \1 » devrait être au singulier. __[i]/ppas(ppas_inversion_être_il_ce)__ (?:est|était|f[uû]t|sera(?:-t|it))-(il|ce) +({w_2}) @@*,$ - <<- morphex(\2, ":(?:[123][sp]|Y|[NAQ].*:[pf])", ":(?:G|W|[me]:[si])|question ") and not (\1 == "ce" and morph(\2, ":Y", False)) + <<- morphex(\2, ":(?:[123][sp]|Y|[NAQ].*:[pf])", ":(?:G|W|[me]:[si])|question/") and not (\1 == "ce" and morph(\2, ":Y", False)) -2>> =suggMasSing(@) # Accord avec le sujet « il » : « \2 » devrait être au masculin singulier. __[i]/ppas(ppas_inversion_être_elle)__ (?:est|était|f[uû]t|sera(?:-t|it))-elle +({w_2}) @@$ <<- morphex(\1, ":(?:[123][sp]|Y|[NAQ].*:[pm])", ":(?:G|W|[fe]:[si])") -1>> =suggFemSing(@) # Accord avec le sujet « elle » : « \1 » devrait être au féminin singulier. __[i]/ppas(ppas_inversion_être_nous)__ (?:sommes|étions|fûmes|fussions|seri?ons)-nous +({w_2}) @@$ - <<- morphex(\1, ":(?:[123][sp]|Y|[NAQ].*:s)", ":[GWpi]|>dire ") + <<- morphex(\1, ":(?:[123][sp]|Y|[NAQ].*:s)", ":[GWpi]|>dire/") -1>> =suggPlur(@) # Accord avec le sujet « nous » : « \1 » devrait être au pluriel. __[i]/ppas(ppas_inversion_être_ils)__ (?:sont|étaient|fu(?:r|ss)ent|ser(?:o|aie)nt)-ils +({w_2}) @@$ - <<- not re.search("(?i)^légion$", \1) and (morphex(\1, ":(?:[123][sp]|Y|[NAQ].*:s)", ":[GWpi]|>dire ") or morphex(\1, ":(?:[123][sp]|[AQ].*:f)", ":[GWme]|>dire ")) + <<- not re.search("(?i)^légion$", \1) and (morphex(\1, ":(?:[123][sp]|Y|[NAQ].*:s)", ":[GWpi]|>dire/") or morphex(\1, ":(?:[123][sp]|[AQ].*:f)", ":[GWme]|>dire/")) -1>> =suggMasPlur(@) # Accord avec « ils » : « \1 » devrait être au masculin pluriel. __[i]/ppas(ppas_inversion_être_elles)__ (?:sont|étaient|fu(?:r|ss)ent|ser(?:o|aie)nt)-elles +({w_2}) @@$ - <<- not re.search("(?i)^légion$", \1) and (morphex(\1, ":(?:[123][sp]|Y|[NAQ].*:s)", ":[GWpi]|>dire ") or morphex(\1, ":(?:[123][sp]|[AQ].*:m)", ":[GWfe]|>dire ")) + <<- not re.search("(?i)^légion$", \1) and (morphex(\1, ":(?:[123][sp]|Y|[NAQ].*:s)", ":[GWpi]|>dire/") or morphex(\1, ":(?:[123][sp]|[AQ].*:m)", ":[GWfe]|>dire/")) -1>> =suggFemPlur(@) # Accord avec « elles » : « \1 » devrait être au féminin pluriel. TEST: serais-je {{fâchés}} contre vous ? TEST: Est-elle {{arriver}} ? TEST: Sont-elles {{arriver}} ? @@ -10312,37 +10312,37 @@ !! !! __[i]/ppas(ppas_je_me_verbe)__ je +(?:ne +|)me +((?:s[eauû]|montr|pens|rév|v[oiîe])\w+) +({w_2}) @@w,$ - <<- morph(\1, ">(?:montrer|penser|révéler|savoir|sentir|voir|vouloir) ", False) and morphex(\2, ":[NAQ].*:p", ":[GWYsi]") + <<- morph(\1, ">(?:montrer|penser|révéler|savoir|sentir|voir|vouloir)/", False) and morphex(\2, ":[NAQ].*:p", ":[GWYsi]") -2>> =suggSing(@) # Accord avec le sujet « je » : « \2 » devrait être au singulier. TEST: je me savais {{implacables}} avec eux __[i]/ppas(ppas_tu_te_verbe)__ tu +(?:ne +|)te +((?:s[eauû]|montr|pens|rév|v[oiîe])\w+) +({w_2}) @@w,$ - <<- morph(\1, ">(?:montrer|penser|révéler|savoir|sentir|voir|vouloir) ", False) and morphex(\2, ":[NAQ].*:p", ":[GWYsi]") + <<- morph(\1, ">(?:montrer|penser|révéler|savoir|sentir|voir|vouloir)/", False) and morphex(\2, ":[NAQ].*:p", ":[GWYsi]") -2>> =suggSing(@) # Accord avec le sujet « je » : « \2 » devrait être au singulier. TEST: quand tu te montres {{infaillibles}} __[i]/ppas(ppas_il_se_verbe)__ (il|ce|ce qui|celui +qui|ça +qui|lui +qui|celui-(?:ci|là)|quiconque|lequel) +(?:ne +|)se +((?:s[eauû]|montr|pens|rév|v[oiîe])\w+) +({w_2}) @@0,w,$ - <<- morph(\2, ">(?:montrer|penser|révéler|savoir|sentir|voir|vouloir) ", False) + <<- morph(\2, ">(?:montrer|penser|révéler|savoir|sentir|voir|vouloir)/", False) and (morphex(\3, ":[NAQ].*:p", ":[GWsi]") or morphex(\3, ":[NAQ].*:f", ":[GWYme]")) and (not re.search("^(?:celui-(?:ci|là)|lequel)$", \1) or not morph(word(-1), ":R", False, False)) -3>> =suggMasSing(@) # Accord avec le sujet « \1 » : « \3 » devrait être au masculin singulier. TEST: lequel se veut {{imbattables}} ? __[i]/ppas(ppas_elle_se_verbe)__ (elle|celle-(?:ci|là)|laquelle) +(?:ne +|)se +((?:s[eauû]|montr|pens|rév|v[oiîe])\w+) +({w_2}) @@0,w,$ - <<- morph(\2, ">(?:montrer|penser|révéler|savoir|sentir|voir|vouloir) ", False) + <<- morph(\2, ">(?:montrer|penser|révéler|savoir|sentir|voir|vouloir)/", False) and (morphex(\3, ":[NAQ].*:p", ":[GWsi]") or morphex(\3, ":[NAQ].*:m", ":[GWYfe]")) and not morph(word(-1), ":R", False, False) -3>> =suggFemSing(@) # Accord avec le sujet « \1 » : « \3 » devrait être au féminin singulier. TEST: Elle se sait plus {{fortes}} qu’eux tous. @@ -10349,84 +10349,84 @@ TEST: elle se vit {{abandonné}} __[i]/ppas(ppas_elle_qui_se_verbe)__ (c?elle +qui) +(?:ne +|)se +((?:s[eauû]|montr|pens|rév|v[oiîe])\w+) +({w_2}) @@0,w,$ - <<- morph(\2, ">(?:montrer|penser|révéler|savoir|sentir|voir|vouloir) ", False) + <<- morph(\2, ">(?:montrer|penser|révéler|savoir|sentir|voir|vouloir)/", False) and (morphex(\3, ":[NAQ].*:p", ":[GWsi]") or morphex(\3, ":[NAQ].*:m", ":[GWYfe]")) -3>> =suggFemSing(@) # Accord avec le sujet « \1 » : « \3 » devrait être au féminin singulier. TEST: à celle qui se révélera {{attentif}} à tous ces problèmes. __[i]/ppas(ppas_nous_nous_verbe)__ nous +(?:ne +|)nous +((?:s[eauû]|montr|pens|rév|v[oiîe])\w*ons) +({w_2}) @@w,$ - <<- morph(\1, ">(?:montrer|penser|révéler|savoir|sentir|voir|vouloir) ", False) and morphex(\2, ":[NAQ].*:s", ":[GWpi]") + <<- morph(\1, ">(?:montrer|penser|révéler|savoir|sentir|voir|vouloir)/", False) and morphex(\2, ":[NAQ].*:s", ":[GWpi]") -2>> =suggPlur(@) # Accord avec le sujet « nous » : « \2 » devrait être au pluriel. TEST: nous nous pensions {{invincible}} jusqu’au jour où tout a basculé. __[i]/ppas(ppas_ils_se_verbe)__ (ils|c?eux +qui|ceux-ci|ceux-là|lesquels) +(?:ne +|)se +((?:s[eauû]|montr|pens|rév|v[oiîe])\w+) +({w_2}) @@0,w,$ - <<- morph(\2, ">(?:montrer|penser|révéler|savoir|sentir|voir|vouloir) ", False) + <<- morph(\2, ">(?:montrer|penser|révéler|savoir|sentir|voir|vouloir)/", False) and (morphex(\3, ":[NAQ].*:s", ":[GWpi]") or morphex(\3, ":[NAQ].*:f", ":[GWYme]")) and (not re.search("^(?:ceux-(?:ci|là)|lesquels)$", \1) or not morph(word(-1), ":R", False, False)) -3>> =suggMasPlur(@) # Accord avec le sujet « \1 » : « \3 » devrait être au masculin pluriel. TEST: ils se montrent {{exigeantes}} __[i]/ppas(ppas_elles_se_verbe)__ (elles|c?elles +qui|celles-(?:ci|là)|lesquelles) +(?:ne +|)se +((?:s[eauû]|montr|pens|rév|v[oiîe])\w+) +({w_2}) @@0,w,$ - <<- morph(\2, ">(?:montrer|penser|révéler|savoir|sentir|voir|vouloir) ", False) + <<- morph(\2, ">(?:montrer|penser|révéler|savoir|sentir|voir|vouloir)/", False) and (morphex(\3, ":[NAQ].*:s", ":[GWpi]") or morphex(\3, ":[NAQ].*:m", ":[GWYfe]")) and (not re.search("^(?:elles|celles-(?:ci|là)|lesquelles)$", \1) or not morph(word(-1), ":R", False, False)) -3>> =suggFemPlur(@) # Accord avec le sujet « \1 » : « \3 » devrait être au féminin pluriel. TEST: elles se sentent {{perdu}} __[i]/ppas(ppas_le_verbe_pensée)__ le ((?:trouv|consid[éè]r|cr[ouû]|rend|voilà)\w*) +({w_2}[esx]) @@w,$ - <<- morph(\1, ">(?:trouver|considérer|croire|rendre|voilà) ", False) and morphex(\2, ":[AQ].*:(?:[me]:p|f)", ":(?:G|Y|[AQ].*:m:[is])") + <<- morph(\1, ">(?:trouver|considérer|croire|rendre|voilà)/", False) and morphex(\2, ":[AQ].*:(?:[me]:p|f)", ":(?:G|Y|[AQ].*:m:[is])") and not (morph(\1, ":Y", False) and morph(\2, ":3s", False)) -2>> =suggMasSing(@) # Accord avec le COD “le” : « \2 » doit être au masculin singulier. __[i]/ppas(ppas_la_verbe_pensée)__ la ((?:trouv|consid[éè]r|cr[ouû]|rend|voilà)\w*) +({w_2}[uiéesx]) @@w,$ - <<- morph(\1, ">(?:trouver|considérer|croire|rendre|voilà) ", False) and morphex(\2, ":[AQ].*:(?:[fe]:p|m)", ":(?:G|Y|[AQ]:f:[is])") + <<- morph(\1, ">(?:trouver|considérer|croire|rendre|voilà)/", False) and morphex(\2, ":[AQ].*:(?:[fe]:p|m)", ":(?:G|Y|[AQ]:f:[is])") and not (morph(\1, ":Y", False) and morph(\2, ":3s", False)) -2>> =suggFemSing(@) # Accord avec le COD “la” : « \2 » doit être au féminin singulier. __[i]/ppas(ppas_les_verbe_pensée)__ les ((?:trouv|consid[éè]r|cr[ouû]|rend|voilà)\w*) +({w_2}) @@w,$ - <<- morph(\1, ">(?:trouver|considérer|croire|rendre|voilà) ", False) and morphex(\2, ":[AQ].*:s", ":(?:G|Y|[AQ].*:[ip])") + <<- morph(\1, ">(?:trouver|considérer|croire|rendre|voilà)/", False) and morphex(\2, ":[AQ].*:s", ":(?:G|Y|[AQ].*:[ip])") and not (morph(\1, ":Y", False) and morph(\2, ":3s", False)) -2>> =suggPlur(@) # Accord avec le COD “les” : « \2 » doit être au pluriel. __[i]/ppas(ppas_me_te_verbe_pensée)__ ([mt]e) ((?:trouv|consid[éè]r|cr[ouû]|rend|voilà)\w*) +({w_2}[sx]) @@0,w,$ - <<- morph(\2, ">(?:trouver|considérer|croire|rendre|voilà) ", False) and morphex(\3, ":[AQ].*:p", ":(?:G|Y|[AQ].*:[is])") + <<- morph(\2, ">(?:trouver|considérer|croire|rendre|voilà)/", False) and morphex(\3, ":[AQ].*:p", ":(?:G|Y|[AQ].*:[is])") and not (morph(\1, ":Y", False) and morph(\2, ":3s", False)) -3>> =suggSing(@) # Accord avec le pronom “\1” : « \3 » doit être au singulier. __[i]/ppas(ppas_se_verbe_pensée)__ se ((?:trouv|consid[éè]r|cr[ouû]|rend)\w*) +({w_3}) @@w,$ - <<- morph(\1, ">(?:trouver|considérer|croire|rendre) .*:3s", False) and morphex(\2, ":[AQ].*:p", ":(?:G|Y|[AQ].*:[is])") + <<- morph(\1, ">(?:trouver|considérer|croire|rendre)/.*:3s", False) and morphex(\2, ":[AQ].*:p", ":(?:G|Y|[AQ].*:[is])") and not (morph(\1, ":Y", False) and morph(\2, ":3s", False)) -2>> =suggSing(@) # Accord avec le pronom “se” (le verbe étant au singulier) : « \2 » doit être au singulier. - <<- __else__ and morph(\1, ">(?:trouver|considérer|croire|rendre) .*:3p", False) and morphex(\2, ":[AQ].*:s", ":(?:G|Y|[AQ].*:[ip])") + <<- __else__ and morph(\1, ">(?:trouver|considérer|croire|rendre)/.*:3p", False) and morphex(\2, ":[AQ].*:s", ":(?:G|Y|[AQ].*:[ip])") and not (morph(\1, ":Y", False) and morph(\2, ":3s", False)) -2>> =suggPlur(@) # Accord avec le pronom “se” (le verbe étant au pluriel) : « \2 » doit être au pluriel. __[i]/ppas(ppas_nous_verbe_pensée)__ nous ((?:trouv|consid[éè]r|cr[ouû]|rend|voilà)\w*) +({w_2}) @@w,$ - <<- ( morphex(\1, ">(?:trouver|considérer|croire|rendre|voilà) ", ":1p") - or (morph(\1, ">(?:trouver|considérer|croire) .*:1p", False) and before(r"\bn(?:ous|e) +$")) ) + <<- ( morphex(\1, ">(?:trouver|considérer|croire|rendre|voilà)/", ":1p") + or (morph(\1, ">(?:trouver|considérer|croire)/.*:1p", False) and before(r"\bn(?:ous|e) +$")) ) and morphex(\2, ":[AQ].*:s", ":(?:G|Y|[AQ].*:[ip])") and not (morph(\1, ":Y", False) and morph(\2, ":3s", False)) -2>> =suggPlur(@) # Accord avec le pronom “nous” : « \2 » doit être au pluriel. #__[i]/ppas(ppas_vous_verbe)__ # vous ((?:trouv|consid[éè]r|cr[ouû]|rend|voilà)\w*) +({w_2}) @@w,$ -# <<- ( morphex(\1, ">(?:trouver|considérer|croire|rendre|voilà) ", ":2p") -# or (morph(\1, ">(?:trouver|considérer|croire) .*:2p", False) and before(r"\b(?:vous|ne) +$")) ) +# <<- ( morphex(\1, ">(?:trouver|considérer|croire|rendre|voilà)/", ":2p") +# or (morph(\1, ">(?:trouver|considérer|croire)/.*:2p", False) and before(r"\b(?:vous|ne) +$")) ) # and morphex(\2, ":[AQ].*:s", ":(?:G|[AQ].*:[ip])") # -2>> =suggPlur(@) # Accord avec le pronom “vous” : « \2 » doit être au pluriel. TEST: ces hommes le rendent {{dingues}} TEST: Il me considère {{stupides}} @@ -10458,15 +10458,15 @@ #__[i]/conj__ fait(s|e|es) ({w1}) <<- morph(\2, ":V") and not morph(\2, ":Y") # ->> fait \1 # Le participe passé de faire reste au masculin singulier s’il est suivi par un verbe à l’infinitif. __[i](p_les_avoir_fait_vinfi)__ - les ({avoir}) +(fait) +(?:[mts](?:e +|’)|)({infi}) @@w,w,$ <<- morph(\1, ">avoir ", False) and morph(\3, ":Y", False) ~2>> _ + les ({avoir}) +(fait) +(?:[mts](?:e +|’)|)({infi}) @@w,w,$ <<- morph(\1, ">avoir/", False) and morph(\3, ":Y", False) ~2>> _ __[i]/ppas(ppas_pronom_avoir)__ (?:j’|je |tu |ils? |elles? |on |et )(?:ne +|n’|l(?:ui|eur) +|)({avoir}) +({w_2}) @@w,$ - <<- not re.search("(?i)^(?:barre|confiance|cours|envie|peine|prise|crainte|cure|affaire|hâte|force|recours)$", \2) and morph(word(-1), ">(?:comme|et|lorsque?|mais|o[uù]|puisque?|qu(?:oique?|i|and)|si(?:non|)) ", False, True) + <<- not re.search("(?i)^(?:barre|confiance|cours|envie|peine|prise|crainte|cure|affaire|hâte|force|recours)$", \2) and morph(word(-1), ">(?:comme|et|lorsque?|mais|o[uù]|puisque?|qu(?:oique?|i|and)|si(?:non|))/", False, True) and morph(\1, ":V0a", False) and not \2.isupper() and morphex(\2, ":(?:[123][sp]|Q.*:[fp])", ":(?:G|W|Q.*:m:[si])") -2>> =suggMasSing(@) # Ce verbe devrait être un participe passé au masculin singulier.|http://fr.wikipedia.org/wiki/Accord_du_participe_pass%C3%A9_en_fran%C3%A7ais TEST: ils leur avaient {{donnés}} du fil à retordre. @@ -10478,11 +10478,11 @@ __[i]/ppas(ppas_nous_vous_avoir)__ ([nv]ous) +(?:ne +|n’|l(?:ui|eur) +|)({avoir}) +({w_2}) @@0,w,$ <<- morph(\1, ":Os", False) - and not re.search("(?i)^(?:barre|confiance|cours|envie|peine|prise|crainte|cure|affaire|hâte|force|recours)$", \3) and morph(word(-1), ">(?:comme|et|lorsque?|mais|o[uù]|puisque?|qu(?:oique?|i|and)|si(?:non|)) ", False, True) + and not re.search("(?i)^(?:barre|confiance|cours|envie|peine|prise|crainte|cure|affaire|hâte|force|recours)$", \3) and morph(word(-1), ">(?:comme|et|lorsque?|mais|o[uù]|puisque?|qu(?:oique?|i|and)|si(?:non|))/", False, True) and morph(\2, ":V0a", False) and not \3.isupper() and morphex(\3, ":(?:[123][sp]|Q.*:[fp])", ":(?:G|W|Q.*:m:[si])") -3>> =suggMasSing(@) # Ce verbe devrait être un participe passé au masculin singulier.|http://fr.wikipedia.org/wiki/Accord_du_participe_pass%C3%A9_en_fran%C3%A7ais TEST: Nous avons {{donne}} tout notre potentiel. @@ -10490,11 +10490,11 @@ TEST: D’un côté, le modèle occidental, […], nous a libérés de […] __[i]/ppas(ppas_det_nom_avoir)__ (l(?:’|es? |a |eurs )|ce(?:s|tte|t|rtaine?s|) |des |quelques |[mts](?:es|on|a) |[nv]o(?:s|tre) ) *({w_2}) +(?:ne +|n’|l(?:ui|eur) +|)({avoir}) +({w_2}) @@0,w,w,$ - <<- not re.search("(?i)^(?:barre|confiance|cours|envie|peine|prise|crainte|cure|affaire|hâte|force|recours)$", \4) and morph(word(-1), ">(?:comme|et|lorsque?|mais|o[uù]|puisque?|qu(?:oique?|i|and)|si(?:non|)) ", False, True) + <<- not re.search("(?i)^(?:barre|confiance|cours|envie|peine|prise|crainte|cure|affaire|hâte|force|recours)$", \4) and morph(word(-1), ">(?:comme|et|lorsque?|mais|o[uù]|puisque?|qu(?:oique?|i|and)|si(?:non|))/", False, True) and not morph(\2, ":G", False) and morph(\3, ":V0a", False) and not \4.isupper() and morphex(\4, ":(?:[123][sp]|Q.*:[fp])", ":(?:G|W|Q.*:m:[si])") and not (\3 == "avions" and morph(\4, ":3[sp]", False)) -4>> =suggMasSing(@) # Ce verbe devrait être un participe passé au masculin singulier.|http://fr.wikipedia.org/wiki/Accord_du_participe_pass%C3%A9_en_fran%C3%A7ais @@ -10541,11 +10541,11 @@ TEST: ces livres m’avaient {{ennuyés}} au-delà du dicible. __[i]/ppas(ppas_qui_avoir)__ qui +(?:n’|l(?:ui|eur) |ne l(?:ui|eur) |ne +|)({avoir}) +({w_2}[es]) @@w,$ - <<- morph(\1, ">avoir ", False) and morphex(\2, ":Q.*:(?:f|m:p)", ":m:[si]") + <<- morph(\1, ">avoir/", False) and morphex(\2, ":Q.*:(?:f|m:p)", ":m:[si]") -2>> =suggMasSing(@) # Le participe passé devrait être au masculin singulier.|http://fr.wikipedia.org/wiki/Accord_du_participe_pass%C3%A9_en_fran%C3%A7ais TEST: des hommes, des femmes, des enfants qui ne leur avaient {{faits}} que du bien. @@ -10589,11 +10589,11 @@ ## avoir avec participe passé __[i]/ppas(ppas_m_t_l_avoir)__ [lmt]’(?:en +|y +|)({avoir}) +({w_3}) @@2,$ - <<- morph(\1, ">avoir ", False) and morphex(\2, ":(?:Y|[123][sp])", ":[QGWMX]") + <<- morph(\1, ">avoir/", False) and morphex(\2, ":(?:Y|[123][sp])", ":[QGWMX]") and not re.search(r"(?i)^t’as +envie", \0) -2>> =suggVerbPpas(@, ":m:s") # Confusion : employez un participe passé. TEST: m’avoir {{terminer}}. TEST: il m’a {{souffler}} la bonne réponse. @@ -10832,11 +10832,11 @@ <<- morph(\2, ":(?:[123][sp]|P)", False) =>> select(\2,":(?:[123][sp]|P)") <<- ~1>> * __> select(\2,":(?:[123][sp]|P)") - <<- not morph(\1, ":X|>rien ", False) ~1>> * + <<- not morph(\1, ":X|>rien/", False) ~1>> * __> select(\2,":(?:[123][sp]|P)") <<- ~1>> * __> select(\2,":(?:[123][sp]|P)") <<- ~1>> * __(p_premier_ne_pro_per_obj5)__ ^( *n’(?:en |y |))({w_2}) @@0,$ <<- morph(\2, ":(?:[123][sp]|P)", False) =>> select(\2,":(?:[123][sp]|P)") - <<- not morph(\1, ":X|>rien ", False) ~1>> * + <<- not morph(\1, ":X|>rien/", False) ~1>> * __(p_premier_ne_pro_per_obj6)__ ^( *ne l’)({w_2}) @@0,$ <<- morph(\2, ":(?:[123][sp]|P)", False) =>> select(\2,":(?:[123][sp]|P)") <<- ~1>> * __(p_premier_ne_pro_per_obj7)__ ^( *ne) ({w_2}) @@0,$ <<- morph(\2, ":(?:[123][sp]|P)", False) =>> select(\2,":(?:[123][sp]|P)") - <<- not morph(\2, ":X|>rien ", False) ~1>> * + <<- not morph(\2, ":X|>rien/", False) ~1>> * TEST: Ne rien céder. TEST: Ne pas manger. TEST: Ne manquer de rien. TEST: Ne jamais miser sur ces tocards. @@ -11044,11 +11044,11 @@ TEST: deux fois par an, souligne le Dr Assouline __[i]/imp(imp_laisser_le_la_les_infi)__ ((laiss\w+) l(?:es|a)) +({w_2}) @@0,0,$ - <<- morph(\2, ">laisser ", False) and morphex(\3, ":(?:Y|X|Oo)", ":[NAB]") + <<- morph(\2, ">laisser/", False) and morphex(\3, ":(?:Y|X|Oo)", ":[NAB]") -1>> =\1.replace(" ", "-") # S’il s’agit d’un impératif, mettez un trait d’union.|http://bdl.oqlf.gouv.qc.ca/bdl/gabarit_bdl.asp?id=4206 TEST: {{Laisse les}} entrer… TEST: {{Laissez la}} venir… @@ -11056,11 +11056,11 @@ TEST: Laissez la peste leur pourrir la vie encore quelque temps. __guerre ", False, False)) ->> \1’en + <<- not (\0.endswith("t-en") and before(r"(?i)\bva$") and morph(word(1), ">guerre/", False, False)) ->> \1’en # « \1e » est ici abrégé, c’est une forme élidée. Il faut mettre une apostrophe et non un trait d’union. TEST: donne{{-m-en}} encore @@ -11240,11 +11240,11 @@ <<- morph(\2, ":(?:[123][sp]|P|Y)", False) =>> select(\2, ":(?:[123][sp]|P|Y)") <<- not morph(\2, ":2s", False) or before(r"(?i)\b(?:je|tu|on|ils?|elles?|nous) +$") ~1>> * __[i](p_pro_per_obj30)__ (t’)({w_2}) @@0,$ <<- morph(\2, ":(?:[123][sp]|P|Y)", False) =>> select(\2, ":(?:[123][sp]|P|Y)") - <<- not morph(\2, ":2s|>(ils?|elles?|on) ", False) or before(r"(?i)\b(?:je|tu|on|ils?|elles?|nous) +$") ~1>> * + <<- not morph(\2, ":2s|>(ils?|elles?|on)/", False) or before(r"(?i)\b(?:je|tu|on|ils?|elles?|nous) +$") ~1>> * __[i>(p_pro_per_obj31)__ (ne +[mtsl]’)({w_1}) @@0,$ <<- morph(\2, ":(?:[123][sp]|P|Y)", False) =>> select(\2, ":(?:[123][sp]|P|Y)") <<- ~1>> * __[i>(p_pro_per_obj32)__ @@ -11310,11 +11310,11 @@ #### CONFUSION veillez/veuillez __[i]/conf(conf_veillez2)__ (veuillez) +à +(ne|{infi}) @@0,$ - <<- isStart() and morph(\2, ":Y|>ne ", False) -1>> veillez # Confusion probable : “veuillez” est une forme conjuguée du verbe “vouloir”.|http://bdl.oqlf.gouv.qc.ca/bdl/gabarit_bdl.asp?id=1939 + <<- isStart() and morph(\2, ":Y|>ne/", False) -1>> veillez # Confusion probable : “veuillez” est une forme conjuguée du verbe “vouloir”.|http://bdl.oqlf.gouv.qc.ca/bdl/gabarit_bdl.asp?id=1939 TEST: {{Veuillez}} à ne pas tomber dans ce piège. TEST: Et {{veuillez}} surtout à ouvrir grand les yeux. TEST: {{Veuillez}}, s’il vous plaît, à prendre vos médicaments. TEST: Veuillez à nouveau faire attention à ce problème. @@ -11322,11 +11322,11 @@ TEST: Veillez à bien fermer les fenêtres. __[i]/conf(conf_veuillez)__ (veillez) +(ne|{infi}) @@0,$ - <<- isStart() and morph(\2, ":Y|>ne ", False) -1>> veuillez + <<- isStart() and morph(\2, ":Y|>ne/", False) -1>> veuillez # Confusion probable : “veiller” signifie “prendre garde” ou “être vigilant”. Pour inviter à faire quelque chose, écrivez “veuillez”.|http://bdl.oqlf.gouv.qc.ca/bdl/gabarit_bdl.asp?id=1939 TEST: {{Veillez}} excuser mon retard. TEST: {{Veillez}} me contacter. TEST: {{Veillez}} me le faire savoir. @@ -11364,11 +11364,11 @@ TEST: en train de {{mangez}} __[i]/infi(infi_verbe)__ ((?:aim|all|v|ir|désir|esp[éè]r|p(?:[eou]|réf[éè]r))\w*) +({w_2}(?:ée?s?|ez)) @@0,$ - <<- morphex(\1, ">(?:aimer|aller|désirer|devoir|espérer|pouvoir|préférer|souhaiter|venir) ", ":[GN]") and morphex(\2, ":V", ":M") + <<- morphex(\1, ">(?:aimer|aller|désirer|devoir|espérer|pouvoir|préférer|souhaiter|venir)/", ":[GN]") and morphex(\2, ":V", ":M") -2>> =suggVerbInfi(@) # S’il s’agit d’une action à accomplir, le verbe devrait être à l’infinitif. TEST: elle préférait {{mangée}} seule. TEST: Il venait, comme d’habitude, {{discuté}} avec son ami. TEST: Ces types-là venaient {{mangé}} chez moi tous les dimanches. @@ -11378,11 +11378,11 @@ TEST: Cette affaire ne va rien {{arrangé}}. __[i]/infi(infi_devoir)__ (d[eouû]\w+) +({w_2}(?:ée?s?|ez)) @@0,$ - <<- morph(\1, ">devoir ", False) and morphex(\2, ":V", ":M") and not morph(word(-1), ":D", False) + <<- morph(\1, ">devoir/", False) and morphex(\2, ":V", ":M") and not morph(word(-1), ":D", False) -2>> =suggVerbInfi(@) # Le verbe devrait être à l’infinitif. TEST: il devait {{utilisé}} son temps à bon escient. TEST: tu dois {{mangé}} @@ -11395,11 +11395,11 @@ TEST: faut-il {{pensé}} à ces choses-là encore et encore ? __[i]/infi(infi_mieux_valoir)__ mieux (?:ne |)(va\w+) +({w_2}(?:ée?s?|ez)) @@w,$ - <<- morph(\1, ">valoir ", False) and morphex(\2, ":(?:Q|2p)", ":[GM]") + <<- morph(\1, ">valoir/", False) and morphex(\2, ":(?:Q|2p)", ":[GM]") -2>> =suggVerbInfi(@) # Le verbe devrait être à l’infinitif. TEST: Mieux vaut {{consacré}} son temps à des occupations utiles. @@ -11412,11 +11412,11 @@ TEST: je vais à Rodez. __[i]/infi(infi_avoir_beau)__ ({avoir}) beau ({w_2}(?:ée?s?|ez|ai[ts]?)) @@0,$ - <<- morph(\1, ">avoir ", False) and morphex(\2, ":V1", ":N") + <<- morph(\1, ">avoir/", False) and morphex(\2, ":V1", ":N") -2>> =suggVerbInfi(@) # Le verbe devrait être à l’infinitif.|http://fr.wiktionary.org/wiki/avoir_beau TEST: Ils ont beau {{consacré}} le plus clair de leur temps à ce projet, ça n’avance guère. @@ -11450,11 +11450,11 @@ !! ## 1sg __[i]/conj(conj_j)__ j’({w_1}) @@2 - <<- morphex(\1, ":V", ":1s|>(?:en|y) ") >>> + <<- morphex(\1, ":V", ":1s|>(?:en|y)/") >>> <<- \1 == "est" or \1 == "es" -1>> ai|aie|suis # Conjugaison erronée. Confusion probable entre “être” et “avoir”. Accord avec « \1 ». Le verbe devrait être à la 1ʳᵉ personne du singulier. <<- __else__ -1>> =suggVerb(@, ":1s") # Conjugaison erronée. Accord avec « je ». Le verbe devrait être à la 1ʳᵉ personne du singulier. __[i]/conj(conj_je)__ (je) +({w_1}) @@0,$ <<- morphex(\2, ":V", ":(?:1s|G)") and not (morph(\2, ":[PQ]", False) and morph(word(-1), ":V0.*:1s", False, False)) >>> @@ -11560,11 +11560,11 @@ TEST: celui qui {{pensent}} mal de toute chose __[i]/conj(conj_ça)__ (ça|chacune?|l’une?|ce(?:ci|la|lui-(?:ci|là)|lle-(?:ci|là))|n`importe quo?i|quelqu(?:’une?|e chose)) +(?:qui +|)({w_1}) @@0,$ - <<- morphex(\2, ":V", ":(?:3s|P|Q|G|3p!)") and not morph(word(-1), ":[VR]|>de ", False, False) + <<- morphex(\2, ":V", ":(?:3s|P|Q|G|3p!)") and not morph(word(-1), ":[VR]|>de/", False, False) -2>> =suggVerb(@, ":3s") # Conjugaison erronée. Accord avec « \1 ». Le verbe devrait être à la 3ᵉ personne du singulier. TEST: chacun {{fais}} comme il peut TEST: quelqu’un {{sauras}} #TEST: quelqu’une se {{montrent}} désagréable # Fuck you, JavaScript (wait for negative lookbehind assertions) @@ -11864,11 +11864,11 @@ # L’accord par syllepse est obligatoire après /la plupart/, ainsi qu’après /nombre/ et /quantité/ employés sans déterminant. L’accord se fait avec le « pseudo-complément ». __[i]/conj(conj_beaucoup_d_aucuns_la_plupart)__ (beaucoup|d’aucuns|la plupart) +({w_2}) @@0,$ - <<- morphex(\2, ":V", ":(?:3p|P|Q|G)") and not morph(word(-1), ":[VR]|>de ", False, False) + <<- morphex(\2, ":V", ":(?:3p|P|Q|G)") and not morph(word(-1), ":[VR]|>de/", False, False) -2>> =suggVerb(@, ":3p") # Conjugaison erronée. Accord avec « \1 ». Le verbe devrait être à la 3ᵉ personne du pluriel. __[i]/conj(conj_beaucoup_d_aucuns_la_plupart_qui)__ (beaucoup|d’aucuns|la plupart) +qui +({w_2}) @@0,$ <<- morphex(\2, ":V", ":(?:3p|P|Q|G)") and not morph(word(-1), ":[VR]", False, False) @@ -11982,11 +11982,11 @@ TEST: L’un comme l’autre devaient y renoncer. __[i]/conj(conj_des_nom1)__ ^ *des +({w_2}) +({w_2}) @@w,$ - <<- morph(\1, ":[NAQ].*:[pi]", False) and morphex(\2, ":V", ":(?:[13]p|P|G|Q|A.*:[pi])") and morph(word(1), ":(?:R|D.*:p)|>au ", False, True) >>> + <<- morph(\1, ":[NAQ].*:[pi]", False) and morphex(\2, ":V", ":(?:[13]p|P|G|Q|A.*:[pi])") and morph(word(1), ":(?:R|D.*:p)|>au/", False, True) >>> <<- not morph(\2, ":[NA]", False) -2>> =suggVerb(@, ":3p") # Conjugaison erronée. Accord avec « des \1… ». Le verbe devrait être à la 3ᵉ personne du pluriel. <<- __else__ and not checkAgreement(\1, \2) -2>> =suggVerb(@, ":3p", suggPlur) # Conjugaison erronée. Accord avec « des \1… ». Le verbe devrait être à la 3ᵉ personne du pluriel. __[i]/conj(conj_des_nom_qui)__ ^ *des +({w_2}) +qui +({w_2}) @@w,$ <<- morph(\1, ":[NAQ].*:[pi]", False) and morphex(\2, ":V", ":(?:[13]p|P|G)") @@ -12169,11 +12169,11 @@ ({w_1}s) tu @@0 <<- morphex(\1, ":V.*:2s", ":[GNW]") and not before(r"(?i)\b(?:je|tu) +$") and morphex(word(1), ":", ":2s", True) ->> \1-tu # Forme interrogative ? Mettez un trait d’union. __[i]/inte(inte_union_il_on)__ ({w_2}[td]) (?:il|on) @@0 - <<- morphex(\1, ":V.*:3s", ":[GNW]") and not before(r"(?i)\b(?:ce|il|elle|on) +$") and morphex(word(1), ":", ":3s|>y ", True) + <<- morphex(\1, ":V.*:3s", ":[GNW]") and not before(r"(?i)\b(?:ce|il|elle|on) +$") and morphex(word(1), ":", ":3s|>y/", True) ->> =\0.replace(" ", "-") # Forme interrogative ? Mettez un trait d’union. __[i]/inte(inte_union_elle)__ (?> \1-elle # Forme interrogative ? Mettez un trait d’union. @@ -12181,11 +12181,11 @@ ({w_2}ons) nous @@0 <<- morphex(\1, ":V.*:1p", ":[GNW]") and not morph(word(-1), ":Os", False, False) and morphex(word(1), ":", ":(?:Y|1p)", True) ->> \1-nous # Forme interrogative ? Mettez un trait d’union. __[i]/inte(inte_union_vous)__ ({w_2}e[zs]) vous @@0 - <<- morphex(\1, ":V.*:2p", ":[GNW]|>vouloir .*:E:2p") and not morph(word(-1), ":Os", False, False) and morphex(word(1), ":", ":(?:Y|2p)", True) + <<- morphex(\1, ":V.*:2p", ":[GNW]|>vouloir/.*:E:2p") and not morph(word(-1), ":Os", False, False) and morphex(word(1), ":", ":(?:Y|2p)", True) ->> \1-vous # Forme interrogative ? Mettez un trait d’union. __[i]/inte(inte_union_ils_elles)__ (?> =\0.replace(" ", "-") # Forme interrogative ? Mettez un trait d’union. @@ -12253,20 +12253,20 @@ __[i]/inte(inte_nous)__ ({w1})-nous @@0 <<- morphex(\1, ":V", ":(?:1p|E:2[sp])") -1>> =suggVerb(@, ":1p") # Forme interrogative ou impérative incorrecte. - <<- morphex(\1, ":", ":V|>chez ") -1>> =suggSimil(\1, ":1p", False) # Forme interrogative ou impérative incorrecte. + <<- morphex(\1, ":", ":V|>chez/") -1>> =suggSimil(\1, ":1p", False) # Forme interrogative ou impérative incorrecte. TEST: {{Prendront}}-nous ->> Prendrons TEST: {{Attendront}}-nous le train ->> Attendrons __[i]/inte(inte_vous)__ ({w1})-vous @@0 <<- morphex(\1, ":V", ":2p") -1>> =suggVerb(@, ":2p") # Forme interrogative ou impérative incorrecte. - <<- not morph(\1, ":V|>chez ", False) -1>> =suggSimil(\1, ":2p", False) # Forme interrogative ou impérative incorrecte. + <<- not morph(\1, ":V|>chez/", False) -1>> =suggSimil(\1, ":2p", False) # Forme interrogative ou impérative incorrecte. TEST: {{Attaquait}}-vous ->> Attaquiez TEST: Elle a de nombreux rendez-vous ce matin. TEST: êtes-vous là ? @@ -12285,14 +12285,14 @@ !!!! Verbe auxiliaire __[i]/conf(conf_avoir_sujet_participe_passé)__ ({avoir})-(?:je|tu|ils?|elles?|on) +({ppas}) @@0,$ - <<- morph(\1, ">avoir ", False) and morph(\2, ":V.......e_.*:Q", False) -1>> _ # Incohérence. La forme verbale “\2” ne peut pas être utilisé avec l’auxiliaire “avoir”, seulement avec l’auxiliaire “être”. + <<- morph(\1, ">avoir/", False) and morph(\2, ":V.......e_.*:Q", False) -1>> _ # Incohérence. La forme verbale “\2” ne peut pas être utilisé avec l’auxiliaire “avoir”, seulement avec l’auxiliaire “être”. __[i]/conf(conf_sujet_avoir_participe_passé)__ (?:j’|je |tu |ils? |elles? |on ) *({avoir}) +({ppas}) @@*,$ - <<- morph(\1, ">avoir ", False) and morph(\2, ":V.......e_.*:Q", False) -1>> _ # Incohérence. La forme verbale “\2” ne peut pas être utilisé avec l’auxiliaire “avoir”, seulement avec l’auxiliaire “être”. + <<- morph(\1, ">avoir/", False) and morph(\2, ":V.......e_.*:Q", False) -1>> _ # Incohérence. La forme verbale “\2” ne peut pas être utilisé avec l’auxiliaire “avoir”, seulement avec l’auxiliaire “être”. TEST: {{Ait}}-il arrivé à ses fins ? TEST: je n’{{avais}} pas parti avec eux. TEST: Avais-je partie liée avec lui ? TEST: il {{avait}} parti. @@ -12307,11 +12307,11 @@ # conditionnel / futur __[i]/vmode(vmode_j_aimerais_vinfi)__ j(?:e +|’)(aimerai|préf[éè]rerai|apprécierai|voudrai|souhaiterai|désirerai|adorerai) +({w_1}) @@w,$ - <<- morphex(\2, ":[YX]|>(?:y|ne|que?) ", ":R") and isStart() -1>> \1s # Si vous exprimez un souhait, utilisez le conditionnel et non le futur. + <<- morphex(\2, ":[YX]|>(?:y|ne|que?)/", ":R") and isStart() -1>> \1s # Si vous exprimez un souhait, utilisez le conditionnel et non le futur. TEST: J’{{aimerai}} savoir ce dont il retourne. TEST: dans tous les cas j’{{aimerai}} ne rien savoir TEST: Je {{voudrai}} qu’il soit déjà là. TEST: J’aimerai ces cours-là autant que les autres. @@ -12319,11 +12319,11 @@ TEST: Je sais que j’aimerai ça, tout comme lui. __[i]/vmode(vmode_j_aurais_aimé_que_avoir_être)__ j’(aurai) +(?:aimé|souhaité|préféré|voulu|apprécié|désiré|adoré) +(que?|ne|{infi}) @@2,$ - <<- morph(\2, ":Y|>(?:ne|que?) ", False) + <<- morph(\2, ":Y|>(?:ne|que?)/", False) -1>> aurais|eusse # Pour un souhait passé, utilisez le conditionnel passé et non le futur antérieur. Exemple pour le futur antérieur : « quand j’aurai fini… » TEST: J’{{aurai}} aimé nous offrir ce magnifique cadeau. TEST: j’{{aurai}} voulu être un artiste. TEST: j’{{aurai}} préféré ne pas avoir à l’entendre. @@ -12360,13 +12360,13 @@ # verbe que + subjonctif __[i]/vmode(vmode_qqch_que_subjonctif1)__ (afin|avant|pour|quoi|(?:perm|fa|v[oe]|ordonn|exig|désir|dout|suff|préf[éè]r)\w+) +que? +({w_2}) +({w_2}) @@0,w,$ - <<- morph(\1, ">(?:afin|avant|pour|quoi|permettre|falloir|vouloir|ordonner|exiger|désirer|douter|préférer|suffire) ", False) + <<- morph(\1, ">(?:afin|avant|pour|quoi|permettre|falloir|vouloir|ordonner|exiger|désirer|douter|préférer|suffire)/", False) and morph(\2, ":(?:Os|M)", False) and morphex(\3, ":I", ":[GYS]") - and not (morph(\1, ">douter ", False) and morph(\3, ":(?:If|K)", False)) + and not (morph(\1, ">douter/", False) and morph(\3, ":(?:If|K)", False)) -3>> =suggVerbMode(@, ":S", \2) # Après « \1 que », ce verbe devrait être au subjonctif. TEST: Il suffit qu’il {{court}} plus TEST: Je veux qu’il {{finit}} son repas. TEST: quoi qu’il en {{conclut}} @@ -12380,12 +12380,12 @@ # Bien que + subjonctif __[i]/vmode(vmode_bien_que_subjonctif)__ bien ?que? ({w_2}) +({w_2}) @@w,$ - <<- morph(\1, ":(?:Os|M)", False) and morphex(\2, ":V.*:I", ":(?:[GSK]|If)|>(?:hériter|recevoir|donner|offrir) ") and isStart() - and not ( morph(\2, ":V0a", False) and morph(word(1), ">(?:hériter|recevoir|donner|offrir) ", False) ) + <<- morph(\1, ":(?:Os|M)", False) and morphex(\2, ":V.*:I", ":(?:[GSK]|If)|>(?:hériter|recevoir|donner|offrir)/") and isStart() + and not ( morph(\2, ":V0a", False) and morph(word(1), ">(?:hériter|recevoir|donner|offrir)/", False) ) and not before0(r"(?i)\bsi ") -2>> =suggVerbMode(@, ":S", \1) # Après « bien que », le verbe s’emploie au subjonctif. TEST: Il ne le savait pas, bien qu’il en {{avait}} entendu parler. TEST: Bien que je {{prends}} mon mal en patience. @@ -12442,10 +12442,14 @@ # Après « quand » ou « lorsque », le verbe ne s’emploie pas au subjonctif mais à l’indicatif. TEST: quand elle {{rencontrât}} son créateur TEST: lorsqu’il y {{eût}} du grabuge, nous montâmes tous sur le pont. + + +@@@@GRAPH: test_graph@@@@ + !! !! !! @@ -16514,13 +16518,11 @@ TEST: Acaste, TEST: Clitandre, marquis TEST: Basque, valet de Célimène, TEST: Un garde de la maréchaussée de France, TEST: Dubois, valet d’Alceste. - TEST: La scène se passe à Paris, dans la maison de Célimène. - TEST: ACTE I TEST: SCÈNE PREMIÈRE. Philinte, Alceste. TEST: PHILINTE. Qu’est-ce donc ? Qu’avez-vous ? TEST: ALCESTE, assis. Laissez-moi, je vous prie. TEST: PHILINTE. Mais encor, dites-moi, quelle bizarrerie… ADDED gc_lang/fr/rules_graph.grx Index: gc_lang/fr/rules_graph.grx ================================================================== --- gc_lang/fr/rules_graph.grx +++ gc_lang/fr/rules_graph.grx @@ -0,0 +1,114 @@ +# +# RÈGLES DE GRAMMAIRE FRANÇAISE POUR GRAMMALECTE +# par Olivier R. +# +# Copyright © 2011-2017. +# +# This file is part of Grammalecte. +# +# Grammalecte is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# Grammalecte is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with Grammalecte. If not, see +# + +# RÈGLES POUR LE GRAPHE DE TOKENS + +# DOCUMENTATION +# Expressions régulières en Python : http://docs.python.org/library/re.html + +# [++] : séparateur des règles pour le paragraphe et des règles pour la phrase. + +# Types d’action: +# ->> erreur +# ~>> préprocesseur de texte +# =>> désambiguïsateur + + +# Fin d’interprétation du fichier avec une ligne commençant par #END + +# ERREURS COURANTES +# http://fr.wikipedia.org/wiki/Wikip%C3%A9dia:Fautes_d%27orthographe/Courantes + + +GRAPH_NAME: test_graph + +__da1__ + ne >donner + <<- =>> select(\2, ":V") + +TEST: je ne donne rien. + + +__da2__ + je >pousser + <<- =>> exclude(\2, ":N") + +TEST: je pousse + + +__da3__ + il >venir + <<- =>> define(\2, [":VVV"]) + +TEST: il vient + + +__pp__ + >avoir marre [d’|des|du|de] + <<- ~>> * + +TEST: J’en ai marre de ces gens-là. + + +__pp2__ + il ne pense qu’ à sa gueule + <<- ~4:7>> que|Z|a|perdu + +TEST: il ne pense qu’à sa gueule. + + +__avoir_confiance_en__ + >avoir confiance dans [moi|toi|soi|lui|elle|nous|vous|eux|elles] + <<- -3>> en # Avoir confiance en quelqu’un ou quelque chose.\3 \1 \2 \3|http://grammalecte.net + +TEST: Elle avait confiance {{dans}} lui. + + +__code_legacy__ + legacy code + code legacy + <<- -1:2>> code hérité|code reliquat|\1-\2|\2-\1 # \1 \2. Anglicisme superflu. + +TEST: c’est du {{legacy code}}. +TEST: ce {{code legacy}} est un cauchemar + + +__être_en_xxxx__ + [>être|>rester|>demeurer] an [désaccord|accord] + <<- -2>> en # Confusion. Un an = une année. + +TEST: Je suis {{an}} désaccord avec lui. + + +__faire_plaisir__ + >faire plaisirs + <<- -2>> plaisir # Faire plaisir : dans cette locution, “plaisir” doit être au singulier. + <<- ~2>> * + +TEST: Ça me fait {{plaisirs}}. + + +__test__ + je ~co[mn]putes? [que|qu’] @(?::Os|:M)¬:X @:I + <<- morph(\4, ":Os|:M", ":X") -5>> \1|\5 # SUBJONCTIF. + +TEST: je conpute qu’Isabelle {{est}} partie. Index: graphspell-js/ibdawg.js ================================================================== --- graphspell-js/ibdawg.js +++ graphspell-js/ibdawg.js @@ -512,11 +512,11 @@ let iAddr2 = this._convBytesToInteger(this.byDic.slice(iEndArcAddr, iEndArcAddr+this.nBytesNodeAddress)); let nRawArc2 = 0; while (!(nRawArc2 & this._lastArcMask)) { let iEndArcAddr2 = iAddr2 + this.nBytesArc; nRawArc2 = this._convBytesToInteger(this.byDic.slice(iAddr2, iEndArcAddr2)); - l.push(sStem + " " + this.lArcVal[nRawArc2 & this._arcMask]); + l.push(sStem + "/" + this.lArcVal[nRawArc2 & this._arcMask]); iAddr2 = iEndArcAddr2+this.nBytesNodeAddress; } } iAddr = iEndArcAddr + this.nBytesNodeAddress; } Index: graphspell-js/spellchecker.js ================================================================== --- graphspell-js/spellchecker.js +++ graphspell-js/spellchecker.js @@ -41,10 +41,14 @@ this.oPersonalDic = this._loadDictionary(personalDic, sPath); this.bExtendedDic = Boolean(this.oExtendedDic); this.bCommunityDic = Boolean(this.oCommunityDic); this.bPersonalDic = Boolean(this.oPersonalDic); this.oTokenizer = null; + // storage + this.bStorage = false; + this._dMorphologies = new Map(); // key: flexion, value: list of morphologies + this._dLemmas = new Map(); // key: flexion, value: list of lemmas } _loadDictionary (dictionary, sPath="", bNecessary=false) { // returns an IBDAWG object if (!dictionary) { @@ -132,10 +136,26 @@ deactivatePersonalDictionary () { this.bPersonalDic = false; } + + // Storage + + activateStorage () { + this.bStorage = true; + } + + deactivateStorage () { + this.bStorage = false; + } + + clearStorage () { + this._dLemmas.clear(); + this._dMorphologies.clear(); + } + // parse text functions parseParagraph (sText) { if (!this.oTokenizer) { @@ -203,21 +223,40 @@ return false; } getMorph (sWord) { // retrieves morphologies list, different casing allowed - let lResult = this.oMainDic.getMorph(sWord); + if (this.bStorage && this._dMorphologies.has(sWord)) { + return this._dMorphologies.get(sWord); + } + let lMorph = this.oMainDic.getMorph(sWord); if (this.bExtendedDic) { - lResult.push(...this.oExtendedDic.getMorph(sWord)); + lMorph.push(...this.oExtendedDic.getMorph(sWord)); } if (this.bCommunityDic) { - lResult.push(...this.oCommunityDic.getMorph(sWord)); + lMorph.push(...this.oCommunityDic.getMorph(sWord)); } if (this.bPersonalDic) { - lResult.push(...this.oPersonalDic.getMorph(sWord)); + lMorph.push(...this.oPersonalDic.getMorph(sWord)); + } + if (this.bStorage) { + this._dMorphologies.set(sWord, lMorph); + this._dLemmas.set(sWord, Array.from(new Set(this.getMorph(sWord).map((sMorph) => { return sMorph.slice(1, sMorph.indexOf("/")); })))); + //console.log(sWord, this._dLemmas.get(sWord)); + } + return lMorph; + } + + getLemma (sWord) { + // retrieves lemmas + if (this.bStorage) { + if (!this._dLemmas.has(sWord)) { + this.getMorph(sWord); + } + return this._dLemmas.get(sWord); } - return lResult; + return Array.from(new Set(this.getMorph(sWord).map((sMorph) => { return sMorph.slice(1, sMorph.indexOf("/")); }))); } * suggest (sWord, nSuggLimit=10) { // generator: returns 1, 2 or 3 lists of suggestions yield this.oMainDic.suggest(sWord, nSuggLimit); Index: graphspell-js/tokenizer.js ================================================================== --- graphspell-js/tokenizer.js +++ graphspell-js/tokenizer.js @@ -16,11 +16,11 @@ "default": [ [/^[   \t]+/, 'SPACE'], [/^\/(?:~|bin|boot|dev|etc|home|lib|mnt|opt|root|sbin|tmp|usr|var|Bureau|Documents|Images|Musique|Public|Téléchargements|Vidéos)(?:\/[a-zA-Zà-öÀ-Ö0-9ø-ÿØ-ßĀ-ʯfi-st_.()-]+)*/, 'FOLDERUNIX'], [/^[a-zA-Z]:\\(?:Program Files(?: \(x86\)|)|[a-zA-Zà-öÀ-Ö0-9ø-ÿØ-ßĀ-ʯfi-st.()]+)(?:\\[a-zA-Zà-öÀ-Ö0-9ø-ÿØ-ßĀ-ʯfi-st_.()-]+)*/, 'FOLDERWIN'], - [/^[,.;:!?…«»“”‘’"(){}\[\]/·–—]+/, 'SEPARATOR'], + [/^[,.;:!?…«»“”‘’"(){}\[\]/·–—]/, 'SEPARATOR'], [/^[A-Z][.][A-Z][.](?:[A-Z][.])*/, 'ACRONYM'], [/^(?:https?:\/\/|www[.]|[a-zA-Zà-öÀ-Ö0-9ø-ÿØ-ßĀ-ʯfi-st_-]+[@.][a-zA-Zà-öÀ-Ö0-9ø-ÿØ-ßĀ-ʯfi-st_-]{2,}[@.])[a-zA-Z0-9][a-zA-Zà-öÀ-Ö0-9ø-ÿØ-ßĀ-ʯfi-st_.\/?&!%=+*"'@$#-]+/, 'LINK'], [/^[#@][a-zA-Zà-öÀ-Ö0-9ø-ÿØ-ßĀ-ʯfi-st_-]+/, 'TAG'], [/^<[a-zA-Zà-öÀ-Ö0-9ø-ÿØ-ßĀ-ʯfi-st]+.*?>|<\/[a-zA-Zà-öÀ-Ö0-9ø-ÿØ-ßĀ-ʯfi-st]+ *>/, 'HTML'], [/^\[\/?[a-zA-Zà-öÀ-Ö0-9ø-ÿØ-ßĀ-ʯfi-st]+\]/, 'PSEUDOHTML'], @@ -32,11 +32,11 @@ "fr": [ [/^[   \t]+/, 'SPACE'], [/^\/(?:~|bin|boot|dev|etc|home|lib|mnt|opt|root|sbin|tmp|usr|var|Bureau|Documents|Images|Musique|Public|Téléchargements|Vidéos)(?:\/[a-zA-Zà-öÀ-Ö0-9ø-ÿØ-ßĀ-ʯfi-st_.()-]+)*/, 'FOLDERUNIX'], [/^[a-zA-Z]:\\(?:Program Files(?: \(x86\)|)|[a-zA-Zà-öÀ-Ö0-9ø-ÿØ-ßĀ-ʯfi-st.()]+)(?:\\[a-zA-Zà-öÀ-Ö0-9ø-ÿØ-ßĀ-ʯfi-st_.()-]+)*/, 'FOLDERWIN'], - [/^[,.;:!?…«»“”‘’"(){}\[\]/·–—]+/, 'SEPARATOR'], + [/^[,.;:!?…«»“”‘’"(){}\[\]/·–—]/, 'SEPARATOR'], [/^[A-Z][.][A-Z][.](?:[A-Z][.])*/, 'ACRONYM'], [/^(?:https?:\/\/|www[.]|[a-zA-Zà-öÀ-Ö0-9ø-ÿØ-ßĀ-ʯfi-st_-]+[@.][a-zA-Zà-öÀ-Ö0-9ø-ÿØ-ßĀ-ʯfi-st_-]{2,}[@.])[a-zA-Z0-9][a-zA-Zà-öÀ-Ö0-9ø-ÿØ-ßĀ-ʯfi-st_.\/?&!%=+*"'@$#-]+/, 'LINK'], [/^[#@][a-zA-Zà-öÀ-Ö0-9ø-ÿØ-ßĀ-ʯfi-st_-]+/, 'TAG'], [/^<[a-zA-Zà-öÀ-Ö0-9ø-ÿØ-ßĀ-ʯfi-st]+.*?>|<\/[a-zA-Zà-öÀ-Ö0-9ø-ÿØ-ßĀ-ʯfi-st]+ *>/, 'HTML'], [/^\[\/?[a-zA-Zà-öÀ-Ö0-9ø-ÿØ-ßĀ-ʯfi-st]+\]/, 'PSEUDOHTML'], @@ -60,36 +60,32 @@ this.aRules = aTkzPatterns[this.sLang]; } * genTokens (sText) { let m; - let i = 0; + let iNext = 0; while (sText) { - let nCut = 1; + let iCut = 1; + let iToken = 0; for (let [zRegex, sType] of this.aRules) { try { if ((m = zRegex.exec(sText)) !== null) { - if (sType == 'SEPARATOR') { - for (let c of m[0]) { - yield { "sType": sType, "sValue": c, "nStart": i, "nEnd": i + m[0].length } - } - } else { - yield { "sType": sType, "sValue": m[0], "nStart": i, "nEnd": i + m[0].length } - } - nCut = m[0].length; + iToken += 1; + yield { "i": iToken, "sType": sType, "sValue": m[0], "nStart": iNext, "nEnd": iNext + m[0].length } + iCut = m[0].length; break; } } catch (e) { helpers.logerror(e); } } - i += nCut; - sText = sText.slice(nCut); + iNext += iCut; + sText = sText.slice(iCut); } } } if (typeof(exports) !== 'undefined') { exports.Tokenizer = Tokenizer; } ADDED graphspell/fr.py Index: graphspell/fr.py ================================================================== --- graphspell/fr.py +++ graphspell/fr.py @@ -0,0 +1,44 @@ +# French language + +dSugg = { + "bcp": "beaucoup", + "ca": "ça", + "cad": "c’est-à-dire", + "cb": "combien|CB", + "cdlt": "cordialement", + "construirent": "construire|construisirent|construisent|construiront", + "càd": "c’est-à-dire", + "dc": "de|donc", + "email": "courriel|e-mail|émail", + "emails": "courriels|e-mails", + "Etes-vous": "Êtes-vous", + "Etiez-vous": "Étiez-vous", + "Etions-nous": "Étions-nous", + "parce-que": "parce que", + "pcq": "parce que", + "pd": "pendant", + "pdq": "pendant que", + "pdt": "pendant", + "pdtq": "pendant que", + "pk": "pourquoi", + "pq": "pourquoi|PQ", + "prq": "presque", + "prsq": "presque", + "qcq": "quiconque", + "qq": "quelque", + "qqch": "quelque chose", + "qqn": "quelqu’un", + "qqne": "quelqu’une", + "qqs": "quelques", + "qqunes": "quelques-unes", + "qquns": "quelques-uns", + "tdq": "tandis que", + "tj": "toujours", + "tjs": "toujours", + "tq": "tant que|tandis que", + "ts": "tous", + "tt": "tant|tout", + "tte": "toute", + "ttes": "toutes", + "y’a": "y a" +} Index: graphspell/ibdawg.py ================================================================== --- graphspell/ibdawg.py +++ graphspell/ibdawg.py @@ -487,11 +487,11 @@ iAddr2 = int.from_bytes(self.byDic[iEndArcAddr:iEndArcAddr+self.nBytesNodeAddress], byteorder='big') nRawArc2 = 0 while not (nRawArc2 & self._lastArcMask): iEndArcAddr2 = iAddr2 + self.nBytesArc nRawArc2 = int.from_bytes(self.byDic[iAddr2:iEndArcAddr2], byteorder='big') - l.append(sStem + " " + self.lArcVal[nRawArc2 & self._arcMask]) + l.append(sStem + "/" + self.lArcVal[nRawArc2 & self._arcMask]) iAddr2 = iEndArcAddr2+self.nBytesNodeAddress iAddr = iEndArcAddr+self.nBytesNodeAddress return l return [] @@ -592,11 +592,11 @@ iAddr2 += self.nBytesArc + self.nBytesNodeAddress nRawArc2 = 0 while not (nRawArc2 & self._lastArcMask): iEndArcAddr2 = iAddr2 + self.nBytesArc nRawArc2 = int.from_bytes(self.byDic[iAddr2:iEndArcAddr2], byteorder='big') - l.append(sStem + " " + self.lArcVal[nRawArc2 & self._arcMask]) + l.append(sStem + "/" + self.lArcVal[nRawArc2 & self._arcMask]) iAddr2 = iEndArcAddr2+self.nBytesNodeAddress if not (nRawArc2 & self._addrBitMask) else iEndArcAddr2 iAddr = iEndArcAddr+self.nBytesNodeAddress if not (nRawArc & self._addrBitMask) else iEndArcAddr return l return [] @@ -704,11 +704,11 @@ iAddr2 = iAddrNode + int.from_bytes(self.byDic[iEndArcAddr:iEndArcAddr+self.nBytesOffset], byteorder='big') nRawArc2 = 0 while not (nRawArc2 & self._lastArcMask): iEndArcAddr2 = iAddr2 + self.nBytesArc nRawArc2 = int.from_bytes(self.byDic[iAddr2:iEndArcAddr2], byteorder='big') - l.append(sStem + " " + self.lArcVal[nRawArc2 & self._arcMask]) + l.append(sStem + "/" + self.lArcVal[nRawArc2 & self._arcMask]) iAddr2 = iEndArcAddr2+self.nBytesNodeAddress if not (nRawArc2 & self._addrBitMask) else iEndArcAddr2+self.nBytesOffset iAddr = iEndArcAddr+self.nBytesNodeAddress if not (nRawArc & self._addrBitMask) else iEndArcAddr+self.nBytesOffset return l return [] Index: graphspell/spellchecker.py ================================================================== --- graphspell/spellchecker.py +++ graphspell/spellchecker.py @@ -6,11 +6,11 @@ # - the main dictionary, bundled with the package # - the extended dictionary # - the community dictionary, added by an organization # - the personal dictionary, created by the user for its own convenience - +import importlib import traceback from . import ibdawg from . import tokenizer @@ -34,10 +34,17 @@ self.oPersonalDic = self._loadDictionary(sfPersonalDic) self.bExtendedDic = bool(self.oExtendedDic) self.bCommunityDic = bool(self.oCommunityDic) self.bPersonalDic = bool(self.oPersonalDic) self.oTokenizer = None + # Default suggestions + self.dDefaultSugg = None + self.loadSuggestions(sLangCode) + # storage + self.bStorage = False + self._dMorphologies = {} # key: flexion, value: list of morphologies + self._dLemmas = {} # key: flexion, value: list of lemmas def _loadDictionary (self, source, bNecessary=False): "returns an IBDAWG object" if not source: return None @@ -97,10 +104,34 @@ self.bCommunityDic = False def deactivatePersonalDictionary (self): self.bPersonalDic = False + + # Default suggestions + + def loadSuggestions (self, sLangCode): + try: + suggest_module = importlib.import_module("."+sLangCode, "graphspell") + except: + print("No suggestion module for language <"+sLangCode+">") + return + self.dDefaultSugg = suggest_module.dSugg + + + # Storage + + def activateStorage (self): + self.bStorage = True + + def deactivateStorage (self): + self.bStorage = False + + def clearStorage (self): + self._dLemmas.clear() + self._dMorphologies.clear() + # parse text functions def parseParagraph (self, sText, bSpellSugg=False): if not self.oTokenizer: @@ -169,25 +200,44 @@ return True return False def getMorph (self, sWord): "retrieves morphologies list, different casing allowed" - lResult = self.oMainDic.getMorph(sWord) + if self.bStorage and sWord in self._dMorphologies: + return self._dMorphologies[sWord] + lMorph = self.oMainDic.getMorph(sWord) if self.bExtendedDic: - lResult.extend(self.oExtendedDic.getMorph(sWord)) + lMorph.extend(self.oExtendedDic.getMorph(sWord)) if self.bCommunityDic: - lResult.extend(self.oCommunityDic.getMorph(sWord)) + lMorph.extend(self.oCommunityDic.getMorph(sWord)) if self.bPersonalDic: - lResult.extend(self.oPersonalDic.getMorph(sWord)) - return lResult + lMorph.extend(self.oPersonalDic.getMorph(sWord)) + if self.bStorage: + self._dMorphologies[sWord] = lMorph + self._dLemmas[sWord] = set([ s[1:s.find("/")] for s in lMorph ]) + return lMorph def getLemma (self, sWord): - return set([ s[1:s.find(" ")] for s in self.getMorph(sWord) ]) + "retrieves lemmas" + if self.bStorage: + if sWord not in self._dLemmas: + self.getMorph(sWord) + return self._dLemmas[sWord] + return set([ s[1:s.find("/")] for s in self.getMorph(sWord) ]) def suggest (self, sWord, nSuggLimit=10): "generator: returns 1, 2 or 3 lists of suggestions" - yield self.oMainDic.suggest(sWord, nSuggLimit) + if self.dDefaultSugg: + if sWord in self.dDefaultSugg: + yield self.dDefaultSugg[sWord].split("|") + elif sWord.istitle() and sWord.lower() in self.dDefaultSugg: + lRes = self.dDefaultSugg[sWord.lower()].split("|") + yield list(map(lambda sSugg: sSugg[0:1].upper()+sSugg[1:], lRes)) + else: + yield self.oMainDic.suggest(sWord, nSuggLimit) + else: + yield self.oMainDic.suggest(sWord, nSuggLimit) if self.bExtendedDic: yield self.oExtendedDic.suggest(sWord, nSuggLimit) if self.bCommunityDic: yield self.oCommunityDic.suggest(sWord, nSuggLimit) if self.bPersonalDic: Index: graphspell/tokenizer.py ================================================================== --- graphspell/tokenizer.py +++ graphspell/tokenizer.py @@ -5,11 +5,11 @@ _PATTERNS = { "default": ( r'(?P/(?:bin|boot|dev|etc|home|lib|mnt|opt|root|sbin|tmp|usr|var|Bureau|Documents|Images|Musique|Public|Téléchargements|Vidéos)(?:/[\w.()-]+)*)', r'(?P[a-zA-Z]:\\(?:Program Files(?: [(]x86[)]|)|[\w.()]+)(?:\\[\w.()-]+)*)', - r'(?P[.,?!:;…«»“”"()/·]+)', + r'(?P[][,.;:!?…«»“”‘’"(){}/·–—])', r'(?P[A-Z][.][A-Z][.](?:[A-Z][.])*)', r'(?P(?:https?://|www[.]|\w+[@.]\w\w+[@.])\w[\w./?&!%=+*"\'@$#-]+)', r'(?P[#@][\w-]+)', r'(?P<\w+.*?>|)', r'(?P\[/?\w+\])', @@ -19,11 +19,11 @@ ), "fr": ( r'(?P/(?:bin|boot|dev|etc|home|lib|mnt|opt|root|sbin|tmp|usr|var|Bureau|Documents|Images|Musique|Public|Téléchargements|Vidéos)(?:/[\w.()-]+)*)', r'(?P[a-zA-Z]:\\(?:Program Files(?: [(]x86[)]|)|[\w.()]+)(?:\\[\w.()-]+)*)', - r'(?P[.,?!:;…«»“”"()/·]+)', + r'(?P[][,.;:!?…«»“”‘’"(){}/·–—])', r'(?P[A-Z][.][A-Z][.](?:[A-Z][.])*)', r'(?P(?:https?://|www[.]|\w+[@.]\w\w+[@.])\w[\w./?&!%=+*"\'@$#-]+)', r'(?P[#@][\w-]+)', r'(?P<\w+.*?>|)', r'(?P\[/?\w+\])', @@ -42,8 +42,13 @@ self.sLang = sLang if sLang not in _PATTERNS: self.sLang = "default" self.zToken = re.compile( "(?i)" + '|'.join(sRegex for sRegex in _PATTERNS[sLang]) ) - def genTokens (self, sText): - for m in self.zToken.finditer(sText): - yield { "sType": m.lastgroup, "sValue": m.group(), "nStart": m.start(), "nEnd": m.end() } + def genTokens (self, sText, bStartEndToken=False): + if bStartEndToken: + yield { "i": 0, "sType": "INFO", "sValue": "", "nStart": 0, "nEnd": 0 } + for i, m in enumerate(self.zToken.finditer(sText), 1): + yield { "i": i, "sType": m.lastgroup, "sValue": m.group(), "nStart": m.start(), "nEnd": m.end() } + if bStartEndToken: + iEnd = len(sText) + yield { "i": i+1, "sType": "INFO", "sValue": "", "nStart": iEnd, "nEnd": iEnd } Index: make.py ================================================================== --- make.py +++ make.py @@ -17,10 +17,11 @@ from distutils import dir_util, file_util import dialog_bundled import compile_rules +import compile_rules_graph import helpers import lex_build sWarningMessage = "The content of this folder is generated by code and replaced at each build.\n" @@ -191,12 +192,15 @@ dVars = xConfig._sections['args'] dVars['locales'] = dVars["locales"].replace("_", "-") dVars['loc'] = str(dict([ [s, [s[0:2], s[3:5], ""]] for s in dVars["locales"].split(" ") ])) ## COMPILE RULES - dResult = compile_rules.make(spLang, dVars['lang'], bJavaScript) - dVars.update(dResult) + dResultRegex = compile_rules.make(spLang, dVars['lang'], bJavaScript) + dVars.update(dResultRegex) + + dResultGraph = compile_rules_graph.make(spLang, dVars['lang'], bJavaScript) + dVars.update(dResultGraph) ## READ GRAMMAR CHECKER PLUGINS print("PYTHON:") print("+ Plugins: ", end="") sCodePlugins = "" @@ -227,11 +231,15 @@ print() # TEST FILES with open("grammalecte/"+sLang+"/gc_test.txt", "w", encoding="utf-8", newline="\n") as hDstPy: hDstPy.write("# TESTS FOR LANG [" + sLang + "]\n\n") - hDstPy.write(dVars['gctests']) + hDstPy.write("# REGEX RULES\n\n") + hDstPy.write(dVars['regex_gctests']) + hDstPy.write("\n\n\n# GRAPH RULES\n\n") + hDstPy.write(dVars['graph_gctests']) + hDstPy.write("\n") createOXT(spLang, dVars, xConfig._sections['oxt'], spLangPack, bInstallOXT) createServerOptions(sLang, dVars) createPackageZip(sLang, dVars, spLangPack) Index: misc/grammalecte.sublime-syntax ================================================================== --- misc/grammalecte.sublime-syntax +++ misc/grammalecte.sublime-syntax @@ -24,10 +24,14 @@ # Bookmarks - match: '^!!.*|^\[\+\+\].*' scope: bookmark + # Bookmarks + - match: '^GRAPH_NAME:.*' + scope: bookmark + # Keywords are if, else. # Note that blackslashes don't need to be escaped within single quoted # strings in YAML. When using single quoted strings, only single quotes # need to be escaped: this is done by using two single quotes next to each # other. @@ -35,11 +39,11 @@ scope: keyword.python - match: '\b(?:True|False|None)\b' scope: constant.language - - match: '\b(?:spell|morph|morphex|stem|textarea0?\w*|before0?\w*|after0?\w*|word|option|define|select|exclude|analysex?|apposition|is[A-Z]\w+|rewriteSubject|checkD\w+|getD\w+|has[A-Z]\w+|sugg[A-Z]\w+|switch[A-Z]\w+|ceOrCet|formatN\w+|mbUnit)\b' + - match: '\b(?:spell|morph|morphex|g_morph|stem|textarea0?\w*|before0?\w*|after0?\w*|word|option|define|select|exclude|g_define|g_select|g_exclude|analysex?|apposition|is[A-Z]\w+|rewriteSubject|checkD\w+|getD\w+|has[A-Z]\w+|sugg[A-Z]\w+|switch[A-Z]\w+|ceOrCet|formatN\w+|mbUnit)\b' scope: entity.name.function - match: '\b(?:replace|endswith|startswith|search|upper|lower|capitalize|strip|rstrip|is(?:upper|lower|digit|title))\b' scope: support.function @@ -47,18 +51,26 @@ scope: support.function.debug - match: '\bre\b' scope: support.class - # Rule options + # Regex rule option - match: '^__[\[<]([isu])[\]>](/\w+|)(\(\w+\)|)(![0-9]|)__|' scope: rule.options captures: 1: rule.casing 2: rule.optionname 3: rule.rulename 4: rule.priority + + # Graph rules option + - match: '^__(\w+)(![0-9]|)__' + scope: rule.options + captures: + 1: rule.rulename2 + 2: rule.priority + # Definitions and options - match: '^OPT(?:GROUP|LANG|PRIORITY)/|^OPTSOFTWARE:' scope: options.command @@ -84,20 +96,31 @@ scope: keyword.action - match: '__also__' scope: keyword.condition.green - match: '__else__' scope: keyword.condition.red - - match: '-(\d*)>>' + - match: '-(\d*(?::\d+|))>>' scope: keyword.error captures: 1: keyword.error.group - - match: '~(\d*)>>' + - match: '~(\d*(?::\d+|))>>' scope: keyword.textprocessor captures: 1: keyword.textprocessor.group - match: '=>>' scope: keyword.disambiguator + + # Tokens + - match: '>\w+' + scope: string.lemma + + - match: '~(?!(?:\d+(?::\d+|)|)>>)[^\s]+' + scope: string.regex + + - match: '@[^@][^\s]+' + scope: string.morph + # Escaped chars - match: '\\(?:\d+|w|d|b|n|s|t)' scope: constant.character.escape @@ -108,11 +131,11 @@ # Example errors - match: '{{.+?}}' scope: message.error # special chars - - match: '[@=*^?!:+<>]' + - match: '[@=*^?!:+<>~]' scope: keyword.other - match: '\(\?(?:[:=!]|italic foreground #A0A0A0 + + name + Rule name + scope + rule.rulename2 + settings + + foreground + #F0D080 + + name Rule priority scope rule.priority @@ -356,10 +367,50 @@ foreground #F06060 + + name + String lemma + scope + string.lemma + settings + + foreground + #FF3030 + background + #402020 + + + + name + String regex + scope + string.regex + settings + + foreground + #30FF30 + background + #204020 + + + + name + String morph + scope + string.morph + settings + + foreground + #8080FF + background + #202080 + + + name JavaScript Dollar scope