Index: compile_rules.py ================================================================== --- compile_rules.py +++ compile_rules.py @@ -1,11 +1,15 @@ +""" +Grammalecte: compile rules +""" import re import traceback import json import compile_rules_js_convert as jsconv +import compile_rules_graph as crg dDEF = {} lFUNCTIONS = [] @@ -17,10 +21,11 @@ sWORDLIMITLEFT = r"(?" try: return re.compile(sRegex).groups except: traceback.print_exc() print(sRegex) @@ -110,13 +116,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 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 @@ -147,11 +161,11 @@ if i == -1: print("# Error: no condition at line " + sLineId) return None sRegex = s[:i].strip() s = s[i+4:] - + # JS groups positioning codes m = re.search("@@\\S+", sRegex) if m: tGroups = jsconv.groupsPositioningCodeToList(sRegex[m.start()+2:]) sRegex = sRegex[:m.start()].strip() @@ -202,18 +216,18 @@ else: print("# Unknown case mode [" + cCaseMode + "] at line " + sLineId) ## check regex try: - z = re.compile(sRegex) + re.compile(sRegex) except: print("# Regex error at line ", nIdLine) print(sRegex) traceback.print_exc() return None ## groups in non grouping parenthesis - for x in re.finditer("\(\?:[^)]*\([[\w -]", sRegex): + for x in re.finditer(r"\(\?:[^)]*\([[\w -]", sRegex): print("# Warning: groups inside non grouping parenthesis in regex at line " + sLineId) #### PARSE ACTIONS lActions = [] nAction = 1 @@ -225,39 +239,50 @@ if not lActions: return None return [sOption, sRegex, bCaseInsensitive, sLineId, sRuleId, nPriority, lActions, tGroups] + +def checkReferenceNumbers (sText, sActionId, nToken): + "check if token references in greater than (debugging)" + for x in re.finditer(r"\\(\d+)", sText): + if int(x.group(1)) > nToken: + print("# Error in token index at line " + sActionId + " ("+str(nToken)+" tokens only)") + print(sText) + + +def checkIfThereIsCode (sText, sActionId): + "check if there is code in (debugging)" + if re.search("[.]\\w+[(]|sugg\\w+[(]|\\([0-9]|\\[[0-9]", sText): + print("# Warning at line " + sActionId + ": This message looks like code. Line should probably begin with =") + print(sText) + def createAction (sIdAction, sAction, nGroup): "returns an action to perform as a tuple (condition, action type, action[, iGroup [, message, URL ]])" - global lFUNCTIONS - m = re.search(r"([-~=>])(\d*|)>>", sAction) if not m: print("# No action at line " + sIdAction) return None #### CONDITION sCondition = sAction[:m.start()].strip() if sCondition: sCondition = prepareFunction(sCondition) - lFUNCTIONS.append(("c_"+sIdAction, sCondition)) - for x in re.finditer("[.](?:group|start|end)[(](\d+)[)]", sCondition): - if int(x.group(1)) > nGroup: - print("# Error in groups in condition at line " + sIdAction + " ("+str(nGroup)+" groups only)") + lFUNCTIONS.append(("_c_"+sIdAction, sCondition)) + checkReferenceNumbers(sCondition, sIdAction, nGroup) if ".match" in sCondition: print("# Error. JS compatibility. Don't use .match() in condition, use .search()") - sCondition = "c_"+sIdAction + sCondition = "_c_"+sIdAction else: sCondition = None #### iGroup / positioning iGroup = int(m.group(2)) if m.group(2) else 0 if iGroup > nGroup: print("# Selected group > group number in regex at line " + sIdAction) - + #### ACTION sAction = sAction[m.end():].strip() cAction = m.group(1) if cAction == "-": ## error @@ -272,87 +297,78 @@ sURL = "" mURL = re.search("[|] *(https?://.*)", sMsg) if mURL: sURL = mURL.group(1).strip() sMsg = sMsg[:mURL.start(0)].strip() + checkReferenceNumbers(sMsg, sIdAction, nGroup) if sMsg[0:1] == "=": sMsg = prepareFunction(sMsg[1:]) - lFUNCTIONS.append(("m_"+sIdAction, sMsg)) - for x in re.finditer("group[(](\d+)[)]", sMsg): - if int(x.group(1)) > nGroup: - print("# Error in groups in message at line " + sIdAction + " ("+str(nGroup)+" groups only)") - sMsg = "=m_"+sIdAction + lFUNCTIONS.append(("_m_"+sIdAction, sMsg)) + sMsg = "=_m_"+sIdAction else: - for x in re.finditer(r"\\(\d+)", sMsg): - if int(x.group(1)) > nGroup: - print("# Error in groups in message at line " + sIdAction + " ("+str(nGroup)+" groups only)") - if re.search("[.]\\w+[(]", sMsg): - print("# Error in message at line " + sIdAction + ": This message looks like code. Line should begin with =") - + checkIfThereIsCode(sMsg, sIdAction) + + checkReferenceNumbers(sAction, sIdAction, nGroup) 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) sAction = sAction.replace("m.group(i[4])", "m.group("+str(iGroup)+")") - for x in re.finditer("group[(](\d+)[)]", sAction): - if int(x.group(1)) > nGroup: - print("# Error in groups in replacement at line " + sIdAction + " ("+str(nGroup)+" groups only)") else: - for x in re.finditer(r"\\(\d+)", sAction): - if int(x.group(1)) > nGroup: - print("# Error in groups in replacement at line " + sIdAction + " ("+str(nGroup)+" groups only)") - if re.search("[.]\\w+[(]|sugg\\w+[(]", sAction): - print("# Error in action at line " + sIdAction + ": This action looks like code. Line should begin with =") + checkIfThereIsCode(sAction, sIdAction) + + if cAction == ">": + ## no action, break loop if condition is False + return [sCondition, cAction, ""] + + if not sAction: + print("# Error in action at line " + sIdAction + ": This action is empty.") + return None 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(("s_"+sIdAction, sAction[1:])) - sAction = "=s_"+sIdAction + lFUNCTIONS.append(("_s_"+sIdAction, sAction[1:])) + sAction = "=_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, iGroup, 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(("p_"+sIdAction, sAction[1:])) - sAction = "=p_"+sIdAction + lFUNCTIONS.append(("_p_"+sIdAction, sAction[1:])) + sAction = "=_p_"+sIdAction elif sAction.startswith('"') and sAction.endswith('"'): sAction = sAction[1:-1] return [sCondition, cAction, sAction, iGroup] 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(("d_"+sIdAction, sAction)) - sAction = "d_"+sIdAction + if "define" in sAction and not re.search(r"define\(dTokenPos, *m\.start.*, \[.*\] *\)", sAction): + print("# Error in action at line " + sIdAction + ": second argument for define must be a list of strings") + print(sAction) + lFUNCTIONS.append(("_d_"+sIdAction, sAction)) + sAction = "_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 _calcRulesStats (lRules): + "count rules and actions" 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): + "display rules numbers" print(" {:>18} {:>18} {:>18} {:>18}".format("DISAMBIGUATOR", "TEXT PROCESSOR", "GRAMMAR CHECKING", "REGEX")) d, nRule = _calcRulesStats(lParagraphRules) print("§ {:>10} actions {:>10} actions {:>10} actions in {:>8} rules".format(d['='], d['~'], d['-'], nRule)) d, nRule = _calcRulesStats(lSentenceRules) print("s {:>10} actions {:>10} actions {:>10} actions in {:>8} rules".format(d['='], d['~'], d['-'], nRule)) @@ -391,11 +407,11 @@ elif sLine.startswith("OPTSOFTWARE:"): lOpt = [ [s, {}] for s in sLine[12:].strip().split() ] # don’t use tuples (s, {}), because unknown to JS elif sLine.startswith("OPT/"): m = re.match("OPT/([a-z0-9]+):(.+)$", sLine) for i, sOpt in enumerate(m.group(2).split()): - lOpt[i][1][m.group(1)] = eval(sOpt) + lOpt[i][1][m.group(1)] = eval(sOpt) elif sLine.startswith("OPTPRIORITY/"): m = re.match("OPTPRIORITY/([a-z0-9]+): *([0-9])$", sLine) dOptPriority[m.group(1)] = int(m.group(2)) elif sLine.startswith("OPTLANG/"): m = re.match("OPTLANG/([a-z][a-z](?:_[A-Z][A-Z]|)):(.+)$", sLine) @@ -415,10 +431,11 @@ dOptions.update({ "dOpt"+k: v for k, v in lOpt }) return dOptions, dOptPriority def printBookmark (nLevel, sComment, nLine): + "print bookmark within the rules file" print(" {:>6}: {}".format(nLine, " " * nLevel + sComment)) def make (spLang, sLang, bJavaScript): "compile rules, returns a dictionary of values" @@ -431,52 +448,73 @@ 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 - lLine = [] lRuleLine = [] lTest = [] lOpt = [] - zBookmark = re.compile("^!!+") - zGraphLink = re.compile(r"^@@@@GRAPHLINK>(\w+)@@@@") + bGraph = False + lGraphRule = [] 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()) - if m: - #lRuleLine.append(["@GRAPHLINK", m.group(1)]) - printBookmark(1, "@GRAPHLINK: " + m.group(1), i) 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("{:<8}".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): - 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) + printBookmark(nExMk-2, sLine[nExMk:-3].strip(), i) + # Graph rules + elif sLine.startswith("@@@@GRAPH:"): + # rules graph call + m = re.match(r"@@@@GRAPH: *(\w+)", sLine.strip()) + if m: + printBookmark(1, "____ GRAPH: " + m.group(1) + " ____", i) + lRuleLine.append([i, "@@@@"+m.group(1)]) + bGraph = True + lGraphRule.append([i, sLine]) + bGraph = True + elif sLine.startswith("@@@@END_GRAPH"): + #lGraphRule.append([i, sLine]) + bGraph = False + elif re.match("@@@@ *$", sLine): + pass + elif bGraph: + lGraphRule.append([i, sLine]) + # Regex rules + elif re.match("[  \t]*$", sLine): + # empty line + pass 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: @@ -515,21 +553,20 @@ # 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: - cType = sFuncName[0:1] - if cType == "c": # condition - sParams = "s, sx, m, dDA, sCountry, bCondMemo" - elif cType == "m": # message - sParams = "s, m" - elif cType == "s": # suggestion - sParams = "s, m" - elif cType == "p": # preprocessor - sParams = "s, m" - elif cType == "d": # disambiguator - sParams = "s, m, dDA" + if sFuncName.startswith("_c_"): # condition + sParams = "s, sx, m, dTokenPos, sCountry, bCondMemo" + elif sFuncName.startswith("_m_"): # message + sParams = "s, m" + elif sFuncName.startswith("_s_"): # suggestion + sParams = "s, m" + elif sFuncName.startswith("_p_"): # preprocessor + sParams = "s, m" + elif sFuncName.startswith("_d_"): # disambiguator + sParams = "s, m, dTokenPos" else: print("# Unknown function type in [" + sFuncName + "]") continue sPyCallables += "def {} ({}):\n".format(sFuncName, sParams) sPyCallables += " return " + sReturn + "\n" @@ -540,16 +577,20 @@ displayStats(lParagraphRules, lSentenceRules) print("Unnamed rules: " + str(nRULEWITHOUTNAME)) - d = { "callables": sPyCallables, - "callablesJS": sJSCallables, - "gctests": sGCTests, - "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 + dVars = { "callables": sPyCallables, + "callablesJS": sJSCallables, + "gctests": sGCTests, + "gctestsJS": sGCTestsJS, + "paragraph_rules": mergeRulesByOption(lParagraphRules), + "sentence_rules": mergeRulesByOption(lSentenceRules), + "paragraph_rules_JS": jsconv.writeRulesToJSArray(mergeRulesByOption(lParagraphRulesJS)), + "sentence_rules_JS": jsconv.writeRulesToJSArray(mergeRulesByOption(lSentenceRulesJS)) } + dVars.update(dOptions) + + # compile graph rules + dVars2 = crg.make(lGraphRule, dDEF, sLang, bJavaScript) + dVars.update(dVars2) + + return dVars ADDED compile_rules_graph.py Index: compile_rules_graph.py ================================================================== --- /dev/null +++ compile_rules_graph.py @@ -0,0 +1,388 @@ +""" +Grammalecte: compile rules +Create a Direct Acyclic Rule Graphs (DARGs) +""" + +import re +import traceback +import json + +import darg + + +dACTIONS = {} +dFUNCTIONS = {} + + +def prepareFunction (s, bTokenValue=False): + "convert simple rule syntax to a string of Python code" + s = s.replace("__also__", "bCondMemo") + s = s.replace("__else__", "not bCondMemo") + s = re.sub(r"(morph|analyse|displayInfo)[(]\\(\d+)", 'g_\\1(lToken[\\2+nTokenOffset]', s) + s = re.sub(r"(select|exclude|define)[(][\\](\d+)", 'g_\\1(lToken[\\2+nTokenOffset]', s) + s = re.sub(r"(tag_before|tag_after)[(][\\](\d+)", 'g_\\1(lToken[\\2+nTokenOffset], dTags', s) + s = re.sub(r"(switchGender|has(?:Mas|Fem)Form)[(]\\(\d+)", '\\1(lToken[\\2+nTokenOffset]["sValue"]', s) + s = re.sub(r"(morph|analyse)\(>1", 'g_\\1(lToken[nLastToken+1]', s) # next token + s = re.sub(r"(morph|analyse)\(<1", 'g_\\1(lToken[nTokenOffset]', s) # previous token + s = re.sub(r"[\\](\d+)\.is(upper|lower|title)\(\)", 'lToken[\\1+nTokenOffset]["sValue"].is\\2()', s) + s = re.sub(r"\bspell *[(]", '_oSpellChecker.isValid(', s) + s = re.sub(r"\bbefore\(\s*", 'look(sSentence[:lToken[1+nTokenOffset]["nStart"]], ', s) # before(s) + s = re.sub(r"\bafter\(\s*", 'look(sSentence[lToken[nLastToken]["nEnd"]:], ', s) # after(s) + s = re.sub(r"\bbefore0\(\s*", 'look(sSentence0[:lToken[1+nTokenOffset]["nStart"]], ', s) # before0(s) + s = re.sub(r"\bafter0\(\s*", 'look(sSentence[lToken[nLastToken]["nEnd"]:], ', s) # after0(s) + if bTokenValue: + # token values are used as parameter + s = re.sub(r"[\\](\d+)", 'lToken[\\1+nTokenOffset]["sValue"]', s) + else: + # tokens used as parameter + s = re.sub(r"[\\](\d+)", 'lToken[\\1+nTokenOffset]', s) + return s + + +def genTokenLines (sTokenLine, dDef): + "tokenize a string and return a list of lines of tokens" + lToken = sTokenLine.split() + lTokenLines = None + for sToken in lToken: + # optional token? + bNullPossible = sToken.startswith("?") and sToken.endswith("¿") + if bNullPossible: + sToken = sToken[1:-1] + # token with definition? + if sToken.startswith("({") and sToken.endswith("})") and sToken[1:-1] in dDef: + sToken = "(" + dDef[sToken[1:-1]] + ")" + elif sToken.startswith("{") and sToken.endswith("}") and sToken in dDef: + sToken = dDef[sToken] + if ( (sToken.startswith("[") and sToken.endswith("]")) or (sToken.startswith("([") and sToken.endswith("])")) ): + # multiple token + bSelectedGroup = sToken.startswith("(") and sToken.endswith(")") + if bSelectedGroup: + sToken = sToken[1:-1] + lNewToken = sToken[1:-1].split("|") + if not lTokenLines: + lTokenLines = [ [s] for s in lNewToken ] + if bNullPossible: + lTokenLines.extend([ [] for i in range(len(lNewToken)+1) ]) + else: + lNewTemp = [] + if bNullPossible: + for aRule in lTokenLines: + for sElem in lNewToken: + aNewRule = list(aRule) + aNewRule.append(sElem) + lNewTemp.append(aNewRule) + else: + sElem1 = lNewToken.pop(0) + for aRule in lTokenLines: + for sElem in lNewToken: + aNewRule = list(aRule) + aNewRule.append("(" + sElem + ")" if bSelectedGroup else sElem) + lNewTemp.append(aNewRule) + aRule.append("(" + sElem1 + ")" if bSelectedGroup else sElem1) + lTokenLines.extend(lNewTemp) + else: + # simple token + if not lTokenLines: + lTokenLines = [[sToken], []] if bNullPossible else [[sToken]] + else: + if bNullPossible: + lNewTemp = [] + for aRule in lTokenLines: + lNew = list(aRule) + lNew.append(sToken) + lNewTemp.append(lNew) + lTokenLines.extend(lNewTemp) + else: + for aRule in lTokenLines: + aRule.append(sToken) + for aRule in lTokenLines: + yield aRule + + +def createRule (iLine, sRuleName, sTokenLine, iActionBlock, sActions, nPriority, dDef): + "generator: create rule as list" + # print(iLine, "//", sRuleName, "//", sTokenLine, "//", sActions, "//", nPriority) + for lToken in genTokenLines(sTokenLine, dDef): + # 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 iAction, sAction in enumerate(sActions.split(" <<- "), 1): + sAction = sAction.strip() + if sAction: + sActionId = sRuleName + "__b" + str(iActionBlock) + "_a" + str(iAction) + "_" + str(len(lToken)) + 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 (sText, dPos): + "change group reference in with values in " + for i in range(len(dPos), 0, -1): + sText = sText.replace("\\"+str(i), "\\"+str(dPos[i])) + return sText + + +def checkTokenNumbers (sText, sActionId, nToken): + "check if token references in greater than (debugging)" + for x in re.finditer(r"\\(\d+)", sText): + if int(x.group(1)) > nToken: + print("# Error in token index at line " + sActionId + " ("+str(nToken)+" tokens only)") + print(sText) + + +def checkIfThereIsCode (sText, sActionId): + "check if there is code in (debugging)" + if re.search("[.]\\w+[(]|sugg\\w+[(]|\\([0-9]|\\[[0-9]", sText): + print("# Warning at line " + sActionId + ": This message looks like code. Line should probably begin with =") + print(sText) + + +def createAction (sActionId, sAction, nPriority, nToken, dPos): + "create action rule as a list" + # Option + sOption = False + m = re.match("/(\\w+)/", sAction) + if m: + sOption = m.group(1) + sAction = sAction[m.end():].strip() + # valid action? + m = re.search("(?P[-~=/])(?P\\d+|)(?P:\\d+|)>> ", sAction) + if not m: + print(" # Error. No action found at: ", sActionId) + print(" ==", sAction, "==") + return None + # Condition + sCondition = sAction[:m.start()].strip() + if sCondition: + sCondition = prepareFunction(sCondition) + sCondition = changeReferenceToken(sCondition, dPos) + dFUNCTIONS["_g_c_"+sActionId] = sCondition + sCondition = "_g_c_"+sActionId + else: + sCondition = "" + # Action + cAction = m.group("action") + sAction = sAction[m.end():].strip() + sAction = changeReferenceToken(sAction, dPos) + if not m.group("start"): + iStartAction = 1 + iEndAction = 0 + else: + iStartAction = int(m.group("start")) + iEndAction = int(m.group("end")[1:]) if m.group("end") else iStartAction + if dPos and m.group("start"): + try: + iStartAction = dPos[iStartAction] + if iEndAction: + iEndAction = dPos[iEndAction] + except: + print("# Error. Wrong groups in: " + sActionId) + print(" iStartAction:", iStartAction, "iEndAction:", iEndAction) + print(" ", dPos) + + if cAction == "-": + ## error + iMsg = sAction.find(" # ") + if iMsg == -1: + sMsg = "# Error. Error message not found." + sURL = "" + print(sMsg + " Action id: " + sActionId) + 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() + checkTokenNumbers(sMsg, sActionId, nToken) + if sMsg[0:1] == "=": + sMsg = prepareFunction(sMsg[1:], True) + dFUNCTIONS["g_m_"+sActionId] = sMsg + sMsg = "=g_m_"+sActionId + else: + checkIfThereIsCode(sMsg, sActionId) + + # checking consistancy + checkTokenNumbers(sAction, sActionId, nToken) + + if cAction == ">": + ## no action, break loop if condition is False + return [sOption, sCondition, cAction, ""] + + if not sAction: + print("# Error in action at line " + sActionId + ": This action is empty.") + + if sAction[0:1] != "=": + checkIfThereIsCode(sAction, sActionId) + + if cAction == "-": + ## error detected --> suggestion + if sAction[0:1] == "=": + sAction = prepareFunction(sAction, True) + dFUNCTIONS["_g_s_"+sActionId] = sAction[1:] + sAction = "=_g_s_"+sActionId + elif sAction.startswith('"') and sAction.endswith('"'): + sAction = sAction[1:-1] + if not sMsg: + print("# Error in action at line " + sActionId + ": The message is empty.") + return [sOption, sCondition, cAction, sAction, iStartAction, iEndAction, nPriority, sMsg, sURL] + elif cAction == "~": + ## text processor + if sAction[0:1] == "=": + dFUNCTIONS["_g_p_"+sActionId] = sAction[1:] + sAction = "=_g_p_"+sActionId + elif sAction.startswith('"') and sAction.endswith('"'): + sAction = sAction[1:-1] + return [sOption, sCondition, cAction, sAction, iStartAction, iEndAction] + elif cAction == "/": + ## tags + return [sOption, sCondition, cAction, sAction, iStartAction, iEndAction] + elif cAction == "=": + ## disambiguator + if sAction[0:1] == "=": + sAction = sAction[1:] + if "define" in sAction and not re.search(r"define\(\\\d+ *, *\[.*\] *\)", sAction): + print("# Error in action at line " + sActionId + ": second argument for must be a list of strings") + sAction = prepareFunction(sAction) + dFUNCTIONS["_g_d_"+sActionId] = sAction + sAction = "_g_d_"+sActionId + return [sOption, sCondition, cAction, sAction] + else: + print("# Unknown action at line " + sActionId) + return None + + +def make (lRule, dDef, sLang, bJavaScript): + "compile rules, returns a dictionary of values" + # for clarity purpose, don’t create any file here + + # removing comments, zeroing empty lines, creating definitions, storing tests, merging rule lines + print(" parsing rules...") + lTokenLine = [] + sActions = "" + nPriority = 4 + dAllGraph = {} + sGraphName = "" + iActionBlock = 0 + + for i, sLine in lRule: + sLine = sLine.rstrip() + if "\t" in sLine: + # tabulation not allowed + print("Error. Tabulation at line: ", i) + exit() + elif sLine.startswith("@@@@GRAPH: "): + # rules graph call + m = re.match(r"@@@@GRAPH: *(\w+)", 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 at line", i) + exit() + elif sLine.startswith("__") and sLine.endswith("__"): + # new rule group + m = re.match("__(\\w+)(!\\d|)__", sLine) + if m: + sRuleName = m.group(1) + iActionBlock = 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.search("^ +<<- ", sLine) or sLine.startswith(" ") \ + or re.search("^ +#", sLine) or re.search(r"^ [-~=>/](?:\d(?::\d+|)|)>> ", sLine) : + # actions + sActions += " " + sLine.strip() + 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, iActionBlock, sActions, nPriority)) + lTokenLine.clear() + sActions = "" + iActionBlock += 1 + elif sLine.startswith((" ")): + # tokens + lTokenLine.append([i, sLine.strip()]) + else: + print("Unknown line:") + print(sLine) + + # processing rules + print(" preparing rules...") + for sGraphName, lRuleLine in dAllGraph.items(): + lPreparedRule = [] + for i, sRuleGroup, sTokenLine, iActionBlock, sActions, nPriority in lRuleLine: + for lRule in createRule(i, sRuleGroup, sTokenLine, iActionBlock, sActions, nPriority, dDef): + lPreparedRule.append(lRule) + # Graph creation + oDARG = darg.DARG(lPreparedRule, sLang) + dAllGraph[sGraphName] = oDARG.createGraph() + # Debugging + #print("\nGRAPH:", sGraphName) + #for e in lPreparedRule: + # print(e) + #for k, v in dAllGraph[sGraphName].items(): + # print(k, "\t", v) + + # 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 dFUNCTIONS.items(): + if sFuncName.startswith("_g_c_"): # condition + sParams = "lToken, nTokenOffset, nLastToken, sCountry, bCondMemo, dTags, sSentence, sSentence0" + 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" + + # Debugging + if False: + print("\nActions:") + for sActionName, aAction in dACTIONS.items(): + print(sActionName, aAction) + print("\nFunctions:") + print(sPyCallables) + + # Result + return { + "graph_callables": sPyCallables, + "rules_graphs": dAllGraph, + "rules_actions": dACTIONS + } Index: compile_rules_js_convert.py ================================================================== --- compile_rules_js_convert.py +++ compile_rules_js_convert.py @@ -1,6 +1,8 @@ -# Convert Python code to JavaScript code +""" +Convert Python code and regexes to JavaScript code +""" import copy import re import json @@ -116,11 +118,15 @@ lNegLookBeforeRegex = None return (sRegex, lNegLookBeforeRegex) def pyRuleToJS (lRule, dJSREGEXES, sWORDLIMITLEFT): + "modify Python rules -> JS rules" 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 @@ -130,27 +136,35 @@ lRuleJS.append(lNegLookBehindRegex) return lRuleJS def writeRulesToJSArray (lRules): + "create rules as a string of arrays (to be bundled in a JSON string)" 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): + "convert to a list of codes (numbers or strings)" 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 ================================================================== --- /dev/null +++ darg.py @@ -0,0 +1,212 @@ +#!python3 + +""" +RULE GRAPH BUILDER +""" + +# by Olivier R. +# License: MPL 2 + + +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): + "insert a new rule (tokens must be inserted in order)" + if aRule < self.aPreviousRule: + 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): + "count nodes within the whole graph" + self.nNode = len(self.lMinimizedNodes) + + def countArcs (self): + "count arcs within the whole graph" + self.nArc = 0 + for oNode in self.lMinimizedNodes: + self.nArc += len(oNode.dArcs) + + def displayInfo (self): + "display informations about the rule graph" + print(" * {:<12} {:>16,}".format("Rules:", self.nRule)) + print(" * {:<12} {:>16,}".format("Nodes:", self.nNode)) + print(" * {:<12} {:>16,}".format("Arcs:", self.nArc)) + + def createGraph (self): + "create the graph as a dictionary" + dGraph = { 0: self.oRoot.getNodeAsDict() } + for oNode in self.lMinimizedNodes: + sHashId = oNode.__hash__() + if sHashId not in dGraph: + dGraph[sHashId] = oNode.getNodeAsDict() + else: + print("Error. Double node… same id: ", sHashId) + print(str(oNode.getNodeAsDict())) + dGraph = self._rewriteKeysOfDARG(dGraph) + return dGraph + + def _rewriteKeysOfDARG (self, dGraph): + "keys of DARG are long numbers (hashes): this function replace these hashes with smaller numbers (to reduce storing size)" + # create translation dictionary + dKeyTrans = {} + for i, nKey in enumerate(dGraph): + dKeyTrans[nKey] = i + # replace keys + dNewGraph = {} + for nKey, dVal in dGraph.items(): + dNewGraph[dKeyTrans[nKey]] = dVal + for nKey, dVal in dGraph.items(): + for sArc, val in dVal.items(): + if type(val) is int: + dVal[sArc] = dKeyTrans[val] + else: + for sArc, nKey in val.items(): + val[sArc] = dKeyTrans[nKey] + return dNewGraph + + +class Node: + """Node of the rule graph""" + + 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): + "reset to 0 the node counter" + 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 = {} + dRule = {} + dLemma = {} + dMeta = {} + 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: + dLemma[sArc[1:]] = oNode.__hash__() + elif sArc.startswith("*") and len(sArc) > 1: + dMeta[sArc[1:]] = oNode.__hash__() + elif sArc.startswith("##"): + dRule[sArc[1:]] = oNode.__hash__() + else: + dNode[sArc] = oNode.__hash__() + if dReValue: + dNode[""] = dReValue + if dReMorph: + dNode[""] = dReMorph + if dLemma: + dNode[""] = dLemma + if dMeta: + dNode[""] = dMeta + if dRule: + dNode[""] = dRule + #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/__init__.py ================================================================== --- gc_core/py/__init__.py +++ gc_core/py/__init__.py @@ -1,2 +1,5 @@ +""" +Grammar checker +""" from .grammar_checker import * Index: gc_core/py/grammar_checker.py ================================================================== --- gc_core/py/grammar_checker.py +++ gc_core/py/grammar_checker.py @@ -1,15 +1,17 @@ -# Grammalecte -# Main class: wrapper +""" +Grammalecte, grammar checker +""" import importlib import json from . import text class GrammarChecker: + "GrammarChecker: Wrapper for the grammar checker engine" def __init__ (self, sLangCode, sContext="Python"): self.sLangCode = sLangCode # Grammar checker engine self.gce = importlib.import_module("."+sLangCode, "grammalecte") @@ -20,49 +22,58 @@ self.oLexicographer = None # Text formatter self.oTextFormatter = None def getGCEngine (self): + "return the grammar checker object" return self.gce def getSpellChecker (self): + "return the spell checker object" return self.oSpellChecker def getTextFormatter (self): - if self.oTextFormatter == None: - self.tf = importlib.import_module("."+self.sLangCode+".textformatter", "grammalecte") - self.oTextFormatter = self.tf.TextFormatter() + "load and return the text formatter" + if self.oTextFormatter is None: + tf = importlib.import_module("."+self.sLangCode+".textformatter", "grammalecte") + self.oTextFormatter = tf.TextFormatter() return self.oTextFormatter def getLexicographer (self): - if self.oLexicographer == None: - self.lxg = importlib.import_module("."+self.sLangCode+".lexicographe", "grammalecte") - self.oLexicographer = self.lxg.Lexicographe(self.oSpellChecker) + "load and return the lexicographer" + if self.oLexicographer is None: + lxg = importlib.import_module("."+self.sLangCode+".lexicographe", "grammalecte") + self.oLexicographer = lxg.Lexicographe(self.oSpellChecker) return self.oLexicographer def displayGCOptions (self): + "display the grammar checker options" self.gce.displayOptions() def getParagraphErrors (self, sText, dOptions=None, bContext=False, bSpellSugg=False, bDebug=False): "returns a tuple: (grammar errors, spelling errors)" aGrammErrs = self.gce.parse(sText, "FR", bDebug=bDebug, dOptions=dOptions, bContext=bContext) aSpellErrs = self.oSpellChecker.parseParagraph(sText, bSpellSugg) return aGrammErrs, aSpellErrs def generateText (self, sText, bEmptyIfNoErrors=False, bSpellSugg=False, nWidth=100, bDebug=False): + "[todo]" pass def generateTextAsJSON (self, sText, bContext=False, bEmptyIfNoErrors=False, bSpellSugg=False, bReturnText=False, bDebug=False): + "[todo]" pass def generateParagraph (self, sText, dOptions=None, bEmptyIfNoErrors=False, bSpellSugg=False, nWidth=100, bDebug=False): + "parse text and return a readable text with underline errors" aGrammErrs, aSpellErrs = self.getParagraphErrors(sText, dOptions, False, bSpellSugg, bDebug) if bEmptyIfNoErrors and not aGrammErrs and not aSpellErrs: return "" return text.generateParagraph(sText, aGrammErrs, aSpellErrs, nWidth) def generateParagraphAsJSON (self, iIndex, sText, dOptions=None, bContext=False, bEmptyIfNoErrors=False, bSpellSugg=False, bReturnText=False, lLineSet=None, bDebug=False): + "parse text and return errors as a JSON string" aGrammErrs, aSpellErrs = self.getParagraphErrors(sText, dOptions, bContext, bSpellSugg, bDebug) aGrammErrs = list(aGrammErrs) if bEmptyIfNoErrors and not aGrammErrs and not aSpellErrs: return "" if lLineSet: 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 @@ -1,7 +1,9 @@ -# Grammalecte -# Grammar checker engine +""" +Grammalecte +Grammar checker engine +""" import re import sys import os import traceback @@ -9,10 +11,23 @@ from itertools import chain 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", \ @@ -31,30 +46,90 @@ _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"): + "initialization of the grammar checker" + global _oSpellChecker + global _sAppContext + global _dOptions + global _oTokenizer + global _createRegexError + 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 + +_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 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 - dDA = {} # Disambiguisator. Key = position; value = list of morphologies + sRealText = sText 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, dPriority, sCountry, dOpt, bShowRuleId, bDebug, bContext) if sNew: sText = sNew except: raise @@ -69,74 +144,80 @@ sText = sText.replace("‑", "-") # nobreakdash # 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, dPriority, sCountry, dOpt, bShowRuleId, bDebug, bContext) aErrors.update(errs) except: raise return aErrors.values() # this is a view (iterable) -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, dPriority, sCountry, dOptions, bShowRuleId, bDebug, bContext): dErrs = {} - bChange = False - bIdRule = option('idrule') - + bParagraphChange = False + bSentenceChange = False + dTokenPos = oSentence.dTokenPos if oSentence else {} for sOption, lRuleGroup in _getRules(bParagraph): - if not sOption or dOptions.get(sOption, False): + if sOption == "@@@@": + # graph rules + if not bParagraph and bSentenceChange: + oSentence.update(s) + bSentenceChange = False + for sGraphName, sLineId in lRuleGroup: + if bDebug: + print("\n>>>> GRAPH:", sGraphName, sLineId) + bParagraphChange, s = oSentence.parse(dAllGraph[sGraphName], dPriority, sCountry, dOptions, bShowRuleId, bDebug, bContext) + dErrs.update(oSentence.dError) + 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: # action in lActions: [ condition, action type, replacement/suggestion/action[, iGroup[, message, URL]] ] try: - bCondMemo = not sFuncCond or globals()[sFuncCond](s, sx, m, dDA, sCountry, bCondMemo) + bCondMemo = not sFuncCond or globals()[sFuncCond](s, sx, m, dTokenPos, sCountry, bCondMemo) 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) + if nErrorStart not in dErrs or nPriority > dPriority.get(nErrorStart, -1): + 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 + bParagraphChange = True + bSentenceChange = True if bDebug: echo("~ " + s + " -- " + m.group(eAct[0]) + " # " + sLineId) elif cActionType == "=": # disambiguation - globals()[sWhat](s, m, dDA) - if bDebug: - echo("= " + m.group(0) + " # " + sLineId + "\nDA: " + str(dDA)) + if not bParagraph: + globals()[sWhat](s, m, dTokenPos) + if bDebug: + echo("= " + m.group(0) + " # " + sLineId) elif cActionType == ">": # we do nothing, this test is just a condition to apply all following actions pass else: echo("# error: unknown action at " + sLineId) elif cActionType == ">": break except Exception as e: raise Exception(str(e), "# " + sLineId + " # " + sRuleId) - if bChange: + if bParagraphChange: 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,30 +239,27 @@ 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" - p.Value = sURL - xErr.aProperties = (p,) + xProperty = PropertyValue() + xProperty.Name = "FullCommentURL" + xProperty.Value = sURL + xErr.aProperties = (xProperty,) 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 +272,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,39 +294,40 @@ 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): + "disable rule " _aIgnoredRules.add(sRuleId) def resetIgnoreRules (): + "clear all ignored rules" _aIgnoredRules.clear() def reactivateRule (sRuleId): + "(re)activate rule " _aIgnoredRules.discard(sRuleId) def listRules (sFilter=None): "generator: returns typle (sOption, sLineId, sRuleId)" @@ -261,220 +336,158 @@ 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): + "display the name of rules, with the filter " 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): + "set option with if it exists" if sOpt in _dOptions: _dOptions[sOpt] = bVal def setOptions (dOpt): + "update the dictionary of options with " for sKey, bVal in dOpt.items(): if sKey in _dOptions: _dOptions[sKey] = bVal def getOptions (): + "return the dictionary of current options" return _dOptions def getDefaultOptions (): + "return the dictionary of default options" return dict(gc_options.getOptions(_sAppContext)) def getOptionsLabels (sLang): + "return options labels" return gc_options.getUI(sLang) def displayOptions (sLang): + "display the list of grammar checking options" echo("List of options") echo("\n".join( [ k+":\t"+str(v)+"\t"+gc_options.getUI(sLang).get(k, ("?", ""))[0] for k, v in sorted(_dOptions.items()) ] )) echo("") def resetOptions (): + "set options to default values" global _dOptions _dOptions = dict(gc_options.getOptions(_sAppContext)) def getSpellChecker (): + "return the spellchecker object" 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 True if option is active" return _dOptions.get(sOpt, False) -def displayInfo (dDA, tWord): +def displayInfo (dTokenPos, 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]])) + if tWord[0] in dTokenPos and "lMorph" in dTokenPos[tWord[0]]: + echo("DA: " + str(dTokenPos[tWord[0]]["lMorph"])) + 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): +def morph (dTokenPos, 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 = dTokenPos[tWord[0]]["lMorph"] if tWord[0] in dTokenPos and "lMorph" in dTokenPos[tWord[0]] else _oSpellChecker.getMorph(tWord[1]) if not lMorph: return False - p = re.compile(sPattern) + zPattern = re.compile(sPattern) if bStrict: - return all(p.search(s) for s in lMorph) - return any(p.search(s) for s in lMorph) + return all(zPattern.search(s) for s in lMorph) + return any(zPattern.search(s) for s in lMorph) -def morphex (dDA, tWord, sPattern, sNegPattern, bNoWord=False): +def morphex (dTokenPos, 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 = dTokenPos[tWord[0]]["lMorph"] if tWord[0] in dTokenPos and "lMorph" in dTokenPos[tWord[0]] 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): + zNegPattern = re.compile(sNegPattern) + if any(zNegPattern.search(s) for s in lMorph): return False # search sPattern - p = re.compile(sPattern) - return any(p.search(s) for s in lMorph) + zPattern = re.compile(sPattern) + return any(zPattern.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): + lMorph = _oSpellChecker.getMorph(sWord) + if not lMorph: return False - if not _dAnalyses[sWord]: - return False - p = re.compile(sPattern) + zPattern = 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(zPattern.search(s) for s in lMorph) + return any(zPattern.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]): + zNegPattern = re.compile(sNegPattern) + if any(zNegPattern.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] ] + zPattern = re.compile(sPattern) + return any(zPattern.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 @@ -512,11 +525,11 @@ if re.search(sPattern, s): return True return False -def look_chk1 (dDA, s, nOffset, sPattern, sPatternGroup1, sNegPatternGroup1=None): +def look_chk1 (dTokenPos, s, nOffset, sPattern, sPatternGroup1, sNegPatternGroup1=None): "returns True if s has pattern sPattern and m.group(1) has pattern sPatternGroup1" m = re.search(sPattern, s) if not m: return False try: @@ -523,63 +536,569 @@ sWord = m.group(1) nPos = m.start(1) + nOffset except: return False if sNegPatternGroup1: - return morphex(dDA, (nPos, sWord), sPatternGroup1, sNegPatternGroup1) - return morph(dDA, (nPos, sWord), sPatternGroup1, False) + return morphex(dTokenPos, (nPos, sWord), sPatternGroup1, sNegPatternGroup1) + return morph(dTokenPos, (nPos, sWord), sPatternGroup1, False) + + +#### Disambiguator + +def select (dTokenPos, nPos, sWord, sPattern, lDefault=None): + "Disambiguation: select morphologies of matching " + if not sWord: + return True + if nPos not in dTokenPos: + print("Error. There should be a token at this position: ", nPos) + return True + 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): + dTokenPos[nPos]["lMorph"] = lSelect + elif lDefault: + dTokenPos[nPos]["lMorph"] = lDefault + return True + + +def exclude (dTokenPos, nPos, sWord, sPattern, lDefault=None): + "Disambiguation: exclude morphologies of matching " + if not sWord: + return True + if nPos not in dTokenPos: + print("Error. There should be a token at this position: ", nPos) + return True + 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): + dTokenPos[nPos]["lMorph"] = lSelect + elif lDefault: + dTokenPos[nPos]["lMorph"] = lDefault + return True + + +def define (dTokenPos, nPos, lMorph): + "Disambiguation: set morphologies of token at with " + if nPos not in dTokenPos: + print("Error. There should be a token at this position: ", nPos) + return True + dTokenPos[nPos]["lMorph"] = lMorph + return True + + + + +#### TOKEN SENTENCE CHECKER + +class TokenSentence: + "Text parser" + + def __init__ (self, sSentence, sSentence0, nOffset): + self.sSentence = sSentence + self.sSentence0 = sSentence0 + self.nOffsetWithinParagraph = nOffset + self.lToken = list(_oTokenizer.genTokens(sSentence, True)) + self.dTokenPos = { dToken["nStart"]: dToken for dToken in self.lToken } + self.dTags = {} + self.dError = {} + self.createError = self._createWriterError if _bWriterError else self._createDictError + + def update (self, sSentence): + "update and retokenize" + self.sSentence = sSentence + self.lToken = list(_oTokenizer.genTokens(sSentence, True)) + + def _getNextMatchingNodes (self, dToken, dGraph, dNode, bDebug=False): + "generator: return nodes where “values” match arcs" + # token value + if dToken["sValue"] in dNode: + if bDebug: + print(" MATCH:", dToken["sValue"]) + yield dGraph[dNode[dToken["sValue"]]] + if dToken["sValue"][0:2].istitle(): # we test only 2 first chars, to make valid words such as "Laissez-les", "Passe-partout". + sValue = dToken["sValue"].lower() + if sValue in dNode: + if bDebug: + print(" MATCH:", sValue) + yield dGraph[dNode[sValue]] + elif dToken["sValue"].isupper(): + sValue = dToken["sValue"].lower() + if sValue in dNode: + if bDebug: + print(" MATCH:", sValue) + yield dGraph[dNode[sValue]] + sValue = dToken["sValue"].capitalize() + if sValue in dNode: + if bDebug: + print(" MATCH:", sValue) + yield dGraph[dNode[sValue]] + # token lemmas + if "" in dNode: + for sLemma in _oSpellChecker.getLemma(dToken["sValue"]): + if sLemma in dNode[""]: + if bDebug: + print(" MATCH: >" + sLemma) + yield dGraph[dNode[""][sLemma]] + # regex value arcs + if "" in dNode: + for sRegex in dNode[""]: + if "¬" not in sRegex: + # no anti-pattern + if re.search(sRegex, dToken["sValue"]): + if bDebug: + print(" MATCH: ~" + sRegex) + yield dGraph[dNode[""][sRegex]] + else: + # there is an anti-pattern + sPattern, sNegPattern = sRegex.split("¬", 1) + if sNegPattern and re.search(sNegPattern, dToken["sValue"]): + continue + if not sPattern or re.search(sPattern, dToken["sValue"]): + if bDebug: + print(" MATCH: ~" + 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"])): + if bDebug: + print(" MATCH: @" + sRegex) + yield dGraph[dNode[""][sRegex]] + else: + # there is an anti-pattern + sPattern, sNegPattern = sRegex.split("¬", 1) + if sNegPattern == "*": + # all morphologies must match with + if sPattern and all(re.search(sPattern, sMorph) for sMorph in _oSpellChecker.getMorph(dToken["sValue"])): + if bDebug: + print(" MATCH: @" + sRegex) + yield dGraph[dNode[""][sRegex]] + else: + if sNegPattern and any(re.search(sNegPattern, sMorph) for sMorph in _oSpellChecker.getMorph(dToken["sValue"])): + continue + if not sPattern or any(re.search(sPattern, sMorph) for sMorph in _oSpellChecker.getMorph(dToken["sValue"])): + if bDebug: + print(" MATCH: @" + sRegex) + yield dGraph[dNode[""][sRegex]] + # meta arc (for token type) + if "" in dNode: + for sMeta in dNode[""]: + # not regex here, we just search if exists within + if sMeta == "*": + if bDebug: + print(" MATCH: *" + sMeta) + yield dGraph[dNode[""]["*"]] + elif "¬" in sMeta: + if dNode["sType"] not in sMeta: + if bDebug: + print(" MATCH: *" + sMeta) + yield dGraph[dNode[""][sMeta]] + elif dNode["sType"] in sMeta: + if bDebug: + print(" MATCH: *" + sMeta) + yield dGraph[dNode[""][sMeta]] + + + def parse (self, dGraph, dPriority, sCountry="${country_default}", dOptions=None, bShowRuleId=False, bDebug=False, bContext=False): + "parse tokens from the text and execute actions encountered" + self.dError = {} + dPriority = {} # Key = position; value = priority + dOpt = _dOptions if not dOptions else dOptions + lPointer = [] + bTagAndRewrite = False + for dToken in self.lToken: + if bDebug: + print("TOKEN:", dToken["sValue"]) + # check arcs for each existing pointer + lNextPointer = [] + for dPointer in lPointer: + for dNode in self._getNextMatchingNodes(dToken, dGraph, dPointer["dNode"], bDebug): + lNextPointer.append({"iToken": dPointer["iToken"], "dNode": dNode}) + lPointer = lNextPointer + # check arcs of first nodes + for dNode in self._getNextMatchingNodes(dToken, dGraph, dGraph[0], bDebug): + lPointer.append({"iToken": dToken["i"], "dNode": dNode}) + # check if there is rules to check for each pointer + for dPointer in lPointer: + #if bDebug: + # print("+", dPointer) + if "" in dPointer["dNode"]: + bChange, dErr = self._executeActions(dGraph, dPointer["dNode"][""], dPointer["iToken"]-1, dToken["i"], dPriority, dOpt, sCountry, bShowRuleId, bDebug, bContext) + self.dError.update(dErr) + if bChange: + bTagAndRewrite = True + if bTagAndRewrite: + self.rewrite(bDebug) + return (bTagAndRewrite, self.sSentence) + + def _executeActions (self, dGraph, dNode, nTokenOffset, nLastToken, dPriority, dOptions, sCountry, bShowRuleId, bDebug, bContext): + "execute actions found in the DARG" + dError = {} + bChange = False + for sLineId, nextNodeKey in dNode.items(): + bCondMemo = None + for sRuleId in dGraph[nextNodeKey]: + try: + if bDebug: + print("ACTION:", sRuleId) + print(dRule[sRuleId]) + sOption, sFuncCond, cActionType, sWhat, *eAct = dRule[sRuleId] + # Suggestion [ option, condition, "-", replacement/suggestion/action, iTokenStart, iTokenEnd, nPriority, message, URL ] + # TextProcessor [ option, condition, "~", replacement/suggestion/action, iTokenStart, iTokenEnd ] + # Disambiguator [ option, condition, "=", replacement/suggestion/action ] + # Sentence Tag [ option, condition, "/", replacement/suggestion/action, iTokenStart, iTokenEnd ] + # Test [ option, condition, ">", "" ] + if not sOption or dOptions.get(sOption, False): + bCondMemo = not sFuncCond or globals()[sFuncCond](self.lToken, nTokenOffset, nLastToken, sCountry, bCondMemo, self.dTags, self.sSentence, self.sSentence0) + if bCondMemo: + if cActionType == "-": + # grammar error + nTokenErrorStart = nTokenOffset + eAct[0] + if "bImmune" not in self.lToken[nTokenErrorStart]: + nTokenErrorEnd = (nTokenOffset + eAct[1]) if eAct[1] else nLastToken + nErrorStart = self.nOffsetWithinParagraph + self.lToken[nTokenErrorStart]["nStart"] + nErrorEnd = self.nOffsetWithinParagraph + self.lToken[nTokenErrorEnd]["nEnd"] + if nErrorStart not in dError or eAct[2] > dPriority.get(nErrorStart, -1): + dError[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("ERROR:", sRuleId, dError[nErrorStart]) + elif cActionType == "~": + # text processor + nEndToken = (nTokenOffset + eAct[1]) if eAct[1] else nLastToken + self._tagAndPrepareTokenForRewriting(sWhat, nTokenOffset + eAct[0], nEndToken, bDebug) + if bDebug: + print("RW:", sRuleId) + bChange = True + elif cActionType == "=": + # disambiguation + globals()[sWhat](self.lToken, nTokenOffset) + if bDebug: + print("DA:", sRuleId) + elif cActionType == ">": + # we do nothing, this test is just a condition to apply all following actions + if bDebug: + print(">>>", sRuleId) + pass + elif cActionType == "/": + # tags + nTokenTag = nTokenOffset + eAct[0] + if sWhat not in self.dTags: + self.dTags[sWhat] = (nTokenTag, nTokenTag) + elif nTokenTag > self.dTags[sWhat][1]: + self.dTags[sWhat] = (self.dTags[sWhat][0], nTokenTag) + if bDebug: + print("/", sRuleId) + else: + print("# error: unknown action at " + sLineId) + elif cActionType == ">": + if bDebug: + print(">!", sRuleId) + break + except Exception as e: + raise Exception(str(e), sLineId, sRuleId, self.sSentence) + return bChange, dError + + 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, nTokenOffset) + 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: + xProperty = PropertyValue() + xProperty.Name = "FullCommentURL" + xProperty.Value = sURL + xErr.aProperties = (xProperty,) + 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, nTokenOffset) + 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, bDebug=False): + "text processor: rewrite tokens between and position" + if bDebug: + print("REWRITING:", nTokenRewriteStart, nTokenRewriteEnd) + 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 + elif sWhat == "_": + # merge tokens + self.lToken[nTokenRewriteStart]["nMergeUntil"] = nTokenRewriteEnd + elif sWhat == "!": + # immunity + if nTokenRewriteEnd - nTokenRewriteStart == 0: + self.lToken[nTokenRewriteStart]["bImmune"] = True + else: + for i in range(nTokenRewriteStart, nTokenRewriteEnd+1): + self.lToken[i]["bImmune"] = 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, bDebug=False): + "rewrite the sentence, modify tokens, purge the token list" + lNewToken = [] + nMergeUntil = 0 + dTokenMerger = None + for dToken in self.lToken: + bKeepToken = True + if "bImmune" in dToken: + nErrorStart = self.nOffsetWithinParagraph + dToken["nStart"] + if nErrorStart in self.dError: + if bDebug: + print("immunity -> error removed:", self.dError[nErrorStart]) + del self.dError[nErrorStart] + if nMergeUntil and dToken["i"] <= nMergeUntil: + dTokenMerger["sValue"] += " " * (dToken["nStart"] - dTokenMerger["nEnd"]) + dToken["sValue"] + dTokenMerger["nEnd"] = dToken["nEnd"] + if bDebug: + print("Merged token:", dTokenMerger["sValue"]) + bKeepToken = False + if "nMergeUntil" in dToken: + if dToken["i"] > nMergeUntil: # this token is not already merged with a previous token + dTokenMerger = dToken + if dToken["nMergeUntil"] > nMergeUntil: + nMergeUntil = dToken["nMergeUntil"] + del dToken["nMergeUntil"] + elif "bToRemove" in dToken: + # remove useless token + self.sSentence = self.sSentence[:dToken["nStart"]] + " " * (dToken["nEnd"] - dToken["nStart"]) + self.sSentence[dToken["nEnd"]:] + if bDebug: + print("removed:", dToken["sValue"]) + bKeepToken = False + # + if bKeepToken: + lNewToken.append(dToken) + if "sNewValue" in dToken: + # rewrite token and sentence + if bDebug: + 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[:dToken["nStart"]] + sNewRepl + self.sSentence[dToken["nEnd"]:] + del dToken["sNewValue"] + if bDebug: + print(self.sSentence) + 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) + + +def g_tag_before (dToken, dTags, sTag): + if sTag not in dTags: + return False + if dToken["i"] > dTags[sTag][0]: + return True + return False + + +def g_tag_after (dToken, dTags, sTag): + if sTag not in dTags: + return False + if dToken["i"] < dTags[sTag][1]: + return True + return False #### Disambiguator -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"))) - 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"))) - 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 +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 + #### GRAMMAR CHECKER PLUGINS ${plugins} +#### CALLABLES FOR REGEX RULES (generated code) + ${callables} + + +#### CALLABLES FOR GRAPH RULES (generated code) + +${graph_callables} Index: gc_core/py/lang_core/gc_options.py ================================================================== --- gc_core/py/lang_core/gc_options.py +++ gc_core/py/lang_core/gc_options.py @@ -1,14 +1,20 @@ +""" +Grammar checker default options +""" + # generated code, do not edit def getUI (sLang): + "returns dictionary of UI labels" if sLang in _dOptLabel: return _dOptLabel[sLang] return _dOptLabel["fr"] def getOptions (sContext="Python"): + "returns dictionary of options" if sContext in dOpt: return dOpt[sContext] return dOpt["Python"] Index: gc_core/py/lang_core/gc_rules.py ================================================================== --- gc_core/py/lang_core/gc_rules.py +++ gc_core/py/lang_core/gc_rules.py @@ -1,5 +1,9 @@ +""" +Grammar checker regex rules +""" + # generated code, do not edit lParagraphRules = ${paragraph_rules} lSentenceRules = ${sentence_rules} ADDED gc_core/py/lang_core/gc_rules_graph.py Index: gc_core/py/lang_core/gc_rules_graph.py ================================================================== --- /dev/null +++ gc_core/py/lang_core/gc_rules_graph.py @@ -0,0 +1,9 @@ +""" +Grammar checker graph rules +""" + +# generated code, do not edit + +dAllGraph = ${rules_graphs} + +dRule = ${rules_actions} Index: gc_core/py/text.py ================================================================== --- gc_core/py/text.py +++ gc_core/py/text.py @@ -1,6 +1,10 @@ #!python3 + +""" +Text tools +""" import textwrap from itertools import chain @@ -41,27 +45,27 @@ lSpellErrs = sorted(aSpellErrs, key=lambda d: d['nStart']) sText = "" nOffset = 0 for sLine in wrap(sParagraph, nWidth): # textwrap.wrap(sParagraph, nWidth, drop_whitespace=False) sText += sLine + "\n" - ln = len(sLine) + nLineLen = len(sLine) sErrLine = "" nLenErrLine = 0 nGrammErr = 0 nSpellErr = 0 for dErr in lGrammErrs: nStart = dErr["nStart"] - nOffset - if nStart < ln: + if nStart < nLineLen: nGrammErr += 1 if nStart >= nLenErrLine: sErrLine += " " * (nStart - nLenErrLine) + "^" * (dErr["nEnd"] - dErr["nStart"]) nLenErrLine = len(sErrLine) else: break for dErr in lSpellErrs: nStart = dErr['nStart'] - nOffset - if nStart < ln: + if nStart < nLineLen: nSpellErr += 1 nEnd = dErr['nEnd'] - nOffset if nEnd > len(sErrLine): sErrLine += " " * (nEnd - len(sErrLine)) sErrLine = sErrLine[:nStart] + "°" * (nEnd - nStart) + sErrLine[nEnd:] @@ -73,11 +77,11 @@ sText += getReadableErrors(lGrammErrs[:nGrammErr], nWidth) del lGrammErrs[0:nGrammErr] if nSpellErr: sText += getReadableErrors(lSpellErrs[:nSpellErr], nWidth, True) del lSpellErrs[0:nSpellErr] - nOffset += ln + nOffset += nLineLen return sText def getReadableErrors (lErrs, nWidth, bSpell=False): "Returns lErrs errors as readable errors" @@ -95,19 +99,19 @@ def getReadableError (dErr, bSpell=False): "Returns an error dErr as a readable error" try: if bSpell: - s = u"* {nStart}:{nEnd} # {sValue}:".format(**dErr) + sText = u"* {nStart}:{nEnd} # {sValue}:".format(**dErr) else: - s = u"* {nStart}:{nEnd} # {sLineId} / {sRuleId}:\n".format(**dErr) - s += " " + dErr.get("sMessage", "# error : message not found") + sText = u"* {nStart}:{nEnd} # {sLineId} / {sRuleId}:\n".format(**dErr) + sText += " " + dErr.get("sMessage", "# error : message not found") if dErr.get("aSuggestions", None): - s += "\n > Suggestions : " + " | ".join(dErr.get("aSuggestions", "# error : suggestions not found")) + sText += "\n > Suggestions : " + " | ".join(dErr.get("aSuggestions", "# error : suggestions not found")) if dErr.get("URL", None): - s += "\n > URL: " + dErr["URL"] - return s + sText += "\n > URL: " + dErr["URL"] + return sText except KeyError: return u"* Non-compliant error: {}".format(dErr) def createParagraphWithLines (lLine): ADDED gc_lang/fr/French_language.txt Index: gc_lang/fr/French_language.txt ================================================================== --- /dev/null +++ gc_lang/fr/French_language.txt @@ -0,0 +1,65 @@ +# NOTES SUR LA LANGUE FRANÇAISE + +## CE QUI ENTOURE UN VERBE + + PRONOMS (avant) + COD COI + le / l’ + la / l’ + les + en + me / m’ me / m’ + te / t’ te / t’ + se / s’ lui + nous nous + vous nous + se / s’ leur + y + + ADVERBE DE NÉGATION (avant) + ne / n’ + + SOMME + [le|la|l’|les|en|me|m’|te|t’|se|s’|nous|vous|lui|leur|y] + + COMBINAISONS VALIDES + ?[ne|n’]¿ [me|te|se] [le|la|l’|les] + ?[ne|n’]¿ [m’|t’|s’] [le|la|l’|les|en|y] + ?[ne|n’]¿ [le|la] [lui|leur] + ?[ne|n’]¿ [l’|les] [lui|leur|en|y] + ?[ne|n’]¿ [lui|leur] en + ?[ne|n’]¿ [nous|vous] [le|la|l’|les|en|y] + ne [le|la|l’|les|me|m’|te|t’|se|s’|nous|vous|lui|leur] + n’ [en|y] + + RÉSUMÉ & SIMPLIFICATION + ?[ne|n’]¿ [le|la|l’|les|en|me|m’|te|t’|se|s’|nous|vous|lui|leur|y] + ?[ne|n’]¿ [me|m’|te|t’|se|s’|nous|vous] [le|la|l’|les|en|y] + ?[ne|n’]¿ [le|la|l’|les] [lui|leur|en|y] + ?[ne|n’]¿ [lui|leur] en + + ADVERBE DE NÉGATION (après) + guère + jamais + pas + plus + point + que / qu’ + rien + + PRONOMS À L’IMPÉRATIF + APRÈS + -moi + -toi + -lui + -leur + -nous + -vous + -le + -la + -les + -en + -y + + AVANT + Uniquement les combinaisons avec l’adverbe de négation [ne|n’] 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 @@ -1,6 +1,9 @@ -# Grammalecte - Conjugueur +""" +Grammalecte - Conjugueur +""" + # License: GPL 3 import re import traceback @@ -27,10 +30,11 @@ _dTenseIdx = { ":PQ": 0, ":Ip": 1, ":Iq": 2, ":Is": 3, ":If": 4, ":K": 5, ":Sp": 6, ":Sq": 7, ":E": 8 } def isVerb (sVerb): + "return True if it’s a existing verb" return sVerb in _dVerb def getConj (sVerb, sTense, sWho): "returns conjugation (can be an empty string)" @@ -54,13 +58,14 @@ return None return _lVtyp[_dVerb[sVerb][0]] def getSimil (sWord, sMorph, bSubst=False): + "returns a set of verbal forms similar to , according to " 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 @@ -98,10 +103,11 @@ aSugg.clear() return aSugg def getConjSimilInfiV1 (sInfi): + "returns verbal forms phonetically similar to infinitive form (for verb in group 1)" if sInfi not in _dVerb: return set() aSugg = set() tTags = _getTags(sInfi) if tTags: @@ -140,16 +146,18 @@ return "" if sSfx == "0": return sWord try: return sWord[:-(ord(sSfx[0])-48)] + sSfx[1:] if sSfx[0] != '0' else sWord + sSfx[1:] # 48 is the ASCII code for "0" - except: + except (IndexError, TypeError): return "## erreur, code : " + str(sSfx) + " ##" - + class Verb (): + "Verb and its conjugation" + def __init__ (self, sVerb, sVerbPattern=""): # conjugate a unknown verb with rules from sVerbPattern if not isinstance(sVerb, str): raise TypeError("sVerb should be a string") if not sVerb: @@ -165,11 +173,11 @@ self.bProWithEn = (self._sRawInfo[5] == "e") self._tTags = _getTags(sVerbPattern) if not self._tTags: raise ValueError("Unknown verb.") self._tTagsAux = _getTags(self.sVerbAux) - self.cGroup = self._sRawInfo[0]; + self.cGroup = self._sRawInfo[0] self.dConj = { ":Y": { "label": "Infinitif", ":": sVerb, }, @@ -289,10 +297,11 @@ except: traceback.print_exc() return "# erreur" def infinitif (self, bPro, bNeg, bTpsCo, bInt, bFem): + "returns string (conjugaison à l’infinitif)" try: if bTpsCo: sInfi = self.sVerbAux if not bPro else "être" else: sInfi = self.sVerb @@ -311,17 +320,19 @@ except: traceback.print_exc() return "# erreur" def participePasse (self, sWho): + "returns past participle according to " try: return self.dConj[":Q"][sWho] except: traceback.print_exc() return "# erreur" def participePresent (self, bPro, bNeg, bTpsCo, bInt, bFem): + "returns string (conjugaison du participe présent)" try: if not self.dConj[":P"][":"]: return "" if bTpsCo: sPartPre = _getConjWithTags(self.sVerbAux, self._tTagsAux, ":PQ", ":P") if not bPro else getConj("être", ":PQ", ":P") @@ -348,10 +359,11 @@ except: traceback.print_exc() return "# erreur" def conjugue (self, sTemps, sWho, bPro, bNeg, bTpsCo, bInt, bFem): + "returns string (conjugue le verbe au temps pour ) " try: if not self.dConj[sTemps][sWho]: return "" if not bTpsCo and bInt and sWho == ":1s" and self.dConj[sTemps].get(":1ś", False): sWho = ":1ś" @@ -370,16 +382,16 @@ if bNeg: sConj = "n’" + sConj if bEli and not bPro else "ne " + sConj if bInt: if sWho == ":3s" and not _zNeedTeuph.search(sConj): sConj += "-t" - sConj += "-" + self._getPronom(sWho, bFem) + sConj += "-" + self._getPronomSujet(sWho, bFem) else: if sWho == ":1s" and bEli and not bNeg and not bPro: sConj = "j’" + sConj else: - sConj = self._getPronom(sWho, bFem) + " " + sConj + sConj = self._getPronomSujet(sWho, bFem) + " " + sConj if bNeg: sConj += " pas" if bTpsCo: sConj += " " + self._seekPpas(bPro, bFem, sWho.endswith("p") or self._sRawInfo[5] == "r") if bInt: @@ -387,11 +399,11 @@ return sConj except: traceback.print_exc() return "# erreur" - def _getPronom (self, sWho, bFem): + def _getPronomSujet (self, sWho, bFem): try: if sWho == ":3s": if self._sRawInfo[5] == "r": return "on" elif bFem: @@ -402,10 +414,11 @@ except: traceback.print_exc() return "# erreur" def imperatif (self, sWho, bPro, bNeg, bTpsCo, bFem): + "returns string (conjugaison à l’impératif)" try: if not self.dConj[":E"][sWho]: return "" if bTpsCo: sImpe = _getConjWithTags(self.sVerbAux, self._tTagsAux, ":E", sWho) if not bPro else getConj(u"être", ":E", sWho) Index: gc_lang/fr/modules/conj_generator.py ================================================================== --- gc_lang/fr/modules/conj_generator.py +++ gc_lang/fr/modules/conj_generator.py @@ -1,22 +1,26 @@ -# Conjugation generator -# beta stage, unfinished, the root for a new way to generate flexions… +""" +Conjugation generator +beta stage, unfinished, the root for a new way to generate flexions… +""" import re def conjugate (sVerb, sVerbTag="i_____a", bVarPpas=True): + "conjugate and returns a list of tuples (conjugation form, tags)" lConj = [] cGroup = getVerbGroupChar(sVerb) for nCut, sAdd, sFlexTags, sPattern in getConjRules(sVerb, bVarPpas): if not sPattern or re.search(sPattern, sVerb): sFlexion = sVerb[0:-nCut] + sAdd if nCut else sVerb + sAdd lConj.append((sFlexion, ":V" + cGroup + "_" + sVerbTag + sFlexTags)) return lConj -def getVerbGroupChar (sVerb, ): +def getVerbGroupChar (sVerb): + "returns the group number of guessing on its ending" sVerb = sVerb.lower() if sVerb.endswith("er"): return "1" if sVerb.endswith("ir"): return "2" @@ -26,10 +30,11 @@ return "3" return "4" def getConjRules (sVerb, bVarPpas=True, nGroup=2): + "returns a list of lists to conjugate a verb, guessing on its ending" if sVerb.endswith("er"): # premier groupe, conjugaison en fonction de la terminaison du lemme # 5 lettres if sVerb[-5:] in oConj["V1"]: lConj = list(oConj["V1"][sVerb[-5:]]) @@ -115,11 +120,11 @@ [2, "ît", ":Sq:3s/*", False], [2, "is", ":E:2s/*", False], [2, "issons", ":E:1p/*", False], [2, "issez", ":E:2p/*", False] ], - + # premier groupe (bien plus irrégulier que prétendu) "V1": { # a # verbes en -er, -ger, -yer, -cer "er": [ Index: gc_lang/fr/modules/cregex.py ================================================================== --- gc_lang/fr/modules/cregex.py +++ gc_lang/fr/modules/cregex.py @@ -1,11 +1,13 @@ -# Grammalecte - Compiled regular expressions +""" +Grammalecte - Compiled regular expressions +""" import re #### Lemme -Lemma = re.compile("^>(\w[\w-]*)") +Lemma = re.compile(r"^>(\w[\w-]*)") #### Analyses Gender = re.compile(":[mfe]") Number = re.compile(":[spi]") @@ -78,13 +80,15 @@ #### FONCTIONS def getLemmaOfMorph (s): + "return lemma in morphology " return Lemma.search(s).group(1) def checkAgreement (l1, l2): + "returns True if agreement in gender and number is possible between morphologies and " # check number agreement if not mbInv(l1) and not mbInv(l2): if mbSg(l1) and not mbSg(l2): return False if mbPl(l1) and not mbPl(l2): @@ -97,10 +101,11 @@ if mbFem(l1) and not mbFem(l2): return False return True def checkConjVerb (lMorph, sReqConj): + "returns True if in " return any(sReqConj in s for s in lMorph) def getGender (lMorph): "returns gender of word (':m', ':f', ':e' or empty string)." sGender = "" @@ -115,11 +120,11 @@ def getNumber (lMorph): "returns number of word (':s', ':p', ':i' or empty string)." sNumber = "" for sMorph in lMorph: - m = Number.search(sWord) + m = Number.search(sMorph) if m: if not sNumber: sNumber = m.group(0) elif sNumber != m.group(0): return ":i" @@ -129,98 +134,126 @@ # mbWhat (lMorph) returns True if lMorph contains What at least once ## isXXX = it’s certain def isNom (lMorph): + "returns True if all morphologies are “nom”" return all(":N" in s for s in lMorph) def isNomNotAdj (lMorph): + "returns True if all morphologies are “nom”, but not “adjectif”" return all(NnotA.search(s) for s in lMorph) def isAdj (lMorph): + "returns True if all morphologies are “adjectif”" return all(":A" in s for s in lMorph) def isNomAdj (lMorph): + "returns True if all morphologies are “nom” or “adjectif”" return all(NA.search(s) for s in lMorph) def isNomVconj (lMorph): + "returns True if all morphologies are “nom” or “verbe conjugué”" return all(NVconj.search(s) for s in lMorph) def isInv (lMorph): + "returns True if all morphologies are “invariable”" return all(":i" in s for s in lMorph) def isSg (lMorph): + "returns True if all morphologies are “singulier”" return all(":s" in s for s in lMorph) def isPl (lMorph): + "returns True if all morphologies are “pluriel”" return all(":p" in s for s in lMorph) def isEpi (lMorph): + "returns True if all morphologies are “épicène”" return all(":e" in s for s in lMorph) def isMas (lMorph): + "returns True if all morphologies are “masculin”" return all(":m" in s for s in lMorph) def isFem (lMorph): + "returns True if all morphologies are “féminin”" return all(":f" in s for s in lMorph) ## mbXXX = MAYBE XXX def mbNom (lMorph): + "returns True if one morphology is “nom”" return any(":N" in s for s in lMorph) def mbAdj (lMorph): + "returns True if one morphology is “adjectif”" return any(":A" in s for s in lMorph) def mbAdjNb (lMorph): + "returns True if one morphology is “adjectif” or “nombre”" return any(AD.search(s) for s in lMorph) def mbNomAdj (lMorph): + "returns True if one morphology is “nom” or “adjectif”" return any(NA.search(s) for s in lMorph) def mbNomNotAdj (lMorph): - b = False + "returns True if one morphology is “nom”, but not “adjectif”" + bResult = False for s in lMorph: if ":A" in s: return False if ":N" in s: - b = True - return b + bResult = True + return bResult def mbPpasNomNotAdj (lMorph): + "returns True if one morphology is “nom” or “participe passé”, but not “adjectif”" return any(PNnotA.search(s) for s in lMorph) def mbVconj (lMorph): + "returns True if one morphology is “nom” or “verbe conjugué”" return any(Vconj.search(s) for s in lMorph) def mbVconj123 (lMorph): + "returns True if one morphology is “nom” or “verbe conjugué” (but not “avoir” or “être”)" return any(Vconj123.search(s) for s in lMorph) def mbMG (lMorph): + "returns True if one morphology is “mot grammatical”" return any(":G" in s for s in lMorph) def mbInv (lMorph): + "returns True if one morphology is “invariable”" return any(":i" in s for s in lMorph) def mbSg (lMorph): + "returns True if one morphology is “singulier”" return any(":s" in s for s in lMorph) def mbPl (lMorph): + "returns True if one morphology is “pluriel”" return any(":p" in s for s in lMorph) def mbEpi (lMorph): + "returns True if one morphology is “épicène”" return any(":e" in s for s in lMorph) def mbMas (lMorph): + "returns True if one morphology is “masculin”" return any(":m" in s for s in lMorph) def mbFem (lMorph): + "returns True if one morphology is “féminin”" return any(":f" in s for s in lMorph) def mbNpr (lMorph): + "returns True if one morphology is “nom propre” or “titre de civilité”" return any(NP.search(s) for s in lMorph) def mbNprMasNotFem (lMorph): + "returns True if one morphology is “nom propre masculin” but not “féminin”" if any(NPf.search(s) for s in lMorph): return False return any(NPm.search(s) for s in lMorph) Index: gc_lang/fr/modules/gce_analyseur.py ================================================================== --- gc_lang/fr/modules/gce_analyseur.py +++ gc_lang/fr/modules/gce_analyseur.py @@ -2,11 +2,11 @@ from . import cregex as cr def rewriteSubject (s1, s2): - # s1 is supposed to be prn/patr/npr (M[12P]) + "rewrite complex subject: a prn/patr/npr (M[12P]) followed by “et” and " if s2 == "lui": return "ils" if s2 == "moi": return "nous" if s2 == "toi": @@ -16,62 +16,57 @@ 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 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) + "use it if won’t be a verb; is assumed to be True via isAmbiguousNAV" + 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) + "use it if can be also a verb; is assumed to be True via isAmbiguousNAV" + 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,24 +77,25 @@ 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) + "check agreement between and " + 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) _zUnitSpecial = re.compile("[µ/⁰¹²³⁴⁵⁶⁷⁸⁹Ωℓ·]") _zUnitNumbers = re.compile("[0-9]") def mbUnit (s): + "returns True it can be a measurement unit" if _zUnitSpecial.search(s): return True if 1 < len(s) < 16 and s[0:1].islower() and (not s[1:].islower() or _zUnitNumbers.search(s)): return True return False @@ -110,10 +106,11 @@ _zEndOfNG1 = re.compile(" *$| +(?:, +|)(?:n(?:’|e |o(?:u?s|tre) )|l(?:’|e(?:urs?|s|) |a )|j(?:’|e )|m(?:’|es? |a |on )|t(?:’|es? |a |u )|s(?:’|es? |a )|c(?:’|e(?:t|tte|s|) )|ç(?:a |’)|ils? |vo(?:u?s|tre) )") _zEndOfNG2 = re.compile(r" +(\w[\w-]+)") _zEndOfNG3 = re.compile(r" *, +(\w[\w-]+)") def isEndOfNG (dDA, s, iOffset): + "returns True if next word doesn’t belong to a noun group" if _zEndOfNG1.match(s): return True m = _zEndOfNG2.match(s) if m and morphex(dDA, (iOffset+m.start(1), m.group(1)), ":[VR]", ":[NAQP]"): return True @@ -126,10 +123,11 @@ _zNextIsNotCOD1 = re.compile(" *,") _zNextIsNotCOD2 = re.compile(" +(?:[mtsnj](e +|’)|[nv]ous |tu |ils? |elles? )") _zNextIsNotCOD3 = re.compile(r" +([a-zéèî][\w-]+)") def isNextNotCOD (dDA, s, iOffset): + "returns True if next word is not a COD" if _zNextIsNotCOD1.match(s) or _zNextIsNotCOD2.match(s): return True m = _zNextIsNotCOD3.match(s) if m and morphex(dDA, (iOffset+m.start(1), m.group(1)), ":[123][sp]", ":[DM]"): return True @@ -138,10 +136,11 @@ _zNextIsVerb1 = re.compile(" +[nmts](?:e |’)") _zNextIsVerb2 = re.compile(r" +(\w[\w-]+)") def isNextVerb (dDA, s, iOffset): + "returns True if next word is a verb" if _zNextIsVerb1.match(s): return True m = _zNextIsVerb2.match(s) if m and morph(dDA, (iOffset+m.start(1), m.group(1)), ":[123][sp]", False): return True @@ -151,6 +150,6 @@ #### Exceptions aREGULARPLURAL = frozenset(["abricot", "amarante", "aubergine", "acajou", "anthracite", "brique", "caca", "café", \ "carotte", "cerise", "chataigne", "corail", "citron", "crème", "grave", "groseille", \ "jonquille", "marron", "olive", "pervenche", "prune", "sable"]) -aSHOULDBEVERB = frozenset(["aller", "manger"]) +aSHOULDBEVERB = frozenset(["aller", "manger"]) Index: gc_lang/fr/modules/gce_suggestions.py ================================================================== --- gc_lang/fr/modules/gce_suggestions.py +++ gc_lang/fr/modules/gce_suggestions.py @@ -6,18 +6,19 @@ ## Verbs def suggVerb (sFlex, sWho, funcSugg2=None): + "change conjugation according to " 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") @@ -38,43 +39,44 @@ if aSugg: return "|".join(aSugg) return "" -def suggVerbPpas (sFlex, sWhat=None): +def suggVerbPpas (sFlex, sPattern=None): + "suggest past participles for " aSugg = set() - for sStem in stem(sFlex): + for sStem in _oSpellChecker.getLemma(sFlex): tTags = conj._getTags(sStem) if tTags: - if not sWhat: + if not sPattern: aSugg.add(conj._getConjWithTags(sStem, tTags, ":PQ", ":Q1")) aSugg.add(conj._getConjWithTags(sStem, tTags, ":PQ", ":Q2")) aSugg.add(conj._getConjWithTags(sStem, tTags, ":PQ", ":Q3")) aSugg.add(conj._getConjWithTags(sStem, tTags, ":PQ", ":Q4")) aSugg.discard("") - elif sWhat == ":m:s": + elif sPattern == ":m:s": aSugg.add(conj._getConjWithTags(sStem, tTags, ":PQ", ":Q1")) - elif sWhat == ":m:p": + elif sPattern == ":m:p": if conj._hasConjWithTags(tTags, ":PQ", ":Q2"): aSugg.add(conj._getConjWithTags(sStem, tTags, ":PQ", ":Q2")) else: aSugg.add(conj._getConjWithTags(sStem, tTags, ":PQ", ":Q1")) - elif sWhat == ":f:s": + elif sPattern == ":f:s": if conj._hasConjWithTags(tTags, ":PQ", ":Q3"): aSugg.add(conj._getConjWithTags(sStem, tTags, ":PQ", ":Q3")) else: aSugg.add(conj._getConjWithTags(sStem, tTags, ":PQ", ":Q1")) - elif sWhat == ":f:p": + elif sPattern == ":f:p": if conj._hasConjWithTags(tTags, ":PQ", ":Q4"): aSugg.add(conj._getConjWithTags(sStem, tTags, ":PQ", ":Q4")) else: aSugg.add(conj._getConjWithTags(sStem, tTags, ":PQ", ":Q1")) - elif sWhat == ":s": + elif sPattern == ":s": aSugg.add(conj._getConjWithTags(sStem, tTags, ":PQ", ":Q1")) aSugg.add(conj._getConjWithTags(sStem, tTags, ":PQ", ":Q3")) aSugg.discard("") - elif sWhat == ":p": + elif sPattern == ":p": aSugg.add(conj._getConjWithTags(sStem, tTags, ":PQ", ":Q2")) aSugg.add(conj._getConjWithTags(sStem, tTags, ":PQ", ":Q4")) aSugg.discard("") else: aSugg.add(conj._getConjWithTags(sStem, tTags, ":PQ", ":Q1")) @@ -82,22 +84,24 @@ return "|".join(aSugg) return "" def suggVerbTense (sFlex, sTense, sWho): + "change to a verb according to and " 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): + "change to a verb at imperative form" 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,19 +112,21 @@ return "|".join(aSugg) return "" def suggVerbInfi (sFlex): - return "|".join([ sStem for sStem in stem(sFlex) if conj.isVerb(sStem) ]) + "returns infinitive forms of " + 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"] _lSubjonctif = [":Sp", ":Sq"] def suggVerbMode (sFlex, cMode, sSuj): + "returns other conjugations of acconding to and " if cMode == ":I": lMode = _lIndicatif elif cMode == ":S": lMode = _lSubjonctif elif cMode.startswith((":I", ":S")): @@ -131,11 +137,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 +153,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 +198,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 +225,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 +255,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 +280,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 +304,35 @@ return "|".join(aSugg) return "" def hasFemForm (sFlex): - for sStem in stem(sFlex): + "return True if there is a feminine form of " + 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): + "return True if there is a masculine form of " + 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 + "return feminine or masculine form(s) of " 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 +343,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 +360,13 @@ 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 + "return plural or singular form(s) of " 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: @@ -368,43 +373,45 @@ return "|".join(aSugg) return "" def hasSimil (sWord, sPattern=None): + "return True if there is words phonetically similar to (according to if required)" 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 "" def suggCeOrCet (sWord): + "suggest “ce” or “cet” or both according to the first letter of " if re.match("(?i)[aeéèêiouyâîï]", sWord): return "cet" if sWord[0:1] == "h" or sWord[0:1] == "H": 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, []) ): + "suggest “les” or “la” according to " + if any( ":p" in sMorph for sMorph in _oSpellChecker.getMorph(sWord) ): return "les|la" return "la" _zBinary = re.compile("^[01]+$") def formatNumber (s): + "add spaces or hyphens to big numbers" nLen = len(s) if nLen < 4: return s sRes = "" # nombre ordinaire @@ -435,10 +442,11 @@ sRes += "|" + s[0:2] + " " + s[2:5] + " " + s[5:7] + " " + s[7:9] # fixe belge 2 return sRes def formatNF (s): + "typography: format NF reference (norme française)" try: m = re.match("NF[  -]?(C|E|P|Q|S|X|Z|EN(?:[  -]ISO|))[  -]?([0-9]+(?:[/‑-][0-9]+|))", s) if not m: return "" return "NF " + m.group(1).upper().replace(" ", " ").replace("-", " ") + " " + m.group(2).replace("/", "‑").replace("-", "‑") @@ -446,10 +454,11 @@ traceback.print_exc() return "# erreur #" def undoLigature (c): + "typography: split ligature character in several chars" if c == "fi": return "fi" elif c == "fl": return "fl" elif c == "ff": @@ -470,10 +479,11 @@ _xNormalizedCharsForInclusiveWriting = str.maketrans({ '(': '_', ')': '_', '.': '_', '·': '_', '–': '_', '—': '_', '/': '_' - }) +}) def normalizeInclusiveWriting (sToken): + "typography: replace word separators used in inclusive writing by underscore (_)" return sToken.translate(_xNormalizedCharsForInclusiveWriting) Index: gc_lang/fr/modules/lexicographe.py ================================================================== --- gc_lang/fr/modules/lexicographe.py +++ gc_lang/fr/modules/lexicographe.py @@ -1,14 +1,17 @@ -# Grammalecte - Lexicographe +""" +Grammalecte - Lexicographe +""" + # License: MPL 2 import re import traceback -_dTAGS = { +_dTAGS = { ':N': (" nom,", "Nom"), ':A': (" adjectif,", "Adjectif"), ':M1': (" prénom,", "Prénom"), ':M2': (" patronyme,", "Patronyme, matronyme, nom de famille…"), ':MP': (" nom propre,", "Nom propre"), @@ -78,11 +81,11 @@ ':C': (" conjonction,", "Conjonction"), ':Ĉ': (" conjonction (él.),", "Conjonction (élément)"), ':Cc': (" conjonction de coordination,", "Conjonction de coordination"), ':Cs': (" conjonction de subordination,", "Conjonction de subordination"), ':Ĉs': (" conjonction de subordination (él.),", "Conjonction de subordination (élément)"), - + ':Ñ': (" locution nominale (él.),", "Locution nominale (élément)"), ':Â': (" locution adjectivale (él.),", "Locution adjectivale (élément)"), ':Ṽ': (" locution verbale (él.),", "Locution verbale (élément)"), ':Ŵ': (" locution adverbiale (él.),", "Locution adverbiale (élément)"), ':Ŕ': (" locution prépositive (él.),", "Locution prépositive (élément)"), @@ -125,18 +128,18 @@ 'elle': " pronom personnel sujet, 3ᵉ pers. fém. sing.", 'nous': " pronom personnel sujet/objet, 1ʳᵉ pers. plur.", 'vous': " pronom personnel sujet/objet, 2ᵉ pers. plur.", 'ils': " pronom personnel sujet, 3ᵉ pers. masc. plur.", 'elles': " pronom personnel sujet, 3ᵉ pers. masc. plur.", - + "là": " particule démonstrative", "ci": " particule démonstrative", - + 'le': " COD, masc. sing.", 'la': " COD, fém. sing.", 'les': " COD, plur.", - + 'moi': " COI (à moi), sing.", 'toi': " COI (à toi), sing.", 'lui': " COI (à lui ou à elle), sing.", 'nous2': " COI (à nous), plur.", 'vous2': " COI (à vous), plur.", @@ -153,18 +156,20 @@ "s'en": " (se) pronom personnel objet + (en) pronom adverbial", } class Lexicographe: + "Lexicographer - word analyzer" def __init__ (self, oSpellChecker): self.oSpellChecker = oSpellChecker self._zElidedPrefix = re.compile("(?i)^([dljmtsncç]|quoiqu|lorsqu|jusqu|puisqu|qu)['’](.+)") self._zCompoundWord = re.compile("(?i)(\\w+)-((?:les?|la)-(?:moi|toi|lui|[nv]ous|leur)|t-(?:il|elle|on)|y|en|[mts][’'](?:y|en)|les?|l[aà]|[mt]oi|leur|lui|je|tu|ils?|elles?|on|[nv]ous)$") self._zTag = re.compile("[:;/][\\w*][^:;/]*") def analyzeWord (self, sWord): + "returns a tuple (a list of morphologies, a set of verb at infinitive form)" try: if not sWord: return (None, None) if sWord.count("-") > 4: return (["élément complexe indéterminé"], None) @@ -192,17 +197,18 @@ aMorph.append( "{} : inconnu du dictionnaire".format(sWord) ) # suffixe d’un mot composé if m2: aMorph.append( "-{} : {}".format(m2.group(2), self._formatSuffix(m2.group(2).lower())) ) # Verbes - aVerb = set([ s[1:s.find(" ")] for s in lMorph if ":V" in s ]) + aVerb = set([ s[1:s.find("/")] for s in lMorph if ":V" in s ]) return (aMorph, aVerb) except: traceback.print_exc() return (["#erreur"], None) def formatTags (self, sTags): + "returns string: readable tags" sRes = "" sTags = re.sub("(?<=V[1-3])[itpqnmr_eaxz]+", "", sTags) sTags = re.sub("(?<=V0[ea])[itpqnmr_eaxz]+", "", sTags) for m in self._zTag.finditer(sTags): sRes += _dTAGS.get(m.group(0), " [{}]".format(m.group(0)))[0] Index: gc_lang/fr/modules/mfsp.py ================================================================== --- gc_lang/fr/modules/mfsp.py +++ gc_lang/fr/modules/mfsp.py @@ -1,6 +1,8 @@ -# Masculins, féminins, singuliers et pluriels +""" +Masculins, féminins, singuliers et pluriels +""" from .mfsp_data import lTagMiscPlur as _lTagMiscPlur from .mfsp_data import lTagMasForm as _lTagMasForm from .mfsp_data import dMiscPlur as _dMiscPlur from .mfsp_data import dMasForm as _dMasForm Index: gc_lang/fr/modules/phonet.py ================================================================== --- gc_lang/fr/modules/phonet.py +++ gc_lang/fr/modules/phonet.py @@ -1,6 +1,9 @@ -# Grammalecte - Suggestion phonétique +""" +Grammalecte - Suggestion phonétique +""" + # License: GPL 3 import re from .phonet_data import dWord as _dWord Index: gc_lang/fr/modules/tests.py ================================================================== --- gc_lang/fr/modules/tests.py +++ gc_lang/fr/modules/tests.py @@ -1,7 +1,10 @@ #! python3 -# coding: UTF-8 + +""" +Grammar checker tests for French language +""" import unittest import os import re import time Index: gc_lang/fr/modules/textformatter.py ================================================================== --- gc_lang/fr/modules/textformatter.py +++ gc_lang/fr/modules/textformatter.py @@ -1,6 +1,10 @@ #!python3 + +""" +Text formatter +""" import re dReplTable = { @@ -65,11 +69,11 @@ "ts_apostrophe": [ ("(?i)\\b([ldnjmtscç])['´‘′`](?=\\w)", "\\1’"), ("(?i)(qu|jusqu|lorsqu|puisqu|quoiqu|quelqu|presqu|entr|aujourd|prud)['´‘′`]", "\\1’") ], "ts_ellipsis": [ ("\\.\\.\\.", "…"), ("(?<=…)[.][.]", "…"), ("…[.](?![.])", "…") ], - "ts_n_dash_middle": [ (" [-—] ", " – "), + "ts_n_dash_middle": [ (" [-—] ", " – "), (" [-—],", " –,") ], "ts_m_dash_middle": [ (" [-–] ", " — "), (" [-–],", " —,") ], "ts_n_dash_start": [ ("^[-—][  ]", "– "), ("^– ", "– "), Index: gc_lang/fr/rules.grx ================================================================== --- gc_lang/fr/rules.grx +++ gc_lang/fr/rules.grx @@ -47,11 +47,11 @@ # http://fr.wikipedia.org/wiki/Wikip%C3%A9dia:Fautes_d%27orthographe/Courantes !! !! -!! Options +!! Options !! !! !! OPTGROUP/basic: typo apos, esp tab, nbsp unit, tu maj, num virg, nf chim, ocr mapos, liga OPTGROUP/gramm: conf sgpl gn @@ -198,14 +198,15 @@ !! !! -!! Définitions pour les regex +!! Définitions pour les regex !! !! !! +# REGEX DEF: avoir [aeo]\w* DEF: etre [êeésf]\w+ DEF: avoir_etre [aeêésfo]\w* DEF: aller (?:ai?ll|v[ao]|ir[aio])\w* DEF: ppas \w[\w-]+[éiust]e?s? @@ -217,60 +218,63 @@ DEF: w1 \w+ DEF: w2 \w\w+ DEF: w3 \w\w\w+ DEF: w4 \w\w\w\w+ - - - -!! -!! -!! -!! -!! -!! -!! -!! -!! -!! -!! -!! -!! -!! -!! -!! -!! -!! -!! -!! -!! PASSE 0: PARAGRAPHE PAR PARAGRAPHE -!! -!! -!! -!! -!! -!! -!! -!! -!! -!! -!! -!! -!! -!! -!! -!! -!! -!! -!! -!! - - - -!! -!! -!!! Espaces & tabulations +# GRAPH +DEF: mois [>janvier|>février|>mars|>avril|>mai|>juin|>juillet|>août|>aout|>septembre|>octobre|>novembre|>décembre|>vendémiaire|>brumaire|>frimaire|>nivôse|>pluviôse|>ventôse|>germinal|>floréal|>prairial|>messidor|>thermidor|>fructidor] +DEF: mi_mois [>mi-janvier|>mi-février|>mi-mars|>mi-avril|>mi-mai|>mi-juin|>mi-juillet|>mi-août|>mi-aout|>mi-septembre|>mi-octobre|>mi-novembre|>mi-décembre|mi-vendémiaire|mi-brumaire|mi-frimaire|mi-nivôse|mi-pluviôse|mi-ventôse|mi-germinal|mi-floréal|mi-prairial|mi-messidor|mi-thermidor|mi-fructidor] +DEF: pronom_obj [moi|toi|soi|lui|elle|nous|vous|eux|elles|moi-même|toi-même|soi-même|lui-même|elle-même|nous-mêmes|vous-même|vous-mêmes|eux-mêmes|elles-mêmes] + + +!! +!! +!! +!! +!! +!! +!! +!! +!! +!! +!! +!! +!! +!! +!! +!! +!! +!! +!! +!! +!! PASSE 0: PARAGRAPHE PAR PARAGRAPHE !! +!! +!! +!! +!! +!! +!! +!! +!! +!! +!! +!! +!! +!! +!! +!! +!! +!! +!! +!! +!! + + + +!! +!! +!!! Espaces & tabulations !! !! !! # Espaces surnuméraires # Note : les tabulations ne sont pas soulignées dans LibreOffice. Mais l’erreur est bien présente. @@ -375,11 +379,11 @@ # !!! !!! -!!! Processeur: efface les ponctuations gênantes (URL, sigles, abréviations, IP, heures, etc.) +!!! Processeur: efface les ponctuations gênantes (URL, sigles, abréviations, IP, heures, etc.) !! !!! !!! # e-mail __(p_email)__ @@ -390,11 +394,10 @@ https?://[\w./?&!%=+*"'@$#-]+ <<- ~>> * __> * <<- ~2>> =\2.capitalize() - <<- =>> define(\2, [":MP:e:i"]) <<- ~3>> * # Numéro de chapitre __(p_chapitre)__ ^\d+[.][\d.-]* <<- ~>> * @@ -524,11 +527,11 @@ !!! !!! -!!! Processeur: balises HTML et LaTeX +!!! Processeur: balises HTML et LaTeX !! !!! !!! # HTML __/html(p_html_amp_xxx)__ &[a-zA-Z]+; <<- ~>> _ @@ -552,11 +555,11 @@ !! !! -!!!! Écritures épicènes dystypographiques +!!!! Écritures épicènes dystypographiques !! !! !! # (attention aux modifs: brainfuck d’intensité non négligeable) # La désambiguïsation est faite lors de la deuxième passe @@ -651,11 +654,11 @@ TEST: il faut en parler à l’{{auteur(e)}} et à son agent. !! !! -!!!! Majuscules manquantes +!!!! Majuscules manquantes !! !! !! # Majuscules après un point __[s]/maj(majuscule_après_point)__ @@ -674,11 +677,11 @@ TEST: {{je}} suis disponible quand tu veux. Mais pas aujourd’hui. !! !! -!!!! Virgules +!!!! Virgules !! !! !! # virgules manquantes __[i>/virg(virgule_manquante_avant_etc)__ {w_1}( etc[.]) @@$ <<- -1>> , etc. # Avant « etc. », il faut mettre une virgule. @@ -686,11 +689,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 @@ -732,11 +735,11 @@ # -1>> \1.|\1…|\1 !|\1 ?|\1 :|\1 ;|\1, # Il manque une ponctuation finale. !! !! -!!!! Espaces manquants +!!!! Espaces manquants !! !! !! __/typo(typo_espace_manquant_après1)__ ({w1})[,:]({w_1}) @@0,$ <<- not \1.isdigit() -2>> " \2" # Il manque un espace. @@ -761,11 +764,11 @@ TEST: pic.twitter.com/PICNAME !! !! -!!!! Points +!!!! Points !! !! !! # Points superflus __[i>/typo(typo_et_cetera)__ etc(?:[.]{3,5}|…) <<- ->> etc. # Un seul point après « etc. » @@ -800,11 +803,11 @@ !! !! -!!!! Tirets +!!!! Tirets !! !! !! # Tirets, énumérations et dialogues __/typo(typo_tiret_début_ligne)__ ^[-_][  ] <<- ->> "— |– " # Dialogues et énumérations : un tiret cadratin ou demi-cadratin, suivi d’un espace insécable, est requis. @@ -823,16 +826,16 @@ TEST: « {{- }}Viens ! On va en finir avec cette affaire, sale fils de pute. ->> "— |– " !! !! -!!!! Ponctuations redondantes +!!!! Ponctuations redondantes !! !! !! # virgules et points -__/typo(typo_virgules_points)__ +__/typo(typo_virgules_points)__ ,[.,]{2,} <<- ->> =\0.replace(",", ".").replace("...", "…") # Erreur de numérisation ? Virgules au lieu de points ? TEST: Ah !{{,.,}} et en quoi consistait le festin ? @@ -871,11 +874,11 @@ TEST: {{» }}C’est ce qu’on croit savoir. ->> "» |« " !! !! -!!!! Signes typographiques +!!!! Signes typographiques !! !! !! __/typo(typo_signe_multiplication)__ (\d+) ?[x*] ?(\d+) @@0,$ <<- not \0.startswith("0x") ->> \1 × \2 # Signe de multiplication typographique. @@ -900,11 +903,11 @@ TEST: __liga__ une belle {{fi}}gure ->> fi !! !! -!!!! Apostrophes +!!!! Apostrophes !! !! !! ## Apostrophe typographique __[i>/apos(apostrophe_typographique)__ ([ldsncjmç]|jusqu|lorsqu|aujourd|presqu|quelqu|puisqu|qu|prud|entr)['´‘′`](?=[\w"«]) @@0 <<- ->> \1’ # Apostrophe typographique. @@ -954,11 +957,11 @@ TEST: Je {{n }}ai pas retrouvé l’ambiance de mes années de lycée. ->> n’ !! !! -!!!! Guillemets typographiques +!!!! Guillemets typographiques !! !! !! __/typo(typo_guillemets_typographiques_doubles_ouvrants)__ "(?=\w) @@ -996,11 +999,11 @@ TEST: J’en ai '''marre''' (syntaxe wiki). !! !! -!!!! Élisions +!!!! Élisions !! !! !! __[i]/typo(typo_élision_déterminants)__ (l[ea] |de )([aâeéêiîoôu]\w+) @@0,$ @@ -1048,11 +1051,11 @@ TEST: {{ce}} animal est dangereux !! !! -!!!! Divers +!!!! Divers !! !! !! ## NF (memo: don’t use flag i) __[s]/nf(nf_norme_française)__ @@ -1071,11 +1074,11 @@ TEST: __chim__ les molécules {{CaCO3}} et {{H2O}}… !! !! -!!!! Cohérence des guillemets +!!!! Cohérence des guillemets !! !! !! __/typo(typo_cohérence_guillemets_chevrons_ouvrants)__ («)[^»“]+?(”) @@0,$ @@ -1129,11 +1132,11 @@ TEST: « J’en suis “malade”. » !! !! -!!!! Espaces insécables avant unités de mesure +!!!! Espaces insécables avant unités de mesure !! !! !! __[s]/unit(unit_nbsp_avant_unités1)__ ((\d+(?:,\d+[⁰¹²³⁴⁵⁶⁷⁸⁹]?|[⁰¹²³⁴⁵⁶⁷⁸⁹]|)) ?)(?:[kcmµn]?(?:[slgJKΩ]|m[²³]?|Wh?|Hz|dB)|[%‰€$£¥Åℓhj]|min|°C|℃)(?![’']) @@0,0 @@ -1167,11 +1170,11 @@ TEST: je veux 200 euros. !! !! -!!!! Grands nombres +!!!! Grands nombres !! !! !! __[s]/num(num_grand_nombre_soudé)__ \d\d\d\d+ @@ -1216,11 +1219,11 @@ TEST: Il a perdu {{20 000}} euros à la Bourse en un seul mois. !! !! -!!!! Dates +!!!! Dates !! !! !! __[i]/date(date_nombres)__ (?> _ # Cette date est invalide. @@ -1233,16 +1236,16 @@ TEST: 12-12-2012 !! !! -!!!! Redondances +!!!! 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. @@ -1250,11 +1253,11 @@ !!! !!! -!!! Processeur: Dernier nettoyage avant coupure du paragraphe en phrases +!!! Processeur: Dernier nettoyage avant coupure du paragraphe en phrases !! !!! !!! # Trait d’union conditionnel (u00AD) __(p_trait_union_conditionnel1)__ \w+‑\w+‑\w+ <<- ~>> =\0.replace("‑", "") @@ -1265,11 +1268,11 @@ __(p_fin_dialogue2)__ ([?!…][?!…  ]*)[ "'”» ]*[a-zéèêîô] @@0 <<- ~1>> , TEST: « Je suis donc perdu ? », dit Paul. TEST: “C’est bon !”, croit savoir Marie. TEST: “Parce que… ?” finit par demander Paul. -TEST: « Dans quel pays sommes-nous ? » demanda un manifestant. +TEST: « Dans quel pays sommes-nous ? » demanda un manifestant. !! !! !! @@ -1288,11 +1291,11 @@ !! !! !! !! !! -!! PASSE 1: PHRASE PAR PHRASE +!! PASSE 1: PHRASE PAR PHRASE !! !! !! !! !! !! @@ -1314,11 +1317,11 @@ [++] -!!!! Doublons (casse identique) +!!!! Doublons (casse identique) !! __[s](doublon)__ ({w1}) {1,3}\1 @@0 <<- not re.search("(?i)^([nv]ous|faire|en|la|lui|donnant|œuvre|h[éoa]|hou|olé|joli|Bora|couvent|dément|sapiens|très|vroum|[0-9]+)$", \1) and not (re.search("^(?:est|une?)$", \1) and before("[’']$")) @@ -1326,11 +1329,11 @@ ->> \1 # Doublon. TEST: Il y a un {{doublon doublon}}. -!!!! Nombres: typographie +!!!! Nombres: typographie !! #(\d\d\d\d)-(\d\d\d\d) <<- ->> \1–\2 # Ne pas séparer deux dates par un trait d’union, mais par un tiret demi-cadratin. __[s]/num(num_lettre_O_zéro1)__ [\dO]+[O][\dO]+ <<- not option("ocr") ->> =\0.replace("O", "0") # S’il s’agit d’un nombre, utilisez le chiffre « 0 » plutôt que la lettre « O ». __[s]/num(num_lettre_O_zéro2)__ [1-9]O <<- not option("ocr") ->> =\0.replace("O", "0") # S’il s’agit d’un nombre, utilisez le chiffre « 0 » plutôt que la lettre « O ». @@ -1341,11 +1344,11 @@ # Nombres ordinaux __[s]/typo(typo_ordinaux_premier)__ 1 ?(?:ier|i?ère)s? <<- ->> =\0.replace(" ", "").replace("è", "").replace("i", "").replace("e", "ᵉ").replace("r", "ʳ").replace("s", "ˢ") - # Nombre ordinal. Premier : 1ᵉʳ. Première : 1ʳᵉ. Premier : 1ᵉʳˢ. Première : 1ʳᵉˢ.|http://bdl.oqlf.gouv.qc.ca/bdl/gabarit_bdl.asp?id=4271 + # Nombre ordinal. Premier : 1ᵉʳ. Première : 1ʳᵉ. Premiers : 1ᵉʳˢ. Premières : 1ʳᵉˢ.|http://bdl.oqlf.gouv.qc.ca/bdl/gabarit_bdl.asp?id=4271 __[s]/typo(typo_ordinaux_deuxième)__ 2 ?nde?s? <<- ->> =\0.replace(" ", "").replace("n", "").replace("d", "ᵈ").replace("e", "ᵉ").replace("s", "ˢ") # Nombre ordinal. Second : 2ᵈ. Seconde : 2ᵈᵉ. Seconds : 2ᵈˢ. Secondes : 2ᵈᵉˢ.|http://bdl.oqlf.gouv.qc.ca/bdl/gabarit_bdl.asp?id=4271 __[s]/typo(typo_ordinaux_nième)__ @@ -1353,11 +1356,11 @@ <<- \0.endswith("s") ->> \1ᵉˢ # Nombre ordinal pluriel. Exemples : 2ᵉˢ, 3ᵉˢ, 4ᵉˢ…|http://bdl.oqlf.gouv.qc.ca/bdl/gabarit_bdl.asp?id=4271 <<- __else__ ->> \1ᵉ # Nombre ordinal singulier. Exemples : 2ᵉ, 3ᵉ, 4ᵉ…|http://bdl.oqlf.gouv.qc.ca/bdl/gabarit_bdl.asp?id=4271 __[s]/typo(typo_ordinaux_romain_premier)__ I ?(?:ier|i?ère)s? <<- ->> =\0.replace(" ", "").replace("è", "").replace("i", "").replace("e", "ᵉ").replace("r", "ʳ").replace("s", "ˢ") - # Nombre ordinal romain. Premier : 1ᵉʳ. Première : Iʳᵉ. Premier : Iᵉʳˢ. Première : Iʳᵉˢ.|http://bdl.oqlf.gouv.qc.ca/bdl/gabarit_bdl.asp?id=4271 + # Nombre ordinal romain. Premier : 1ᵉʳ. Première : Iʳᵉ. Premiers : Iᵉʳˢ. Premières : Iʳᵉˢ.|http://bdl.oqlf.gouv.qc.ca/bdl/gabarit_bdl.asp?id=4271 __[s]/typo(typo_ordinaux_romain_deuxième)__ II ?nde?s? <<- ->> =\0.replace(" ", "").replace("n", "").replace("d", "ᵈ").replace("e", "ᵉ").replace("s", "ˢ") # Nombre ordinal romain. Second : IIᵈ. Seconde : IIᵈᵉ. Seconds : IIᵈˢ. Secondes : IIᵈᵉˢ.|http://bdl.oqlf.gouv.qc.ca/bdl/gabarit_bdl.asp?id=4271 __[s]/typo(typo_ordinaux_romains_nième)__ @@ -1378,11 +1381,11 @@ TEST: Le {{XXIème}} siècle. ->> XXIᵉ TEST: le {{XXè}} siècle. ->> XXᵉ -!!!! Écritures épicènes invariables +!!!! Écritures épicènes invariables !! __[i](d_typo_écriture_épicène_pluriel)__ ({w_1}[éuitsrn])_(?:[nt]|)e_s @@0 <<- morphex(\1, ":[NAQ]", ":G") =>> define(\1, [":N:A:Q:e:p"]) @@ -1389,53 +1392,53 @@ __[i](d_typo_écriture_épicène_singulier)__ ({w_2}[éuitsrn])_e @@0 <<- morph(\1, ":[NAQ]", False) =>> define(\1, [":N:A:Q:e:s"]) -!!!! Dates +!!!! Dates !! __[i]/date(date_jour_mois_année)__ - (\d\d?) (janvier|février|ma(?:rs|i)|a(?:vril|o[ûu]t)|jui(?:n|llet)|septembre|octobre|novembre|décembre) (\d\d\d+) @@0,w,$ + (\d\d?) (janvier|février|ma(?:rs|i)|a(?:vril|o[ûu]t)|jui(?:n|llet)|septembre|octobre|novembre|décembre) (\d\d\d+) @@0,w,$ <<- not checkDateWithString(\1, \2, \3) ->> _ # Cette date est invalide. TEST: {{29 février 2011}} __[i]/date(date_journée_jour_mois_année1)__ - (lundi|m(?:ardi|ercredi)|jeudi|vendredi|samedi|dimanche),? (?:le |)(\d\d?)-(\d\d?)-(\d\d+) @@0,w,w,$ + (lundi|m(?:ardi|ercredi)|jeudi|vendredi|samedi|dimanche),? (?:le |)(\d\d?)-(\d\d?)-(\d\d+) @@0,w,w,$ <<- not after(r"^ +av(?:ant|) +J(?:C|ésus-Christ)") and not checkDay(\1, \2, \3, \4) -1>> =getDay(\2, \3, \4) # Le jour de la date suivante est incorrect. TEST: {{mercredi}}, le 10-06-2014 ->> mardi __[i]/date(date_journée_jour_mois_année2)__ - (lundi|m(?:ardi|ercredi)|jeudi|vendredi|samedi|dimanche),? (?:le |)(\d\d?) (janvier|février|ma(?:rs|i)|a(?:vril|o[ûu]t)|jui(?:n|llet)|septembre|octobre|novembre|décembre) (\d\d+) @@0,w,w,$ + (lundi|m(?:ardi|ercredi)|jeudi|vendredi|samedi|dimanche),? (?:le |)(\d\d?) (janvier|février|ma(?:rs|i)|a(?:vril|o[ûu]t)|jui(?:n|llet)|septembre|octobre|novembre|décembre) (\d\d+) @@0,w,w,$ <<- not after(r"^ +av(?:ant|) +J(?:C|ésus-Christ)") and not checkDayWithString(\1, \2, \3, \4) -1>> =getDayWithString(\2, \3, \4) # Le jour de la date suivante est incorrect. TEST: {{mercredi}}, le 10 juin 2014 ->> mardi TEST: {{lundi}}, 18 août 1912 ->> dimanche TEST: lundi, 18 août 1912 avant Jésus-Christ (date imaginaire) __[i]/date(date_mois_31)__ 31 (avril|juin|septembre|novembre) @@3 - <<- ->> 30 \1 # Cette date est invalide. Il n’y a que 30 jours en \1. + <<- ->> 30 \1 # Cette date est invalide. Il n’y a que 30 jours en \1. TEST: le {{31 avril}} __[i]/date(date_février)__ 3[01] février - <<- ->> 28 février|29 février # Cette date est invalide. Il n’y a que 28 ou 29 jours en février. + <<- ->> 28 février|29 février # Cette date est invalide. Il n’y a que 28 ou 29 jours en février. TEST: le {{30 février}} !!! !!! -!!! Processeur: épuration des signes inutiles et quelques simplifications +!!! Processeur: épuration des signes inutiles et quelques simplifications !! !!! !!! # fin de phrase __(p_fin_de_phrase)__ [.?!:;…][ .?!… »”")]*$ <<- ~>> * @@ -1462,11 +1465,11 @@ # faux positifs avec adverbes de négation __[i](p_pas_mal)__ pas mal <<- not morph(word(-1), ":D", False) ~>> * __[i](p_pas_assez)__ pas assez ({w_2}) @@$ <<- morph(\1, ":A", False) and not morph(word(-1), ":D", False) ~>> * # faux positifs avec «à chez» -__[i](p_de_chez_à_chez_pronom)__ de chez \w+ (?:à|jusqu à) chez (?:moi|toi|lui|elles?|eux|nous|vous) <<- ~>> * +__[i](p_de_chez_à_chez_pronom)__ de chez \w+ (?:à|jusqu à) chez (?:moi|toi|lui|elles?|eux|nous|vous) <<- ~>> * __[i](p_de_chez)__ (jusqu à|de) chez @@0 <<- ~1>> * # faux positifs __[i](p_en_tout_et_pour_tout)__ en tout et pour tout <<- ~>> * __[i](p_au_sortir_de)__ au (sortir) de?s? @@3 <<- ~1>> * @@ -1501,23 +1504,23 @@ TEST: New York {{étaient}} {{devenue}} la plaque tournante de tous les trafics. !! !! -!!!! Traits d’union +!!!! Traits d’union !! !! !! __> - # Le “t” euphonique n’est pas nécessaire avec “\2”.|http://bdl.oqlf.gouv.qc.ca/bdl/gabarit_bdl.asp?T1=t+euphonique&id=2513 <<- __else__ and \1 != "-t-" and \1 != "-T-" -1>> -t- # Pour le “t” euphonique, il faut deux traits d’union. Pas d’apostrophe. Pas d’espace. - <<- ~1>> -t- + <<- \1 != "-t-" ~1>> -t- __> - # Le “t” euphonique est superflu quand le verbe se termine par “t” ou “d”.|http://bdl.oqlf.gouv.qc.ca/bdl/gabarit_bdl.asp?T1=t+euphonique&id=2513 - <<- ~1>> -t- + <<- \1 != "-t-" ~1>> -t- __> -t-\2 # Il faut un “t” euphonique.|http://bdl.oqlf.gouv.qc.ca/bdl/gabarit_bdl.asp?T1=t+euphonique&id=2513 TEST: va{{ t’}}il y parvenir ? ->> -t- TEST: A{{ t’}}elle soif ? ->> -t- @@ -1594,17 +1597,17 @@ TEST: c’est {{in}} stage de réinsertion. TEST: Dans ce drive in douze hommes mangent. __[i]/tu(tu_préfixe_mi)__ - mi ({w2}) @@$ <<- morph(\1, ":[NAQ]", False) ->> mi-\1 # S’il s’agit d’un seul mot, il manque un trait d’union. + mi ({w2}) @@$ <<- morph(\1, ":[NAQ]", False) ->> mi-\1 # S’il s’agit d’un seul mot, il manque un trait d’union. TEST: J’ai été engagé pour un {{mi temps}}. __[i]/tu(tu_préfixe_quasi)__ - (?:l(?:es?|a|eurs?)|ce(?:tte|t|s|)|des?|m(?:a|on|es)|[ts](?:es|a)) (quasi ({w1})) @@$,$ + (?:l(?:es?|a|eurs?)|ce(?:tte|t|s|)|des?|m(?:a|on|es)|[ts](?:es|a)) (quasi ({w1})) @@$,$ <<- morphex(\2, ":N", ":[AGW]") -1>> quasi-\2 # Il manque un trait d’union : « quasi » s’accroche au mot qui le suit s’il s’agit d’un nom. <<- ~1>> \2 TEST: leurs {{quasi indifférences}} @@ -1684,15 +1687,15 @@ __[i]/tu(tu_nord_sud_ouest)__ (nord|sud) ouest @@0 <<- ->> \1-ouest # Il manque un trait d’union. __[i]/tu(tu_nord_sud_est)__ (nord|sud) est @@0 <<- isEnd() ->> \1-est # Il manque un trait d’union. __[i]/tu(tu_outre_mer)__ outre mer <<- ->> outre-mer # Il manque un trait d’union. __[i]/tu(tu_ouï_dire)__ ou[iï] dire <<- morph(word(-1), ":G") ->> ouï-dire # Il manque un trait d’union. __[i]/tu(tu_par_préposition)__ - par (desso?us|devant|delà|derrière|dehors|dedans|devers) @@$ + par (desso?us|devant|delà|derrière|dehors|dedans|devers) @@$ <<- ->> par-\1 # Il manque un trait d’union. __[i]/tu(tu_par_ci_par_là)__ par ci,? par là <<- ->> par-ci par-là|par-ci, par-là # Trait(s) d’union manquant(s). __[i]/tu(tu_prêt_à_porter)__ - (prêts?) à porter @@0 + (prêts?) à porter @@0 <<- before(r"(?i)\b(?:les?|du|des|un|ces?|[mts]on) +") ->> \1-à-porter # Il manque les traits d’union. __[i]/tu(tu_plate_forme)__ plates? formes? <<- ->> plate-forme|plates-formes|plateforme|plateformes # Il manque un trait d’union. Vous pouvez aussi souder les deux mots. __[i]/tu(tu_quelques_uns_unes)__ quelques (une?s) @@$ <<- ->> quelques-\1 # Il manque un trait d’union. __[i]/tu(tu_plus_moins_values)__ (plus|moins) (values?) @@0,$ <<- ->> \1-\2 # Il manque un trait d’union. __[i]/tu(tu_rez_de_chaussée)__ rez de chaussées? <<- ->> rez-de-chaussée # Il manque un trait d’union. @@ -1712,11 +1715,11 @@ TEST: {{le}} {{bouche à oreille}} TEST: à ce {{moment là}} ->> moment-là TEST: une {{plus value}} ->> plus-value TEST: Il est {{en-dessous}} de tout. ->> en dessous TEST: Ils sont {{en-deçà}} de tout ->> en deçà -TEST: {{Là bas}}. ->> Là-bas +TEST: {{Là bas}}. ->> Là-bas TEST: {{Au dessus}} ->> Au-dessus TEST: {{ci dessus}} ->> ci-dessus TEST: {{par dessus}} ->> par-dessus TEST: {{au delà}} ->> au-delà TEST: {{ci devant}}. ->> ci-devant @@ -1759,11 +1762,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}} ? @@ -1804,14 +1807,14 @@ __[u]/tu(tu_Pas_de_Calais)__ Pas de Calais <<- ->> Pas-de-Calais # Il manque les traits d’union. __[u]/tu(tu_Rhône_Alpes)__ Rhône Alpes <<- ->> Rhône-Alpes # Il manque les traits d’union. __[u]/tu(tu_Saône_et_Loire)__ Saône et Loire <<- ->> Saône-et-Loire # Il manque un trait d’union. __[u]/tu(tu_Jésus_Christ)__ Jésus Christ <<- ->> Jésus-Christ # Il manque un trait d’union. __[u]/tu(tu_Jean_prénom)__ - Jean (Baptiste|Claude|François|Jacques|Louis|Luc|Marc|Marie|Michel|Paul|Philippe|Pierre) @@$ + Jean (Baptiste|Claude|François|Jacques|Louis|Luc|Marc|Marie|Michel|Paul|Philippe|Pierre) @@$ <<- ->> Jean-\1 # Il manque un trait d’union. __[u]/tu(tu_Marie_Prénom)__ - Marie (Ange|Agnès|Anne|Antoinette|Cécile|Chantal|Charlotte|Christine|Claire|Claude|Dominique|France|Françoise|Hélène|Jeanne|José|Josèphe|Line|Louise|Madeleine|Noëlle|Odile|Paule|Pierre|Rose|Thérèse) @@$ + Marie (Ange|Agnès|Anne|Antoinette|Cécile|Chantal|Charlotte|Christine|Claire|Claude|Dominique|France|Françoise|Hélène|Jeanne|José|Josèphe|Line|Louise|Madeleine|Noëlle|Odile|Paule|Pierre|Rose|Thérèse) @@$ <<- ->> Marie-\1 # Il manque un trait d’union. __[s]/tu(tu_St_Ste_Bidule)__ ((Ste?) )[A-ZÉÈÎ]\w+ @@0,0 <<- -1>> \2- # Il manque un trait d’union s’il s’agit d’une église, d’une cité, d’une communauté… S’il s’agit d’une personne, écrivez « saint(e) » sans majuscule, sans trait d’union. @@ -1898,11 +1901,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 " @@ -1913,11 +1916,11 @@ TEST: Elle y arriva {{lors qu}}’elle trouva l’astuce permettant l’ouverture de la porte. TEST: Dès lors qu’on sait comment s’y prendre, aucune raison de faillir. -!!!! Virgules +!!!! Virgules !! # Dialogues __[u]/virg(virgule_dialogue_après_nom_propre)__ ([A-ZÉÈ][\w-]+) (\w+-(?:moi|toi|l(?:ui|a|e(?:ur|s|))|nous|vous|je|tu|ils|elles)) @@0,$ <<- morphex(\1, ":M", ":G") and not morph(\2, ":N", False) and isStart() @@ -1941,34 +1944,34 @@ 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. -!!!! Apostrophe manquante (2) +!!!! Apostrophe manquante (2) !! __/typo(typo_apostrophe_manquante_audace2)__ ^ *([LDSNCJMTÇ] )[aeéiouhAEÉIOUHyîèêôûYÎÈÊÔÛ] @@* <<- option("mapos") -1>> =\1[:-1]+"’" # Il manque peut-être une apostrophe. TEST: __mapos__ {{L }}opinion des gens, elle s’en moquait. -!!!! A / À: accentuation la préposition en début de phrase +!!!! 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)__ @@ -1982,11 +1985,11 @@ TEST: A bientôt fini son devoir. TEST: A priori, nul ne peut y parvenir sans une aide extérieure. TEST: A devient notre meilleure chance d’y parvenir. -!!!! Accentuation des majuscules +!!!! Accentuation des majuscules !! __[u]/maj(maj_accents)__ E(?:tat|glise|co(?:le|nomie)|quipe|lectri(?:cité|que)|gal(?:ité|ement)|té)s? @@1 <<- ->> ="É"+\0[1:] # Accentuez les majuscules. <<- ~>> ="É"+\0[1:] @@ -1995,22 +1998,22 @@ !!! !!! -!!! Désambiguïsation +!!! Désambiguïsation !! !!! !!! # 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)__ @@ -2052,11 +2055,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 @@ -2099,11 +2102,11 @@ !! !! -!!!! OCR +!!!! OCR !! !! !! # ? __(?: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 @@ -2467,11 +2470,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. @@ -2695,11 +2698,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 @@ -2752,11 +2755,11 @@ TEST: __ocr__ par beaucoup d’argent ? {{{Il}} débouche le Jack Daniels !! !! -!!!! Incohérences de base +!!!! Incohérences de base !! !! !! ### double négation __[i](double_négation)__ @@ -2780,13 +2783,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. @@ -2793,13 +2796,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. @@ -2814,11 +2817,11 @@ !! !! -!!!! Style +!!!! Style !! !! !! #__bs__ Mr <<- ->> M. # M. est l’usage courant pour “Monsieur”. « Mr » est l’abréviation ancienne, française. @@ -2832,11 +2835,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. @@ -2849,11 +2852,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}} @@ -2925,53 +2928,53 @@ #enjoindre à qqn de faire qqch !! !! -!!!! Pléonasmes +!!!! 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. @@ -2978,19 +2981,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 @@ -3061,11 +3064,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 @@ -3072,11 +3075,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 @@ -3101,18 +3104,18 @@ TEST: Ce couple va donner à la France sa très importante collection qui rejoindra le musée d’Orsay !! !! -!!!! Confusions +!!!! Confusions !! !! !! __[s>/conf(conf_ne_n)__ [nN]e n’ <<- ->> ne m’|n’ # Incohérence. Double négation. __[s>/conf(conf_pronoms1)__ [mtMT]e ([nmst](?:’|e )) @@$ <<- ->> \1 # Incohérence. __[s>/conf(conf_pronoms2)__ [sS]e ([mst](?:’|e )) @@$ <<- ->> \1 # Incohérence. -__[s>/conf(conf_de_d)__ [dD][eu] d’(?![A-ZÉÂÔÈ]) <<- ->> d’ # Incohérence. +__[s>/conf(conf_de_d)__ [dD][eu] d’(?![A-ZÉÂÔÈ]) <<- ->> d’ # Incohérence. TEST: Il {{ne n’}}arrive jamais à l’heure. TEST: Ça {{me te }}prend la tête, toutes ces complications vaines. TEST: il {{se m’}}est difficile d’y parvenir. TEST: Ça t’arrive {{de d’}}arriver à l’heure ? @@ -3143,11 +3146,11 @@ __[i]/conf(conf_malgré_le_la_les_leur)__ malgré l(?:es? +|eurs? +|a +|’)({w_3}) @@$ <<- morphex(\1, ":", ":[GNAWMB]") -1>> =suggSimil(\1, ":[NA]", True) # Incohérence : après “malgré”, on devrait trouver un groupe nominal. -TEST: malgré l’{{arrête}} qui interdisait le port +TEST: malgré l’{{arrête}} qui interdisait le port TEST: malgré les deux précédentes erreurs __[i]/conf(conf_ma_ta_cette_verbe)__ ([mt]a|cette) +({w_2}) @@0,$ @@ -3282,19 +3285,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}} @@ -3336,15 +3339,15 @@ #__[i]/conf__ # très +(bien|\w+ent) +({w2}) @@w,$ # <<- morph(\1, ":W", False) and morphex(\2, ":[123][sp]", ":[GAQW]") -2>> _ # # Incohérence avec « très » : « \2 » n’est ni un adjectif, ni un participe passé, ni un adverbe. - + __[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. @@ -3394,11 +3397,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 @@ -3416,11 +3419,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 : @@ -3442,11 +3445,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 : @@ -3462,16 +3465,16 @@ __[i]/conf(conf_a_à_au_aux)__ (à) aux?(?! (?:moins|plus)) @@0 <<- -1>> a # Confusion : “à” est une préposition. Pour le verbe avoir, écrivez : __[i]/conf(conf_a_à_base_cause)__ (a) (?:base|cause) d(?:es?|u|) @@0 <<- not before(r"(?i)\bce que?\b") -1>> à # Confusion. Utilisez la préposition « à ». __[i]/conf(conf_a_à_faim_peur_honte_soif)__ - (à) +(?:faim|peur|honte|soif) @@0 <<- -1>> a # Confusion : “à” est une préposition. Pour le verbe avoir, écrivez : + (à) +(?:faim|peur|honte|soif) @@0 <<- -1>> a # Confusion : “à” est une préposition. Pour le verbe avoir, écrivez : __[i]/conf(conf_a_à_part)__ (a) part ({w1}) @@0,7 <<- morph(\2, ":(?:M[12]|D|Oo)") -1>> à # Confusion probable. __[i]/conf(conf_a_à_les_à)__ les (à)(?! côtés| peu près| prioris?| post[eé]rioris?| valoirs?| pics?| propos) @@4 <<- -1>> a - # Confusion : “à” est une préposition. Pour le verbe avoir, écrivez : + # Confusion : “à” est une préposition. Pour le verbe avoir, écrivez : __[i]/conf(conf_a_à_avant_conj_prep)__ (à) +(?:a(?:fin|lors|près|uprès|ux?(?! moins| plus| mieux)|vant|vec)|au-de(?:dans|hors|là|sso?us|vant)|chez|d(?:ans|evant|ès|déjà|onc|urant)|lorsque?|malgré|par(?:ce|mi|)|p(?:endant|our|uisque)|que?|sur|tandis) @@0 <<- -1>> a # Confusion probable : “à” est une préposition. Écrivez “a” pour la conjugaison de “avoir”. __[i]/conf(conf_a_participe_passé_ou_vconj)__ (à) +({w_2}) @@0,$ @@ -3498,11 +3501,11 @@ (a) (?:[mts](?:es|on|a)|[nv]o(?:s|tre)|leurs?) (?:avis|c(?:onnaissance|ôtés)|c(?:œur|orps) défendant|dé(?:pens|triment)|disposition|encontre|égard|grand(?: désarroi|e (?:surprise|tristesse))|guise|insu|portée|risques et périls|sujet|tour) @@0 <<- not before(r"(?i)(?:\bque? |[ln]’$|(?> à # Confusion probable : “a” est la conjugaison du verbe “avoir”. Utilisez la préposition “à”.|http://fr.wiktionary.org/wiki/%C3%A0 __[s]/conf(conf_a_à_infi)__ (?> à # Confusion probable : “a” est la conjugaison du verbe “avoir”. Utilisez la préposition “à”.|http://fr.wiktionary.org/wiki/%C3%A0 __[s]/conf(conf_a_à_après_interrogative)__ \w+-(?:je|ils?|elles?|je|tu|on|vous|nous) (a)(?! priori| posteriori| fortiori) @@$ <<- -1>> à # Confusion probable : “a” est la conjugaison du verbe “avoir”. Utilisez la préposition “à”.|http://fr.wiktionary.org/wiki/%C3%A0 @@ -3580,11 +3583,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. @@ -3591,11 +3594,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. @@ -3640,11 +3643,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 : @@ -3663,11 +3666,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 @@ -3684,31 +3687,31 @@ __[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. TEST: {{Se}} sont de grands enfants. TEST: {{Se}} sera une fille. TEST: {{ceux}} seraient des jours heureux TEST: Pour {{se}} faire, ils sont prêts à tout. -TEST: {{se}} {{ne}} peut être ainsi. +TEST: {{se}} ne peut être ainsi. TEST: C’est tout {{se}} qu’il y a TEST: Tout {{se}} que je fais TEST: tout {{se}} qu’il entend TEST: {{Ce}} {{promener}} est relaxant. TEST: Il {{ce}} {{sent}} seul @@ -3743,11 +3746,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 @@ -3782,11 +3785,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}}. @@ -3847,15 +3850,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|nombre) ") and not morph(word(1), ">(?:financi[eè]re?|pécuni(?:er|aire)|sociaux)s? ", False, False) + <<- morphex(\1, ":V", ":Q|>(?:profiter|bénéficier|nombre)/") and not morph(word(1), ">(?:financi[eè]re?|pécuni(?:er|aire)|sociaux)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)|sociaux)s? ", False, False) + <<- not morph(\1, ">(?:profiter|bénéficier)/", False) and not morph(word(1), ">(?:financi[eè]re?|pécuni(?:er|aire)|sociaux)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”… @@ -3932,11 +3935,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. @@ -3967,15 +3970,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. @@ -3989,21 +3992,21 @@ <<- -2>> golf # Confusion. Le golfe est une zone de mer ou d’un lac avancée dans les terres. Ex : Le golfe Persique. __[i]/conf(conf_golfe)__ (golf) (persique|d[ue] (?:Bengale|Botnie|Gascogne|Gabès|Guinée|Lion|Morbihan|Mexique|Porto|Saint-Laurent|Thaïlande|Tonkin|Tunis|Winam)|d’(?:Aden|Ajaccio|Alaska|Hammamet)) @@0,$ <<- -1>> golfe # Confusion. Le golf est un sport. __[i]/conf(conf_Golfe)__ - (?:guerre|émir|monarchie)s? du (golf) @@$ <<- -1>> Golfe # Confusion. Le golf est un sport. + (?:guerre|émir|monarchie)s? du (golf) @@$ <<- -1>> Golfe # Confusion. Le golf est un sport. TEST: Il a réalisé un documentaire sur la guerre du {{Golf}}. TEST: C’est un bon joueur de {{golfe}}. TEST: Le {{golf}} Persique est presque une mer fermée. TEST: J’ai fait de la voile dans le {{golf}} du Morbihan. TEST: Le {{golf}} d’Aden. # haut delà / au-delà -__[i]/conf(conf_au_delà)__ haut[- ]del[àa] <<- ->> au-delà # Confusion. +__[i]/conf(conf_au_delà)__ haut[- ]del[àa] <<- ->> au-delà # Confusion. TEST: il va dans l’{{haut delà}} # héro / héros @@ -4030,11 +4033,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 ? @@ -4082,11 +4085,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. @@ -4167,11 +4170,11 @@ __[i]/conf(conf_cela_peut_être_adj)__ ^ *(?:cela|ceci) (peut-être) ({w_2}) @@w,$ <<- morph(\2, ":[AQ]", False) -1>> peut être # Confusion probable : « peut-être » signifie « possiblement ». __[i]/conf(conf_peu_à_peu)__ peu[xt]? a peu[xt]? - <<- ->> peu à peu # Confusion : « peu à peu » ou « petit à petit ». + <<- ->> peu à peu # Confusion : « peu à peu » ou « petit à petit ». __[i]/conf(conf_peu_importe)__ (peu[tx]) importe(?:nt|) @@w <<- morph(word(-1), ":C", False, True) -1>> peu # Confusion : « \1 » est une conjugaison de “pouvoir”, utilisez “peu” pour dire “pas beaucoup”. __[i]/conf(conf_adv_de_peu)!6__ (?:très|trop|de|quelque|pour|à) (peu[tx]) @@$ @@ -4216,11 +4219,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. @@ -4235,34 +4238,57 @@ # 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. TEST: Il est {{près}} à les aider TEST: Elle va regarder ça de plus {{prêt}}. + +@@@@ +@@@@ +@@@@ +@@@@ +@@@@GRAPH: graphe1 _ +@@@@ +@@@@ +@@@@ +@@@@ + # quand / quant / qu’en -__[i]/conf(conf_quant_à)__ - (?(?: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)__ - (qu en) (?:je|tu|ils?) @@0 - <<- not after("^ +ne s(?:ai[st]|u[st]|urent|avai(?:[ts]|ent)) ") -1>> quand # Confusion probable. Pour évoquer un moment, écrivez : +__conf_quand_quant_qu_en__ + quand à + <<- /conf/ not morph(<1, ">(?:arriver|venir|à|revenir|partir|repartir|aller|de)/") and not after("^ +[mts]on tour[, ]") + -1>> quant # Confusion probable. Quand = à quel moment. Quant à = à propos de.|https://fr.wiktionary.org/wiki/quant_%C3%A0 + + quand [au|aux] + <<- /conf/ not morph(<1, ">(?:arriver|venir|à|revenir|partir|repartir|aller|de)/") + -1>> quant # Confusion probable. Quand = à quel moment. Quant à = à propos de.|https://fr.wiktionary.org/wiki/quant_%C3%A0 + + [quand|quant] @:P + <<- -1>> qu’en # Confusion probable. + + [quand|quant] [est|était] il [de|des|du] + [quand|quant] [est-il|était-il] [de|des|du] + <<- -1>> qu’en # Confusion probable. Ce qu’il en est de… → Qu’en est-il de… ? + + quant ~¬à|aux + <<- -1>> quand # Confusion. Quand = à quel moment. Quant à = à propos de. + + [qu’|qu] en [je|tu|il|ils] + <<- not after("^ ne s(?:ai[st]|u[ts]|avai(?:s|t|ent)|urent) ") + -1:2>> quand # Confusion probable. Pour évoquer un moment, écrivez “quand”.|https://fr.wiktionary.org/wiki/quand TEST: {{Quant}} est-il du chien ? TEST: {{Quand}} à ma santé, elle est défaillante. TEST: {{Quant}} ils… TEST: {{quant}} je… @@ -4273,39 +4299,51 @@ TEST: être rassuré quant à l’avenir du continent européen TEST: il comprit trop tard qu’en elle naquit alors le doute qui l’éloigna de lui à jamais. TEST: Quand à mon tour je réalise l’imposture, c’est trop tard. -# quand bien même -__[i]/conf(conf_quand_bien_même)__ - combien même <<- not after("^ si ") ->> quand bien même # Locution conjonctive.|https://fr.wiktionary.org/wiki/quand_bien_m%C3%AAme +__conf_quand_bien_même__ + tant bien même + <<- /conf/ ->> quand bien même # Confusion. Écrivez « quand bien même ».|http://www.academie-francaise.fr/tant-bien-meme + combien même ~¬si + <<- /conf/ -1:2>> quand bien même # Locution conjonctive.|https://fr.wiktionary.org/wiki/quand_bien_m%C3%AAme + +TEST: il sera condamné {{tant bien même}} il prouverait que c’était un accident. TEST: J’irai, {{combien même}} vous seriez tous contre moi. TEST: Il partirait en guerre quand bien même devrait-il être tout seul. TEST: Elle veut savoir combien même si ça ne lui est d’aucune utilité immédiate. -# qu’elle / quelle -__[i]/conf(conf_quelle_nom_adj)__ - (qu elles?) +(?!seule?s?)({w_2}) @@0,$ - <<- morphex(\2, ":[NAQ]", ":(?:G|[123][sp]|W)") -1>> =\1.replace(" ", "") # Confusion probable. Ex : Quelle femme ! Je crois qu’elle réussira. +__conf_qu_elle_quelle__ + [que|qu’|qu] elle @:[NAQ]¬:(?:G|[123][sp]|W|Oo|X)|>seule?/ + <<- /conf/ -1:2>> quelle # Confusion probable. Ex : Quelle femme ! Je crois qu’elle réussira. + + [que|qu’|qu] elles @:[NAQ]¬:(?:G|[123][sp]|W|Oo|X)|>seule?/ + <<- /conf/ -1:2>> quelles # Confusion probable. Ex : Quelle femme ! Je crois qu’elle réussira. + + quelle [ne|n’|me|m’|te|t’|se|s’|nous|vous|le|la|l’|les|lui|leur|en|y] + <<- /conf/ not (morph(\2, ">en/") and morph(>1, ":V0e")) -1>> qu’elle # Confusion. Le sujet “elle” doit être séparée de la conjonction “que”. + + quelle @:V¬:[NA].*:[fe]|>(?:être|plus) + <<- /conf/ \2.islower() and not (morph(\2, ">(?:pouvoir|devoir)/") and morph(>1, ":V0e")) and not (morph(\2, ":V0a") and after("^ +été ")) + -1>> qu’elle # Confusion. Le sujet “elle” doit être séparée de la conjonction “que”. + + quelles [ne|n’|me|m’|te|t’|se|s’|nous|vous|le|la|l’|les|lui|leur|en|y] + <<- /conf/ not (morph(\2, ">en/") and morph(>1, ":V0e")) -1>> qu’elles # Confusion. Le sujet “elles” doit être séparée de la conjonction “que”. + + quelles @:V¬:[NA].*:[fe]|>(?:être|plus) + <<- /conf/ \2.islower() and not (morph(\2, ">(?:pouvoir|devoir)/") and morph(>1, ":V0e")) and not (morph(\2, ":V0a") and after("^ +été ")) + -1>> qu’elles # Confusion. Le sujet “elles” doit être séparée de la conjonction “que”. + + quelle >être @:[QA]¬:G + <<- /conf/ morph(\2, ":[123][sp]") -1>> qu’elle # Confusion. Le sujet “elle” doit être séparée de la conjonction “que”. + + quelles >être @:[QA]¬:G + <<- /conf/ morph(\2, ":[123][sp]") -1>> qu’elles # Confusion. Le sujet “elles” doit être séparée de la conjonction “que”. 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)) >>> - <<- \1.endswith("e") and not morph(\2, ":V0e", False) and not (morph(\2, ":V0a", False) and after("^ +été ")) - -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) and not (morph(\2, ":V0a", False) and after("^ +été ")) - -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 - TEST: Je sais {{quelle}} est partie. TEST: {{Quelle}} partit prendre son repas à la cantine, je n’en avais cure. TEST: Il se plaint {{quelle}} ne nous dit rien. TEST: {{Quelles}} sont intelligentes, ces filles-là. TEST: {{Quelle}} a du répondant, cette gamine ! @@ -4315,160 +4353,213 @@ TEST: Je crois {{quelle}} n’en sait pas assez pour nous nuire. TEST: {{Quelles}} t’arrivent seulement à la cheville, voilà qui serait étonnant. TEST: {{Quelles}} m’engueulent encore une seule fois et elles vont le regretter. TEST: Je crois {{quelle}} est partie. TEST: il pense {{quelles}} sont devenues dangereuses. +TEST: je crois qu’elle seule peut y parvenir TEST: Quelle est sa passion ? TEST: Quelles sont leurs principales études ? TEST: Quelles en sont les conséquences ? TEST: Quelle plus belle complicité que… TEST: Quelle peut être la date de clôture d’un exercice ? TEST: Quelle doit être la date du mariage ? TEST: Quelles ont été les annonces faites ? +TEST: Elle cache qu’elle a été en prison. +TEST: Elle avait été accueillie avec joie. -# savoir / ignorer -__[i]/conf(être_pas_sans_savoir)__ - ({etre}) pas sans (ignor(?:e[rz]|ée?s?|ai[st])) @@0,$ - <<- morph(\1, ":V0e", False) - -2>> savoir # Confusion : vous écrivez l’inverse de ce que vous voulez dire.|http://fr.wiktionary.org/wiki/vous_n%E2%80%99%C3%AAtes_pas_sans_savoir +__ne_pas_être_sans_savoir__ + >être [pas|plus|jamais|guère] sans >ignorer + <<- /conf/ -4>> savoir # Confusion : vous écrivez l’inverse de ce que vous voulez dire.|http://fr.wiktionary.org/wiki/vous_n%E2%80%99%C3%AAtes_pas_sans_savoir + + ne [pas|plus|jamais|guère] être sans >ignorer + <<- /conf/ -5>> savoir # Confusion probable : vous écrivez l’inverse de ce que vous voulez dire.|http://fr.wiktionary.org/wiki/vous_n%E2%80%99%C3%AAtes_pas_sans_savoir TEST: Vous n’êtes pas sans {{ignorer}} que… +TEST: ne pas être sans {{ignorer}} la cause de ces phénomènes. -## s’en / sens / sans / cent / cens -__[i]/conf(conf_il_on_s_en)__ (?:ils?|on) (san[sg]|cen[st]|c’en) ({w_2}) @@w,$ - <<- isStart() and morph(\2, ":V", False) - -1>> s’en # Confusion probable. -__[i]/conf(conf_elle_s_en)__ elles? (san[sg]|cen[st]|c’en) ({w_2}) @@w,$ - <<- isStart() and morph(\2, ":V", False) and not ( \1 == "sans" and morph(\2, ":[NY]", False) ) - -1>> s’en # Confusion probable. +__conf_s_en_sens_sans_cent_cens__ + [|,] [il|ils|on] [c’|ç’] en @:V + [|,] [elle|elles] [c’|ç’] en @:V¬:V0e + <<- /conf/ -3:4>> s’en # Confusion probable. + + [|,] [il|ils|on] [sans|sang|sangs|cens|cent] @:V + [|,] [elle|elles] [sang|sangs|cens|cent] @:V + [|,] [elle|elles] sans @:V¬:(?:[NYDA]|Oo)|>(?:y|en) + <<- /conf/ -3>> s’en # Confusion probable. TEST: il {{c’en}} est vite lassé. TEST: {{S’en}} était vraiment trop ! TEST: Car {{s’en}} était vraiment fini ! TEST: elle {{sang}} était voulu - - -## son / sont -__[i]/conf(conf_ne_sont)__ - ne (?:l(?:e|eur|ui) |[nv]ous |)(son) @@$ - <<- -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_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) - 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”. +TEST: elle sans y penser +TEST: elle sans vergogne +TEST: elle sans la condamner +TEST: elles sans un sou en poche +TEST: elles sans grandes convictions +TEST: elle c’en était trop (TODO : proposer une virgule) + + +__conf_son_sont__ + [ne|me|te|se] son + <<- /conf/ -2>> sont # Confusion : “son” est un déterminant ou un nom masculin. Le verbe “être” à la 3ᵉ personne du pluriel s’écrit “sont”. + + ne [le|leur|leurs|lui|nous|vous] son + <<- /conf/ -3>> sont # Confusion : “son” est un déterminant ou un nom masculin. Le verbe “être” à la 3ᵉ personne du pluriel s’écrit “sont”. + + [qui|que|comment|pourquoi|lorsque|quand] son [,|@:[DR]] + <<- /conf/ -2>> sont # Confusion probable : “son” est un déterminant ou un nom masculin. Le verbe “être” à la 3ᵉ personne du pluriel s’écrit “sont”. + + [|,] sont @:[NA].*:[me]:s|>[aeéiîou].*/:[NA].*:f:s¬:[GW] + <<- /conf/ -2>> son # Confusion probable : “sont” est le verbe “être” à la 3ᵉ personne du pluriel. Pour le déterminant, écrivez “son”. + + sont @:[NA].*:[me]:s|>[aeéiîou].*/:[NA].*:f:s¬:[GW] + <<- /conf/ morph(<1, ":V", ":[NA].*:[pi]|>(?:ils|elles|vous|nous|leur|lui|[nmts]e)/") and not before(r"(?i)\bce que? |[mts]’en +$") + -1>> son # Confusion probable : “sont” est le verbe “être” à la 3ᵉ personne du pluriel. Pour le déterminant, écrivez “son”. + + [à|chez|dès|par] sont + [avec|contre|devant|derrière|pour|sans|sur] sont @:[NA].*:[me]:s|>[aeéiîou].*/:[NA].*:f:s¬:[GW] + <<- /conf/ -2>> son # Confusion probable : “sont” est le verbe “être” à la 3ᵉ personne du pluriel. Pour le déterminant, écrivez “son”. + + en sont @:[NA].*:[me]:s|>[aeéiîou].*/:[NA].*:f:s¬:[GW] + <<- /conf/ not before(r"(?i)\b(?:ce que? |ils|elles|leur|lui|nous|vous|[mtsl]’)$") + -2>> son # Confusion probable : “sont” est le verbe “être” à la 3ᵉ personne du pluriel. Pour le déterminant, écrivez “son”. TEST: ne leur {{son}} pas odieux. TEST: Ces chiens me {{son}} odieux. TEST: {{sont}} {{pain}} TEST: en {{sont}} {{absence}} TEST: qui {{son}} ces gens ? TEST: ces gens qui {{son}}, dans le meilleur des cas, des imbéciles ne peuvent nous aider. TEST: elles s’en sont mal portées TEST: ils en sont reconnaissants +TEST: sont loin, ces gens… TEST: Il ne sait pas vraiment ce que sont la peur et la souffrance. -# statu / statut -__[i]/conf(conf_statu_quo)__ - statu[tse] quo <<- ->> statu quo # Confusion.|https://fr.wiktionary.org/wiki/statu_quo -__[i]/conf(conf_statue_statut)__ - statu(?! quo) <<- ->> statut|statue # Confusion : “statu” ne s’emploie que dans l’expression “statu quo”. +__conf_statu_statue_statut__ + [statut|statue|status] quo + <<- /conf/ ->> statu quo # Confusion. Écrivez “statu quo”.|https://fr.wiktionary.org/wiki/statu_quo + + statu ~¬[qQ][uU][oO] + <<- /conf/ -1>> statut|statue # Confusion : “statu” ne s’emploie que dans l’expression “statu quo”. TEST: Ça n’en finit pas, c’est le {{statut quo}}. TEST: Quelle splendide {{statu}}. -# sur / sûr -__[i]/conf(conf_sûr_de_nom_propre)__ - (sur) d(?:e |’)([A-ZÉÈ][\w-]+) @@0,$ - <<- morph(\2, ":M[12]", False) -1>> sûr - # Confusion probable : “sur” est une préposition ou un adjectif signifiant acide ou aigre ; utilisez “sûr” pour certain, vrai ou sans danger.|http://fr.wiktionary.org/wiki/sur +__conf_sûr_sur__ + sur [que|qu’|qu] + sur [de|d’] {pronom_obj} + sur [de|d’] @:M + sur [de|d’] [ne|n’|me|m’|te|t’|se|s’] + sur [de|d’] [le|les|mon|ton|son|ma|ta|sa|mes|tes|ses|ce|cet|cette|ces|cela|ceci|ça] + sur [de|d’] la @:Y + <<- /conf/ -1>> sûr + # Confusion probable : “sur” est une préposition ou un adjectif signifiant acide ou aigre ; utilisez “sûr” pour certain, vrai ou sans danger.|http://fr.wiktionary.org/wiki/sur + + sur [de|d’] @:Y + <<- /conf/ -1>> sûr + # Confusion probable : “sur” est une préposition ou un adjectif signifiant acide ou aigre ; utilisez “sûr” pour certain, vrai ou sans danger.|http://fr.wiktionary.org/wiki/sur + + en lieu sur + <<- /conf/ -3>> sûr + # Confusion probable : “sur” est une préposition ou un adjectif signifiant acide ou aigre ; utilisez “sûr” pour certain, vrai ou sans danger.|http://fr.wiktionary.org/wiki/sur + + [sure|surs|sures] [de|d’|que|qu’|qu] + <<- /conf/ -1>> =\1.replace("sur", "sûr") + # Confusion probable : “sur” un adjectif signifiant acide ou aigre ; utilisez “sûr” pour certain, vrai ou sans danger.|http://fr.wiktionary.org/wiki/sur TEST: Je suis {{sur}} de Patrick. +TEST: Je suis {{sure}} qu’il ne va pas tarder à venir +TEST: {{sures}} d’elles-mêmes, elles ne s’en laissent pas conter. +TEST: {{sur}} de toi et de moi, que peut-il nous arriver, sinon le meilleur. +TEST: Il est tellement {{sur}} de la trouver. +TEST: ils sont en lieu {{sur}} et introuvables. +TEST: il s’étendit sur de la mousse à mémoire de forme -# tant / temps (1re partie) -__[i]/conf(conf_en_temps_de)__ - en (tant?) de? @@3 - <<- not after("^[ ’](?:lieux|endroits|places|mondes|villes|pays|régions|cités)") -1>> temps # Confusion. Écrivez « en temps de » si vous évoquez une période de temps. -__[i]/conf(conf_de_temps_en_temps)__ de tant? en tant? <<- ->> de temps en temps # Confusion. Écrivez « de temps en temps ». -__[i]/conf(conf_un_temps_soit_peu)__ un (temps|tan) soi[tes]? peu @@3 <<- -1>> tant # Confusion. Écrivez « un tant soit peu ». -__[i]/conf(conf_tant_de)__ a(?:près|vec) (temps|tan) de? @@w <<- -1>> tant # Confusion.|http://fr.wiktionary.org/wiki/tant -__[i]/conf(conf_à_temps_partiel)__ [àa] (tant?) (?:partiel|plein)s? @@2 <<- -1>> temps # Confusion. -__[i]/conf(conf_en_même_temps)__ en mêmes? (tant?) @@$ <<- -1>> temps # Confusion. +__conf_tant_temps_tan_1__ + en [tant] [de|d’] [guerre|paix|crise|doute|pluie] + <<- /conf/ -2>> temps # Confusion. Écrivez « en temps de » si vous évoquez une période de temps. + + de [tant|tan|tans] en [tant|tan|tans] + <<- /conf/ ->> de temps en temps # Confusion. Écrivez « de temps en temps ». + + un [temps|tan|tans] [soi|sois|soie|soies|soit] peu + <<- /conf/ -2>> tant # Confusion. Écrivez « un tant soit peu ». + + un [temps|tan|tans|tant] [soi|sois|soie|soies] peu + <<- /conf/ -3>> soit # Confusion. Écrivez « un tant soit peu ». + + [après|avec] [temps|tan|tans] [de|d’] + <<- /conf/ -2>> tant # Confusion. Écrivez “tant” pour évoquer une quantité de quelque chose.|http://fr.wiktionary.org/wiki/tant + + [à|a] [tan|tans|tant] [partiel|partiels|plein|pleins] + <<- /conf/ -2>> temps # Confusion. Pour ce qui est temporel, écrivez “temps”. + + en [même|mêmes] [tant|tan|tans] + <<- /conf/ ->> en même temps # Confusion. Pour ce qui est temporel, écrivez “temps”. TEST: en {{tant}} de guerre, il faut savoir faire face et ne pas faiblir face à l’adversité. TEST: ils vont {{de tan en tan}} au restaurant TEST: un {{temps}} soit peu perdu dans cette affaire. TEST: après {{temps}} de souffrance, il faut savoir lâcher prise. TEST: il travaille à {{tant}} partiel -TEST: en même {{tant}}, on s’en moque, de toutes ces histoires ennuyeuses. +TEST: {{en même tant}}, on s’en moque, de toutes ces histoires ennuyeuses. TEST: ce qui a commencé en 2011 en tant d’endroits du pourtour méditerranéen TEST: elle est allée en tant de lieux qu’il est difficile de suivre son trajet. -# tant bien même -__[i]/conf(conf_tant_bien_même)__ - tant bien même <<- ->> quand bien même # Confusion. Écrivez « quand bien même ».|http://www.academie-francaise.fr/tant-bien-meme - -TEST: il sera condamné {{tant bien même}} il prouverait que c’était un accident. - - -# voie / vois / voix -# ->> voix -__[i]/conf(conf_à_haute_voix)__ à haute (voi[tes]) @@8 <<- -1>> voix # Confusion.|http://fr.wiktionary.org/wiki/voix -__[i]/conf(conf_à_voix)__ à (voi[tes]) (?:basse|haute) @@2 <<- -1>> voix # Confusion.|http://fr.wiktionary.org/wiki/voix -__[i]/conf(conf_de_vive_voix)__ de vives? (voi[est]) @@$ <<- -1>> voix # Confusion.|http://fr.wiktionary.org/wiki/voix - -TEST: à haute {{voie}} -TEST: à {{voie}} haute -TEST: de vive {{voie}} - -# ->> voie -__[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 -__[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 +__conf_voie_voix_vois_voit__ + à haute [voie|vois|voit] + de vive [voie|vois|voit] + <<- /conf/ -3>> voix # Confusion. La voix est un son humain, animal ou instrumental. Pour évoquer un chemin, écrivez “voie”.|http://fr.wiktionary.org/wiki/voie + + à [voie|vois|voit] [basse|haute] + <<- /conf/ -2>> voix # Confusion. La voix est un son humain, animal ou instrumental. Pour évoquer un chemin, écrivez “voie”.|http://fr.wiktionary.org/wiki/voie + + sur la bonne voix + <<- /conf/ -4>> voie # Confusion. La voix est un son humain, animal ou instrumental. Pour évoquer un chemin, écrivez “voie”.|http://fr.wiktionary.org/wiki/voie + + >ouvrir la voix [à|au|aux] + <<- /conf/ -3>> voie # Confusion. La voix est un son humain, animal ou instrumental. Pour évoquer un chemin, écrivez “voie”.|http://fr.wiktionary.org/wiki/voie + + en voix [de|d’] [développement|disparition|guérison|résorption|acquisition|achèvement|extinction|obtention] + par voix de >conséquence + <<- /conf/ -2>> voie # Confusion. La voix est un son humain, animal ou instrumental. Pour évoquer un chemin, écrivez “voie”.|http://fr.wiktionary.org/wiki/voie + + 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] + voix [abdominales|anales|biliaires|carrossables|communales|expresss|interdites|intramusculaires|intraveineuses|piétonnes|principales|prioritaires|privées|publiques|désertes|romaines|appiennes|flaminiennes|ferrées|ferroviaires|lactées|lacrymales|aériennes|maritimes|fluviales|terrestres|navigables|détournées|déviées|buccales|digestives|urinaires|respiratoires|parallèles|administratives|diplomatiques|gouvernementales|législatives|hiérarchiques|rectilignes|sinueuses|souterraines|urbaines] + <<- /conf/ -1>> voie # Confusion. La voix est un son humain, animal ou instrumental. Pour évoquer un chemin, écrivez “voie”.|http://fr.wiktionary.org/wiki/voie TEST: sur la bonne {{voix}} TEST: ces patients sont en {{voix}} de guérison. TEST: il faut ouvrir la {{voix}} aux nouveaux venus. TEST: Je propse que, par {{voix}} de conséquence, nous partions immédiatement. TEST: C’est une {{voix}} interdite. -# 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) - -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 - # Confusion probable : “voire” signifie “et même possiblement”. Pour le verbe, écrivez “voir”.|https://fr.wiktionary.org/wiki/voire +__conf_voir_voire__ + voir [grand|petit|rouge] + <<- ~2>> ! + + voir @:A¬:[NGM] + <<- /conf/ not \2.istitle() and not morph(<1, ":O[os]|>(?:[ndmts]e|falloir|pouvoir|savoir|de)/") + and not before(r"(?i)\b[ndmts](?:e |’(?:en |y ))(?:pas |jamais |) *$") + -1>> voir # Confusion probable : “voir” est un verbe concernant la perception visuelle. Pour signifier “et même possiblement”, écrivez :|https://fr.wiktionary.org/wiki/voire + + [comme|lorque|puisque|quand|que|quoique|si] (voire) + [ni|et|par|pour|sans] (voire) + [>aller|>falloir|>pouvoir|>vouloir] ?[guère|jamais|pas|plus|point|rien]¿ (voire) + <<- /conf/ -1>> 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: il faut penser juste et {{voire}} vrai. +TEST: Je ne vais jamais {{voire}} ces gens-là. TEST: Elles vont voir rouge en apprenant cet échec. TEST: Voir les enfants jouer ne me rend pas nostalgique. TEST: Il faut voir grand. TEST: Il sait voir grand. TEST: Il sait voir telle ou telle chose avec acuité. @@ -4485,52 +4576,80 @@ !! !! -!!!! Pronoms + incohérences -!! -!! -__[i](p_m_enfin)__ m’enfin <<- ~>> * - -__[i]/conf(conf_j_y_en_qqch)__ - (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) - -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. -__[i]/conf(conf_ne_pronom_qqch)__ - (ne (?:l(?:es? +|eur +|a +|’)|[nv]ous))({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. -__[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) - -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) - -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) - -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) - -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) - -2>> =suggSimil(\2, ":3s", False) # Incohérence avec « \1 » : « \2 » devrait être un verbe. +!!!! Verbes composés !! +!! +!! + +#__verbes_composés_interrogatifs__ +# ~-(?:je|tu|ils?|elles?)$ +# <<- =>> define(\1, [":VCi"]) +# +#__verbes_composés_impératifs__ +# ~-(?:moi|toi|leur|en|y)$ +# <<- =>> define(\1, [":VCe"]) +# +#__verbes_composés_interrogatifs_et_impératifs__ +# ~-[nv]ous$ +# <<- =>> define(\1, [":VCi", ":VCe"]) + + +__rendez_vous__ + ne [le|la|les] [lui|leur] (rendez-vous) + ne me [le|la|les] (rendez-vous) + ne [lui|leur] en (rendez-vous) + ne [le|la|les|lui|leur] (rendez-vous) + [me|ne|nous|vous|lui] (rendez-vous) + <<- =>> define(\1, [":VCi1:2p"]) + <<- ~1>> ! + +# [un|mon|ton|son|ce|mes|tes|ses|leurs] rendez-vous +# rendez-vous seulement défini comme :N:m:i + + + + +!! +!! +!!!! Pronoms + incohérences !! +!! +!! + +__m_enfin__ + m’ enfin + <<- ~2>> ! + <<- ~>> * + + +__non_verbe_après_préverbes__ + [ne|n’] [le|la|l’|les] [lui|leur|en|y] @:[NAQ]¬:(?:[123][sp]|Y|P|Oo|X) + [ne|n’] [lui|leur] en @:[NAQ]¬:(?:[123][sp]|Y|P|Oo|X) + <<- /conf/ -4>> =suggSimil(\4, ":(?:[123][sp]|Y)", False) # Incohérence avec « \1 \2 \3 » : « \4 » devrait être un verbe. + + [ne|n’] [le|la|l’|les|nous|vous|lui|leur] @:[NAQ]¬:(?:[123][sp]|Y|P|Oo|X)|>(?:pas|presque|jamais|rien|guère|point|plus)/ + n’ [en|y] @:[NAQ]¬:(?:[123][sp]|Y|W|P|Oo|X)|>(?:pas|presque|jamais|rien|guère|point|plus)/ + [me|m’|te|t’|se|s’] [le|la|l’|les|en|y] @:[NAQ]¬:(?:[123][sp]|Y|P|Oo|X)|>(?:pas|presque|jamais|rien|guère|point|plus)/ + <<- /conf/ -3>> =suggSimil(\3, ":(?:[123][sp]|Y)", False) # Incohérence avec « \1 \2 » : « \3 » devrait être un verbe. + + [nous|vous] [le|la|l’|les|en|y] @:[NAQ]¬:(?:[123][sp]|Y|P|Oo|X)|>(?:pas|presque|jamais|rien|guère|point|plus)/ + <<- /conf/ not morph(<1, ":R|>de/") -3>> =suggSimil(\3, ":(?:[123][sp]|Y)", False) # Incohérence avec « \1 \2 » : « \3 » devrait être un verbe. + + [ne|n’] @:[NAQ]¬:(?:[123][sp]|Y|W|P|Oo|X)|>(?:[mtsl]|même|pas|presque|jamais|rien|guère|point|plus)/ + [me|m’|te|t’|se] @:[NAQ]¬:(?:[123][sp]|Y|P|Oo|X)|>(?:l|pas|presque|jamais|rien|guère|point|plus)/ + [s’] @:[NAQ]¬:(?:[123][sp]|Y|P|Oo|X)|>ils?/ + <<- /conf/ -2>> =suggSimil(\2, ":(?:[123][sp]|Y)", False) # Incohérence avec « \1 » : « \2 » devrait être un verbe. + + [c’|ç’] @:[NAQ]¬:(?:[123][sp]|Y|P|Oo|X)|>que?/ + <<- /conf/ -2>> =suggSimil(\2, ":3s", False) # Incohérence avec « \1 » : « \2 » devrait être un verbe. + + j’ [en|y] @:[NAQ]¬:(?:[123][sp]|Y|P|Oo|X) + <<- /conf/ -3>> =suggSimil(\3, ":1s", False) # Incohérence avec « \1 \2 » : « \3 » devrait être un verbe. + + j’ @:[NAQ]¬:(?:[123][sp]|Y|P|Oo|X) + <<- /conf/ -2>> =suggSimil(\2, ":1s", 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}}. TEST: ne {{pensée}} rien, jamais @@ -4553,54 +4672,159 @@ TEST: il est normal de ne presque pas payer des gens qui effectuent un travail TEST: j’ai l’impression de ne même pas savoir ce qu’est un « juif français ». TEST: C’que j’comprends, c’est qu’il y a des limites à ce qu’on peut supporter. TEST: la tentation pour certains médias de ne tout simplement pas rémunérer notre travail si celui-ci n’est finalement pas publié. TEST: Ne parfois pas être celui qui sabote l’ambiance. +TEST: il s’en va. +TEST: C’en est trop ! +TEST: T’y viendras, comme tout le monde. +TEST: Nous y voilà enfin. +TEST: T’y voilà propulsé. +TEST: t’en voilà débarrassée. +TEST: N’oublient-ils pas ce qu’ils étaient autrefois… +TEST: Ne presque jamais réussir un plat aussi simple, c’est de l’incompétence pure et simple. + + +__p_notre_père_qui_es_au_cieux__ + notre père qui [es|est] aux cieux + <<- ~4>> ! + <<- ~3:0>> _ !! !! -!!!! Formes verbales sans sujet +!!!! Formes verbales sans sujet !! !! !! + +__tag_sujets__ + [je|j’] + [moi|moi-même] qui + [moi|moi-même] [seul|seule] + <<- />> 1s + + tu + t’ @:2s + t’ [en|y] @:2s + [toi|toi-même] ?,¿ qui + [toi|toi-même] [seul|seule] + <<- />> 2s + + nous + nous ?,¿ qui + nous-même + nous-mêmes + nous [seul|seuls|seules] + [et|ou] [moi|moi-même] + ni [moi|moi-même] + [moi|moi-même] et + <<- />> 1p + + vous + vous ?,¿ qui + vous-même + vous-mêmes + vous [seul|seule|seuls|seules] + [et|ou] [toi|toi-même] + ni [toi|toi-même] + [toi|toi-même] et + <<- />> 2p + ## Incohérences avec formes verbales 1sg et 2sg sans sujet -__[i](p_notre_père_qui_es_au_cieux)__ notre père (qui est? aux cieux) @@11 <<- ~1>> * - -__[i]/conj(conj_xxxai_sans_sujet)!3__ - \w*ai(?! je) - <<- ( morph(\0, ":1s") or ( before("> +$") and morph(\0, ":1s", False) ) ) and not (\0[0:1].isupper() and before0(r"\w")) - and not before(r"(?i)\b(?:j(?:e |[’'])|moi(?:,? qui| seul) )") - ->> =suggVerb(@, ":3s") # Incohérence. Ceci est un verbe à la 1ʳᵉ personne du singulier. Sujet (“je” ou “moi qui”) introuvable. -__[i]/conj(conj_xxxes_sans_sujet)!3__ - \w*es(?! tu) - <<- morphex(\0, ":2s", ":(?:E|G|W|M|J|[13][sp]|2p)") and not \0[0:1].isupper() and not isRealStart() - and ( not morph(\0, ":[NAQ]", False) or before("> +$") ) - and not before(r"(?i)\bt(?:u |[’']|oi,? qui |oi seul )") - ->> =suggVerb(@, ":3s") # Incohérence. Ceci est un verbe à la 2ᵉ personne du singulier. Sujet (“tu” ou “toi qui”) introuvable. -__[i]/conj(conj_xxxas_sans_sujet)!3__ - \w+as(?! tu) - <<- morphex(\0, ":2s", ":(?:G|W|M|J|[13][sp]|2p)") and not (\0[0:1].isupper() and before0(r"\w")) - and ( not morph(\0, ":[NAQ]", False) or before("> +$") ) - and not before(r"(?i)\bt(?:u |[’']|oi,? qui |oi seul )") - ->> =suggVerb(@, ":3s") # Incohérence. Ceci est un verbe à la 2ᵉ personne du singulier. Sujet (“tu” ou “toi qui”) introuvable. -__[i]/conj(conj_xxxxs_sans_sujet)!3__ - \w+[iudnrtpcï]s(?! (?:tu|je)) - <<- morphex(\0, ":[12]s", ":(?:E|G|W|M|J|3[sp]|2p|1p)") and not (\0[0:1].isupper() and before0(r"\w")) - and ( not morph(\0, ":[NAQ]", False) or before("> +$") or ( re.search("(?i)^étais$", \0) and not morph(word(-1), ":[DA].*:p", False, True) ) ) - and not before(r"(?i)\b(?:j(?:e |[’'])|moi(?:,? qui| seul) |t(?:u |[’']|oi,? qui |oi seul ))") - ->> =suggVerb(@, ":3s") # Incohérence. Le sujet de cette forme verbale est introuvable. -__[i]/conj(conj_peux_veux_sans_sujet)!3__ - [pv]eux(?! (?:tu|je)) - <<- not (\0[0:1].isupper() and before0(r"\w")) and not before(r"(?i)\b(?:j(?:e |[’'])|moi(?:,? qui| seul) |t(?:u |[’']|oi,? qui |oi seul ))") - ->> =suggVerb(@, ":3s") # Incohérence. Le sujet de cette forme verbale est introuvable. -__[i]/conj(conj_équivaux_prévaux_sans_sujet)!3__ - (?:équi|pré|)vaux(?! (?:tu|je)) - <<- not (\0[0:1].isupper() and before0(r"\w")) - and not (\0 == "vaux" and morph(word(-1), ":(?:R|D.*:p)", False, False)) - and not before(r"(?i)\b(?:j(?:e |[’'])|moi(?:,? qui| seul) |t(?:u |[’']|oi,? qui |oi seul ))") - ->> =suggVerb(@, ":3s") # Incohérence. Le sujet de cette forme verbale est introuvable. + +__conj_xxxai__sans_sujet!3__ + [se|s’] ?[en|y|le|la|l’|les]¿ (~ai$) + <<- /conj/ morph(\1, ":1s", ":(?:G|W|M|J|3[sp])") + -1>> =suggVerb(\1, ":3s") # Incohérence. Ceci est un verbe à la 1ʳᵉ personne du singulier. Sujet (“je” ou “moi qui”) introuvable. + + [ne|n’] ?[le|la|l’|les|en|me|m’|te|t’|nous|vous|lui|leur|y]¿ (~ai$) ~¬[jJ]e + <<- /conj/ morph(\1, ":1s", ":(?:E|G|W|M|J|3[sp])") and not tag_before(\1, "1s") + -1>> =suggVerb(\1, ":3s") # Incohérence. Ceci est un verbe à la 1ʳᵉ personne du singulier. Sujet (“je” ou “moi qui”) introuvable. + + [me|m’|te|t’|nous|vous] ?[le|la|l’|les|en|y]¿ (~ai$) ~¬[jJ]e + [le|la|l’|les] [lui|leur|en|y] (~ai$) ~¬[jJ]e + [lui|leur] en (~ai$) ~¬[jJ]e + <<- /conj/ morph(\1, ":1s", ":(?:E|G|W|M|J|3[sp])") and not tag_before(\1, "1s") + -1>> =suggVerb(\1, ":3s") # Incohérence. Ceci est un verbe à la 1ʳᵉ personne du singulier. Sujet (“je” ou “moi qui”) introuvable. + + ~ai$ ~¬[jJ]e + <<- morph(\1, ":1s", ":(?:E|G|W|M|J|3[sp]|N|A|Q)") and not (\1.istitle() and before0(r"\w")) and not tag_before(\1, "1s") + -1>> =suggVerb(\1, ":3s") # Incohérence. Ceci est un verbe à la 1ʳᵉ personne du singulier. Sujet (“je” ou “moi qui”) introuvable. + + +__conj_xxxas_xxxes__sans_sujet!3__ + [se|s’] ?[en|y|le|la|l’|les]¿ (~[ae]s$) + <<- /conj/ morph(\1, ":2s", ":(?:G|W|M|J|3[sp])") + -1>> =suggVerb(\1, ":3s") # Incohérence. Ceci est un verbe à la 2ᵉ personne du singulier. Sujet (“tu” ou “toi qui”) introuvable. + + [ne|n’] ?[le|la|l’|les|en|me|m’|te|t’|nous|vous|lui|leur|y]¿ (~[ae]s$) ~¬[tT]u + <<- /conj/ morph(\1, ":2s", ":(?:E|G|W|M|J|3[sp])") and not tag_before(\1, "2s") + -1>> =suggVerb(\1, ":3s") # Incohérence. Ceci est un verbe à la 2ᵉ personne du singulier. Sujet (“tu” ou “toi qui”) introuvable. + + [me|m’|te|t’|nous|vous] ?[le|la|l’|les|en|y]¿ (~[ae]s$) ~¬[tT]u + [le|la|l’|les] [lui|leur|en|y] (~[ae]s$) ~¬[tT]u + [lui|leur] en (~[ae]s$) ~¬[tT]u + <<- /conj/ morph(\1, ":2s", ":(?:E|G|W|M|J|3[sp])") and not tag_before(\1, "2s") + -1>> =suggVerb(\1, ":3s") # Incohérence. Ceci est un verbe à la 2ᵉ personne du singulier. Sujet (“tu” ou “toi qui”) introuvable. + + ~[ae]s$ ~¬[tT]u + <<- /conj/ morph(\1, ":2s", ":(?:E|G|W|M|J|3[sp]|N|A|Q)") and not (\1.istitle() and before0(r"\w")) and not tag_before(\1, "2s") + -1>> =suggVerb(\1, ":3s") # Incohérence. Ceci est un verbe à la 2ᵉ personne du singulier. Sujet (“tu” ou “toi qui”) introuvable. + + +__conj_xxxxxs_sans_sujet!3__ + [se|s’] ?[en|y|le|la|l’|les]¿ (~[iudnrtpcï]s$) + <<- /conj/ morph(\1, ":[12]s", ":(?:G|W|M|J|3[sp]|2p|1p)") + -1>> =suggVerb(\1, ":3s") # Incohérence. Le sujet de cette forme verbale est introuvable. + + [ne|n’] ?[le|la|l’|les|en|me|m’|te|t’|nous|vous|lui|leur|y]¿ (~[iudnrtpcï]s$) ~¬(?:[tT]u|[jJ]e) + <<- /conj/ morph(\1, ":[12]s", ":(?:E|G|W|M|J|3[sp]|2p|1p)") + and not tag_before(\1, "1s") and not tag_before(\1, "2s") + -1>> =suggVerb(\1, ":3s") # Incohérence. Le sujet de cette forme verbale est introuvable. + + [me|m’|te|t’|nous|vous] ?[le|la|l’|les|en|y]¿ (~[iudnrtpcï]s$) ~¬(?:[tT]u|[jJ]e) + [le|la|l’|les] [lui|leur|en|y] (~[iudnrtpcï]s$) ~¬(?:[tT]u|[jJ]e) + [lui|leur] en (~[iudnrtpcï]s$) ~¬(?:[tT]u|[jJ]e) + <<- /conj/ morph(\1, ":[12]s", ":(?:E|G|W|M|J|3[sp]|2p|1p)") + and not tag_before(\1, "1s") and not tag_before(\1, "2s") + -1>> =suggVerb(\1, ":3s") # Incohérence. Le sujet de cette forme verbale est introuvable. + + étais ~¬(?:[tT]u|[jJ]e) + <<- /conj/ not (\1.istitle() and before0(r"\w")) and not morph(<1, ":[DA].*:p") + and not tag_before(\1, "1s") and not tag_before(\1, "2s") + -1>> =suggVerb(\1, ":3s") # Incohérence. Le sujet de cette forme verbale est introuvable. + + ~[iudnrtpcï]s$ ~¬(?:[tT]u|[jJ]e) + <<- /conj/ morph(\1, ":[12]s", ":(?:E|G|W|M|J|3[sp]|2p|1p|V0e|N|A|Q)") and not (\1.istitle() and before0(r"\w")) + and not tag_before(\1, "1s") and not tag_before(\1, "2s") + -1>> =suggVerb(\1, ":3s") # Incohérence. Le sujet de cette forme verbale est introuvable. + + +__conj_peux_veux_vaux_équivaux_prévaux_sans_sujet!3__ + [se|s’] ?[en|y|le|la|l’|les]¿ ([peux|veux|vaux|équivaux|prévaux]) + <<- /conj/ -1>> =suggVerb(\1, ":3s") # Incohérence. Le sujet de cette forme verbale est introuvable. + + [ne|n’] ?[le|la|l’|les|en|me|m’|te|t’|nous|vous|lui|leur|y]¿ ([peux|veux|vaux|équivaux|prévaux]) ~¬(?:[tT]u|[jJ]e) + <<- /conj/ not tag_before(\1, "1s") and not tag_before(\1, "2s") + -1>> =suggVerb(\1, ":3s") # Incohérence. Le sujet de cette forme verbale est introuvable. + + [me|m’|te|t’|nous|vous] ?[le|la|l’|les|en|y]¿ ([peux|veux|vaux|équivaux|prévaux]) ~¬(?:[tT]u|[jJ]e) + [le|la|l’|les] [lui|leur|en|y] ([peux|veux|vaux|équivaux|prévaux]) ~¬(?:[tT]u|[jJ]e) + [lui|leur] en ([peux|veux|vaux|équivaux|prévaux]) ~¬(?:[tT]u|[jJ]e) + <<- /conj/ not tag_before(\1, "1s") and not tag_before(\1, "2s") + -1>> =suggVerb(\1, ":3s") # Incohérence. Le sujet de cette forme verbale est introuvable. + + vaux ~¬(?:[tT]u|[jJ]e) + <<- /conj/ not (\1.istitle() and before0(r"\w")) and not tag_before(\1, "1s") and not tag_before(\1, "2s") + and not morph(<1, ":(?:R|D.*:p)") + -1>> =suggVerb(\1, ":3s") # Incohérence. Le sujet de cette forme verbale est introuvable. + + [peux|veux|équivaux|prévaux] ~¬(?:[tT]u|[jJ]e) + <<- /conj/ not (\1.istitle() and before0(r"\w")) and not tag_before(\1, "1s") and not tag_before(\1, "2s") + -1>> =suggVerb(\1, ":3s") # Incohérence. Le sujet de cette forme verbale est introuvable. + TEST: Caroline, quand l’heure viendra, {{décideras}} de la conduite à tenir. TEST: ceux-là, dans tous les cas de figure et dans tous les coups ratés, {{comprenais}} mal pourquoi on leur en voulait. TEST: Lui, quand il y pensait, en {{arrivai}} à chaque fois à la même conclusion. TEST: Elle, ici et dans tous les cas de figure, {{veux}} toujours en faire plus. @@ -4611,20 +4835,17 @@ TEST: ces gens qui vont par monts et par vaux. TEST: pour ne justement pas donner l’impression de s’être trompé. ## Incohérences avec formes verbales 1pl et 2pl sans sujet -__[i]/conj(conj_xxxons_sans_sujet)!3__ - \w+(?:ons|[âîûn]mes)(?! nous) - <<- morphex(\0, ":V.*:1p", ":[EGMNAJ]") and not (\0[0:1].isupper() and before(r"\w")) - and not before0(r"\b(?:[nN]ous(?:-mêmes?|)|(?:[eE]t|[oO]u) moi(?:-même|)|[nN]i (?:moi|nous)),? ") - ->> =suggVerb(@, ":3p") # Incohérence. Ceci est un verbe à la 1ʳᵉ personne du pluriel. Sujet (“nous” ou équivalent) introuvable. -__[i]/conj(conj_xxxez_sans_sujet)!3__ - \w+(?:ez|[âîûn]tes)(?! vous) - <<- morphex(\0, ":V.*:2p", ":[EGMNAJ]") and not (\0[0:1].isupper() and before(r"\w")) - and not before0(r"\b(?:[vV]ous(?:-mêmes?|)|(?:[eE]t|[oO]u) toi(?:-même|)|[tT]oi(?:-même|) et|[nN]i (?:vous|toi)),? ") - ->> _ # Incohérence. Ceci est un verbe à la 2ᵉ personne du pluriel. Sujet (“vous” ou équivalent) introuvable. +__conj_xxxons_sans_sujet!3__ + @:1p¬:[EGMNAJ] ~¬[nN]ous + <<- /conj/ not (\1.istitle() and before0(r"\w")) and not tag_before(\1, "1p") -1>> =suggVerb(\1, ":3p") # Ceci est un verbe à la 1ʳᵉ personne du pluriel. Sujet (“nous” ou équivalent) introuvable. + +__conj_xxxez_sans_sujet!3__ + @:2p¬:[EGMNAJ] ~¬[vV]ous + <<- /conj/ not (\1.istitle() and before0(r"\w")) and not tag_before(\2, "2p") -1>> =suggVerb(\1, ":3p") # Ceci est un verbe à la 2ᵉ personne du pluriel. Sujet (“vous” ou équivalent) introuvable. TEST: les hommes et les femmes, qui sans un bruit, sans une parole amère, {{continuerons}} leur tâche n’en seront pas plus récompensés. TEST: il était dit que cette femme et son frère {{promènerez}} leur chien à cette heure de la journée. TEST: cet homme et cette femme {{pouvez}} y parvenir avec de la persévérance TEST: Comme on lui disait que vous-même aviez déjà consulté le notaire @@ -4636,19 +4857,33 @@ !! !! -!!!! Locutions invariables +!!!! Locutions invariables !! !! !! -## plus que prévu / mois que prévu -__[i]/sgpl(sgpl_que_prévu1)__ (plus|moins|autant) +que (prévu(?:es?|s)) @@0,$ <<- -2>> prévu # Invariable. Implicitement, \1 que ce qui était prévu. -__[i]/sgpl(sgpl_que_prévu2)__ (plus|moins|aussi) +({w_2}) +que (prévu(?:es?|s)) @@0,w,$ <<- -3>> prévu # Invariable. Implicitement, \1 \2 que ce qui était prévu. -__[i]/sgpl(sgpl_que_prévu3)__ (plus|moins|autant) +d(?:e |’)({w_2}) +que (prévu(?:es?|s)) @@0,w,$ <<- -3>> prévu # Invariable. Implicitement, \1 \2 que ce qui était prévu. -__[i]/sgpl(sgpl_comme_adj)__ comme ((annoncé|convenu|prévu)(?:es?|s)) @@6,6 <<- -1>> \2 # Invariable. Implicitement, comme ce qui était \2. +__locutions_invariables__ + [plus|moins|autant] que [prévue|prévus|prévues] + <<- /sgpl/ -3>> prévu # Invariable. Implicitement, \1 que ce qui était prévu. + + [plus|moins|aussi] ** que [prévue|prévus|prévues] + <<- /sgpl/ -4>> prévu # Invariable. Implicitement, \1 \2 que ce qui était prévu. + + [plus|moins|autant] [de|d’] ** que [prévue|prévus|prévues] + <<- /sgpl/ -5>> prévu # Invariable. Implicitement, \1 \2 \3 que ce qui était prévu. + + comme [annoncés|annoncée|annoncées] + <<- /sgpl/ -2>> annoncé # Invariable. Implicitement, comme ce qui était annoncé. + + comme [convenus|convenue|convenues] + <<- /sgpl/ -2>> convenu # Invariable. Implicitement, comme ce qui était convenu. + + comme [prévue|prévus|prévues] + <<- /sgpl/ -2>> prévu # Invariable. Implicitement, comme ce qui était prévu. + TEST: il y en a autant que {{prévus}}. TEST: elles sont plus nombreuses plus que {{prévues}} TEST: il y a moins de bouffe que {{prévue}} TEST: comme {{annoncés}}, ils sont arrivés @@ -4660,101 +4895,133 @@ !! !! -!!!! Tout, tous, toute, toutes +!!!! Tout, tous, toute, toutes !! !! !! -__[i](p_fais_les_tous)__ - fai(?:tes|sons|s)-(?:les|[nv]ous) (tou(?:te|)s) @@$ <<- ~1>> * -__[i](p_tout_débuts_petits)__ - (tout) (?:débuts|petits) @@0 <<- before(r"\b(aux|[ldmtsc]es|[nv]os|leurs) +$") ~1>> * -__[i](p_les_tout_xxx)__ - (?:[ldmtsc]es|[nv]os|leurs|aux) (tout) ({w_2}) @@w,$ - <<- morph(\2, ":[AQ].*:[pi]", False) ~1>> * +__purge_tout_tous_toutes__ + [fais-les|fais-nous] [tous|toutes] + [faisons-les|faisons-nous|faisons-vous] [tous|toutes] + [faites-les|faites-nous|faites-vous] [tous|toutes] + <<- ~2>> * + + [laisse-les|laisse-nous] [tous|toutes] + [laissons-les|laissons-nous|laissons-vous] [tous|toutes] + [laissez-les|laissez-nous|laissez-vous] [tous|toutes] + <<- ~2>> * + + [les|des|mes|tes|ses|ces|nos|vos|leurs|aux] tout [débuts|petits] + [les|des|mes|tes|ses|ces|nos|vos|leurs|aux] tout @:A.*:[pi] + <<- ~2>> * -__[i]/gn(gn_tous_deux)__ - (tout) deux @@0 <<- isStart() -1>> tous # Locution pronominale : « tous deux ».|https://fr.wiktionary.org/wiki/tous_deux +__tout_det__ + [|,] tout [deux|trois] + <<- /gn/ -2>> tous # Locution pronominale : « tous deux ».|https://fr.wiktionary.org/wiki/tous_deux + + tout [mes|tes|ses|ces|nos|vos|leurs|ceux|celles] + <<- /gn/ not morph(<1, ">(?:d[eu]|avant|après|malgré)/") -1>> tous # Erreur d’accord probable avec « \2 ». + + tout les @:¬:(?:3s|Oo) + <<- /gn/ not morph(<1, ">(?:d[eu]|avant|après|malgré)/") -1>> tous # Erreur d’accord probable avec « les \3 ». TEST: {{Tout}} deux sont partis les premiers. - - -__[i]/gn(gn_tous_déterminant_pluriel)__ - tout(?= [cmts]es\b) - <<- not before(r"(?i)\b(?:d[eu]|avant|après|sur|malgré) +$") ->> tous # Erreur d’accord probable. - TEST: {{Tout}} mes hommes sont venus. -TEST: Malgré tout ces hommes sont quand même revenus. - - -__[i]/gn(gn_tous_les)__ - (tout) les ({w_2}) @@0,$ - <<- not before(r"(?i)\b(?:d[eu]|avant|après|sur|malgré) +$") and not morph(\2, ":(?:3s|Oo)", False) - -1>> tous # Erreur d’accord probable avec « les \2 ». - TEST: {{Tout}} les hommes sont dingues. - - -__[i]/gn(gn_tous_ceux)__ - tout(?= ceux\b) - <<- not before(r"(?i)\b(?:d[eu]|avant|après|sur|malgré) +$") ->> tous # Erreur d’accord probable avec « ceux ». - TEST: Donne à manger à {{tout}} ceux qui sont là. TEST: Revenus de tout ceux qui sont partis ont perdu la foi. +TEST: car malgré tout ceux qui persistent obtiennent parfois justice. +TEST: je ne connais pas du tout ceux dont tu parles. +TEST: Malgré tout ces hommes sont quand même revenus. +TEST: Les tout premiers hommes. +TEST: Les tout petits ne sont pas des légumes. -__[i]/gn(gn_toutes_déterminant_fem_plur)__ toute(?= (?:celles|[clmtsd]es)\b) <<- ->> toutes # Erreur d’accord probable. -__[i]/gn(gn_tout_ce)__ toute(?= cet?\b) <<- ->> tout # Erreur d’accord probable. -__[i]/gn(gn_tout_mon)__ toute(?= mon [bcdfgjklmnpqrstvwxz]) <<- ->> tout # Erreur d’accord probable. +__toute_det__ + toute [celles|les|des|mes|tes|ses|ces] + <<- /gn/ -1>> toutes # Erreur d’accord probable avec “\2”. + + toute [ce|cet] + <<- /gn/ -1>> tout # Erreur d’accord probable avec “\2”. + + toute mon ~^[bcdfgjklmnpqrstvwxz] + <<- /gn/ -1>> tout # Erreur d’accord probable avec “\2”. TEST: {{Toute}} celles qui viendront… TEST: et {{toute}} ce barouf ne nous a apporté que des ennuis. 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) - -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) - -1>> tous # Erreur d’accord probable. « \2 » est masculin. - <<- __also__ and hasFemForm(\2) -2>> =suggFemPlur(@, True) # Erreur d’accord probable. « \1 » est féminin. +__tous_det_nom__ + [|,] tous [des|mes|tes|ses|ces] @:[NA].*:f¬:[me] + [|,] tous [les] @:[NA].*:f¬:(?:3p|[me]) + <<- /gn/ -2>> toutes # Erreur d’accord probable : « \4 » est féminin. + <<- /gn/ __also__ and hasFemForm(\4) -4>> =suggMasPlur(\4, True) # Erreur d’accord probable : « \2 » est masculin. + + tous [des|mes|tes|ses|ces] @:[NA].*:f¬:[me] + tous [les] @:[NA].*:f¬:(?:3p|[me]) + <<- /gn/ morph(<1, ":", ":(?:R|[123][sp]|Q)|>(?:[nv]ous|eux)/") -1>> toutes # Erreur d’accord probable : « \3 » est féminin. + <<- /gn/ __also__ and hasFemForm(\3) -3>> =suggMasPlur(\3, True) # Erreur d’accord probable : « \1 » est masculin. TEST: {{tous}} ces {{idiotes}} +TEST: indubitablement {{tous}} des {{privilégiées}} + + +__toutes_det_nom__ + [|,] toutes [des|mes|tes|ses|ces] @:[NA].*:m¬:[fe] + [|,] toutes [les] @:[NA].*:m¬:(?:3p|[fe]) + <<- /gn/ -2>> tous # Erreur d’accord probable : « \4 » est masculin. + <<- /gn/ __also__ and hasFemForm(\4) -4>> =suggFemPlur(\4, True) # Erreur d’accord probable : « \2 » est féminin. + + toutes [des|mes|tes|ses|ces] @:[NA].*:m¬:[fe] + toutes [les] @:[NA].*:m¬:(?:3p|[fe]) + <<- /gn/ morph(<1, ":", ":(?:R|[123][sp]|Q)|>(?:[nv]ous|eux)/") -1>> tous # Erreur d’accord probable : « \3 » est masculin. + <<- /gn/ __also__ and hasFemForm(\3) -3>> =suggFemPlur(\3, True) # Erreur d’accord probable : « \1 » est féminin. + 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) - -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) - -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) - -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) - -1>> =suggFemPlur(@, True) # “\1” devrait être au féminin pluriel. +TEST: vraiment {{toutes}} des {{costauds}} + + +__tout_nom__ + [|,] tout @:N.*:[fp]¬:(?:A|W|G|M|Y|[me]:[is]|3s) + de tout @:N.*:[fp]¬:(?:A|W|G|M|Y|[me]:[is]|3s) + <<- /gn/ -3>> =suggMasSing(\3, True) # Accord avec “tout” : “\3” devrait être au masculin singulier. + + tout @:N.*:[fp]¬:(?:A|W|G|M|Y|[me]:[is]|3s) + <<- /gn/ morph(<1, ":R", ":D.*:p") -2>> =suggMasSing(\2, True) # Accord avec “tout” : “\2” devrait être au masculin singulier. + +__toute_nom__ + [|,] toute @:[NA].*:[mp]¬:(?:W|G|M|[fe]:[is]) + de toute @:[NA].*:[mp]¬:(?:W|G|M|Y|[fe]:[is]) + <<- /gn/ -3>> =suggFemSing(\3, True) # Accord avec “toute” : “\3” devrait être au féminin singulie + + toute @:[NA].*:[mp]¬:(?:W|G|M|Y|[fe]:[is]) + <<- /gn/ morph(<1, ":R") -2>> =suggFemSing(\2, True) # Accord avec “toute” : “\2” devrait être au féminin singulier. + +__tous_nom__ + [|,] tous @:[NA].*:[fs]¬:(?:W|G|M|[me]:[ip]) + de tous @:[NA].*:[fs]¬:(?:W|G|M|Y|[me]:[ip]) + <<- /gn/ -3>> =suggMasPlur(\3, True) # Accord avec “tous” : “\3” devrait être au masculin pluriel. + + tous @:[NA].*:[fs]¬:(?:W|G|M|Y|[me]:[ip]) + <<- /gn/ morph(<1, ":R") -2>> =suggMasPlur(\2, True) # Accord avec “tous” : “\2” devrait être au masculin pluriel. + +__toutes_nom__ + [|,] toutes @:[NA].*:[ms]¬:(?:W|G|M|[fe]:[ip]) + de toutes @:[NA].*:[ms]¬:(?:W|G|M|Y|[fe]:[ip]) + <<- /gn/ -3>> =suggFemPlur(\3, True) # Accord avec “toutes” : “\3” devrait être au féminin pluriel. + + toutes @:[NA].*:[ms]¬:(?:W|G|M|Y|[fe]:[ip]) + <<- /gn/ morph(<1, ":R") -2>> =suggFemPlur(\2, True) # Accord avec “toutes” : “\2” devrait être au féminin pluriel. TEST: Tout {{hommes}} TEST: De tous {{âge}} ! -TEST: avec toutes {{femme}} ->> femmes -TEST: sur toutes {{armure}} ->> armures +TEST: avec toutes {{femme}} ->> femmes +TEST: sur toutes {{armure}} ->> armures TEST: Toute {{époux}} doit faire preuve de bienveillance TEST: Il se souvient de toute mon histoire. TEST: Tout les sépare. TEST: les tout débuts du mouvement ouvrier TEST: vos tout débuts furent difficiles @@ -4763,68 +5030,160 @@ !! !! -!!!! Adverbes de négation +!!!! Adverbes de négation !! !! !! -__[i]/neg(ne_manquant1)__ - (?:je|tu|ils?|on|elles?) ([bcdfgjklmnpqrstvwxz][\w-]*) (pas|rien|jamais|guère) @@w,$ - <<- morph(\1, ":[123][sp]", False) and not (re.search("(?i)^(?:jamais|rien)$", \2) and before(r"\b(?:que?|plus|moins) ")) - -1>> ne \1 # Ne … \2 : il manque l’adverbe de négation. - -__[i]/neg(ne_manquant2)__ - (?:je|tu|ils?|on|elles?) ([aeéiouœ][\w-]*) (pas|rien|jamais|guère) @@w,$ - <<- morph(\1, ":[123][sp]", False) and not (re.search("(?i)^(?:jamais|rien)$", \2) and before(r"\b(?:que?|plus|moins) ")) - -1>> n’\1 # Ne … \2 : il manque l’adverbe de négation. - -__[i]/neg(ne_manquant3)__ - (?:je|tu|ils?|on|elles?) ([mts](?:e +|’(?:en|y) +|’)|[vn]ous +|l(?:e +|a +|eur +|ui +|l’))({w_1}) (pas|rien|jamais|guère) @@*,w,$ - <<- morph(\2, ":[123][sp]", False) and not (re.search("(?i)^(?:jamais|rien)$", \3) and before(r"\b(?:que?|plus|moins) ")) - -1>> ne \1 # Ne … \3 : il manque l’adverbe de négation. - -__[i]/neg(ne_manquant4)__ - (?:je|tu|ils?|on|elles?) (y|en) ({w_1}) (pas|rien|jamais|guère) @@w,w,$ - <<- morph(\2, ":[123][sp]", False) and not (re.search("(?i)^(?:jamais|rien)$", \3) and before(r"\b(?:que?|plus|moins) ")) - -1>> n’\1 # Ne … \3 : il manque l’adverbe de négation. +__ne_manquant__ + [|,] je [le|la|l’|les|me|m’|te|t’|se|s’|nous|vous|lui|leur] @:1s¬:(?:Oo|X) [pas|rien|jamais|guère|point] + [|,] tu [le|la|l’|les|me|m’|te|t’|se|s’|nous|vous|lui|leur] @:2s¬:(?:Oo|X) [pas|rien|jamais|guère|point] + [|,] [il|elle|on] [le|la|l’|les|me|m’|te|t’|se|s’|nous|vous|lui|leur] @:3s¬:(?:Oo|X) [pas|rien|jamais|guère|point] + [|,] nous [le|la|l’|les|me|m’|te|t’|se|s’|nous|vous|lui|leur] @:1p¬:(?:Oo|X) [pas|rien|jamais|guère|point] + [|,] vous [le|la|l’|les|me|m’|te|t’|se|s’|nous|vous|lui|leur] @:2p¬:(?:Oo|X) [pas|rien|jamais|guère|point] + [|,] [ils|elles] [le|la|l’|les|me|m’|te|t’|se|s’|nous|vous|lui|leur] @:3p¬:(?:Oo|X) [pas|rien|jamais|guère|point] + <<- /neg/ -3>> ne \3 # Ne … \5 : il manque l’adverbe de négation. + + [|,] [je|j’] [en|y] @:1s¬:(?:Oo|X) [pas|rien|jamais|guère|point] + [|,] tu [en|y] @:2s¬:(?:Oo|X) [pas|rien|jamais|guère|point] + [|,] [il|elle|on] [en|y] @:3s¬:(?:Oo|X) [pas|rien|jamais|guère|point] + [|,] nous [en|y] @:1p¬:(?:Oo|X) [pas|rien|jamais|guère|point] + [|,] vous [en|y] @:2p¬:(?:Oo|X) [pas|rien|jamais|guère|point] + [|,] [ils|elles] [en|y] @:3p¬:(?:Oo|X) [pas|rien|jamais|guère|point] + <<- /neg/ -3>> n’\3 # Ne … \5 : il manque l’adverbe de négation. + + [|,] je [me|m’|te|t’|se|s’|nous|vous] [le|la|l’|les|en|y] @:1s¬:(?:Oo|X) [pas|rien|jamais|guère|point] + [|,] tu [me|m’|te|t’|se|s’|nous|vous] [le|la|l’|les|en|y] @:2s¬:(?:Oo|X) [pas|rien|jamais|guère|point] + [|,] [il|elle|on] [me|m’|te|t’|se|s’|nous|vous] [le|la|l’|les|en|y] @:3s¬:(?:Oo|X) [pas|rien|jamais|guère|point] + [|,] nous [me|m’|te|t’|se|s’|nous|vous] [le|la|l’|les|en|y] @:1p¬:(?:Oo|X) [pas|rien|jamais|guère|point] + [|,] vous [me|m’|te|t’|se|s’|nous|vous] [le|la|l’|les|en|y] @:2p¬:(?:Oo|X) [pas|rien|jamais|guère|point] + [|,] [ils|elles] [me|m’|te|t’|se|s’|nous|vous] [le|la|l’|les|en|y] @:3p¬:(?:Oo|X) [pas|rien|jamais|guère|point] + [|,] je [le|la|l’|les] [lui|leur|en|y] @:1s¬:(?:Oo|X) [pas|rien|jamais|guère|point] + [|,] tu [le|la|l’|les] [lui|leur|en|y] @:2s¬:(?:Oo|X) [pas|rien|jamais|guère|point] + [|,] [il|elle|on] [le|la|l’|les] [lui|leur|en|y] @:3s¬:(?:Oo|X) [pas|rien|jamais|guère|point] + [|,] nous [le|la|l’|les] [lui|leur|en|y] @:1p¬:(?:Oo|X) [pas|rien|jamais|guère|point] + [|,] vous [le|la|l’|les] [lui|leur|en|y] @:2p¬:(?:Oo|X) [pas|rien|jamais|guère|point] + [|,] [ils|elles] [le|la|l’|les] [lui|leur|en|y] @:3p¬:(?:Oo|X) [pas|rien|jamais|guère|point] + [|,] je [lui|leur] en @:1s¬:(?:Oo|X) [pas|rien|jamais|guère|point] + [|,] tu [lui|leur] en @:2s¬:(?:Oo|X) [pas|rien|jamais|guère|point] + [|,] [il|elle|on] [lui|leur] en @:3s¬:(?:Oo|X) [pas|rien|jamais|guère|point] + [|,] nous [lui|leur] en @:1p¬:(?:Oo|X) [pas|rien|jamais|guère|point] + [|,] vous [lui|leur] en @:2p¬:(?:Oo|X) [pas|rien|jamais|guère|point] + [|,] [ils|elles] [lui|leur] en @:3p¬:(?:Oo|X) [pas|rien|jamais|guère|point] + <<- /neg/ -3>> ne \3 # Ne … \6 : il manque l’adverbe de négation. + + [|,] [je|j’] @>[aeéiouœ].*:1s¬:(?:Oo|X) [pas|rien|jamais|guère|point] + [|,] tu @>[aeéiouœ].*:2s¬:(?:Oo|X) [pas|rien|jamais|guère|point] + [|,] [il|elle|on] @>[aeéiouœ].*:3s¬:(?:Oo|X) [pas|rien|jamais|guère|point] + [|,] nous @>[aeéiouœ].*:1p¬:(?:Oo|X) [pas|rien|jamais|guère|point] + [|,] vous @>[aeéiouœ].*:2p¬:(?:Oo|X) [pas|rien|jamais|guère|point] + [|,] [ils|elles] @>[aeéiouœ].*:3p¬:(?:Oo|X) [pas|rien|jamais|guère|point] + <<- /neg/ -3>> n’\3 # Ne … \4 : il manque l’adverbe de négation. + + [|,] je @>[bcdfgjklmnpqrstvwxz].*:1s¬:(?:Oo|X) [pas|rien|jamais|guère|point] + [|,] tu @>[bcdfgjklmnpqrstvwxz].*:2s¬:(?:Oo|X) [pas|rien|jamais|guère|point] + [|,] [il|elle|on] @>[bcdfgjklmnpqrstvwxz].*:3s¬:(?:Oo|X) [pas|rien|jamais|guère|point] + [|,] nous @>[bcdfgjklmnpqrstvwxz].*:1p¬:(?:Oo|X) [pas|rien|jamais|guère|point] + [|,] vous @>[bcdfgjklmnpqrstvwxz].*:2p¬:(?:Oo|X) [pas|rien|jamais|guère|point] + [|,] [ils|elles] @>[bcdfgjklmnpqrstvwxz].*:3p¬:(?:Oo|X) [pas|rien|jamais|guère|point] + <<- /neg/ -3>> ne \3 # Ne … \4 : il manque l’adverbe de négation. TEST: __neg__ On {{a}} pas compris. TEST: __neg__ Il {{part}} pas encore. -TEST: __neg__ On {{vous }}a pas compris. +TEST: __neg__ On {{vous}} a pas compris. TEST: __neg__ On {{en}} a pas. TEST: __neg__ Il {{y}} a jamais d’eau. - +TEST: __neg__ je {{deviendrai}} pas hargneux. +TEST: __neg__ il {{le}} lui donne pas souvent. !! !! -!!!! Infinitif +!!!! Infinitif !! !! !! -__[i](p_ne_plus_pas_jamais_beaucoup_trop_rien)__ - 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) - -1>> =suggVerbInfi(@) # Le verbe devrait être à l’infinitif. - -TEST: ne jamais {{cédé}} +__infi_ne_pas_jamais_etc__ + ne ?presque¿ [pas|rien|guère|point] ?[le|la|l’|les|leur|lui|nous|vous|me|m’|te|t’|se|s’|en|y]¿ (@:[VNA]¬:(?:Y|W|X|O[ow])|>que?/) + ne ?presque¿ [pas|rien|guère|point] [trop|beaucoup] ?[le|la|l’|les|leur|lui|nous|vous|me|m’|te|t’|se|s’|en|y]¿ (@:[VNA]¬:(?:Y|W|X|O[ow])|>que?/) + ne ?presque¿ [pas|rien|guère|point] non plus ?[le|la|l’|les|leur|lui|nous|vous|me|m’|te|t’|se|s’|en|y]¿ (@:[VNA]¬:(?:Y|W|X|O[ow])|>que?/) + ne ?presque¿ jamais ?[rien|plus|trop|beaucoup]¿ ?[le|la|l’|les|leur|lui|nous|vous|me|m’|te|t’|se|s’|en|y]¿ (@:[VNA]¬:(?:Y|W|X|O[ow])|>que?/) + ne ?presque¿ jamais ?[rien|plus]¿ non plus ?[le|la|l’|les|leur|lui|nous|vous|me|m’|te|t’|se|s’|en|y]¿ (@:[VNA]¬:(?:Y|W|X|O[ow])|>que?/) + ne ?presque¿ plus ?[jamais|rien|guère|trop|beaucoup]¿ ?[le|la|l’|les|leur|lui|nous|vous|me|m’|te|t’|se|s’|en|y]¿ (@:[VNA]¬:(?:Y|W|X|O[ow])|>que?/) + ne ?presque¿ plus ?[jamais|rien|guère]¿ non plus ?[le|la|l’|les|leur|lui|nous|vous|me|m’|te|t’|se|s’|en|y]¿ (@:[VNA]¬:(?:Y|W|X|O[ow])|>que?/) + ne ?presque¿ [pas|rien|guère|point] [m’|t’|s’|nous|vous|les|lui|leur|l’] [en|y] (@:[VNA]¬:(?:Y|W|X|O[ow])|>que?/) + ne ?presque¿ [pas|rien|guère|point] [trop|beaucoup] [m’|t’|s’|nous|vous|les|lui|leur|l’] [en|y] (@:[VNA]¬:(?:Y|W|X|O[ow])|>que?/) + ne ?presque¿ [pas|rien|guère|point] non plus [m’|t’|s’|nous|vous|les|lui|leur|l’] [en|y] (@:[VNA]¬:(?:Y|W|X|O[ow])|>que?/) + ne ?presque¿ jamais ?[rien|plus|trop|beaucoup]¿ [m’|t’|s’|nous|vous|les|lui|leur|l’] [en|y] (@:[VNA]¬:(?:Y|W|X|O[ow])|>que?/) + ne ?presque¿ jamais ?[rien|plus]¿ non plus [m’|t’|s’|nous|vous|les|lui|leur|l’] [en|y] (@:[VNA]¬:(?:Y|W|X|O[ow])|>que?/) + ne ?presque¿ plus ?[jamais|rien|guère|trop|beaucoup]¿ [m’|t’|s’|nous|vous|les|lui|leur|l’] [en|y] (@:[VNA]¬:(?:Y|W|X|O[ow])|>que?/) + ne ?presque¿ plus ?[jamais|rien|guère]¿ non plus [m’|t’|s’|nous|vous|les|lui|leur|l’] [en|y] (@:[VNA]¬:(?:Y|W|X|O[ow])|>que?/) + ne ?presque¿ [pas|rien|guère|point] [me|te|nous|vous] [le|la|les] (@:[VNA]¬:(?:Y|W|X|O[ow])|>que?/) + ne ?presque¿ [pas|rien|guère|point] [trop|beaucoup] [me|te|nous|vous] [le|la|les] (@:[VNA]¬:(?:Y|W|X|O[ow])|>que?/) + ne ?presque¿ [pas|rien|guère|point] non plus [me|te|nous|vous] [le|la|les] (@:[VNA]¬:(?:Y|W|X|O[ow])|>que?/) + ne ?presque¿ jamais ?[rien|plus|trop|beaucoup]¿ [me|te|nous|vous] [le|la|les] (@:[VNA]¬:(?:Y|W|X|O[ow])|>que?/) + ne ?presque¿ jamais ?[rien|plus]¿ non plus [me|te|nous|vous] [le|la|les] (@:[VNA]¬:(?:Y|W|X|O[ow])|>que?/) + ne ?presque¿ plus ?[jamais|rien|guère|trop|beaucoup]¿ [me|te|nous|vous] [le|la|les] (@:[VNA]¬:(?:Y|W|X|O[ow])|>que?/) + ne ?presque¿ plus ?[jamais|rien|guère]¿ non plus [me|te|nous|vous] [le|la|les] (@:[VNA]¬:(?:Y|W|X|O[ow])|>que?/) + ne ?presque¿ [pas|rien|guère|point] [le|la|les] [lui|leur] (@:[VNA]¬:(?:Y|W|X|O[ow])|>que?/) + ne ?presque¿ [pas|rien|guère|point] [trop|beaucoup] [le|la|les] [lui|leur] (@:[VNA]¬:(?:Y|W|X|O[ow])|>que?/) + ne ?presque¿ [pas|rien|guère|point] non plus [le|la|les] [lui|leur] (@:[VNA]¬:(?:Y|W|X|O[ow])|>que?/) + ne ?presque¿ jamais ?[rien|plus|trop|beaucoup]¿ [le|la|les] [lui|leur] (@:[VNA]¬:(?:Y|W|X|O[ow])|>que?/) + ne ?presque¿ jamais ?[rien|plus]¿ non plus [le|la|les] [lui|leur] (@:[VNA]¬:(?:Y|W|X|O[ow])|>que?/) + ne ?presque¿ plus ?[jamais|rien|guère|trop|beaucoup]¿ [le|la|les] [lui|leur] (@:[VNA]¬:(?:Y|W|X|O[ow])|>que?/) + ne ?presque¿ plus ?[jamais|rien|guère]¿ non plus [le|la|les] [lui|leur] (@:[VNA]¬:(?:Y|W|X|O[ow])|>que?/) + <<- /infi/ -1>> =suggVerbInfi(\1) # Après “ne pas”, “ne jamais”, “ne plus”, “ne rien”… le verbe devrait être à l’infinitif. + + n’ [en|y] ?presque¿ [pas|rien|guère|point] (@:[VNA]¬:(?:Y|W|X)) + n’ [en|y] ?presque¿ [pas|rien|guère|point] [trop|beaucoup] (@:[VNA]¬:(?:Y|W|X)) + n’ [en|y] ?presque¿ [pas|rien|guère|point] non plus (@:[VNA]¬:(?:Y|W|X)) + n’ [en|y] ?presque¿ jamais ?[rien|plus|trop|beaucoup]¿ (@:[VNA]¬:(?:Y|W|X)) + n’ [en|y] ?presque¿ jamais ?[rien|plus]¿ non plus (@:[VNA]¬:(?:Y|W|X)) + n’ [en|y] ?presque¿ plus ?[jamais|rien|guère|trop|beaucoup]¿ (@:[VNA]¬:(?:Y|W|X)) + n’ [en|y] ?presque¿ plus ?[jamais|rien|guère]¿ non plus (@:[VNA]¬:(?:Y|W|X)) + n’ [en|y] ?presque¿ [pas|rien|guère|point] (@:[VNA]¬:(?:Y|W|X)) + n’ [en|y] ?presque¿ [pas|rien|guère|point] [trop|beaucoup] (@:[VNA]¬:(?:Y|W|X)) + n’ [en|y] ?presque¿ [pas|rien|guère|point] non plus (@:[VNA]¬:(?:Y|W|X)) + n’ [en|y] ?presque¿ jamais ?[rien|plus|trop|beaucoup]¿ (@:[VNA]¬:(?:Y|W|X)) + n’ [en|y] ?presque¿ jamais ?[rien|plus]¿ non plus (@:[VNA]¬:(?:Y|W|X)) + n’ [en|y] ?presque¿ plus ?[jamais|rien|guère|trop|beaucoup]¿ (@:[VNA]¬:(?:Y|W|X)) + n’ [en|y] ?presque¿ plus ?[jamais|rien|guère]¿ non plus (@:[VNA]¬:(?:Y|W|X)) + n’ [en|y] ?presque¿ [pas|rien|guère|point] (@:[VNA]¬:(?:Y|W|X)) + n’ [en|y] ?presque¿ [pas|rien|guère|point] [trop|beaucoup] (@:[VNA]¬:(?:Y|W|X)) + n’ [en|y] ?presque¿ [pas|rien|guère|point] non plus (@:[VNA]¬:(?:Y|W|X)) + n’ [en|y] ?presque¿ jamais ?[rien|plus|trop|beaucoup]¿ (@:[VNA]¬:(?:Y|W|X)) + n’ [en|y] ?presque¿ jamais ?[rien|plus]¿ non plus (@:[VNA]¬:(?:Y|W|X)) + n’ [en|y] ?presque¿ plus ?[jamais|rien|guère|trop|beaucoup]¿ (@:[VNA]¬:(?:Y|W|X)) + n’ [en|y] ?presque¿ plus ?[jamais|rien|guère]¿ non plus (@:[VNA]¬:(?:Y|W|X)) + n’ [en|y] ?presque¿ [pas|rien|guère|point] (@:[VNA]¬:(?:Y|W|X)) + n’ [en|y] ?presque¿ [pas|rien|guère|point] [trop|beaucoup] (@:[VNA]¬:(?:Y|W|X)) + n’ [en|y] ?presque¿ [pas|rien|guère|point] non plus (@:[VNA]¬:(?:Y|W|X)) + n’ [en|y] ?presque¿ jamais ?[rien|plus|trop|beaucoup]¿ (@:[VNA]¬:(?:Y|W|X)) + n’ [en|y] ?presque¿ jamais ?[rien|plus]¿ non plus (@:[VNA]¬:(?:Y|W|X)) + n’ [en|y] ?presque¿ plus ?[jamais|rien|guère|trop|beaucoup]¿ (@:[VNA]¬:(?:Y|W|X)) + n’ [en|y] ?presque¿ plus ?[jamais|rien|guère]¿ non plus (@:[VNA]¬:(?:Y|W|X)) + <<- /infi/ -1>> =suggVerbInfi(\1) # Après “ne pas”, “ne jamais”, “ne plus”, “ne rien”… le verbe devrait être à l’infinitif. + + ne [pas|jamais|plus|rien|guère|point] [beaucoup|trop] + <<- ~3>> * + +TEST: ne jamais les {{cédé}} +TEST: ne point nous {{donné}} TEST: ne rien {{finit}} TEST: ne jamais plus s’y {{frottait}} TEST: ne plus guère y {{pensée}} TEST: ne pas les {{contrariés}} TEST: Ne rien m’en {{dit}} TEST: Ne jamais lui {{donnait}} sa chance. +TEST: Ne jamais les leur {{montré}} TEST: Il a décidé de ne plus {{mangés}} avec nous. TEST: ne plus {{mangez}} fait maigrir TEST: ne plus {{mangées}} fait maigrir TEST: ne pas {{allé}} +TEST: ne jamais plus me les {{montrés}} TEST: Ne jamais {{mangez}} de viande ! TEST: J’espère ne pas te déranger TEST: Ne pas te le donner, ce serait une insulte. TEST: ne jamais vraiment évoquer le sujet TEST: déterminés à ne pas se laisser récupérer @@ -4831,308 +5190,1177 @@ TEST: de ne pas en élire du tout TEST: Mais gare à ne pas non plus trop surestimer la menace TEST: ne jamais beaucoup bosser, c’est sa devise. -__[i]/imp(imp_infinitif_erroné)__ - n(?:e +|’)({w_2}er) +(?:pas|jamais) @@w - <<- morph(\1, ":V1.*:Y", False) and isStart() -1>> =suggVerbTense(\1, ":E", ":2p") # Confusion probable : “\1” est un verbe à l’infinitif. Si vous vouliez utiliser l’impératif, écrivez : +__imp_ne_infinitif_negadv__ + [|,] [ne|n’] @:V1.*:Y [pas|plus|jamais] + <<- /imp/ -3>> =suggVerbTense(\3, ":E", ":2p") # Confusion probable : “\1” est un verbe à l’infinitif. Si vous vouliez utiliser l’impératif, écrivez : -TEST: Non, ne {{manger}} pas ça. +TEST: Non, ne {{manger}} pas ça. ->> mangez TEST: Ne {{donner}} jamais à manger ces saloperies au chat. ->> donnez !!! !!! -!!! Processeur: épuration des adverbes, locutions adverbiales, interjections et expressions usuelles +!!! Processeur: épuration des adverbes, locutions adverbiales, interjections et expressions usuelles !! !!! !!! -# Dates -__[s](p_date)__ - (?:[dD]epuis le|[lL]e|[dD]u|[aA]u|[jJ]usqu au|[àÀ] compter du) (?:1(?:er|ᵉʳ)|\d\d?) (?:janvier|février|mars|avril|mai|juin|juillet|ao[ûu]t|septembre|octobre|novembre|décembre|vendémiaire|brumaire|frimaire|nivôse|pluviôse|ventôse|germinal|floréal|prairial|messidor|thermidor|fructidor)(?: \d+| dernier| prochain|) <<- ~>> * -__[i](p_en_l_an_de_grâce_année)__ - en l’an (?:de grâce |)\d+ <<- ~>> * -__[s](p_en_de_mois_année)__ - (?:[eE]n +|[dD](?:e +|’))(?:janvier|février|mars|avril|mai|juin|juillet|ao[ûu]t|septembre|octobre|novembre|décembre|vendémiaire|brumaire|frimaire|nivôse|pluviôse|ventôse|germinal|floréal|prairial|messidor|thermidor|fructidor) +\d{2,4} <<- ~>> * -__[i](p_en_année)__ - en \d\d+ <<- not morph(word(1), ":[AN].*:[pi]", False, False) ~>> * -__[i](p_de_année)__ - (de \d\d+) ({w_2}) @@0,$ <<- morph(\2, ":A.*:s", False) ~1>> * -__[s](p_à_la_mi_mois)__ - [àÀ] la mi-(?:janvier|février|mars|avril|mai|juin|juillet|ao[ûu]t|septembre|octobre|novembre|décembre|vendémiaire|brumaire|frimaire|nivôse|pluviôse|ventôse|germinal|floréal|prairial|messidor|thermidor|fructidor)(?:\d{2,4}|) <<- ~>> * -__[i](p_à_l_été_automne_hiver)__ - à l’(?:été|automne|hiver) \d{2,4} <<- ~>> * -__[i](p_au_printemps)__ - au printemps \d{2,4} <<- ~>> * +__purge_dates__ + depuis le [1er|1ᵉʳ|~\d\d?] {mois} ?[dernier|prochain|~\d{2,5}]¿ + [le|du|au] [1er|1ᵉʳ|~\d\d?] {mois} ?[dernier|prochain|~\d{2,5}]¿ + [jusqu’|jusqu] au [1er|1ᵉʳ|~\d\d?] {mois} ?[dernier|prochain|~\d{2,5}]¿ + à compter du [1er|1ᵉʳ|~\d\d?] {mois} ?[dernier|prochain|~\d{2,5}]¿ + en l’ an ~\d{2,5} + en l’ an de grâce ~\d{2,5} + en {mois} ~\d{2,5} + [de|d’|D’] {mois} ~\d{2,5} + en ~\d{2,5} [,|] + en ~\d{2,5} @:¬:[AN].*:[pi] + de ~\d{2,5} @:A.*:s + à la {mi_mois} ?~\d{2,5}¿ + <<- ~>> * + +TEST: ils sont depuis le 2 janvier {{parti}} à l’étranger. +TEST: ils sont depuis le 2 janvier 2012 {{parti}} à l’étranger. + + +__purge_saisons__ + à l’ [été|automne|hiver] ~\d{2,4} + au printemps ~\d{2,4} + <<- ~>> * TEST: Une étude de 2005 publiée dans le Journal TEST: Les cinq variantes de la couverture du magazine Wired d’avril 2016 consacrée à Silicon Valley. TEST: c’est donc la cinquième en 50 ans -# nombres -__[i](p_un_nombre)__ - un (\d+) ({w_2}) @@w,$ <<- morph(\2, ":A.*:s") ~1>> * +__purge_un_nombre__ + un ~\d+ @:A.*:s¬:G + <<- ~2>> * TEST: l’équipe veut aussi voir dans la lettre le nombre d’or, un symbole d’harmonie, ainsi qu’un 6 retourné. ## moi/toi/lui/elle/nous/vous/eux/elles seul·e·s -__[i](p_moi_toi_seul)__ [mt]oi (seule?) @@4 <<- ~1>> * -__[i](p_lui_seul)__ lui (seul) @@4 <<- ~1>> * -__[i](p_elle_seule)__ elle (seule) @@5 <<- ~1>> * -__[i](p_nous_seuls)__ [nv]ous (seule?s) @@5 <<- ~1>> * -__[i](p_eux_seuls)__ eux (seuls) @@4 <<- ~1>> * -__[i](p_elles_seules)__ elles (seules) @@6 <<- ~1>> * - -## personne d’autre que… -__[i](p_personne_d_autre_que)__ - personne (d’autre qu(?:e |’)(?:lui|elles?|[nv]ous|eux)) @@$ <<- ~1>> * - -## Avant -__[i](p_dès_qqch)__ dès (?:à présent|aujourd’hui|maintenant|lors|que possible|(?:demain|hier)(?: (?:soir|matin|après-midi)|)) <<- ~>> * -__[i](p_et_qqch)__ et (?:ainsi de suite|tutti quanti) <<- ~>> * -__[i](p_et_ou)__ et(/ou) @@2 <<- ~1>> * -__[i](p_quant_à_présent)__ quant à présent <<- ~>> * -__[i](p_ni_qqch_ni_qqch)__ - ni (?:à|avec|contre|pour|chez|sur|sous|devant|derrière) *(?:[tm]oi|lui|elles?|eux|[nv]ous|),? ni (?:à|avec|contre|pour|chez|sur|sous|devant|derrière) (?:[mt]oi|lui|elles?|eux|[nv]ous) <<- ~>> * - - -## Inconditionnel -__[i](p_24h_sur_24)__ 24 ?h(?:eures|) ?(?:sur |/ ?)24 <<- ~>> * -__[i](p_7j_sur_7)__ 7 ?j(?:ours|) ?(?:sur |/ ?)7 <<- ~>> * -__[i](p_sept_j_sur_sept)__ sept jours sur sept <<- ~>> * -__[i](p_vq_h_sur_vq_)__ vingt-quatre heures sur vingt-quatre <<- ~>> * -__> * -__[i](p_à_nn_pour_cent)__ à \d+(?:,\d+|) % <<- ~>> * -__[i](p_à_côté_de)__ à côté (?:de (?:ça|lui|[mt]oi|[nv]ous)|d’(?:elles|eux))(?! et) <<- ~>> * -__[i](p_à_la_qqch)__ à la (?:bo(?:nne franquette|urre)|con|dér(?:ive|obée)|diable|fois|leur|limite du supportable|longue|lumière de tout ce(?:ci|la)|manque|mords-moi-le-nœud|papa|petite semaine|pointe du progrès|première occasion|queue leu leu|ramasse|re(?:nverse|dresse|scousse)|sauvette|surprise générale|virgule près|volée) <<- ~>> * -__[i](p_à_heure)__ à \d\d? ?h(?: ?\d\d|)(?: (?:du (?:matin|soir)|de l’après-midi|ce (?:matin|soir)|cet après-midi|demain (?:matin|soir|après-midi)|)|) <<- ~>> * -__[i](p_à_loc_qqch1)__ à (?:califourchon|chacun|confesse|contre(?:cœur|temps)|demi-mot|foison|grand-peine|loisir|merveille|moitié|nouveau|outrance|peine|perpétuité|présent|raison|rallonge|rebrousse-poil|reculons|regret|renverse|risque|tâtons|tort|tout-va) <<- ~>> * -__[i](p_à_loc_qqch2)__ à (?:au(?:cun prix|trui|tre chose)|bas (?:co[ûu]t|prix)|bâ(?:bord|tons rompus)|beaucoup près|belles dents|bien (?:des égards|pire|y (?:penser|réfléchir|songer|repenser))|bon (?:compte|escient|droit)|bout (?:de (?:bras|souffle|forces?)|nerfs|portant|touchant)|bras (?:ouverts|le corps)|brève échéance|but (?:non |)lucratif|cause d(?:e (?:ça|[mt]oi|lui|[nv]ous)|’e(?:lles?|ux))|ce (?:compte-là|moment-là|titre)|cet (?:égard|instant(?: précis|))|cette (?:date|époque(?: de l’année|)|heure de la (?:journée|nuit)|occasion)|chaque (?:fois|instant)|chaudes larmes|cœur (?:joie|ouvert|perdu)|ciel ouvert|contre-cœur|corps perdu|cou(?:p sûr|per le souffle|rt terme|rte (?:échéance|portée))|couilles rabattues|de (?:nombreuses|multiples) reprises|des kilomètres à la ronde|défaut d’autre chose|dose homéopathique|double (?:titre|tranchant)|durée limitée|en (?:juger par (?:[mts]on|[nv]otre|leur) expérience|perdre (?:haleine|la tête))|faible (?:allure|revenu)|feu et à sang|flanc de (?:colline|montagne)|fleur de peau|franchement parler|géométrie variable|grand(?:-peine|e échelle)|haut risque|hue et à dia|huis clos|intervalles (?:ir|)réguliers|juste (?:raison|titre)|long terme|longue(?: échéance| portée|ur (?:de (?:temps|journée))|d’année)|loyer modéré|main(?: (?:armée|droite|gauche|levée)|s nues)|maint(?:s égards|es reprises)|marche forcée|merveille|mi-(?:course|distance|temps)|mi(?:di|nuit)(?: pile|)|moindres frais|mots couverts|moyen(?: terme|ne échéance)|n’en (?:pas douter|point douter|plus finir)|outrance|parler franc|part (?:entière|ça|ce(?:la|ci))|partir de là|part(?:ir de rien|s égales)|pas de (?:géant|loup|tortue|velours)|personne en danger|perte de vue|petit(?: feu|e (?:dose|échelle))|peu (?:de (?:distance|choses près|frais)|près)|pieds joints|pile ou face|plat(?: ventre|e couture)|plein(?: (?:régime|temps|nez)|s poumons)|plus (?:forte raison|d’un titre)|point nommé|portée de (?:main|tir)|première vue|prix (?:cassé|modique)s?|proprement parler|qui (?:mieux mieux|que ce soit|de droit)|quelque(?: distance|s (?:exceptions|nuances) près)|ras bords?|rude épreuve|s’y méprendre|somme nulle|tel point|temps (?:plein|partiel|complet)|tête reposée|tire[ -]d’aile|titre (?:conservatoire|d’exemple|expérimental|indicatif|informatif|grâcieux|personnel|posthume)|tombeau ouvert|tort (?:ou à raison|et à travers)|tour de (?:bras|rôle)|tous (?:crins|points de vue)|toutes (?:fins utiles|jambes)|tribord|tu et à toi|un moment donné|usage interne|visage (?:découvert|humain)|vive allure|voix (?:haute|basse)|vol d’oiseau|vrai dire|vue d’œil|y (?:regarder de plus près|réfléchir)) <<- ~>> * -__[i](p_à_partir_de)__ à partir (?:de (?:demain(?: matin| midi| soir|)|là|maintenant|rien)|d’(?:aujourd’hui|hier(?: matin| midi| soir|)|ici)) <<- ~>> * -__[i](p_à_quelques_uns)__ à quelques-un(?:s d’entre (?:eux|nous|vous)|es d’entre (?:nous|vous|elles)) <<- ~>> * -__[i](p_à_tout_qqch)__ à tout(?: (?:âge|bout de champ|crin|instant|jamais|le (?:moins|monde)|moment|point de vue|prix|un chacun)|e (?:allure|bride|épreuve|force|heure(?: d(?:u jour|e la nuit)|)|vitesse|volée)) <<- ~>> * -__[i](p_à_l_qqch)__ à l’(?:heure (?:actuelle|qu il est)|accoutumée|amiable|avance|aven(?:ir(?: incertain)|ant)|air libre|aveuglette|emporte-pièce|échelle (?:nationale|mondiale|régionale|départementale|cantonale|locale|galactique|universelle)|évidence|exclusion de toute autre chose|improviste|inverse|occasion|ordre du jour|œil nu|en croire|un(?:animité| (?:d’entre eux|des leurs)|e (?:d’entre elles|des leurs))) <<- ~>> * -__[i](p_à_det_plur_qqch)__ à (?:[mts]es|[nv]os|leurs) (?:côtés|dépens|risques et périls|trousses) <<- ~>> * -__[i](p_à_det_sing_fem_qqch)__ à (?:[mts]a|[nv]otre|leur) (?:connaissance|disposition|grande (?:surprise|tristesse)|guise|juste mesure|portée) <<- ~>> * -__[i](p_à_det_sing_mas_qqch)__ à (?:[mts]on|[nv]otre|leur) (?:avis|c(?:œur|orps) défendant|détriment|encontre|égard|grand (?:désarroi|soulagement)|insu|sujet|tour) <<- ~>> * -__[i](p_à_midi_minuit)__ à mi(?:di|nuit)(?: pile|) <<- ~>> * -__[i](p_à_cette_heure)__ à cette heure(?: (?:du jour|de la nuit|tardive|matinale)|) <<- ~>> * -__[i](p_a_loc_latine)__ [aà] (?:priori|post[eé]riori|contrario|cappella|minima) <<- ~>> * -__[i](p_ab_loc_latine)__ ab (?:absurdo|initio) <<- ~>> * -__[i](p_ad_loc_latine)__ ad (?:hoc|vitam æternam|hominem|infinitum|nauseam|valorem|patres) <<- ~>> * -__[i](p_advienne_que_pourra)__ advienne que pourra <<- ~>> * -__[i](p_après_qqch)__ après (?:[mts]oi|lui|eux|mûre réflexion|tout,|un certain temps|cette date(?: fatidique|)|un bon bout de temps) <<- ~>> * -__[i](p_qqch_après_identique)__ (heure|minute|seconde|jour|nuit|semaine|trimestre|semestre|mois|décennie|année|siècle|génération) après \1 @@0 <<- ~>> * -__[i](p_au_dessus_delà_qqch)__ au-de(?:ssus (?:de (?:[mts]oi|lui|[nv]ous)|d’(?:eux|elles?))|là du descriptible) <<- ~>> * -__[i](p_au_qqch)__ au (?:[xXvViI]+[eᵉ] siècle|bas mot|beau fixe|bon moment|bout (?:du (?:compte|rouleau)|d’un moment)|cas par cas|commencement|contraire|coude à coude|coup par coup|déb(?:otté|but)|demeurant|doigt mouillé|fil (?:des ans|du temps)|grand (?:complet|jamais)|hasard|jour (?:et à l’heure dits|le jour)|jugé|leur|lieu de (?:ce(?:la|ci)|ça|quoi)|loin|même titre que n’importe l(?:aquelle|equel) d’entre (?:nous|vous|eux|elles)|milieu de nulle part|moment opportun|pas de (?:charge|course)|plus (?:haut point|près|pressé|vite|tôt|tard)|premier abord|préalable|propre comme au figuré|quotidien|ras des pâquerettes|saut du lit|sens (?:figuré|large|propre)|surplus) <<- ~>> * -__[i](p_au_adj_moment)__ au (?:dernier|même|bon|mauvais) (?:moment|instant) <<- ~>> * -__[i](p_au_cours_des)__ au cours des (?:deux|trois|quatre|cinq|six|sept|huit|neux|dix|onze|douze|treize|quatorze|quinze|seize|dix-(?:sept|huit|neuf)|vingt|trente|quarante|cinquante|soixante|soixante-dix|quatre-vingt|quatre-vingt-dix|cent) (?:derni(?:ère|er)s|prochaine?s) (?:années|mois|siècles) <<- ~>> * -__[i](p_au_fond_de_qqch)__ (?:tout |)au fond (?:de (?:[mts]oi|lui|[nv]ous)|d’(?:elles?|eux))(?:-mêmes?|) <<- ~>> * -__[i](p_aux_qqch)__ aux (?:abois|leurs|mien(?:ne|)s|tien(?:ne|)s|sien(?:ne|)s) <<- ~>> * -__[i](p_autant_que_qqch)__ autant que (?:nécessaire|possible|prévu|faire se peut) <<- ~>> * -__[i](p_autour_de_qqch)__ autour (?:d’(?:eux|elles?)|de (?:lui|[nv]ous|[mt]oi)) <<- ~>> * -__[i](p_autrement_dit)__ autrement dit <<- ~>> * -__[i](p_av_JC)__ av. J.-C. <<- ~>> * -__[i](p_avant_qqch)__ avant (?:longtemps|terme|tout le monde|toute(?: chose|s choses)|d’aller plus loin|J.-C.|Jésus-Christ|d’en arriver là|de faire quoi que ce soit(?: de stupide|)|qu il ne soit trop tard|un bon bout de temps) <<- ~>> * -__[i](p_avec_qqch1)__ avec (?:brio|joie|légèreté|insistance|peine|autre chose|pertes et fracas|un peu de chance|tout le respect que (?:je (?:vous|te|l(?:eur|ui)) dois|nous (?:vous|te|l(?:eur|ui)) devons)|tout un chacun|un peu de chance) <<- ~>> * -__[i](p_avec_qqch2)__ avec (?:autrui|[mts]oi|lui|e(?:ux|lles?)|[nv]ous(?: autres)|le plus grand soin|tout le monde|tout ça|on ne sait quo?i)(?! qui) <<- ~>> * -__[i](p_beaucoup_plus_moins)__ beaucoup (?:plus|moins) <<- ~>> * -__[i](p_bel_et_bien)__ bel et bien <<- ~>> * -__[i](p_bien_adv_temps)__ bien (?:assez tôt|des fois|souvent) <<- ~>> * -__[i](p_bon_gré_mal_gré)__ bon gré,? mal gré <<- ~>> * -__[i](p_bras_dessus_dessous)__ bras dessus,? bras dessous <<- ~>> * -__[i](p_çà_et_là)__ çà et là <<- ~>> * -__[i](p_ce_faisant)__ ce faisant <<- ~>> * -__[i](p_ceci_qqch)__ ceci (?:mis à part|va sans dire) <<- ~>> * -__[i](p_cela_qqch)__ cela (?:mis à part|va sans dire) <<- ~>> * -__[i](p_ces_derniers_temps)__ ces derniers temps <<- ~>> * -__[i](p_ceux_d_entre_pronom)__ ce(?:lui|lles?|ux) (d’entre (?:[nv]ous|eux|elles)) @@$ <<- ~1>> * -__[i](p_cette_fois_là)__ cette fois-(?:là|ci) <<- ~>> * -__[i](p_chacun_d_entre_nous)__ chacune? (d’entre (?:[nv]ous|eux|elles)) @@$ <<- ~1>> * -__[i](p_chaque_fois)__ chaque fois <<- ~>> * -__[i](p_chemin_de_fer)__ chemins? (de fer) @@$ <<- ~1>> * -__[i](p_chez)__ chez (?:[mt]oi|lui|e(?:ux|lles?)|[nv]ous|autrui|quelqu’une?|on ne sait qui) <<- ~>> * -__[i](p_comme_qqch)__ comme (?:avant|autrefois|d’habitude|toujours|de juste|bon (?:me|te|l(?:ui|eur)|[nv]ous) semble|au bon vieux temps|cul et chemise|frappée?s? par la foudre|n’importe où(?: ailleurs|)|par (?:enchantement|magie|un fait exprès)|promis|qui dirait|si de rien n’était|tout un chacun) <<- ~>> * -__[i](p_comme_tant_d_autres)__ comme tant d’autres (?:avant|après) (?:[mts]oi|lui|[nv]ous|eux|elles?)(?! qui) <<- ~>> * -__[i](p_contrairement_aux_apparences)__ contrairement aux apparences <<- ~>> * -__[i](p_contre_qqch)__ contre (?:mauvaise fortune,? bon cœur|nature|toute (?:attente|vraisemblance)|vents et marées|[mts]oi|lui|elles?|[nv]ous|eux|(?:[mts]on|[nv]otre|leur) gré) <<- ~>> * -__[i](loc_côte_à_côte)__ - c[ôo]tt?es? [àaá] c[ôo]tt?es? - <<- not re.search("(?i)^côte à côte$", \0) ->> côte à côte # Locution adverbiale invariable. Écrivez “côte à côte”.|https://fr.wiktionary.org/wiki/c%C3%B4te_%C3%A0_c%C3%B4te - <<- ~>> * -__[i](p_coute_que_coute)__ co[ûu]te que co[ûu]te <<- ~>> * -__[i](p_crois_le_ou_non)__ cro(?:yez|ois)-le ou (?:non|pas) <<- ~>> * -__[i](p_cul_par_dessur_tête)__ cul par-dessus tête <<- ~>> * -__[i](p_dans_qqch)__ dans (?:ces? cas(?: précis|-là|-ci| particuliers?|)|l’i(?:déal|mmédiat)|la mesure du possible|les années \d\d+|peu de temps|tout (?:ce(?:la|ci)|ça)|très peu de temps|un(?: cas comme dans l’autre|e (?:certaine|large|moindre) mesure)) <<- ~>> * -__[i](p_début_mois)__ début (?:janvier|février|mars|avril|mai|juin|juillet|ao[ûu]t|septembre|octobre|novembre|décembre)(?: \d\d\d\d|) <<- ~>> * -__[i](p_d_qqch)__ d’(?:abord|affilée|ailleurs|année en année|aujourd’hui|antan|autant (?:plus|moins)|autre(?:fois|s fois| part)|arr(?:arrache-?pied|ière en avant)|avant en arrière|à côté|âge mûr|emblée|empoigne|en face|entr(?:e (?:[nv]ous|eux|elles)|ée de jeu)|est en ouest|extrême[ -](?:droite|gauche)|égale? à égale?|habitude|heure en heure|hier(?: (?:matin|soir|après-midi)|)|ici(?: là| peu(?: de temps|)| très peu(?: de temps|)|)|ordinaire|origine (?:inconnue|douteuse)|ordre général|ouest en est|ore?s et déjà|un (?:autre côté|(?:bout à|côté comme de) l’autre|commun accord)) <<- ~>> * -__[i](p_d_une_qqch)__ d’une (?:autre trempe|(?:façon|manière) ou d’une autre|certaine (?:façon|manière)|tout autre ampleur|(?:minute|seconde) à l’autre) <<- ~>> * -__[i](p_d_où_que)__ d’où qu (?:(?:il|elle|on) vienne|(?:ils|elles) viennent) <<- ~>> * -__[i](p_de_ci_de_là)__ de-ci,? de-là <<- ~>> * -__[i](p_de_heure)__ de \d\d? ?h(?: ?\d\d|)(?: (?:du (?:matin|soir)|de l’après-midi|ce (?:matin|soir)|cet après-midi|demain (?:matin|soir|après-midi))|) <<- ~>> * -__[i](p_de_qqch)__ de (?:\d+(?:,\d+|) ?%|cesse|conserve|facto|fait|guingois|luxe|nouveau|permanence|partout|préférence|profundis|rechange|routine|surcro[îi]t|visu|A à Z|bas(?: (?:en haut|étage)|se (?:condition|extraction|))|bon (?:aloi|cœur|gré|matin|sens|ton)|bonne (?:facture|famille|foi|heure|humeur|grâce|qualité|compagnie)|bric et de broc|but en blanc|ce(?: (?:fait(?: même|)|seul fait|point de vue)|tte sorte|t acabit)|courte (?:durée|vue)|dernière minute|demain(?: (?:matin|soir|après-midi)|)|droite (?:à|comme de) gauche|fâcheuse mémoise|fil en aiguille|fond en comble|fort (?:loin|près)|fra[iî]che date|ga[îi]e?té de cœur|gauche (?:à|comme de) droite|grande (?:taille|envergure)|gré ou de force|guerre lasse|haut(?: (?:en bas|rang|vol)|e (?:lutte|stature|volée))|jour comme de nuit|là-bas|la (?:meilleure (?:manière|façon) possible|même (?:façon|manière)|sorte|tête aux pieds|veille)|loin(?: en loin|)|longue (?:date|durée|haleine)|main de ma[îi]tre|mauvais(?: (?:aloi|go[ûu]t|gré)|e (?:foi|grâce|humeur))|mieux en mieux|nature (?:inconnue|indéterminée|insolite)|nombreuses (?:fois|années plus (?:tôt|tard))|nos jours|notoriété publique|nulle part|pire en pire|près(?: ou de loin|)|par(?: le monde(?: entier|)|t et d’autre)|petite taille|pied ferme|premi(?:er (?:ordre|plan)|ère main)|plein (?:droit|fouet)|plus (?:belle|près)|première (?:catégorie|nécessité)|prime abord|proche en proche|pure forme|sang-froid|seconde (?:zone|importance|main)|si bon(?: matin|ne heure)|source sûre|taille moyenne|telle sorte|temps (?:en temps|à autre)|tr(?:ès|op) (?:loin|près)|vive voix) <<- ~>> * -__[i](p_de_nous_vous_tous)__ de [nv]ous tous <<- ~>> * -__[i](p_de_tout_qqch)__ de tou(?:t (?:poil|temps|à l’heure|premier (?:ordre|plan))|tes (?:parts|pièces|sortes|(?:[mts]es|leurs|[nv]os) forces)|te (?:éternité|évidence|façon|urgence)|s (?:côtés|bords)) <<- ~>> * -__[i](p_de_ceux_celles)__ de ce(?:ux|lles)-(?:ci|là)(?! qui) <<- ~>> * -__[i](p_de_det_mas_qqch)__ de (?:[mts]on|[nv]otre|leur) (?:mieux|plein gré|point de vue|propre (?:cru|chef)|vivant) <<- ~>> * -__[i](p_de_det_fem_qqch)__ de (?:[mts]a|[nv]otre|leur) part <<- ~>> * -__[i](p_de_qqch_en_identique)__ de (moins|plus|mieux|pire|jour|minute|semaine|mois|trimestre|semestre|siècle|millénaire|décennie) en \1 @@3 <<- ~>> * -__> * -__[i](p_des_qqch)__ des (?:fois|pieds à la tête|uns et des autres|(?:années|mois|siècles|millénaires|décennies|semaines) plus t(?:ôt|ard)) <<- ~>> * -__[i](p_depuis_qqch)__ depuis (?:assez longtemps|belle lurette|bien longtemps|de (?:très |)longues années|des lustres|longtemps|lors|peu de temps|quelque temps|quelques (?:secondes|minutes|heures|jours|semaines|mois|trimestres|semestres|années|décennies|siècles|millénaires)|si longtemps|toujours|tout ce temps|très longtemps) <<- ~>> * -__[i](p_depuis_tps)__ depuis (\d+ (?:ans|années|mois|semaines|jours|heures|minutes|secondes|)|les années \d\d+) @@$ <<- ~>> * -__[i](p_Dieu_en_garde_témoin)__ Dieu (?:[mt]’en (?:garde|soit témoin)|[nv]ous en (?:garde|soit témoin)|l(?:es |’)en garde|l(?:eur|ui) en soit témoin) <<- ~>> * -__[i](p_du_moins)__ du moins <<- ~>> _ -__[i](p_du_qqch)__ du (?:[xXvViI]+[eᵉ] siècle|bout des lèvres|début à la fin|fond du cœur|jour au lendemain|haut en bas|même (?:acabit|tonneau)|moins,? pas|(?:nord|sud) au (?:nord|sud)|tout au tout) <<- ~>> * -__[i](p_demain)__ (?:après-|avant |)demain(?: matin| soir| après-midi|) <<- ~>> * -__[i](p_don_Juan)__ (don) Juan @@0 <<- ~1>> * -__[i](p_du_même_ordre_coup)__ du même (?:ordre|coup) <<- ~>> * -__[i](p_en_nombre_années)__ en \d\d+(?: ans| années| mois| semaines| jours| heures| minutes| secondes|) <<- ~>> * -__[i](p_en_cours)__ en cours(?! d[e’]) <<- ~>> * -__[i](p_en_pronom)__ en (?:[mt]oi|eux|elles?) <<- ~>> * -__[i](p_en_qqch1)__ en (?:aparté|apparence|arrière|avance|avant|cachette|ceci|cela|clair|commun|conséquence|continu|contrepartie|définitive|détail|direct|douce|effet|émoi|filigrane|général|goguette|hâte|majorité|outre|pâmoison|parallèle|partie|particulier|permanence|personne|pratique|prime|privé|principe|priorité|public|réalité|retour|revanche|rien|rogne|route|secret|silence|somme|suspens|théorie|trompe-l’œil|vain|vérité|ville|vitesse) <<- ~>> * -__[i](p_en_qqch2)__ en (?:aucun(?: cas|e (?:circonstance|façon|manière))|bon(?: état|ne (?:compagnie|et due forme|posture|santé(?: physique| mentale|)|voie))|bout de course|cas d(?:e (?:besoin|doute)|’urgence)|chacune? d(?:e [nv]ous|’(?:eux|elles))|chair et en os|chute libre|comparution immédiate|connaissance de cause|coupe réglée|cours de route|d’autres (?:circonstances|termes|temps)|de telles circonstances|début d(?:e (?:journée|matinée|soirée)|’après-midi)|définitive|dehors de (?:tout|)(?:ça|cela|ceci)|dents de scie|dernier (?:lieu|recours|ressort)|désespoir de cause|détention provisoire|direction d(?:u (?:nord|sud)(?:-est|-ouest|)|e l’(?:est|ouest))|état (?:de (?:choc(?: circulatoire|)|marche)|d’ébriété(?: avancée|))|excellent état|file indienne|fin d(?:e (?:compte|journée|matinée|soirée)|’après-midi)|forte (?:baisse|hausse)|gage de bonne foi|garde à vue(?: prolongée|)|grand(?: nombre|e (?:difficulté|majorité|partie|pompe))|haut lieu|l’occurrence|lieu sûr|ligne de (?:compte|mire)|mains propres|mauvais(?: état|e (?:posture|santé))|même temps|milieu d(?:e (?:journée|matinée|soirée)|’après-midi)|nombre (?:plus que |)suffisant|partant de zéro|plein(?: air| cœur| jour|e (?:gueule|figure|forme|poire|nuit|tronche))|perte de vitesse|peu de temps|piteux état|point d(?:e mire|’orgue)|position de (?:force|faiblesse)|premi(?:er lieu|ère (?:instance|ligne))|pure perte|quantité (?:plus que |)suffisante|quelque sorte|queue de peloton|rangs serrés|rase campagne|règle générale|roue libre|sens inverse|si peu de temps|sous-main|tête à tête|temps (?:et en heure|normal|opportun|ordinaire|utile|voulu)|termes choisis|toile de fond|tous (?:les cas|sens)|tout (?:bien tout honneur|cas|genre|lieu|et pour tout|état de cause|premier lieu|sens|temps)|toute(?: (?:bonne foi|circonstance|connaissance de cause|confiance|discrétion|franchise|hâte|impartialité|impunité|innocence|légalité|liberté|logique|sécurité|simplicité)|s circonstances)|un (?:clin d’œil|rien de temps)|une autre occasion|vase clos|voie de développement|y réfléchissant bien) <<- ~>> * -__[i](p_en_mois_dernier)__ en (?:janvier|février|mars|avril|mai|jui(?:n|llet)|ao[ûu]t|septembre|octobre|novembre|décembre) dernier <<- ~>> * -__[i](p_en_dat_mas_qqch)__ en (?:[mts]on|leur|[nv]otre) (?:âme et conscience|for intérieur|nom propre) <<- ~>> * -__[i](p_en_ce_qqch)__ en ce(?: (?:moment|temps-là|qui (?:[mt]e|l(?:es?|a)|[nv]ous) concern(?:e|ait))|t instant) <<- ~>> * -__[i](p_encore_qqch)__ encore (?:une fois|et (?:encore|toujours)) <<- ~>> * -__[i](p_envers_qqch)__ envers (?:autrui|et contre tout|les uns et les autres|tout le monde) <<- ~>> * -__[i](p_entre_qqch)__ entre (?:(?:[mt]oi|lui|elles?|[nv]ous|eux) et (?:[mt]oi|lui|elles?|[nv]ous|eux)|chien et loup|de (?:bonnes|mauvaises) mains|l’une? et l’autre|les uns et les autres|quat(?:re[- ]z-?yeux|’ z-?yeux)) <<- ~>> * -__[i](p_entre_date)__ entre (?:janvier|février|mars|avril|mai|juin|juillet|ao[ûu]t|septembre|octobre|novembre|décembre) (?:\d\d{1,3} |)et (?:janvier|février|mars|avril|mai|juin|juillet|ao[ûu]t|septembre|octobre|novembre|décembre)(?: \d\d{1,3}|) <<- ~>> * -__[i](p_épaule_contre_épaule)__ épaule contre épaule <<- ~>> * -__[i](p_été_comme_hiver)__ été comme hiver <<- ~>> * -__[i](p_oh_ah_euh_eh_bien)__ (?:oh|ah|euh|eh bien) <<- ~>> * -__[i](p_ex_loc_latine)__ ex (?:nihilo|cathedra|absurdo|abrupto) <<- ~>> * -__[i](p_face_à_face)__ face à face <<- ~>> * -__[i](p_nombre_fois_de_suite)__ (?:deux|trois|quatre|cinq|six|sept|huit|neuf|dix|onze|douze|treize|quatorze|quinze|seize|vingt|trente|quarante|cinquante|soixante|cent) fois de suite <<- ~>> * -__[i](p_grosso_modo)__ grosso modo <<- ~>> * -__[i](p_grand_bien_lui_fasse)__ grand bien lui fasse <<- isStart() ~>> * -__[i](p_hier)__ (?:avant-|)hier(?: matin| soir| après-midi|) <<- ~>> * -__[i](p_hors_de_qqch)__ hors (?:de (?:contrôle|portée)|d’(?:atteinte|état de nuire)|du commun) <<- ~>> * -__[i](p_ici_qqch)__ ici(?: comme ailleurs| ou ailleurs| et (?:là|maintenant)| même|-bas) <<- ~>> * -__[i](p_id_est)__ id est <<- ~>> * -__[i](p_il_y_a_qqch)__ il y a (?:longtemps|peu de temps|très (?:longtemps|peu de temps)|(?:quelques|moins de \d+|\d+) (?:secondes|minutes|heures|jours|semaines|mois|an(?:née|)s|siècles|millénaires)|quelque temps) <<- ~>> * -__[i](p_il_n_y_a_pas_qqch)__ il n’y a pas (?:si |)longtemps <<- ~>> * -__[i](p_illico_presto)__ illico presto <<- ~>> * -__[i](p_in_loc_latine)__ in (?:abstracto|extenso|extremis|fine|petto|situ|utero|vitro|vivo) <<- ~>> * -__[i](p_ipso_facto)__ ipso facto <<- ~>> * -__[i](p_j_en_passe)__ j’en passe et des meilleure?s <<- ~>> * -__[i](p_jour_pour_jour)__ jour pour jour <<- ~>> * -__[i](p_jusque_là)__ jusque-là <<- ~>> * -__[i](p_jusque_qqch)__ jusqu (?:alors|ici|aujourd’hui|au bout des ongles) <<- ~>> * -__[i](p_jusque_à_qqch)__ jusqu à (?:aujourd’hui|bac|présent|maintenant|récemment|(?:demain|hier)(?: matin| soir| après-midi|)|nouvel ordre|plus (?:ample informé|soif)|preuve du contraire|la (?:fin de(?: (?:[mts]es|[nv]os|leurs) jours|s temps)|tombée de la nuit)|(?:[mts]on|leur|[nv]otre) dernier souffle(?: de vie|)|ce que (?:mort s’ensuive|(?:j’en sache|tu en saches|(?:il|elle|on) en sache|nous en sachions|vous en sachiez|(?:ils|elles) en sachent) plus)|Noël|Pâques) <<- ~>> * -__[i](p_la_qqch)__ la (?:plupart du temps|main dans la main|mort dans l’âme) <<- ~>> * -__[i](p_le_qqch)__ le (?:cas échéant|moins (?:du monde|souvent)|plus (?:tôt|tard|souvent|de (?:temps|monde)) possible|moment venu|plus souvent) <<- ~>> * -__[i](p_là_qqch)__ là(?:-bas|-haut|-de(?:dans|hors|rrière|sso?us|vant)| non plus) <<- ~>> * -__[i](p_l_un_qqch)__ l’une? (?:après|pour|de(?:rrière|)|avec|contre|sur|près de) l’autre <<- ~>> * -__[i](p_le_pour_et_le_contre)__ le pour et le contre <<- ~>> =\0.replace(" ", "_") -__[i](p_les_uns_les_autres)__ les une?s (?:des |(?:après |pour |avec |contre |sur |derrière |devant |)les) autres <<- ~>> * -__[i](p_non_loin)__ non loin (?:d’ici|de là) <<- ~>> * -__[i](p_loin_qqch)__ loin (?:de (?:là|tout ça)|d’(?:être|ici)|s’en fa(?:ut|llait)) <<- ~>> * -__[i](p_maintes_fois)__ (?:[lcd]es |)maintes fois <<- ~>> * -__[i](p_malgré_pronom)__ malgré (?:[mt]oi|lui|elles?|[nv]ous|eux)(?! qui) <<- ~>> * -__[i](p_malgré_ça)__ malgré (?:ça|cela|tout) <<- ~>> * -__[i](p_manu_militari)__ manu militari <<- ~>> * -__[i](p_mieux_vaut_tard_que_jamais)__ mieux va(?:u|lai)t tard que jamais <<- ~>> * -__[i](p_moins_que_nécessaire)__ moins que (?:nécessaire|prévu) <<- ~>> * -__[i](p_moitié_qqch_moitié_qqch)__ moitié ({w2}),? moitié ({w2}) @@7,$ <<- ~>> * -__[i](p_mot_pour_mot)__ mot pour mot <<- ~>> * -__[i](p_mutatis_mutandis)__ mutatis mutandis <<- ~>> * -__[i](p_ne_vous_en_déplaise)__ ne (?:vous |l(?:ui|eur) |t’)en déplaise <<- ~>> * -__[i](p_nez_à_nez)__ nez à nez <<- ~>> * -__[i](p_ni_qqch)__ ni (?:de près,? ni de loin|plus ni moins|vu,? ni connu) <<- ~>> * -__[i](p_non_qqch)__ non (?:plus|sans raison|seulement) <<- ~>> * -__[i](p_nulle_part)__ nulle part <<- ~>> * -__[i](p_ô_combien)__ ô combien <<- ~>> * -__[i](p_ou_bien)__ ou (bien) @@3 <<- ~1>> * -__[i](p_ou_qqch_d_approchant)__ ou quelque chose d’approchant <<- ~>> * -__[i](p_où_bon_nous_semble)__ où bon (?:me|te|lui|nous|vous|leur) semble <<- ~>> * -__[i](p_oui_et_ou_non)__ oui (?:ou|et) non <<- ~>> * -__[i](p_outre_mesure)__ outre mesure <<- ~>> * -__[i](p_qqch_par_qqch)__ (une?|deux|trois|quatre|cinq|six|sept|huit|neuf|dix|onze|douze|treize|quatorze|quinze|seize|vingt|trente|quarante|cinquante|soixante|cent|mille|éta[pg]e|morceau|pièce) par \1 @@0 <<- ~>> * -__[i](p_par_qqch1)__ par (?:à-coups|ailleurs|avance|chance|conséquent|curiosité|contre|défaut|définition|endroits|essence|ex(?:cellence|emple)|hasard|ici|inadvertance|là|moments|monts et par vaux|nature|principe|terre) <<- ~>> * -__[i](p_par_qqch2)__ par (?:la (?:même occasion|suite)|(?:bien des|certains) (?:aspects|côtés)|acquit de conscience|beau temps|bonté de cœur|ce biais|égard pour (?:moi|toi|lui|elles?|eux|nous|vous)(?! qui)|lui-même|elle(?:-même|)|eux(?:-mêmes|)|elles(?:-mêmes|)|le passé|les temps qui courent|[nv]ous-mêmes?|[mt]oi(?:-même|)|temps de pluie|tout le monde|voie (?:de (?:conséquence|mer|terre)|d’exception)) <<- ~>> * -__[i](p_par_ci_par_là)__ par-ci,? par-là <<- ~>> * -__[i](p_par_position)__ par-de(?:vant|rrière|ssus (?:le marché|tout)) <<- ~>> * -__[i](p_par_devers_pronom)__ par-devers (?:moi|toi|lui|elles?|lui|eux|nous|vous) <<- ~>> * -__[i](p_par_nombre_fois)__ par (?:deux|trois|quatre|cinq|six|sept|huit|neuf|dix|onze|douze|treize|quatorze|quinze|seize|vingt|trente|quarante|cinquante|soixante|cent) fois <<- ~>> * -__[i](p_parmi_qqch)__ parmi (?:[nv]ous(?: autres|)|eux|elles) <<- ~>> * -__[i](p_partant_de_là)__ partant de là <<- ~>> * -__[i](p_pas_qqch)__ pas (?:du tout|à pas|le moins du monde) <<- ~>> * -__[i](p_pendant_qqch)__ pendant (?:ce temps-là|(?:bien |si |assez |très |)longtemps|plusieurs (?:heures|minutes|secondes|mois|semaines|jours|années|siècles|millénaires|décennies)|quelque temps) <<- ~>> * -__[i](p_petit_à_petit)__ petit à petit <<- ~>> * -__[i](p_peu_qqch)__ peu (?:à peu|de temps auparavant|ou prou) <<- ~>> * -__[i](p_pile_poil)__ pile poil <<- ~>> * -__[i](p_plein_qqch)__ plein (?:nord|sud|ouest|de fois) <<- ~>> * +__purge_pronom_seul__ + [moi|toi] [seul|seule] + lui seul + elle seule + [nous|vous] [seuls|seules] + eux seuls + elles seules + <<- ~1>> * + + +__purge_début_phrase__ + car + de plus + et ?puis¿ + mais + m’ est avis [que|qu’|qu] + or donc + puis + [|,] grand bien lui fasse + <<- ~1:0>> * + + +__purge_horaires_et_durée__ + 24 [heures|h] [sur|/] 24 + 7 [jours|j] [sur|/] 7 + sept [jours|j] [sur|/] sept + vingt-quatre heures [sur|/] vingt-quatre + <<- ~>> * + + heure après heure + minute après minute + seconde après seconde + jour après jour + nuit après nuit + semaine après semaine + trimestre après trimestre + semestre après semestre + mois après mois + décennie après décennie + année après année + siècle après siècle + génération après génération + <<- ~>> * + + [à|de] ~\d\d? h ?~\d\d?¿ + [à|de] ~\d\d? h ?~\d\d?¿ [du|ce] [matin|soir] + [à|de] ~\d\d? h ?~\d\d?¿ de l’ après-midi + [à|de] ~\d\d? h ?~\d\d?¿ cet après-midi + [à|de] ~\d\d? h ?~\d\d?¿ demain [matin|soir|après-midi] + <<- ~>> * + +TEST: Le train de 2 h 47 {{arriveraient}} en retard. +TEST: Le train de 2 h 47 du matin {{arriveraient}} en retard. + + +__purge_prépositions_qqn__ + [après|avant|avec|pour|contre|sans|envers|chez|en|malgré|selon] les uns et les autres [|,|@:[VXG]¬>qui] + <<- ~1:6>> * + + [après|avant|avec|pour|contre|sans|envers|chez|d’|D’|malgré|selon] on ne sait [qui|quoi] [|,|@:[VXG]¬>qui] + <<- ~1:5>> * + + [après|avant|avec|pour|contre|sans|envers|chez|de|en|malgré|selon] tout un chacun [|,|@:[VXG]¬>qui] + [après|avant|avec|pour|contre|sans|envers|chez|de|en|malgré|selon] tout le monde [|,|@:[VXG]¬>qui] + <<- ~1:4>> * + + [après|avant|avec|pour|contre|sans|envers|chez|de|en|malgré] tout ça [|,|@:[VXG]¬>qui] + [après|avant|avec|pour|contre|sans|envers|chez|de|en|malgré|selon] [vous|nous] autres [|,|@:[VXG]¬>qui] + <<- ~1:3>> * + + [après|avant|avec|pour|contre|sans|envers|chez|de|d’|D’|en|malgré|selon] [autrui|quelqu’un|quelqu’une] [|,|@:[VXG]¬>qui] + [après|avant|avec|envers|chez|malgré|selon] {pronom_obj} [|,|@:[VXG]¬>qui] + [contre|pour|sans|de|en] [moi|toi|soi|elle|eux|elles|moi-même|toi-même|soi-même|lui-même|elle-même|nous-mêmes|vous-même|vous-mêmes|eux-mêmes|elles-mêmes] [|,|@:[VXG]¬>qui] + <<- ~1:2>> * + + par égard pour [moi|toi|soi|elle|eux|elles|moi-même|toi-même|soi-même|lui-même|elle-même|nous-mêmes|vous-même|vous-mêmes|eux-mêmes|elles-mêmes] [|,|@:[VXG]¬>qui] + <<- ~1:4>> * + + en [moi|toi|soi|elle|eux|elles|moi-même|toi-même|soi-même|lui-même|elle-même|nous-mêmes|vous-même|vous-mêmes|eux-mêmes|elles-mêmes] + <<- ~>> * + + [après|avant|avec|pour|contre|sans|envers|chez|de|en|malgré|selon] [celui-ci|celui-là|celle-ci|celle-là|ceux-ci|ceux-là|celles-ci|celles-là] + <<- ~>> * + + entre [moi|toi|lui|elle|elles|nous|vous|eux] et [moi|toi|lui|elle|elles|nous|vous|eux] + entre [nous|vous|eux|elles] [deux|trois|quatre|cinq|six|sept|huit|neuf|dix] + <<- ~>> * + + ni [après|avec|chez|contre|de|derrière|devant|envers|malgré|pour|sans|sous|sur] [moi|toi|lui|elle|elles|eux|nous|vous] ?,¿ ni [après|avec|chez|contre|de|derrière|devant|envers|malgré|pour|sans|sous|sur] [moi|toi|lui|elle|elles|eux|nous|vous] + <<- ~>> * + + parmi [nous|vous] ?autres¿ + parmi [eux|elles] + <<- ~>> * + + par-devers [moi|toi|lui|elle|elles|lui|eux|nous|vous] + <<- ~>> * + + quant à [moi|toi|lui|elle|elles|lui|eux|nous|vous] [|,|@:[VXG]¬>qui] + <<- ~1:3>> * + +TODO: comme + + +__simplifications_partielles__ + comme tant d’ autres @:R + <<- ~1:4>> * + + en cours @¬>de + <<- ~1:2>> * + + et / ou + <<- ~2:3>> * + + personne d’ autre [que|qu’|qu] [moi|toi|lui|elle|elles|nous|vous|eux] + <<- ~2:0>> * + + +__purge_locutions_latines__ + [a|à] [priori|postériori|posteriori|contrario|cappella|minima] + ab [absurdo|initio] + ad [hoc|hominem|infinitum|nauseam|valorem|patres] + ad vitam æternam + ex [nihilo|cathedra|absurdo|abrupto] + id est + in [abstracto|extenso|extremis|fine|petto|situ|utero|vitro|vivo] + ipso facto + mutatis mutandis + <<- ~>> * + + +__purge_locutions__ + à ~\d+(?:,\d+|) % + à [autrui|bâbord|califourchon|chacun|confesse|contrecœur|contre-cœur|contretemps|demi-mot|foison|grand-peine|loisir|merveille|moitié|nouveau|outrance|peine|perpétuité|présent|raison|rallonge|rebrousse-poil|reculons|regret|renverse|risque|tâtons|tort|tribord|tout-va] + à aucun prix + à autre chose + à bas [cout|coût|prix] + à bâtons rompus + à beaucoup près + à belles dents + à bien des égards + à bien pire + à bon [compte|escient|droit] + à bout de [bras|souffle|force|forces|nerf|nerfs] + à bout [portant|touchant] + à bras ouverts + à bras le corps + à brève échéance + à but ?non¿ lucratif + à cause [de|d’] [ça|moi|toi|lui|nous|vous|elle|elles|eux] + à ce [compte-là|moment-là|titre] + à cet égard + à cet instant ?[exact|précis]¿ + à cette [date|occasion] + à cette époque + à cette époque de l’ année + à cette heure + à cette heure du jour + à cette heure de la [journée|nuit] + à cette heure [tardive|matinale] + à ciel ouvert + à chaque [fois|instant] + à chaudes larmes + à cœur [joie|ouvert|perdu] + à corps perdu + à côté [de|d’] [ça|moi|toi|lui|nous|vous|elle|elles|eux] + à couilles rabattues + à coup sûr + à couper le souffle + à court terme + à courte [échéance|portée] + à des kilomètres à la ronde + à défaut d’autre chose + à dose homéopathique + à durée limitée + à de [nombreuses|multiples] reprises + à double [titre|tranchant] + à en juger par [mon|ton|son|notre|votre|leur] expérience + à en perdre haleine + à en perdre la tête + à faible [allure|revenu] + à feu et à sang + à flanc de [colline|montagne] + à fleur de peau + à franchement parler + à géométrie variable + à grande échelle + à haut risque + à hue et à dia + à huis clos + à intervalles [irréguliers|réguliers] + à juste [raison|titre] + à l’ heure actuelle + à l’ heure [qu’|qu] il est + à l’ accoutumée + à l’ amiable + à l’ avance + à l’ avenir + à l’ avenir incertain + à l’ avenant + à l’ air libre + à l’ aveuglette + à l’ emporte-pièce + à l’ échelle [nationale|mondiale|régionale|départementale|cantonale|locale|galactique|universelle] + à l’ évidence + à l’ exclusion de toute autre chose + à l’ improviste + à l’ inverse + à l’ occasion + à l’ ordre du jour + à l’ œil nu + à l’ en croire + à l’ unanimité + à l’ un d’ entre eux + à l’ une d’ entre elles + à l’ [un|une] des leurs + à la [bourre|con|dérive|dérobée|diable|fois|leur|longue|manque|mords-moi-le-nœud|papa|ramasse|renverse|redresse|rescousse|sauvette|volée] + à la bonne franquette + à la limite du supportable + à la lumière de tout [ceci|cela|ça] + à la petite semaine + à la pointe du progrès + à la première occasion + à la queue leu leu + à la surprise générale + à la virgule près + à long terme + à longue [échéance|portée] + à longueur [de|d’] [temps|journée|année] + à loyer modéré + à main [armée|droite|gauche|levée] + à mains nues + à maints égards + à maintes reprises + à marche forcée + à merveille + à [midi|minuit] ?pile¿ + à [mi-course|mi-distance|mi-temps] + à moindres frais + à mots couverts + à moyen terme + à moyenne échéance + à [mes|tes|ses|nos|vos|leurs] [côtés|dépens|trousses] + à [mes|tes|ses|nos|vos|leurs] risques et périls + à [ma|ta|sa|notre|votre|leur] [connaissance|disposition|guise|portée] + à [ma|ta|sa|notre|votre|leur] grande [surprise|tristesse] + à [ma|ta|sa|notre|votre|leur] juste mesure + à [mon|ton|son|notre|votre|leur] [avis|détriment|encontre|égard|insu|sujet|tour] + à [mon|ton|son|notre|votre|leur] [cœur|corps] défendant + à [mon|ton|son|notre|votre|leur] grand [désarroi|soulagement] + à n’ en pas douter + à n’ en plus finir + à n’ en point douter + à parler franc + à part [entière|ça|cela|ceci] + à parts égales + à partir [de|d’] [aujourd’hui|ici|là|maintenant|rien] + à partir [de|d’] [demain|hier] ?[matin|midi|soir]¿ + à pas de [géant|loup|tortue|velours] + à personne en danger + à perte de vue + à petit feu + à petite [dose|échelle] + à peu de choses près + à peu de [distance|frais] + à peu près + à pieds joints + à pile ou face + à plat ventre + à plate couture + à plein [régime|temps|nez] + à pleins poumons + à plus forte raison + à plus d’un titre + à point nommé + à portée de [main|tir] + à première vue + à prix [cassé|modique|cassés|modiques] + à proprement parler + à qui de droit + à qui mieux mieux + à qui que ce soit + à quelque distance + à quelques [exceptions|nuances] près + à quelques-uns d’ entre [nous|vous|eux] + à quelques-unes d’ entre [nous|vous|elles] + à ras [bord|bords] + à rude épreuve + à s’ y méprendre + à somme nulle + à tel point + à temps [plein|partiel|complet] + à tête reposée + à tire d’ [aile|ailes] + à [tire-d’aile|tire-d’ailes] + à titre [conservatoire|expérimental|indicatif|informatif|grâcieux|personnel|posthume] + à titre d’ exemple + à tombeau ouvert + à tort ou à raison + à tort et à travers + à tour de [bras|rôle] + à tout [âge|crin|instant|jamais|moment|prix] + à tout bout de champ + à tout le [moins|monde] + à tout point de vue + à tout un chacun + à toute [allure|bride|épreuve|force|vitesse|volée] + à toute heure + à toute heure du jour + à toute heure du jour et de la nuit + à toute heure de la nuit + à toute heure de la nuit et du jour + à tous crins + à tous points de vue + à toutes fins utiles + à toutes jambes + à tu et à toi + à un moment donné + à usage interne + à visage découvert + à visage humain + à vive allure + à voix [haute|basse] + à vol d’ oiseau + à vrai dire + à vue d’ œil + à ?bien¿ y regarder de plus près + à ?bien¿ y [penser|réfléchir|songer|repenser] + advienne que pourra + ah + après cette date ?fatidique¿ + après [moi|toi|soi|lui|eux] + après mûre réflexion + après tout , + après un certain temps + après un bon bout de temps + au-dessus [de|d’] {pronom_obj} + au-delà du descriptible + au [dernier|même|bon|mauvais] [moment|instant] + au bas mot + au beau fixe + au bon moment + au bout du [compte|rouleau] + au bout d’ un moment + au cas par cas + au commencement + au contraire + au coude à coude + au coup par coup + au cours des @:B [dernières|derniers|prochaines|prochains] [années|mois|siècles] <<- ~>> * + au demeurant + au doigt mouillé + au débotté + au début + au fil des ans + au fil du temps + au grand [complet|jamais] + au hasard + au jour et à l’ heure dits + au jugé + au le jour + au leur + au lieu de [cela|ceci|ça|quoi] + au loin + au milieu de nulle part + au moment opportun + au même titre que n’ importe [laquelle|lequel] d’ entre [nous|vous|eux|elles] + au pas de [charge|course] + au plus [près|pressé|vite|tôt|tard] + au plus haut point + au premier abord + au propre comme au figuré + au préalable + au quotidien + au ras des pâquerettes + au saut du lit + au sens [figuré|large|propre] + au surplus + au ~[xXvViI]+[eᵉ] siècle + ?tout¿ au fond [de|d’] {pronom_obj} + aux [abois|leurs|mien|miens|mienne|miennes|tien|tiens|tienne|tiennes|sien|siens|sienne|siennes|nôtres|vôtres] + autant que [nécessaire|possible|prévu] + autant que faire se peut + autour [de|d’] {pronom_obj} + autrement dit + av. J.-C. + avant longtemps + avant terme + avant tout le monde + avant toute chose + avant toutes choses + avant d’ aller plus loin + avant J.-C. + avant Jésus-Christ + avant d’ en arriver là + avant de faire quoi que ce soit + avant de faire quoi que ce soit [de|d’] ?@:W¿ [stupide|crétin|con|idiot] + avant [qu’|qu] il ne soit trop tard + avant un bon bout de temps + avec [brio|joie|légèreté|insistance|peine] + avec autre chose + avec le plus grand soin + avec pertes et fracas + avec un peu de chance + avec tout le respect que je [vous|te|leur|lui] dois + avec tout le respect que nous [vous|te|leur|lui] devons + avec tout un chacun + avec un peu de chance + beaucoup [plus|moins] + bel et bien + bien assez tôt + bien des fois + bien souvent + bon gré ?,¿ mal gré + bras dessus ?,¿ bras dessous + çà et là + ce faisant + [cela|ça|ceci] mis à part + [cela|ça|ceci] va sans dire + ces derniers temps + cette [fois-là|fois-ci] + chaque fois + comme avant + comme autrefois + comme d’ habitude + comme toujours + comme de juste + comme bon [me|te|lui|leur|nous|vous] semble + comme au bon vieux temps + comme cul et chemise + comme [frappé|frappée|frappés|frappées] par la foudre + comme n’ importe où ?ailleurs¿ + comme par [enchantement|magie] + comme par un fait exprès + comme promis + comme qui dirait + comme si de rien n’ était + contrairement aux apparences + contre mauvaise fortune,? bon cœur + contre nature + contre toute [attente|vraisemblance] + contre vents et marées + contre [mon|ton|son|notre|votre|leur] gré + côte à côte + [coute|coûte] que [coute|coûte] + [croyez-le|crois-le] ou [non|pas] + cul par-dessus tête + dans [ce|ces] [cas-là|cas-ci] + dans ce cas [précis|particulier] + dans ces cas [précis|particuliers] + dans l’ [idéal|immédiat] + dans la mesure du possible + dans les années ~\d\d+ + dans peu de temps + dans tout [cela|ça|ceci] + dans très peu de temps + dans un cas comme dans l’autre + dans une [certaine|large|moindre] mesure + début {mois} ~\d\d{2,5} + au début {mois} ~\d\d{2,5} + en ce début {mois} ~\d\d{2,5} + d’ abord + d’ affilée + d’ ailleurs + d’ année en année + d’ aujourd’hui + d’ antan + d’ autant [plus|moins] + d’ [autrefois|part] + d’ autres fois + d’ [arrache-pied|arrachepied] + d’ arrière en avant + d’ avant en arrière + d’ à côté + d’ âge mûr + d’ emblée + d’ empoigne + d’ en face + d’ entre [nous|vous|eux|elles] + d’ entrée de jeu + d’ est en ouest + d’ extrême [droite|gauche] + d’ [extrême-droite|extrême-gauche] + d’ [égal|égale] à [égal|égale] + d’ habitude + d’ heure en heure + d’ hier ?[matin|soir|après-midi]¿ + d’ ici ?[là|peu]¿ + d’ ici peu de temps + d’ ordinaire + d’ origine [inconnue|douteuse|plébéienne|aristocratique] + d’ ordre général + d’ où [qu’|qu] [il|elle|on] vienne + d’ où [qu’|qu] [ils|elles] viennent + d’ ouest en est + d’ [ors|ores] et déjà + d’ un autre côté + d’ un [bout|jour] à l’ autre + d’ un côté comme de l’ autre + d’ un commun accord + d’ une autre trempe + d’ une [façon|manière] ou d’une autre + d’ une certaine [façon|manière] + d’ une tout autre ampleur + d’ une [minute|seconde] à l’ autre + de-ci ?,¿ de-là + de ~\d+(?:,\d+|) % + de [cesse|conserve|facto|fait|guingois|luxe|nouveau|permanence|partout|préférence|profundis|rechange|routine|surcroît|surcroit|visu] + de A à Z + de bas (?:en haut|étage) + de basse [condition|extraction] + de bon [aloi|cœur|gré|matin|sens|ton] + de bonne [facture|famille|foi|heure|humeur|grâce|qualité|compagnie] + de bric et de broc + de but en blanc + de ce fait ?[incontestable|irréfutable|même]¿ + de ce seul fait + de ce point de vue + de cette sorte + de cet acabit + de courte [durée|vue] + de dernière minute + de demain [matin|soir|après-midi] + de droite à gauche + de droite comme de gauche + de fâcheuse mémoise + de fil en aiguille + de fond en comble + de fort [loin|près] + de [fraîche|fraiche] date + de [gaieté|gaîté|gaité] de cœur + de gauche à droite + de gauche comme de droite + de grande [taille|envergure] + de gré ou de force + de guerre lasse + de haut en bas + de haut [rang|vol] + de haute [lutte|stature|volée] + de jour comme de nuit + de là-bas + de la meilleure [manière|façon] possible + de la même [façon|manière] + de la sorte + de la tête aux pieds + de la veille + de loin + de loin en loin + de longue [date|durée|haleine] + de main de [maître|maitre] + de mauvais [aloi|goût|gout|gré] + de mauvaise [foi|grâce|humeur] + de mieux en mieux + de nature [étrangère|inconnue|indéterminée|insolite] + de nombreuses années plus [tôt|tard] + de nombreuses fois + de nos jours + de notoriété publique + de nulle part + de pire en pire + de près + de près ou de loin + de par le monde ?entier¿ + de part et d’autre + de petite taille + de pied ferme + de plein [droit|fouet] + de plus [belle|près] + de premier [ordre|plan] + de première [catégorie|main|nécessité] + de prime abord + de proche en proche + de pure forme + de sang-froid + de seconde [catégorie|zone|importance|main] + de si bon matin + de si bonne heure + de source sûre + de taille moyenne + de telle sorte + de temps à autre + de temps en temps + de [très|trop] [loin|près] + de vive voix + de [nous|vous] tous + de tous [côtés|bords] + de tout [poil|temps] + de tout à l’ heure + de tout premier [ordre|plan] + de toute [éternité|évidence|façon|urgence] + de toutes [parts|pièces|sortes] + de toutes [mes|tes|ses|nos|vos|leurs] forces + de [mon|ton|son|notre|votre|leur] mieux + de [mon|ton|son|notre|votre|leur] plein gré + de [mon|ton|son|notre|votre|leur] point de vue + de [mon|ton|son|notre|votre|leur] propre [cru|chef] + de [mon|ton|son|notre|votre|leur] vivant + de [ma|ta|sa|notre|votre|leur] part + de moins en moins + de plus en plus + de mieux en mieux + de pire en pire + de jour en jour + de minute en minute + de semaine en semaine + de mois en mois + de trimestre en trimestre + de semestre en semestre + de siècle en siècle + de millénaire en millénaire + de décennie en décennie + [après-demain|demain] ?[matin|soir|après-midi]¿ + avant demain ?[matin|soir|après-midi]¿ + depuis @:B [ans|années|mois|semaines|jours|heures|minutes|secondes] + depuis ~\d+ [ans|années|mois|semaines|jours|heures|minutes|secondes] + depuis assez longtemps + depuis belle lurette + depuis bien longtemps + depuis de ?très¿ longues années + depuis des lustres + depuis les années ~\d\d+ + depuis longtemps + depuis lors + depuis peu de temps + depuis quelque temps + depuis quelques [secondes|minutes|heures|jours|semaines|mois|trimestres|semestres|années|décennies|siècles|millénaires] + depuis si longtemps + depuis toujours + depuis tout ce temps + depuis très longtemps + des fois + des pieds à la tête + des uns et des autres + des [années|mois|siècles|millénaires|décennies|semaines] plus [tôt|tard] + dès [maintenant|lors|aujourd’hui] + dès à présent + dès que possible + dès [demain|hier] ?[soir|matin|après-midi]¿ + Dieu [m’|t’] en [garde|préserve] + Dieu [m’|t’] en soit témoin + Dieu [nous|vous] en [garde|préserve] + Dieu [nous|vous] en soit témoin + Dieu [les|l’] en [garde|préserve] + Dieu [leur|lui] en soit témoin + du ~[xXvViI]+[eᵉ] siècle + du [Ier|Iᵉʳ|1er|1ᵉʳ] siècle + du bout des lèvres + du début à la fin + du fond du cœur + du jour au lendemain + du haut en bas + du même [acabit|coup|ordre|tonneau] + du moins ?,¿ pas + du [nord|sud] au [nord|sud] + du tout au tout + eh bien + en \d\d+ [ans|années|mois|semaines|jours|heures|minutes|secondes] + en [aparté|apparence|arrière|avance|avant|cachette|ceci|cela|clair|commun|conséquence|continu|contrepartie|définitive|détail|direct|douce|effet|émoi|filigrane|général|goguette|hâte|majorité|outre|pâmoison|parallèle|partie|particulier|permanence|personne|pratique|prime|privé|principe|priorité|public|réalité|retour|revanche|rien|rogne|route|secret|silence|somme|suspens|théorie|trompe-l’œil|vain|vérité|ville|vitesse] + en aucun cas + en aucune [circonstance|façon|manière] + en bon état + en bonne [compagnie|posture|voie] + en bonne et due forme| + en bonne santé ?[physique|mentale|psychique]¿ + en bout de course + en cas [de|d’] [besoin|doute|urgence] + en [chacun|chacune] [de|d’] [nous|vous|eux|elles] + en chair et en os + en chute libre + en comparution immédiate + en connaissance de cause + en coupe réglée + en cours de route + en d’autres [circonstances|termes|temps] + en de telles circonstances + en début [de|d’] [journée|matinée|soirée|après-midi] + en définitive + en dehors de ?tout¿ [ça|cela|ceci] + en dents de scie + en dernier [lieu|recours|ressort] + en désespoir de cause + en détention provisoire + en direction de l’ [est|ouest] + en direction du [nord|nord-est|nord-ouest|sud|sud-est|sud-ouest] + en état de choc ?circulatoire¿ + en état de marche + en état d’ ébriété ?avancée¿ + en excellent état + en file indienne + en fin [de|d’] [compte|journée|matinée|soirée|après-midi] + en forte [baisse|hausse] + en gage de bonne foi + en garde à vue ?prolongée¿ + en grand nombre + en grende [difficulté|majorité|partie|pompe] + en haut lieu + en l’occurrence + en lieu sûr + en ligne de [compte|mire] + en mains propres + en mauvais état + en mauvaise [posture|santé] + en même temps + en milieu [de|d’] [journée|matinée|soirée|après-midi] + en nombre suffisant + en nombre plus que suffisant + en partant de zéro + en plein [air|cœur|jour] + en pleine [gueule|figure|forme|poire|nuit|tronche] + en perte de vitesse + en peu de temps + en piteux état + en point [de|d’] [mire|orgue] + en position de [force|faiblesse] + en premier lieu + en première [instance|ligne] + en pure perte + en quantité suffisante + en quantité plus que suffisante + en quelque sorte + en queue de peloton + en rangs serrés + en rase campagne + en règle générale + en roue libre + en sens inverse + en si peu de temps + en sous-main + en tête à tête + en temps et en heure + en temps [normal|opportun|ordinaire|utile|voulu] + en termes choisis + en toile de fond + en tous les cas + en tous les sens + en tout bien tout honneur + en tout [cas|genre|lieu|sens|temps] + en tout et pour tout + en tout état de cause + en tout premier lieu + en toute bonne foi + en toute connaissance de cause + en toute [circonstance|confiance|discrétion|franchise|hâte|impartialité|impunité|innocence|légalité|liberté|logique|sécurité|simplicité] + en toutes circonstances + en un clin d’œil + en un rien de temps + en une autre occasion + en vase clos + en voie de développement + en y réfléchissant bien + en [janvier|février|mars|avril|mai|juin|juillet|août|aout|septembre|octobre|novembre|décembre] dernier + en [mon|ton|son|leur|notre|votre] âme et conscience + en [mon|ton|son|leur|notre|votre] for intérieur + en [mon|ton|son|leur|notre|votre] nom propre + en ce [moment|temps-là] + en ce qui [me|te|le|la|les|nous|vous] [concerne|concernait] + en cet instant + encore une fois + encore et [encore|toujours] + entre {mois} ?~\d{2,5}¿ et {mois} ?~\d{2,5}¿ + entre chien et loup + entre de [bonnes|mauvaises] mains + entre l’ [un|une] et l’ autre + entre les uns et les autres + entre [quatre|quatr’|quat’] [zyeux|yeux] + entre [quatre-zyeux|quatr’zyeux|quat’zyeux|quatre-yeux|quatr’yeux|quat’yeux] + envers et contre tout + épaule contre épaule + et ainsi de suite + et tutti quanti + été comme hiver + euh + face à face + @:B fois de suite + grosso modo + [hier|avant-hier] ?[matin|soir|après-midi]¿ + hors [de|d’] [contrôle|portée|atteinte] + hors d’ état de nuire + hors du commun + ici [comme|ou] ailleurs + ici et [là|maintenant] + ici même + ici-bas + il y a ?très¿ longtemps + il y a ?très¿ peu de temps + il y a quelques [secondes|minutes|heures|jours|semaines|mois|année|ans|siècles|millénaires] + il y a moins de ~\d+ [secondes|minutes|heures|jours|semaines|mois|année|ans|siècles|millénaires] + il y a ~\d+ [secondes|minutes|heures|jours|semaines|mois|année|ans|siècles|millénaires] + il y a quelque temps + il n’y a pas ?si¿ longtemps + illico presto + j’ en passe et des [meilleurs|meilleures] + jour pour jour + [jusqu’|jusqu] [alors|ici|aujourd’hui|Noël|Pâques] + [jusqu’|jusqu] au bout des ongles + [jusqu’|jusqu] au nouvel an + [jusqu’|jusqu] à aujourd’hui + [jusqu’|jusqu] à bac + [jusqu’|jusqu] à présent + [jusqu’|jusqu] à maintenant + [jusqu’|jusqu] à récemment + [jusqu’|jusqu] à [demain|hier] ?[matin|soir|après-midi]¿ + [jusqu’|jusqu] à nouvel ordre + [jusqu’|jusqu] à plus ample informé + [jusqu’|jusqu] à plus soif + [jusqu’|jusqu] à preuve du contraire + [jusqu’|jusqu] à la fin de [mes|tes|ses|nos|vos|leurs] jours + [jusqu’|jusqu] à la fin des temps + [jusqu’|jusqu] à la tombée de la nuit + [jusqu’|jusqu] à [mon|ton|son|notre|votre|leur] dernier souffle + [jusqu’|jusqu] à [mon|ton|son|notre|votre|leur] dernier souffle de vie + [jusqu’|jusqu] à ce que mort s’ensuive + [jusqu’|jusqu] à ce que [j’|il|elle|on] en sache plus + [jusqu’|jusqu] à ce que tu en saches plus + [jusqu’|jusqu] à ce que nous en sachions plus + [jusqu’|jusqu] à ce que vous en sachiez plus + [jusqu’|jusqu] à ce que [ils|elles] en sachent plus + jusque-là + la plupart du temps + la main dans la main + là-bas + là-haut + là-dedans + là-dehors + là-derrière + là-dessous + là-dessus + là-devant + là non plus + la mort dans l’ âme + le cas échéant + le moins du monde + le [moins|plus] [tôt|tard|souvent] + le [moins|plus] de [temps|monde] possible + le moment venu + les [uns|unes] des autres + les [uns|unes] [après|avec|chez|contre|de|derrière|devant|envers|malgré|pour|sans|sous|sur] les autres + l’ [un|une] [après|avec|chez|contre|de|derrière|devant|envers|malgré|pour|sans|sous|sur] l’ autre + l’ [un|une] près de l’autre + loin [de|d’] là + loin [de|d’] tout [ça|cela|ceci] + loin d’ [être|ici] + loin s’ en [faut|fallait] + maintes fois + malgré [ça|cela|ceci|tout] + manu militari + mieux [vaut|valait] tard que jamais + moins que [nécessaire|prévu] + moitié ** ?,¿ moitié ** + mot pour mot + ne [lui|leur|m’|t’|nous|vous] en déplaise + nez à nez + non loin [de|d’] [ici|là] + nulle part + ô combien + oh + ou quelque chose d’ approchant + où bon [me|te|lui|nous|vous|leur] semble + oui [ou|et] non + outre mesure + ni de près ?,¿ ni de loin + ni plus ?,¿ ni moins + ni vu ?,¿ ni connu + non [plus|seulement] + non sans raison + quant à présent + par [à-coups|ailleurs|avance|chance|conséquent|curiosité|contre|défaut|définition|endroits|essence|excellence|exemple|hasard|ici|inadvertance|là|moments|nature|principe|terre] + par acquit de conscience + par beau temps + par bien des [aspects|côtés] + par bonté de cœur + par ce biais + par certains [aspects|côtés] + par la même occasion + par la suite + par le passé + par les temps qui courent + par monts et par vaux + par temps de pluie + par tout le monde + par voie de [conséquence|mer|terre] + par voie d’exception + par @:B fois + un par un + une par une + deux par deux + trois par trois + quatre par quatre + cinq par cinq + six par six + sept par sept + huit par huit + neuf par neuf + dix par dix + onze par onze + douze par douze + treize par treize + quatorze par quatorze + quinze par quinze + seize par seize + vingt par vingt + trente par trente + quarante par quarante + cinquante par cinquante + soixante par soixante + cent par cent + mille par mille + bout par bout + étage par étage + étape par étape + fragment par fragment + morceau par morceau + niveau par niveau + pièce par pièce + par-ci ?,¿ par-là + par-devant + par-derrière + par-dessus le marché + par-dessus tout + partant de là + pas du tout + pas à pas + pas le moins du monde + pendant ce temps-là + pendant ?[bien|si|assez|très]¿ longtemps + pendant plusieurs [heures|minutes|secondes|mois|semaines|jours|années|siècles|millénaires|décennies] + pendant quelque temps + petit à petit + peu à peu + peu de temps auparavant + peu ou prou + pile poil + plein [nord|sud|ouest] + plein de fois + plus bas que terre + plus d’ une fois + plus du tout + plus jamais + plus que [nécessaire|prévu|jamais] + plus que tout au monde + plus que toute autre chose + plus [tôt|tard] que [prévu|nécessaire] + plusieurs fois + plusieurs fois de suite + pour ainsi dire + pour ce faire + pour ce que [j’|tu] en [sais|savais] + pour couronner le tout + pour de bon + pour faire bonne mesure + pour faire simple + pour la [première|seconde|dernière|~ième$] fois + pour la [première|seconde|dernière|~ième$] fois de ma vie + pour la [première|seconde|dernière|~ième$] fois de suite + pour la suite + pour le [moment|moins] + pour le meilleur et pour le pire + pour l’ [essentiel|instant|heure] + pour quelque [part|temps] + pour rien au monde + pour tout dire + pour un oui ou pour un non + pour une fois + pour y parvenir + pour ça [vaut|valait] + pour [ma|ta|sa|notre|votre|leur] [gouverne|part] + pour [mon|ton|son|notre|votre|leur] propre [compte|bien] + pour [celui|celle|ceux|celles] que [ça|cela|ceci] intéresse + pour [celui|celle|ceux|celles] et [celui|celle|ceux|celles] que [ça|cela|ceci] intéresse + pour [m’|t’|s’|nous|vous] en rendre compte + quand bien même + quand bon [me|te|lui|nous|vous|leur] [semble|semblera|semblait] + quant à [ça|cela|ceci] + que [ça|ceci|cela] [me|te|lui|leur|nous|vous] plaise ou non + que je le veuille ou non + que tu le veuilles ou non + [qu’|qu] [il|elle|on] le veuille ou non + que vous le vouliez ou non + que nous le voulions ou non + [qu’|qu] [ils|elles] le veuillent ou non + [qu’|qu] à cela ne tienne + quel [qu’|qu] en soit le [moyen|prix|danger] + quel [qu’|qu] en soit le risque ?financier¿ + quelle [qu’|qu] en soit la [cause|raison] + quelque [part|temps] + quelques fois + quelques [instants|secondes|minutes|heures|jours|semaines|mois|années|décennies|siècles|millénaires|trimestres|semestres] auparavant + quelques [instants|secondes|minutes|heures|jours|semaines|mois|années|décennies|siècles|millénaires|trimestres|semestres] plus [tard|tôt] + qui plus est + quoi [qu’|qu] il [arrive|arrivât|advienne|advînt] + quoi [qu’|qu] il en [coûte|coûtât|coute|coutât] + sans [grande|grosse] difficulté ?[apparente|aucune|financière|majeure|particulière]¿ + sans [ambages|arrêt|cesse|conteste|doute|encombre|encombres|fin|relâche|répit|trêve|vergogne] + sans aucun doute + sans autre forme de procès + sans commune mesure + sans coup férir + sans crier gare + sans difficulté ?[apparente|aucune|financière|majeure|particulière]¿ + sans dire mot + sans états d’ âme + sans foi ?,¿ ni loi + sans l’ ombre d’ un doute + sans le faire exprès + sans le vouloir + sans mot dire + sans nul doute + sans queue ni tête + sans raison apparente + sans ?grand¿ succès + sans faire de vagues + sans s’ en rendre compte + sans s’ en apercevoir + sans l’ aide de personne + sans y faire attention + sans y prendre [garde|goût|gout] + sans y [parvenir|réussir|réfléchir|songer|penser] + sans pour autant y faire attention + sans pour autant y prendre [garde|goût|gout] + sans pour autant y [parvenir|réussir|réfléchir|songer|penser] + séance tenante + selon toute vraisemblance + semble-t-il + semblait-il + sens dessus dessous + [seule|seul] à [seule|seul] + s’ il [te|vous] [plaît|plait] + si besoin est + si [bas|haut|longtemps|nécessaire|possible|soudain] + si [cela|ça|ceci] ne tenait [qu’|qu] à [moi|toi|lui|eux|elle|elles|nous|vous] + six pieds sous terre + sine die + sine qua non + soit dit en passant + soi-disant + sous aucun prétexte + sous bonne [escorte|garde] + sous coupe réglée + sous haute surveillance + stricto sensu + sur ce , + sur ce plan-là + sur le [long|moyen|court] terme + sur le qui-vive + sur la forme comme sur le fond + sur la même longueur d’ onde + sur [mon|ton|son|notre|votre|leur] [trente-et-un|31] + sur [mon|ton|son|notre|votre|leur] trente et un + tant bien que mal + tant s’ en faut + tôt ou tard + tous comptes faits + tous frais payés + tout à [fait|coup] + tout à l’ heure + tout au plus + tout aussi bien + tout bien [considéré|réfléchi] + tout compte fait + tout de [même|suite|go] + tout du long + tout [bonnement|simplement] + tout feu ?,¿ tout [flamme|flammes] + tout le temps + toutes affaires cessantes + toutes choses égales par ailleurs + toutes griffes dehors + toutes proportions gardées + trait pour trait + très [bas|haut|bien|mal] + un à un + une à une + un jour ou l’autre + un instant plus [tôt|tard] + un [millier|million|milliard] de fois + un moment plus [tôt|tard] + un peu mieux + un peu moins bien + un peu partout + un peu plus [tôt|tard] que prévu + un tant soit peu + une à une + une autre fois + une bonne fois pour toutes + une dernière fois + une fois de plus + une fois n’ est pas coutume + une fois pour toutes + urbi et orbi + vaille que vaille + ventre à terre + vers nulle part + <<- ~>> * + + +@@@@ +@@@@END_GRAPH _ +@@@@ + + __[i](p_plus_avant)__ plus avant(?! de | que?) <<- ~>> * -__[i](p_plus_qqch)__ plus (?:du tout|que (?:nécessaire|prévu|jamais|tout(?: au monde|e autre chose))|jamais|bas que terre|d’une fois) <<- ~>> * -__[i](p_plusieurs_fois)__ plusieurs fois(?: de suite)? <<- ~>> * -__[i](p_pour_qqch)__ pour (?:autrui|le (?:moment|moins|meilleur et pour le pire)|une fois|l’(?:essentiel|instant)|l’heure|de bon|la suite|un oui ou pour un non|ainsi dire|ce faire|quelque (?:part|temps)|tout (?:le monde|un chacun|dire)|faire (?:bonne mesure|simple)|y parvenir|couronner le tout|rien au monde|ce que (?:(?:j’|tu )en sais)|ça va(?:ut|lait)) <<- ~>> * -__[i](p_pour_pronom)__ pour (?:[mt]oi|elles?|eux|ça|cela|ceci|ceux-(?:là|ci)|celles?-(?:là|ci))(?! qui) <<- ~>> * -__[i](p_pour_xxx_fois)__ pour la (?:première|seconde|{w_2}ième|dernière) fois(?: de suite| de ma vie|) <<- ~>> * -__[i](p_pour_det_fem_qqch)__ pour (?:[mts]a|[nv]otre|leur) (?:gouverne|part) <<- ~>> * -__[i](p_pour_det_mas_qqch)__ pour (?:[mts]on|[nv]otre|leur) propre (?:compte|bien) <<- ~>> * -__[i](p_pour_xxx_que_ça_intéresse)__ pour ce(?:lles?|ux|lui) (?:et ce(?:lles?|ux|lui) |)que (?:ça|ce(?:la|ci)) intéresse <<- ~>> * -__[i](p_pour_s_en_rendre_compte)__ pour (?:[mts]’|[vn]ous )en rendre compte <<- ~>> * -__[i](p_quand_qqch)__ quand b(?:ien même|on (?:[mt]e|l(?:ui|eur)|[nv]ous) semble) <<- ~>> * -__[i](p_quant_à_pronom1)__ quant à (?:[mt]oi|lui|elles?|[nv]ous|eux)(?! qui) <<- ~>> * -__[i](p_quant_à_pronom2)__ quant à (?:ça|cela|ceci) <<- ~>> * -__[i](p_que_ça_plaise_ou_non)__ que (?:ça|ceci|cela) (?:me|te|l(?:ui|eur)|[nv]ous) plaise ou non <<- ~>> * -__[i](p_que_voulu_ou_non)__ que (?:je le veuille|tu le veuilles|vous le vouliez|nous le voulions) ou non <<- ~>> * -__[i](p_que_xxx_ou_non)__ qu (?:à cela ne tienne|(?:(?:il|elle|on) le veuille|(?:ils|elles) le veuillent) ou non) <<- ~>> * -__[i](p_quel_qu_en_soit_le_qqch)__ quel qu en soit le (?:moyen|prix|risque(?: financier|)|danger) <<- ~>> * -__[i](p_quelle_qu_en_soit_la_qqch)__ quelle qu en soit la (?:cause|raison) <<- ~>> * -__[i](p_quelque_qqch)__ quelque(?: (?:part|temps)|s fois) <<- ~>> * -__[i](p_quelques_tps_adv)__ quelques (?:instants|secondes|minutes|heures|jours|semaines|mois|années|décennies|siècles|millénaires|trimestres|semestres) (?:auparavant|plus (?:tard|tôt)) <<- ~>> * -__[i](p_qui_plus_est)__ qui plus est <<- ~>> * + + __[i](p_qui_loc_tps)__ qui (ce (?:jour|matin|après-midi|soir)-là|cette (?:nuit|matinée|soirée)-là) @@4 <<- ~1>> * -__[i](p_quoi_qu_il_qqch)__ quoi qu il (?:(?:arriv|en co[ûu]t)(?:e|ât)|adv(?:ienne|înt)) <<- ~>> * -__[i](p_sans_difficulté)__ sans (?:grande|grosse) difficulté(?: apparente| aucune| financière| majeure| particulière|) <<- ~>> * -__[i](p_sans_qqch)__ sans (?:ambages|arrêt|au(?:cun doute|tre forme de procès)|cesse|commune mesure|conteste|coup férir|crier gare|difficulté(?: apparente| aucune| financière| majeure| particulière|)|dire mot|doute|encombres?|états d’âme|fin|foi,? ni loi|l’ombre d’un doute|le (?:faire exprès|vouloir)|mot dire|nul doute|queue ni tête|raison apparente|relâche|répit|(?:grand |)succès|trêve|vergogne|(?:pour autant |)y (?:prendre g(?:arde|o[ûu]t)|faire attention|parvenir|réussir|réfléchir|songer|penser)|faire de vagues|s’en (?:rendre compte|apercevoir)|l’aide de personne) <<- ~>> * -__[i](p_séance_tenante)__ séance tenante <<- ~>> * -__[i](p_selon_qqch)__ selon (?:toute vraisemblance|(?:[mt]oi|lui|elles?|eux|nous|vous)(?! qui)) <<- ~>> * -__[i](p_semble_t_il)__ sembl(?:e-t-il|ait-il) <<- ~>> * -__[i](p_sens_dessus_dessous)__ sens dessus dessous <<- ~>> * -__[i](p_seul_à_seul)__ seule?s? à seule?s? <<- ~>> * -__[i](p_stp_svp)__ s’il (?:te|vous) pla[îi]t <<- ~>> * -__[i](p_si_qqch)__ si (?:bas|besoin est|haut|longtemps|nécessaire|possible|soudain|(?:cela|ça) ne tenait qu à (?:moi|toi|lui|eux|elles?|nous|vous)) <<- ~>> * -__[i](p_six_pieds_sous_terre)__ six pieds sous terre <<- ~>> * -__[i](p_sine_loc_latine)__ sine (?:die|qua non) <<- ~>> * -__[i](p_soi_qqch)__ soi(?:t dit en passant|-disant) <<- ~>> * -__[i](p_sous_qqch)__ sous (?:aucun prétexte|bonne (?:escorte|garde)|coupe réglée|haute surveillance) <<- ~>> * -__[i](p_stricto_sensu)__ stricto sensu <<- ~>> * -__[i>(p_sur_ce)__ sur ce, <<- ~>> * -__[i](p_sur_qqch)__ sur (?:ce plan-là|le (?:(?:long|moyen|court) terme|qui-vive)|la (?:forme comme sur le fond|même longueur d’onde)|(?:leur|[mts]on|[nv]otre) (?:trente[ -]et[ -]un|31)) <<- ~>> * -__[i](p_tant_qqch)__ tant (?:bien que mal|s’en faut) <<- ~>> * -__[i](p_tôt_ou_tard)__ tôt ou tard <<- ~>> * -__[i](loc_tour_à_tour)__ - tours? [àa] tours? - <<- not re.search("(?i)^tour à tour$", \0) ->> tour à tour # Locution adverbiale invariable. Écrivez “tour à tour”.|https://fr.wiktionary.org/wiki/tour_%C3%A0_tour - <<- ~>> * -__[i](p_tous_qqch)__ tous (?:comptes faits|frais payés) <<- ~>> * -__[i](p_tout_qqch)__ tout (?:à (?:fait|coup|l’heure)|le temps|de (?:même|suite|go)|au plus|aussi bien|simplement|bonnement|compte fait|feu,? tout flammes?|bien (?:considéré|réfléchi)|du long) <<- ~>> * -__[i](p_toutes_qqch)__ toutes (?:affaires cessantes|choses égales par ailleurs|griffes dehors|proportions gardées) <<- ~>> * -__[i](p_trait_pour_trait)__ trait pour trait <<- ~>> * -__[i](p_très_adverbe)__ très (?:bas|haut|bien|mal) <<- ~>> * -__[i](p_un_à_un)__ (une?) à \1 @@0 <<- ~>> * -__[i](p_un_qqch)__ un (?:à un|jour ou l’autre|instant plus (?:tôt|tard)|milli(?:er|on|ard) de fois|moment plus (?:tôt|tard)|peu (?:mieux|moins bien|partout|plus t(?:ôt|ard) que prévu)|tant soit peu) <<- ~>> * -__[i](p_plus_tôt_tard_que)__ plus t(?:ôt|ard) que (?:prévu|nécessaire) <<- ~>> * -__[i](p_une_qqch)__ une (?:à une|autre fois|bonne fois pour toutes|dernière fois|fois(?: pour toutes| de plus| n’est pas coutume)) <<- ~>> * -__[i](p_une_fois)__ une fois <<- ~>> _ -__[i](p_urbi_et_orbi)__ urbi et orbi <<- ~>> * -__[i](p_v_divers)__ v(?:aille que vaille|entre à terre|ers nulle part) <<- ~>> * + + + + TEST: ils vont et viennent, toujours {{cotes a cotes}}… TEST: Nous irons {{tours à tours}} chercher du bois. TEST: Ma thèse en 180 secondes. -# je / tu / il / elles / nous / vous / ils / elles +# je / tu / il / elles / nous / vous / ils / elles __[i](p_je_vous_en_prie)__ je (?:t’en|vous en) (?:prie|supplie) <<- ~>> * __[i](p_nous_vous_en_prions)__ nous (?:t’en|vous en) (?:prions|supplions) <<- ~>> * # mot-là __[i](p_qqch_tiret_là)__ ({w1})(-là) @@0,$ <<- morphex(\1, ":[NAQ]", ":G") ~2>> * @@ -5140,10 +6368,12 @@ # Après __[i](p_adv_longtemps)__ (?:bien|si|assez) longtemps <<- ~>> * __[i](p_plus_loc_adv)__ plus (?:près|loin|tôt|tard|ou moins|que (?:nécessaire|jamais)|d’une fois) <<- ~>> * ## Simplification partielle +__[i](p_ceux_d_entre_pronom)__ ce(?:lui|lles?|ux) (d’entre (?:[nv]ous|eux|elles)) @@$ <<- ~1>> * +__[i](p_chacun_d_entre_nous)__ chacune? (d’entre (?:[nv]ous|eux|elles)) @@$ <<- ~1>> * __[i](p_tout_au_long_de)__ (tout au long) d(?:es?|u) @@0 <<- not morph(word(-1), ":R", False, False) ~1>> au __[i](p_à_loc_de1)__ à (bonne distance|bord|cause|contre-courant|côté|court|défaut|droite|gauche|hauteur|l’(?:aff[ûu]t|arrière|autre bout|aune|avant|écart|égard|extérieur|encontre|ins(?:u|tar)|intérieur|opposé|orée|approche)|la (?:hauteur|portée|suite)|partir|portée|pro(?:ximité|pos)|quelques (?:mètres|kilomètres|lieues|pas|centaines de mètres|minutes|heures)|rebours) d(?:es?|u) @@2 <<- ~1>> * __[i](p_à_loc_de2)__ à (base|force|grand(?: renfort|s coups)|raison) de? @@2 <<- ~1>> * __[i](p_au_loc_de)__ au (bout|beau milieu|courant|cours|détriment|fin fond|grand dam|fur et à mesure|gré|l(?:ieu|ong|arge)|milieu|nez et à la barbe|plus profond|profit|s(?:ein|ortir|ujet)|vu(?: et au su|)) d(?:es?|u) @@3 <<- ~1>> * __[i](p_aux_loc_de)__ aux (abords|dépens) d(?:es?|u) @@4 <<- ~1>> * @@ -5154,11 +6384,11 @@ __[i](p_en_loc_de2)__ en (flagrant délit|matière) de? @@3 <<- ~1>> * __[i](p_en_proie_à)__ (en proie) à @@0 <<- ~1>> * __[i](p_eu_égard_à)__ (eu égard) (?:à|aux?) @@0 <<- ~1>> * __[i](p_la_une_de)__ la (une) d(?:es?|u) @@3 <<- ~1>> _ __[i](p_le_long_de)__ le (long) d(?:es?|u) @@3 <<- ~1>> _ -__[i](p_par_le_biais_de)__ par (le biais|l’entremise) d(?:es?|u) @@4 <<- ~1>> * +__[i](p_par_le_biais_de)__ par (le biais|l’entremise) d(?:es?|u) @@4 <<- ~1>> * __[i](p_pour_ou_contre)__ pour (ou contre) @@5 <<- ~1>> * __[i](p_rien_comparé_à)__ rien +(comparé) +à @@w <<- ~1>> * __[i](p_suite_à)__ (suite) (?:à|aux?) @@0 <<- not before(r"(?i)\b(?:une|la|cette|[mts]a|[nv]otre|de) +") ~1>> * __[i](p_vent_debout_contre)__ (vent debout) contre @@0 <<- ~1>> * @@ -5165,10 +6395,18 @@ # Déterminant + nombre __[i](p_dét_plur_nombre_nom)__ (?:[dmts]es|nos|vos|le(?:ur|)s) (\d+(?: ou \d+|)) ({w_2}) @@w,$ <<- morphex(\2, ":[NA].*:[pi]", ":(?:V0|3p)|>(?:janvier|février|mars|avril|mai|juin|juillet|ao[ûu]t|septembre|octobre|novembre|décembre|vendémiaire|brumaire|frimaire|nivôse|pluviôse|ventôse|germinal|floréal|prairial|messidor|thermidor|fructidor)") ~1>> * + + +__[i](p_du_moins)__ du moins <<- ~>> _ +__[i](p_don_Juan)__ (don) Juan @@0 <<- ~1>> * +__[i](p_le_pour_et_le_contre)__ le pour et le contre <<- ~>> =\0.replace(" ", "_") +__[i](p_ou_bien)__ ou (bien) @@3 <<- ~1>> * +__[i](p_une_fois)__ une fois <<- ~>> _ + ## Simplifications des substantifs __[i](loc_arc_à_poulies)__ arcs? (([àa]) poulies) @@$,w <<- \2 == "a" -2>> à # Confusion : “a” est une conjugaison du verbe “avoir”. Pour la préposition, écrivez “à”. @@ -5202,11 +6440,11 @@ __[i](loc_chair_à)__ chairs? (([àa]) (?:pâté|canons?)) @@$,w <<- \2 == "a" -2>> à # Confusion : “a” est une conjugaison du verbe “avoir”. Pour la préposition, écrivez “à”. <<- ~1>> * __[i](p_chambre_de)__ chambres? (d’(?:agriculture|hôtes?)|de (?:commerce|compensation|décompression|dégrisement)) @@$ <<- ~1>> * -__[i](p_chemin_de_traverse)__ chemins? (de traverse) @@$ <<- ~1>> * +__[i](p_chemin_de_traverse)__ chemins? (de (?:traverse|fer)) @@$ <<- ~1>> * __[i](p_chili_con_carne)__ chilis? (con carne) @@$ <<- ~1>> * __[i](p_chef_d_œuvre)__ chefs?(-d’œuvre) @@$ <<- ~1>> * __[i](p_clair_comme)__ claire?s? (comme (?:de l’eau de (?:boudin|roche|source)|du (?:cristal|jus de (?:boudin|chaussettes?|chique)))) @@$ <<- ~1>> * __[i](p_commis_d_office)__ commise?s? (d’office) @@$ <<- ~1>> * __[i](p_convention)__ conventions? (récepteur|générateur) @@$ <<- ~1>> * @@ -5289,13 +6527,13 @@ __[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_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>> * __[i](p_partie_de_jambe_en_l_air)__ parties? (de jambes en l’air) @@$ <<- ~1>> * @@ -5426,18 +6664,18 @@ <<- ~>> soixante __[i](p_80_qqch)__ quatre-vingt-(?:un|d(?:eux|ix|ouze)|tr(?:ois|eize)|quat(?:re|orze)|cinq|six|sept|huit|neuf|onze|quinze|seize) <<- ~>> quatre-vingts __[i](p_qqch_100)__ - ((?:d(?:eux|ouze)|tr(?:ois|eize)|quat(?:re|orze)|cinq|s(?:ix|seize)|sept|huit|neuf|onze|quinze) cents) +({w_2}) @@0,$ + ((?:d(?:eux|ouze)|tr(?:ois|eize)|quat(?:re|orze)|cinq|s(?:ix|seize)|sept|huit|neuf|onze|quinze) cents) +({w_2}) @@0,$ <<- morphex(\2, ":[NAQ].*:[pi]", ":(?:G|3p)") ~1>> cent __[i](p_qqch_1000)__ (?:deux|trois|quatre|cinq|six|sept|huit|neuf|cent) (?:cent |)mille <<- ~>> mille __[i](p_det_plur_nombre_nom)__ - (?:le(?:ur|)s|des|ses|ces|mes|tes|nos|vos) +((?:quelque +|)(?:d(?:eux|ix|ouze)|tr(?:ois|eize|ente)|qua(?:t(?:re(?:-vingts|)|orze)|rante)|cinq(?:uante|)|s(?:ix|eize|oixante)|sept|huit|neuf|onze|quinze|vingt|cent|mille|\d+)) +({w_2}) @@w,$ + (?:le(?:ur|)s|des|ses|ces|mes|tes|nos|vos) +((?:quelque +|)(?:d(?:eux|ix|ouze)|tr(?:ois|eize|ente)|qua(?:t(?:re(?:-vingts|)|orze)|rante)|cinq(?:uante|)|s(?:ix|eize|oixante)|sept|huit|neuf|onze|quinze|vingt|cent|mille|\d+)) +({w_2}) @@w,$ <<- morphex(\2, ":[NAQ].*:[pi]", ":(?:G|3p)") ~1>> * __[i](p_une_heure)__ (?:à |d(?:e +|’))une +heure(?: (?:d(?:eu|i)x|tr(?:ois|eize|ente)|qu(?:a(?:t(?:re|orze)|rante)|inze)|cinq(?:uante|)|s(?:ix|ept|eize)|huit|neuf|onze|douze|vingt|décente|(?:très |)tardive)|) <<- ~>> * __[i](p_nombre_heure)__ @@ -5446,15 +6684,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>> * @@ -5464,11 +6702,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 @@ -5554,11 +6792,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>> * @@ -5584,22 +6822,25 @@ __[i](p_la_xxx_la_plus_adj)__ la ({w_2}) (la plus) ({w_2}) @@3,w,$ <<- morphex(\1, ":[NAQ].*:[fe]", ":G") and morph(\3, ":[AQ].*:[fe]", False) ~2>> * __[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>> * + <<- 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>> * __[i](p_loc_adj_adv)__ - (à (?:demi|peine|peu près)|depuis peu|quelque peu|pas très|un (?:petit |)peu(?: plus| moins|)|peu|plus|moins|si) +({w_2}) @@0,$ + (à (?:demi|peine|peu près)|depuis peu|quelque peu|pas très|un (?:petit |)peu(?: plus| moins|)|peu|plus|moins) +({w_2}) @@0,$ <<- morph(\2, ":[AW]", False) ~1>> * +__[i](p_si_adj_adv)__ + (si) +({w_2}) @@0,$ + <<- morph(\2, ":[AW]", False) and not (\2 == "bien" and after("^ +que? ")) ~1>> * __[i](p_un_brin_chouïa_rien_tantinet_soupçon)__ (un (?:brin|chou[iï]a|rien|minimum|soupçon|tantinet)(?: trop|)) ({w_2}) @@0,$ <<- morphex(\2, ":A", ":G") ~1>> * __[i](p_assez_trop_adv_xxxment)__ (?:assez|trop) +(\w+ment) @@$ @@ -5718,94 +6959,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[mn]p?tes?)) @@0,$,$ - <<- morph(\1, ">laisser ", False) >>> + <<- morph(\1, ">laisser/", False) >>> <<- \3 != "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}}. @@ -5897,15 +7138,15 @@ # # //////////////////////////////////////// RÈGLES DE CONTRÔLE //////////////////////////////////////// # -!!!! Redondances dans la phrase - +!!!! 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. @@ -5914,11 +7155,11 @@ !! !! -!!!! Groupe nominal (1) +!!!! Groupe nominal (1) !! !! !! #### 1 mot @@ -5956,15 +7197,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. @@ -6047,15 +7288,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. @@ -6157,11 +7398,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 ». @@ -6196,11 +7437,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]") @@ -6485,20 +7726,20 @@ ## 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 !! !! -!!!! Groupe nominal (2) +!!!! Groupe nominal (2) !! !! !! ## Sans article @@ -6599,11 +7840,11 @@ <<- __also__ and hasFemForm(\1) -1>> =switchGender(@) # Accord de genre erroné avec « \2 ». <<- not re.search("(?i)^air$", \1) and not \2.startswith("seul") and morph(\1, ":[si]") and morph(\2, ":[NAQ].*:p") and not apposition(\1, \2) and not morph(word(-1), ":[NAQ]", False, False) -2>> =suggSing(@) # Accord de nombre erroné avec « \1 » : « \2 » devrait être au singulier. - + TEST: L’{{amande}} {{amer}} TEST: l’{{amicale}} {{animal}} TEST: du chien et de l’{{excellente}} {{collier}} qu’il avait autour du cou. TEST: du chien et de l’{{étonnante}} {{collier}} qu’il avait autour du cou. @@ -6622,15 +7863,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 @@ -6651,15 +7892,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}} @@ -6674,25 +7915,25 @@ <<- morph(\1, ":D", False) >>> <<- not \3.startswith("seul") and morphex(\2, ":[NAQ].*:[me]", ":(?:B|G|V0)") and morph(\3, ":[NAQ].*:f") and not apposition(\2, \3) and not before(r"\b(?:et|ou|de) +$") -3>> =suggMasSing(@, True) # Accord de genre erroné : « \2 » est masculin, « \3 » est féminin. - <<- not \3.startswith("seul") + <<- not \3.startswith("seul") and morphex(\2, ":[NAQ].*:[si]", ":G") and morphex(\3, ":[NAQ].*:p", ":[GWsi]") and not apposition(\2, \3) and not before(r"\b(?:et|ou|de) +$") -3>> =suggMasSing(@) # Accord de nombre erroné avec « \2 » : « \3 » devrait être au singulier. __[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) - -3>> =suggMasSing(@) # Accord de nombre erroné avec « \2 » : « \3 » devrait être au singulier. + 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… @@ -6701,24 +7942,24 @@ (?> =suggMasSing(@, True) # Accord de genre erroné : « \1 » est masculin, « \2 » est féminin. - <<- not \2.startswith("seul") + <<- not \2.startswith("seul") and morphex(\1, ":[NAQ].*:[si]", ":G") and morphex(\2, ":[NAQ].*:p", ":[GWsi]") and not apposition(\1, \2) and not before(r"\b(?:et|ou|de) +$") -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) - -2>> =suggMasSing(@) # Accord de nombre erroné avec « \1 » : « \2 » devrait être au singulier. + 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. @@ -6726,25 +7967,25 @@ (?> =suggMasSing(@, True) # Accord de genre erroné : « \1 » est masculin, « \2 » est féminin. - <<- not \2.startswith("seul") + <<- not \2.startswith("seul") and morphex(\1, ":[NAQ].*:[si]", ":G") and morphex(\2, ":[NAQ].*:p", ":[GWsi]") and not apposition(\1, \2) and not before(r"\b(?:et|ou|de) +$") -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) - -2>> =suggMasSing(@) # Accord de nombre erroné avec « \1 » : « \2 » devrait être au singulier. + 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}} @@ -6753,24 +7994,24 @@ <<- morph(\1, ":D", False) >>> <<- \2 != "fois" and not \3.startswith("seul") and morphex(\2, ":[NAQ].*:[fe]", ":(?:B|G|V0)") and morph(\3, ":[NAQ].*:m") and not apposition(\2, \3) and not before(r"\b(?:et|ou|de) +$") -3>> =suggFemSing(@, True) # Accord de genre erroné : « \2 » est féminin, « \3 » est masculin. - <<- not \3.startswith("seul") + <<- not \3.startswith("seul") and morphex(\2, ":[NAQ].*:[si]", ":G") and morphex(\3, ":[NAQ].*:p", ":[GWsi]") and not apposition(\2, \3) and not before(r"\b(?:et|ou|de) +$") -3>> =suggFemSing(@) # Accord de nombre erroné avec « \2 » : « \3 » devrait être au singulier. __[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. @@ -6787,15 +8028,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}} ? @@ -6817,16 +8058,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é}} @@ -6847,16 +8088,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. @@ -6875,11 +8116,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? +$")) ) @@ -6905,11 +8146,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? +$")) ) @@ -6935,11 +8176,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]")) @@ -6965,11 +8206,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]")) @@ -6986,16 +8227,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}} @@ -7011,11 +8252,11 @@ !! !! -!!!! Groupe nominal (3) +!!!! Groupe nominal (3) !! !! !! ## nombre @@ -7065,11 +8306,11 @@ !! !! -!!!! Groupe nominal: Accords avec de / des / du +!!!! Groupe nominal: Accords avec de / des / du !! !! !! __[i]/gn(gn_devinette1)__ (?:[lmts]a|une|cette) +{w_2} +d(?:e (?:[lmts]a|cette)|’une) +(?!des )({w_2}) +({w_2}) @@w,$ @@ -7127,11 +8368,11 @@ !! !! -!!!! Singuliers & Pluriels +!!!! Singuliers & Pluriels !! !! !! #### Prépositions @@ -7260,11 +8501,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. @@ -7280,14 +8521,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. @@ -7342,20 +8583,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. @@ -7367,11 +8608,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}}. @@ -7398,11 +8639,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é)__ @@ -7494,11 +8735,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. @@ -7525,26 +8766,26 @@ !! !! -!!!! Confusions +!!!! Confusions !! !! !! # à / 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. @@ -7579,21 +8820,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 @@ -7613,11 +8854,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. @@ -7637,24 +8878,24 @@ # 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}}. # annales / anal-e-s __[i]/conf(conf_annales1)__ (anale?s?) (?:littéraires?|politiques?|ecclésiastiques?|du (?:bac(?:calauréat|)|brevet)|de (?:physique|chimie|mathématiques|biologie|géographie)|d’histoire) @@0 - <<- -1>> annales # Confusion : “\1” est l’adjectif relatif à l’anus.|http://fr.wiktionary.org/wiki/annales + <<- -1>> annales # Confusion : “\1” est l’adjectif relatif à l’anus.|http://fr.wiktionary.org/wiki/annales __[i]/conf(conf_annales2)__ [lcdmts]es (anale?s?) @@4 <<- -1>> annales # Confusion : “\1” est l’adjectif relatif à l’anus.|http://fr.wiktionary.org/wiki/annales TEST: des {{anales}} littéraires @@ -7680,11 +8921,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)__ @@ -7727,15 +8968,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}} @@ -7812,11 +9053,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 @@ -7840,11 +9081,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}}. @@ -7929,16 +9170,21 @@ (c[ôo]tes?) de mailles? @@0 <<- -1>> =\1.replace("ô", "o").replace("t", "tt") # Confusion : écrivez « cotte » pour la cotte de mailles.|https://fr.wiktionary.org/wiki/cotte_de_mailles __[i]/conf(conf_avoir_la_cote)__ ({avoir}) +la (côte) @@0,$ <<- morph(\1, ":V0a", False) -2>> cote # Confusion probable : utilisez « cote » (cotation).|http://fr.wiktionary.org/wiki/cote +__[i](conf_côte_à_côte)__ + c[ôo]tt?es? [àaá] c[ôo]tt?es? + <<- not re.search("(?i)^côte à côte$", \0) ->> côte à côte # Locution adverbiale invariable. Écrivez “côte à côte”.|https://fr.wiktionary.org/wiki/c%C3%B4te_%C3%A0_c%C3%B4te + <<- ~>> * TEST: Rien ne vaut une bonne {{cote}} de bœuf. TEST: Elles ont passé une radiographie des {{cottes}}. TEST: Quelle est sa {{côte}} de popularité TEST: il a réussi à percer sa {{cote}} de mailles. TEST: Il a la {{côte}} auprès de ses collègues +TEST: ils sont {{cotte à cotte}} TEST: on a atteint la cote d’alerte. # cou / coup / coût __[i]/conf(conf_coup_de)__ @@ -7958,15 +9204,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. @@ -7975,14 +9221,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). @@ -8012,16 +9258,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. @@ -8028,21 +9274,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. @@ -8050,11 +9296,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. @@ -8080,11 +9326,11 @@ TEST: Béatrice Dalle et Claude __[i]/conf(conf_où_est)__ où (et) +({w_1}) @@w,$ - <<- morphex(\2, ":D", ":R|>(?:quand|pourquoi)") or (\2 == "l" and after("^’")) + <<- morphex(\2, ":D", ":R|>(?:quand|pourquoi)") or (\2 == "l" and after("^’")) -1>> est # Confusion probable : “et” est une conjonction de coordination. Pour le verbe être à la 3ᵉ personne du singulier, écrivez : TEST: où {{et}} cet ennemi ? TEST: où {{et}} l’homme qui est passé ce matin ? TEST: Je veux savoir où et quand, où et pouquoi. @@ -8123,15 +9369,15 @@ # foi / fois __[i]/conf(conf_bonne_mauvaise_foi)__ (mauvaise|bonne) (fois) @@0,$ - <<- not ( \1 == "bonne" and before(r"(?i)\bune +$") and after("(?i)^ +pour toute") ) + <<- 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. @@ -8281,17 +9527,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}} ? @@ -8314,11 +9560,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à ! @@ -8332,16 +9578,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 @@ -8504,11 +9750,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 @@ -8518,11 +9764,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 @@ -8539,19 +9785,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}}. @@ -8672,11 +9918,11 @@ # # Confusion probable : « quelquefois » est un adverbe qui signifie « parfois » ; ne pas confondre avec les quelques fois qu’il est advenu ou qu’il adviendra quelque chose.|http://bdl.oqlf.gouv.qc.ca/bdl/gabarit_bdl.asp?id=4334 __[i]/conf(conf_quelquefois_quelques_fois)__ qu(?: elles? que fois?|elles? que fois?|elque fois) <<- ->> quelquefois|quelques fois - # Confusion. Utilisez « quelquefois » si vous voulez dire « parfois ». Utilisez « quelques fois » pour évoquer ce qui est advenu ou adviendra plusieurs fois.|http://bdl.oqlf.gouv.qc.ca/bdl/gabarit_bdl.asp?id=4334 + # Confusion. Utilisez « quelquefois » si vous voulez dire « parfois ». Utilisez « quelques fois » pour évoquer ce qui est advenu ou adviendra plusieurs fois.|http://bdl.oqlf.gouv.qc.ca/bdl/gabarit_bdl.asp?id=4334 TEST: {{Quelles que fois}}, on y comprend plus rien. TEST: {{Qu’elle que fois}}, on y comprend plus rien. TEST: Il y va {{quelque fois}} par an. @@ -8696,11 +9942,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}}. @@ -8758,20 +10004,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}}. @@ -8789,14 +10035,14 @@ __[i]/conf(conf_en_soi)__ (?> soi # Confusion probable. __[i]/conf(conf_quel_que_soit2)__ - quel(?:le|)s? que (soi(?:es?|)) @@$ <<- -1>> soit|soient # Confusion probable. + 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 @@ -8815,45 +10061,15 @@ <<- isStart() -1>> soit # Confusion probable : pour évoquer une option, écrivez “soit”.|https://fr.wiktionary.org/wiki/soit#Conjonction TEST: {{soi}} je vais au cinéma, {{soi}} je m’abstiens. TEST: {{soie}} j’arrive avant tout le monde. - -# sur / sûr -__[i]/conf(conf_sûr_que)__ - (sure?s?) que? @@0 - <<- -1>> =\1.replace("sur", "sûr") - # Confusion probable : “sur” est une préposition ou un adjectif signifiant acide ou aigre ; utilisez “sûr” pour certain, vrai ou sans danger.|http://fr.wiktionary.org/wiki/sur -__[i]/conf(conf_sûre_surs_de)__ - (sur(?:es?|s)) de? @@0 - <<- -1>> =\1.replace("sur", "sûr") - # Confusion probable : “sur” un adjectif signifiant acide ou aigre ; utilisez “sûr” pour certain, vrai ou sans danger.|http://fr.wiktionary.org/wiki/sur -__[i]/conf(conf_sûr_de)__ - (sur) d(?:e (?:m(?:oi|es?|on|a)|t(?:oi|es?|on|a)|vous|nous|l(?:ui|es?)|s(?:oi|es?|on|a)|ce(?:ci|la|s|tte|t|)|ça)|’(?:elles?|eux)) @@0 - <<- -1>> sûr - # Confusion probable : “sur” est une préposition ou un adjectif signifiant acide ou aigre ; utilisez “sûr” pour certain, vrai ou sans danger.|http://fr.wiktionary.org/wiki/sur -__[i]/conf(conf_sûr_de_vinfi)__ - (sur) de (?:l(?:a |’|es? |ui |eur )|)({infi}) @@0,$ - <<- morph(\2, ":Y", False) - -1>> =\1.replace("sur", "sûr") - # Confusion probable : “sur” est une préposition ou un adjectif signifiant acide ou aigre ; utilisez “sûr” pour certain, vrai ou sans danger.|http://fr.wiktionary.org/wiki/sur -__[i]/conf(conf_en_lieu_sûr)__ - en lieu (sur) @@8 - <<- -1>> sûr - # Confusion probable : “sur” est une préposition ou un adjectif signifiant acide ou aigre ; utilisez “sûr” pour certain, vrai ou sans danger.|http://fr.wiktionary.org/wiki/sur - -TEST: Je suis {{sure}} qu’il ne va pas tarder à venir -TEST: {{sures}} d’elles-mêmes, elles ne s’en laissent pas conter. -TEST: {{sur}} de toi et de moi, que peut-il nous arriver, sinon le meilleur. -TEST: Il est tellement {{sur}} de la trouver. -TEST: ils sont en lieu {{sur}} et introuvables. - # 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") @@ -8869,14 +10085,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}}. @@ -8887,11 +10103,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. @@ -8909,18 +10125,24 @@ # 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}}. TEST: je ne peux pas lui donner {{tord}}. + +__[i]/conf(conf_tour_à_tour)__ + tours? [àa] tours? + <<- not re.search("(?i)^tour à tour$", \0) ->> tour à tour # Locution adverbiale invariable. Écrivez “tour à tour”.|https://fr.wiktionary.org/wiki/tour_%C3%A0_tour + <<- ~>> * + # venimeux / vénéneux __[i]/conf(conf_qqch_venimeux)__ (?:serpent|araignée|scorpion|vipère|cobra|crapaud|grenouille|dendrobate|poulpe|guêpe|abeille|méduse|morsure|piqûre|dard|dent|croc|crochet)s? +(vénéneu(?:x|ses?)) @@$ <<- -1>> =\1.replace("énén", "enim") # Confusion : “vénéneux” se dit des plantes, employez “venimeux”. @@ -8970,11 +10192,11 @@ # nouveau / nouvel # TODO -!!!! Mots composés +!!!! Mots composés !! __[i]/mc(mc_mot_composé)__ ({w2})-({w2}) @@0,$ <<- not \1.isdigit() and not \2.isdigit() and not morph(\0, ":", False) and not morph(\2, ":G", False) and spell(\1+\2) @@ -8988,11 +10210,11 @@ !! !! -!!!! Casse: majuscules et minuscules +!!!! Casse: majuscules et minuscules !! !! !! # Les jours __[s]/maj(maj_jours_semaine)__ @@ -9078,11 +10300,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. @@ -9158,11 +10380,11 @@ !! !! !! !! !! -!!! Conjugaisons +!!! Conjugaisons !! !! !! !! !! !! @@ -9173,11 +10395,11 @@ !! !! !! -!!!! Infinitif +!!!! Infinitif !! !! !! __[i]/infi(infi_à_en)__ à en ({w_2}) @@5 @@ -9217,11 +10439,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. @@ -9249,11 +10471,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 ? @@ -9269,41 +10491,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. @@ -9310,19 +10532,19 @@ 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. -!!!! Participes présents +!!!! Participes présents !! __[i]/conj(conj_participe_présent)__ (?:ne|lui|me|te|se|nous|vous) ({w_2}ants) @@$ <<- morph(\1, ":A", False) -1>> =\1[:-1] # Un participe présent est invariable.|http://fr.wiktionary.org/wiki/participe_pr%C3%A9sent TEST: nous {{épuisants}} à la tâche pour des clopinettes, nous défaillîmes. @@ -9329,11 +10551,11 @@ !!! !!! -!!! Processeur: simplification des substantifs +!!! Processeur: simplification des substantifs !! !!! !!! ### @ : we remove @ we introduced after le/la/les in some cases __(p_arobase)__ @ <<- ~>> * @@ -9350,11 +10572,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>> * @@ -9371,11 +10593,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}) @@$ @@ -9425,11 +10647,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() ~>> * @@ -9455,11 +10677,11 @@ TEST: Notre but n’était pas de devenir célèbres. TEST: sans qu’on ait à le lui ordonner -!!!! OCR +!!!! OCR !! # Participes passés __[i]/ocr(ocr_être_participes_passés)__ ({etre}) +({w_2}es?) @@0,$ <<- morph(\1, ":V0e", False) >>> @@ -9469,11 +10691,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. @@ -9493,11 +10715,11 @@ # TEST: __ocr__ vous êtes {{presses}} de monter à bord de ce train-ci. # Fonctionne avec nous serons, mais pas nous sommes (bug de JavaScript?) -!!!! Confusions +!!!! Confusions !! ## guerre / guère __[i]/conf(conf_ne_pronom_pronom_verbe_guère)__ ne (?:[mts]e|la|les?|[nv]ous|lui|leur) (?:la |les? |lui |leur |l’|)\w{w_2} (?:plus |)(guerre) @@$ <<- -1>> guère # Confusion. La guerre est conflit. Pour l’adverbe signifiant “peu”, écrivez : @@ -9548,22 +10770,22 @@ ## 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}}. -!!!! Adverbes après verbe +!!!! 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. @@ -9588,11 +10810,11 @@ !! !! -!!!! Infinitif +!!!! Infinitif !! !! !! __[i]/infi(infi_d_en_y)__ d’(?:en|y) +({w_2}) @@$ @@ -9626,11 +10848,11 @@ TEST: de me le {{facturez}} __[i]/infi(infi_faire)__ (f(?:ai|[iî]|er|on)\w+) +({w_2}(?:ée?s?|ez)) @@0,$ - <<- morph(\1, ">faire ", False) and not before(r"(?i)\b(?:en|[mtsldc]es?|[nv]ous|un) +$") and morphex(\2, ":V", ":M") + <<- morph(\1, ">faire/", False) and not before(r"(?i)\b(?:en|[mtsldc]es?|[nv]ous|un) +$") and morphex(\2, ":V", ":M") and not (re.search("(?i)^fait$", \1) and \2.endswith("é")) and not (re.search("(?i)^faits$", \1) and \2.endswith("és")) -2>> =suggVerbInfi(@) # Le verbe devrait être à l’infinitif. TEST: elle fit peu {{mangé}} les enfants @@ -9638,11 +10860,11 @@ TEST: Tu fais {{décoloré}} tes cheveux ? __[i]/infi(infi_vouloir)__ (v[oe]u\w+) +({w_2}(?:ée?s?|ez)) @@0,$ - <<- morph(\1, ">vouloir ", False) and not before(r"(?i)\b(?:[mtsldc]es?|[nv]ous|un) +$") and morphex(\2, ":V", ":M") + <<- morph(\1, ">vouloir/", False) and not before(r"(?i)\b(?:[mtsldc]es?|[nv]ous|un) +$") and morphex(\2, ":V", ":M") and not (re.search("(?i)^vouloir$", \1) and \2.endswith("é")) and not (re.search("(?i)^vouloirs$", \1) and \2.endswith("és")) -2>> =suggVerbInfi(@) # Le verbe devrait être à l’infinitif. TEST: je veux {{changé}} @@ -9653,11 +10875,11 @@ TEST: en voulant {{changé}} __[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. @@ -9669,11 +10891,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 @@ -9701,40 +10923,40 @@ !! !! -!!!! Usage pronominal avec “avoir” au lieu d’“être” +!!!! Usage pronominal avec “avoir” au lieu d’“être” !! !! !! __[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. @@ -9743,11 +10965,11 @@ !! !! -!!!! Participes passés: se +être +verbe +!!!! Participes passés: se +être +verbe !! !! !! __[i]/ppas(ppas_je_me_être_verbe)__ je +(?:ne +|)m(?:e +|’(?:y +|))(?:s[uo]i[st]|étai[st]|fu(?:sses?|s|t)|serai[st]?) +({w_3}) @@$ @@ -9769,54 +10991,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. @@ -9836,17 +11058,17 @@ !! !! -!!!! Participes passés: se +laisser +adjectif +!!!! Participes passés: se +laisser +adjectif !! !! !! __[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. @@ -9855,11 +11077,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. @@ -9869,17 +11091,17 @@ TEST: nous laisserons étourdi cet homme. !! !! -!!!! Participes passés: être, avoir été, sembler (+être via pp), devenir, rester, (re)devenir, paraître + participe passé / adj +!!!! Participes passés: être, avoir été, sembler (+être via pp), devenir, rester, (re)devenir, paraître + participe passé / adj !! !! !! __[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 @@ -9894,21 +11116,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. @@ -9918,11 +11140,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}}. @@ -9937,13 +11159,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 @@ -9951,23 +11173,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 @@ -9975,31 +11197,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 @@ -10014,11 +11236,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 @@ -10032,11 +11254,11 @@ <<- morph(\2, ":V0a", False) >>> <<- morphex(\3, ":[123]s", ":[GNAQWY]") -3>> =suggVerbPpas(@) # Après « avoir été », il faut un participe passé. <<- not before("[çcCÇ]’$|[cC]e n’$|[çÇ]a (?:n’|)$") and not before("(?i)^ *ne pas ") and not morph(word(-1), ":Y", False) >>> <<- morphex(\3, ":Y", ":A") -1>> _ # Tournure familière. Utilisez « être allé » plutôt que « avoir été ». <<- morphex(\3, ":V1..t.*:Y", ":A") -3>> =suggVerbPpas(@) # Incohérence. Après « avoir été », il faut un participe passé (à moins que « avoir été » signifie ici « être allé »). - + TEST: j’ai été {{instruis}} par elle TEST: avoir été {{prit}} par surprise TEST: Ils {{ont été}} {{réaliser}} à partir d’éléments naturels. TEST: J’{{ai été}} camper dans les Alpes. TEST: Tu {{as été}} prendre du bois. @@ -10049,20 +11271,20 @@ TEST: Partir aurait été assurer sa survie. !! !! -!!!! Participes passés: pouvoir/sembler/paraître/vouloir/devoir/croire/déclarer/penser/dire/affirmer + être/avoir été +!!!! Participes passés: pouvoir/sembler/paraître/vouloir/devoir/croire/déclarer/penser/dire/affirmer + être/avoir été !! !! !! __[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}} @@ -10071,11 +11293,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}} @@ -10082,11 +11304,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}} @@ -10095,11 +11317,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. @@ -10106,11 +11328,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é}} @@ -10117,40 +11339,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}} @@ -10161,11 +11383,11 @@ TEST: elles peuvent avoir été {{trompé}} TEST: elles souhaitent être plus {{considérée}} -!!!! Participes passés: accord en nombre avec la conjugaison de « être » +!!!! Participes passés: accord en nombre avec la conjugaison de « être » !! ## Contrôle de l’ __[i]/ppas(ppas_être_accord_singulier)__ ({w_2}) +(?:qui +|)(?:ne +|n’|)(?:est|était|f[uû]t|sera(?:it|)|a(?:vait|ura|urait|it|) +été|e[uû]t +été) +({w_2}) @@0,$ <<- morphex(\2, ":[NAQ].*:p", ":[GMWYsi]") and not morph(\1, ":G", False) @@ -10175,11 +11397,11 @@ ({w_2}) +(?:qui +|)(?:ne +|n’|)(?:sont|étaient|fu(?:r|ss)ent|ser(?:ont|aient)|soient|ont +été|a(?:vaient|uront|uraient|ient) +été|eu(?:r|ss)ent +été) +({w_2}) @@0,$ <<- not re.search("(?i)^légion$", \2) and morphex(\2, ":[NAQ].*:s", ":[GWYpi]") and not morph(\1, ":G", False) -2>> =suggPlur(@) # Accord avec « être » : « \2 » devrait être au pluriel. -!!!! Participes passés: accord en genre avec le substantif précédent +!!!! Participes passés: accord en genre avec le substantif précédent !! __[i]/ppas(ppas_sujet_être_accord_genre)__ (?> =suggSing(@) # Si cet adjectif se réfère au pronom « \2 », l’adjectif devrait être au singulier (et accordé en genre). @@ -10236,19 +11458,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. @@ -10256,11 +11478,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. @@ -10276,11 +11498,11 @@ !! !! -!!!! Inversion verbe/sujet +!!!! Inversion verbe/sujet !! !! !! __[i]/ppas(ppas_inversion_être_je)__ (?:s[ou]is|étais|fus(?:sé|)|serais?)-je +({w_2}) @@$ <<- morphex(\1, ":(?:[123][sp]|Y|[NAQ].*:p)", ":[GWsi]") @@ -10289,27 +11511,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}} ? @@ -10332,11 +11554,11 @@ TEST: Est-il question de ceci ou de cela ? TEST: Est-ce former de futurs travailleurs ou bien des citoyens -## Accord et incohérences +## Accord et incohérences __[i]/ppas(ppas_sont)__ sont ({w_2}) @@5 <<- morphex(\1, ":[NAQ]", ":[QWGBMpi]") and not re.search("(?i)^(?:légion|nombre|cause)$", \1) and not before(r"(?i)\bce que?\b") -1>> =suggPlur(@) # Incohérence : « \1 » est au singulier. Ou vous confondez « sont » et « son », ou l’accord en nombre est incorrect. <<- __else__ and morphex(\1, ":V", ":(?:N|A|Q|W|G|3p)") and not before(r"(?i)\bce que?\b") @@ -10346,43 +11568,43 @@ !! !! -!!!! Se croire/considérer/montrer/penser/révéler/savoir/sentir/voir/vouloir + participe passé/adj +!!!! Se croire/considérer/montrer/penser/révéler/savoir/sentir/voir/vouloir + participe passé/adj !! !! !! __[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. @@ -10389,84 +11611,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}} @@ -10490,23 +11712,23 @@ !! !! -!!!! Avoir + participes passés +!!!! Avoir + participes passés !! !! !! #__[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. @@ -10518,11 +11740,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. @@ -10530,11 +11752,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 @@ -10581,11 +11803,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. @@ -10629,11 +11851,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. @@ -10643,11 +11865,11 @@ !! !! -!!!! COD précédant que +!!!! COD précédant que !! !! !! __[i]/ppas(ppas_det_plur_COD_que_avoir)__ ([ldmtsc]es) +({w_2}) +que? +(?:j’|tu |ils? |[nv]ous |elles? |on ) *(?:ne +|n’|)({avoir}) +({w_2}[éiust]e?)(?! [mts]’) @@0,w,w,$ @@ -10716,11 +11938,11 @@ TEST: Avoir {{marcher}} toute la journée m’a épuisée. -!!!! du / dû +!!!! du / dû !! __[i]/ppas(ppas_avoir_dû_vinfi)__ ({avoir}) +(due?s?) +(?:[mts]’|)({w_2}) @@0,w,$ <<- morph(\1, ":V0a", False) and (morph(\3, ":Y") or re.search("^(?:[mtsn]e|[nv]ous|leur|lui)$", \3)) -2>> dû # Participe passé de devoir : « dû ». @@ -10760,11 +11982,11 @@ TEST: A-t-il déjà {{signer}} le contrat ? !! !! -!!!! Participes passés avec formes interrogatives +!!!! Participes passés avec formes interrogatives !! !! !! __[i]/ppas(ppas_avoir_pronom1)__ (?> =suggMasSing(@) # Avec « avoir », il faut un participe passé au masculin singulier. __[i]/ppas(ppas_l_m_t_avoir_pronom)__ ([ltm]’)({avoir})[- ](?:je|tu|ils?|elles?|t-(?:ils?|elles?|on)|[nv]ous|on) +({w2}s) @@0,2,$ - <<- morph(\2, ":V0a", False) and morphex(\3, ":(?:Y|2p|Q.*:p)", ":[si]") + <<- morph(\2, ":V0a", False) and morphex(\3, ":(?:Y|2p|Q.*:p)", ":[si]") -3>> =suggMasSing(@) # Accord avec le COD « \1 » : e participe passé « \2 » devrait être au singulier (et accordé en genre). __[i]/ppas(ppas_les_avoir_pronom)__ les +({avoir})-(?:je|tu|ils?|elles?|t-(?:ils?|elles?|on)|[nv]ous|on) +({w_1}(?:[éiut]e?|is|se)) @@w,$ @@ -10827,11 +12049,11 @@ __[i]/conj(conj_vous_verbe2)__ vous [nm](?:e +(?:les? |l’|la |[nv]ous |)|’)({w_3}) @@$ <<- morphex(\1, ":V", ":2p") and isStart() -1>> =suggVerb(@, ":2p") # Conjugaison erronée. Accord avec « vous ». Le verbe devrait être à la 2ᵉ personne du pluriel. TEST: vous ne l’{{avait}} pas vu. -TEST: je crois, vous m’{{avais}} trompé… +TEST: je crois, vous m’{{avais}} trompé… ## se + incohérence __[i]/conj(conj_se_incohérence)__ s(?:e +(?:les? +|la +|)|’(?:en +|y +|))({w_2}(?:e[zs]|ons|is|us)) @@$ @@ -10844,11 +12066,11 @@ TEST: se {{crois}} élu par Dieu… TEST: avec ceux se trouvant sur leur chemin -!!!! Confusions ou/où +!!!! Confusions ou/où !! __[i]/conf(conf_det_nom_où_pronom)__ ^ *(?:l(?:es? +|a +|’)|[nv]o(?:s|tre) +|ce(?:t|tte|s|) +|[mts](?:es|on|a) +|des +)({w_2}) +(ou) +(?:je|tu|ils?|elles? +> +\w+|[nv]ous +> +\w+) @@w,w <<- morphex(\1, ":[NAQ]", ":G") -2>> où # Confusion probable. Pour évoquer un lieu ou un moment, écrivez :|http://fr.wiktionary.org/wiki/o%C3%B9 @@ -10858,11 +12080,11 @@ !!! !!! -!!! Processeur avant impératif +!!! Processeur avant impératif !! !!! !!! __(p_n_importe_qui_quoi)__ n(’)importe quo?i @@1 <<- ~1>> ` @@ -10872,11 +12094,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. @@ -10903,11 +12125,11 @@ TEST: Ne m’en rien laisser. !! !! -!!!! Impératif ! +!!!! Impératif ! !! !! !! # Confusions __[i]/imp(imp_confusion_2e_pers_pluriel)__ @@ -10950,19 +12172,20 @@ # verbes du 2ᵉ et du 3ᵉ groupe en -t __[i]/imp(imp_vgroupe2_vgroupe3_t)__ ^ *(\w+t)(?![- ](?:je|tu|[nv]ous|ils?|elles?|on|t-ils?|t-elles?)) @@$ <<- morphex(\1, ":V[23].*:Ip.*:3s", ":[GNA]|>(?:devoir|suffire)") and analyse(\1[:-1]+"s", ":E:2s", False) - and not (re.search("(?i)^vient$", \1) and after("^ +(?:l[ea]|se |s’)")) + and not (re.search("(?i)^vient$", \1) and after("^ +(?:l[ea]|[sd]e |[sd]’)")) and not (re.search("(?i)^dit$", \1) and after("^ +[A-ZÉÈÂÎ]")) -1>> =\1[:-1]+"s" # S’il s’agit d’un impératif, la terminaison est “is”, non “it”. TEST: {{Finit}} ton assiette. TEST: Ne {{pourrit}} pas l’ambiance. TEST: Suffit de s’en servir. TEST: Et ne doit pas être rejeté dans les limbes. TEST: Vient s’ajouter à ce contexte la perception, partagée par beaucoup, du caractère fortement menaçant de l’environnement économique et géopolitique. +TEST: À son bord vient d’embarquer un nouvel équipage # verbes du 3ᵉ groupe en -d __[i]/imp(imp_vgroupe3_d)__ ^ *(\w+d)(?![- ](?:je|tu|[nv]ous|ils?|elles?|on|t-ils?|t-elles?)) @@$ @@ -11035,11 +12258,11 @@ TEST: explique-{{leurs}} de quoi il est question. !! !! -!!!! Impératif: traits d’union manquants +!!!! Impératif: traits d’union manquants !! !! !! __[i]/imp(imp_union_moi_toi)__ (?laisser .*:E", False) and morphex(\3, ":(?:Y|X|Oo)", ":[NAB]") + <<- morph(\2, ">laisser/.*:E", 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… @@ -11097,11 +12320,11 @@ TEST: le coût humain de la guerre qu’il a laissé les submerger. __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 @@ -11156,11 +12379,11 @@ !!! !!! -!!! Processeur: destruction des pronoms qui précèdent un verbe et de l’adverbe de négation “ne”. +!!! Processeur: destruction des pronoms qui précèdent un verbe et de l’adverbe de négation “ne”. !! !!! !!! # Brainfuck (ici, prudence !) __[i](p_pro_per_obj01)__ @@ -11281,11 +12504,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)__ @@ -11304,11 +12527,11 @@ !! !! -!!!! Confusions +!!!! Confusions !! !! !! #### CONFUSION a / à __[i]/conf(conf_pronom_verbe_à)__ @@ -11347,15 +12570,15 @@ TEST: Notre communauté vous est redevable. TEST: l’humour est affaire de culture TEST: Aller chercher l’air pur à la campagne est peine perdue. -#### CONFUSION veillez/veuillez +#### 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. @@ -11363,11 +12586,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. @@ -11380,11 +12603,11 @@ !! !! -!!!! Infinitif +!!!! Infinitif !! !! !! __[i]/infi(infi_comment_où)__ (?:comment|où) +({w_2}(?:ée?s?|ez)) @@$ @@ -11405,11 +12628,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. @@ -11419,11 +12642,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é}} @@ -11436,11 +12659,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. @@ -11453,11 +12676,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. @@ -11484,18 +12707,18 @@ TEST: Ne pas aimer n’est pas oublier l’autre. !! !! -!!!! Conjugaison +!!!! Conjugaison !! !! !! ## 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)) >>> @@ -11601,11 +12824,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) @@ -11736,11 +12959,11 @@ TEST: l’un des chants les plus diffusés pendant la Révolution culturelle __[i]/conj(conj_infi)__ ^ *({infi}) +({w_2}) @@*,$ - <<- morph(\1, ":Y", False) and morph(\2, ":V.[a-z_!?]+(?!.*:(?:3s|P|Q|Y|3p!))") + <<- morph(\1, ":Y", False) and morph(\2, ":V.[a-z_!?]+(?!.*:(?:3s|P|Q|Y|3p!))") -2>> =suggVerb(@, ":3s") # Conjugaison erronée. Accord avec « \1… ». Le verbe devrait être à la 3ᵉ personne du singulier. TEST: manger {{fais}} grossir. TEST: boire immodérément {{nuis}} à la santé @@ -11905,11 +13128,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) @@ -12023,11 +13246,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)") @@ -12036,11 +13259,11 @@ TEST: Des hommes {{arrive}}. TEST: Des femmes ne {{demande}} rien. TEST: des femmes qui {{conduise}} la marche du monde. -!!!! Quel(le) que soit / quel(le)s que soient +!!!! Quel(le) que soit / quel(le)s que soient !! # singulier __[i]/conj(conj_quel_quelle_que_3sg1)__ quel(?:le|)s? que ([sf]\w+) +(?:l[ea]|ce(?:t|tte|)|[mts](?:a|on)|[nv]otre|leur) @@w <<- morphex(\1, ":V0e", ":3s") @@ -12113,11 +13336,11 @@ !! !! -!!!! Inversion verbe sujet +!!!! Inversion verbe sujet !! !! !! __[i]/conj(conj_que_où_comment_verbe_sujet_sing)__ (?:que?|où|comment|combien|dont|quand|pourquoi) +({w1}) (l(?:e(?:ur | )|a |’)|[mts](?:on|a) |ce(?:t|tte|) |[nv]otre |du ) *(?!plupart|majorité)({w1}) @@w,w,$ @@ -12192,11 +13415,11 @@ !! !! -!!!! Formes interrogatives ? +!!!! Formes interrogatives ? !! !! !! __[i]/inte(inte_union_xxxe_je)__ (?> \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. @@ -12222,11 +13445,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. @@ -12294,20 +13517,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à ? @@ -12322,50 +13545,62 @@ TEST: {{attaquant}}-ils ->> attaquent TEST: {{prendrons}}-elles un verre avec moi ? -!!!! Verbe auxiliaire +!!!! 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. + + + +@@@@ +@@@@ +@@@@ +@@@@ +@@@@GRAPH: last_graph +@@@@ +@@@@ +@@@@ +@@@@ + !! !! -!!!! Modes verbaux +!!!! Modes verbaux !! !! !! # 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. +__vmode_j_aimerais_vinfi__ + [|,] [je|j’] [aimerai|préférerai|préfèrerai|apprécierai|voudrai|souhaiterai|désirerai|adorerai] @:[YX]|>(?:y|ne|que?)/¬:R + <<- /vmode/ -3>> \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. TEST: J’aimerai la danse et la musique, puisque vous l’exigez. 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) - -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… » +__vmode_j_aurais_aimé_que_vinfi__ + j’ aurai [aimé|souhaité|préféré|voulu|apprécié|désiré|adoré] [que|qu’|qu|ne|n’|@:Y] + <<- /vmode/ -2>> 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. TEST: j’{{aurai}} préféré l’entendre un autre jour. @@ -12374,41 +13609,39 @@ TEST: Quand j’aurai fini ce boulot, je ne sais pas ce que je ferai. TEST: Quand j’aurai soif et faim, je m’arrêterai. # Si suivi du conditionnel ou du subjonctif -__[i]/vmode(vmode_si_sujet1)__ - si +({w1}) +({w_2}) @@w,$ - <<- morph(\1, ":(?:Os|M)", False) and morphex(\2, ":[SK]", ":(?:G|V0|I)") and isStart() - -2>> _ # Ce verbe ne devrait être ni au conditionnel, ni au subjonctif. -__[i]/vmode(vmode_si_sujet2)__ - (?:si [jt]’|s’ils? +)({w_2}) @@$ - <<- morphex(\1, ":[SK]", ":(?:G|V0|I)") and isStart() - -1>> _ # Ce verbe ne devrait être ni au conditionnel, ni au subjonctif. +__vmode_si_sujet__ + [|,] si [j’|J’|t’|T’] @:[SK]¬:(?:G|V0|I) + [|,] si @:(?:Os|M) @:[SK]¬:(?:G|V0|I) + [|,] s’ [il|ils] @:[SK]¬:(?:G|V0|I) + <<- /vmode/ -4>> _ # Ce verbe ne devrait être ni au conditionnel, ni au subjonctif. TEST: Si Pierre {{avancerait}} sa voiture de quelques mètres, ça nous permettrait de passer. TEST: s’ils ne {{mangeraient}} pas tous les jours, ils seraient moins gros. +TEST: Si j’{{irais}} le faire # Dès que + indicatif -__[i]/vmode(vmode_dès_que)__ - dès +que? +({w_2}) +({w_2}) @@w,$ - <<- morph(\1, ":(?:Os|M)", False) and morphex(\2, ":S", ":[IG]") -2>> =suggVerbMode(@, ":I", \1) # Ce verbe ne devrait pas être au subjonctif. -# <<- morph(\1, ":(?:Os|M)", False) and morph(\2, ":K", False) -2>> =suggVerbMode(@, ":If", \1) # Ce verbe ne devrait pas être au conditionnel. +__vmode_dès_que__ + dès [que|qu’|qu] @:(?:Os|M) @:S¬:[IG] + <<- /vmode/ -4>> =suggVerbMode(\4, ":I", \3) # Ce verbe ne devrait pas être au subjonctif. +# <<- morph(\1, ":(?:Os|M)", False) and morph(\2, ":K", False) -2>> =suggVerbMode(@, ":If", \1) # Ce verbe ne devrait pas être au conditionnel. #TEST: dès que je le {{verrais}} TEST: dès qu’il le {{voie}} TEST: donnant à entendre qu’il avait l’intention de violer Laura dès qu’il en aurait l’occasion # 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) - and morph(\2, ":(?:Os|M)", False) and morphex(\3, ":I", ":[GYS]") - 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. +__vmode_qqch_que_subjonctif1__ + [afin|avant|pour|quoi|>permettre|>falloir|>vouloir|>ordonner|>exiger|>désirer|>préférer|>suffire] [que|qu’|qu] @:(?:Os|M) @:I¬:[GYS] + <<- /vmode/ -4>> =suggVerbMode(\4, ":S", \3) # Après « \1 que », ce verbe devrait être au subjonctif. + + >douter [que|qu’|qu] @:(?:Os|M) @:I¬:(?:[GYSK]|If) + <<- /vmode/ morph(\1, ":V", ":N") -4>> =suggVerbMode(\4, ":S", \3) # 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}} TEST: Je ne veux pas que tu {{es}} des ennuis @@ -12417,77 +13650,124 @@ TEST: Je ne doute pas qu’ils réussiront leur mission. TEST: Je me doutais bien qu’Apple pourrait marcher TEST: il ne fait aucun doute qu’Amazon le sait. TEST: quoi que nous autres hommes ayons pu faire + +__vmode_qqch_que_subjonctif2__ + à condition [que|qu’|qu] @:(?:Os|M) @:I¬:[GYS] + pour peu [que|qu’|qu] @:(?:Os|M) @:I¬:[GYS] + il peut [que|qu’|qu] @:(?:Os|M) @:I¬:[GYS] + <<- /vmode/ -5>> =suggVerbMode(\5, ":S", \4) # Ce verbe devrait être au subjonctif. + +TEST: à condition qu’il {{finit}} son boulot. +TEST: pour peu qu’il {{prend}} son devoir sérieux… +TEST: il se peut que nous {{avons}} tort. + # 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) ) - and not before0(r"(?i)\bsi ") - -2>> =suggVerbMode(@, ":S", \1) # Après « bien que », le verbe s’emploie au subjonctif. +__vmode_bien_que_subjonctif__ + [|,] bien [que|qu’|qu] @:(?:Os|M) @:I¬:(?:[GSK]|If|V0a)|>(?:hériter|recevoir|donner|offrir)/ + <<- /vmode/ -5>> =suggVerbMode(\5, ":S", \1) # Après « bien que », le verbe s’emploie au subjonctif. + + [|,] bien [que|qu’|qu] @:(?:Os|M) >avoir @:[QYG]¬>(?:hériter|recevoir|donner|offrir)/ + <<- /vmode/ morph(\5, ":I", ":S") -5>> =suggVerbMode(\5, ":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. TEST: C’est un joli bien. Bien qu’il a hérité de son oncle, notez bien. TEST: Bien qu’il avait donné à ses enfants. TEST: si bien que je me suis toujours demandée si cela ne m’avait pas un peu bousillé les yeux - # Malgré que + subjonctif # «Malgré que» peut être utilisé délibérément pour un parler populaire qui ignore le subjonctif. # --> pas de règle de contrôle sur ce point. -__[i]/vmode(vmode_qqch_que_subjonctif2)__ - (?:à condition|pour peu|il +peut) +que? +({w1}) +({w_2}) @@w,$ - <<- morph(\1, ":(?:Os|M)", False) and morphex(\2, ":", ":[GYS]") -2>> =suggVerbMode(@, ":S", \1) # Ce verbe devrait être au subjonctif. - -TEST: à condition qu’il {{finit}} son boulot. -TEST: pour peu qu’il {{prend}} son devoir sérieux… -TEST: il se peut que nous {{avons}} tort. - # indicatif nécessaire -__[i]/vmode(vmode_sujet_indicatif)__ - ^ *(je|j’(?:en|y)|tu|ils?|elles?|on|nous|vous) +({w_2}) @@*,$ - <<- morphex(\2, ":S", ":[GIK]") and not re.search("^e(?:usse|û[mt]es|ût)", \2) - -2>> =suggVerbMode(@, ":I", \1) # Ce verbe ne devrait pas être au subjonctif. -__[i]/vmode(vmode_j_indicatif)__ - ^ *j’({w_2}) @@$ - <<- morphex(\1, ":S", ":[GIK]") and \1 != "eusse" -1>> =suggVerbMode(@, ":I", "je") # Ce verbe ne devrait pas être au subjonctif. +__vmode_sujet_indicatif__ + [je|tu|il|ils|elle|elles|on|nous|vous] @:S¬:[GIK]|V0a.*:Sq + <<- /vmode/ -3>> =suggVerbMode(\3, ":I", \2) # Ce verbe ne devrait pas être au subjonctif. + + j’ @:S¬:[GIK]|V0a.*:Sq:1s + <<- /vmode/ -3>> =suggVerbMode(\3, ":I", "je") # Ce verbe ne devrait pas être au subjonctif. + + j’ [en|y] @:S¬:[GIK]|V0a.*:Sq + <<- /vmode/ -4>> =suggVerbMode(\4, ":I", "je") # Ce verbe ne devrait pas être au subjonctif. TEST: Il {{ait}} parti. TEST: Il en {{conclue}} qu’il a eu raison. TEST: j’en {{aie}} marre TEST: j’{{aie}} faim # Après que + indicatif -__[i]/vmode(vmode_après_que_indicatif)__ - après que? ({w_2}) +({w_2}) @@w,$ - <<- morph(\1, ":(?:Os|M)", False) and (morphex(\2, ":V.*:S", ":[GI]") or morph(\2, ":V0e.*:S", False)) - -2>> =suggVerbMode(@, ":I", \1) - # Après « après que », le verbe ne s’emploie pas au subjonctif mais à l’indicatif, si l’action s’est déroulée de façon certaine. +__vmode_après_que_indicatif__ + après [que|qu’|qu] @:(?:Os|M) @:V.*:S¬:[GI] + après [que|qu’|qu] @:(?:Os|M) @:V0e.*:S + <<- /vmode/ -4>> =suggVerbMode(\4, ":I", \3) # Après « après que », le verbe ne s’emploie pas au subjonctif mais à l’indicatif, si l’action s’est déroulée de façon certaine. TEST: Après qu’il {{ait}} allé TEST: Après que Paul {{ait}} mangé son repas. TEST: Après qu’il {{soit}} parti, il plut. # Quand/lorsque + indicatif -__[i]/vmode(vmode_quand_lorsque_indicatif)__ - (?:quand|lorsque?) ({w_2}) +({w_2}) @@w,$ - <<- morph(\1, ":(?:Os|M)", False) and (morphex(\2, ":V.*:S", ":[GI]") or morph(\2, ":V0e.*:S", False)) - -2>> =suggVerbMode(@, ":I", \1) - # Après « quand » ou « lorsque », le verbe ne s’emploie pas au subjonctif mais à l’indicatif. +__vmode_quand_lorsque_indicatif__ + [quand|lorsque|lorsqu’|lorsqu] @:(?:Os|M) @:V.*:S¬:[GI] + [quand|lorsque|lorsqu’|lorsqu] @:(?:Os|M) @:V0e.*:S + <<- /vmode/ -3>> =suggVerbMode(\3, ":I", \2) # 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. +@@@@ +@@@@END_GRAPH +@@@@ + + + +@@@@ +@@@@ +@@@@ +@@@@ +@@@@GRAPH: test +@@@@ +@@@@ +@@@@ +@@@@ + +__code_legacy__ + legacy code + code legacy + <<- -1:2>> code hérité|code reliquat # \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. Pour la préposition, écrivez “en”. + +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}}. + + + +@@@@ +@@@@END_GRAPH +@@@@ + !! !! !! !! @@ -12505,11 +13785,11 @@ !! !! !! !! !! -!! TESTS: Faux positifs potentiels +!! TESTS: Faux positifs potentiels !! !! !! !! !! !! @@ -12528,11 +13808,11 @@ !! !! !! !! -!!! À trier +!!! À trier !! TEST: L’homme sur le bateau de Patrick {{viens}} de temps en temps {{mangé}} chez moi. TEST: Ces marchands {{passe}} leur temps à se quereller. TEST: Ils jugeront en toute impartialité de ce cas {{délirante}}. TEST: Ils sont de manière si étonnante et si admirable {{arrivé}} à ce résultat… TEST: Les tests grand public de Jean-Paul {{montre}} des résultats surprenants. @@ -12567,15 +13847,15 @@ TODO: André Juin était un sculpteur français. TODO: La bataille de Monte Cassino révèle le génie militaire du général Juin. TODO: Les côtes sont dans leur ensemble extrêmement découpées. -!!! Indécidable +!!! Indécidable !! TEST: Du sable fin grippe les rouages (accord avec ce qui précède). TEST: Du monde noir sortent les envahisseurs (accord avec ce qui suit). -!!! Autres tests +!!! Autres tests !! TEST: Ça a l’air de t’aller. TEST: Et je m’en sors. TEST: C’est à chacun d’entre nous de suivre le modèle d’Amos. TEST: C’est toi qui voulais y aller. TEST: je ne suis qu’une joueuse en robe de soirée. @@ -12636,11 +13916,11 @@ TEST: Ce qu’ils nous ont fait TEST: Comment vous expliquez ça ? TEST: Comment vous expliquer ça ? -!!! Tests historiques +!!! Tests historiques !! ## Version 0.5.14 TEST: par le léger tissu de rayonne qui les protégeait en ce moment. ## Version 0.5.11 @@ -13412,11 +14692,11 @@ TEST: À qui mieux mieux, à qui mieux mieux TEST: L’est est loin, la gare de l’est aussi. -!!! Tests repris de LanguageTool +!!! Tests repris de LanguageTool !! ## NOTE : ces textes contiennent parfois des erreurs (corrigées quand repérées par le correcteur) TEST: Au voisinage du zéro absolu de température. TEST: La couronne périphérique alterne falaises abruptes et plages. TEST: Henri VIII rencontre François Iᵉʳ. @@ -15477,11 +16757,11 @@ TEST: Le 29 février 2016. TEST: Le 29 février 2020. TEST: Le 29-février-2004 -!!! Le Horla, de Guy de Maupassant +!!! Le Horla, de Guy de Maupassant !! # Nouvelle intégrale (228 lignes) # Certains points diffèrent du texte original tiré de Wikisource : # — les paragraphes sont souvent scindés pour des raisons pratiques. # — les virgules avant les points de suspension ont été supprimées # — moyen âge -> Moyen Âge @@ -15849,11 +17129,11 @@ TEST: Après l’homme le Horla. — Après celui qui peut mourir tous les jours, à toutes les heures, à toutes les minutes, par tous les accidents, est venu celui qui ne doit mourir qu’à son jour, à son heure, à sa minute, parce qu’il a touché la limite de son existence ! TEST: Non… non… sans aucun doute, sans aucun doute… il n’est pas mort… Alors… alors… il va donc falloir que je me tue, moi !… # FIN DU HORLA -!!! Double assassinat dans la rue morgue, d’Edgar Poe +!!! Double assassinat dans la rue morgue, d’Edgar Poe !! # Texte tiré de Wikisource # Les paragraphes ont été découpés pour réduire la longueur des tests. TEST: DOUBLE ASSASSINAT DANS LA RUE MORGUE — Edgar Poe TEST: Quelle chanson chantaient les sirènes ? quel nom Achille avait-il pris, quand il se cachait parmi les femmes ? – Questions embarrassantes, il est vrai, mais qui ne sont pas situées au-delà de toute conjecture. TEST: Sir Thomas Browne. @@ -16393,11 +17673,11 @@ TEST: Mais, après tout, c’est un brave homme. Je l’adore particulièrement pour un merveilleux genre de cant auquel il doit sa réputation de génie. TEST: Je veux parler de sa manie de nier ce qui est, et d’expliquer ce qui n’est pas[2]. # FIN DU DOUBLE ASSASSINAT DANS LA RUE MORGUE -!!! Vers Dorés, de Pythagore +!!! Vers Dorés, de Pythagore !! # Origine? TEST: Aux dieux, suivant les lois, rends de justes hommages ; TEST: Respecte le serment, les héros et les sages ; TEST: Honore tes parents, tes rois, tes bienfaiteurs ; TEST: Choisi parmi tes amis les hommes les meilleurs. @@ -16493,11 +17773,11 @@ TEST: Fin des vers dorés de Pythagore TEST: Note : Chez les Pythagoriciens, la monade ou l’unité représente Dieu-même, parce qu’elle n’est engendrée par aucun nombre, qu’elle les engendre tous, qu’elle est simple et sans aucune composition. La dyade, ou le nombre deux, est l’image de la nature créée, parce qu’elle est le premier produit de l’unité, parce qu’elle est inspirée, parce qu’ayant des parties elle peut se décomposer et se défendre. La monade et la dyade réunies forment le ternaire, et représentent l’immensité de tout ce qui existe, l’être immuable et la matière altérable et changeante. J’ignore par quelle propriété le quaternaire, le nombre quatre, est encore un emblème de la divinité. # FIN DES VERS DORÉS DE PYTHAGORE -!!! Épître du feu philosophique, de Jean Pontanus +!!! Épître du feu philosophique, de Jean Pontanus !! # Les paragraphes ont été découpés et ne correspondent pas à ceux du texte. TEST: Épître du Feu Philosophique TEST: Lettre concernant la pierre dite philosophale TEST: Jean Pontanus TEST: in Theatrum Chimicum, 1614, t. III @@ -16539,11 +17819,11 @@ TEST: Si tu penses bien profondément aux propriétés du feu ci-dessus, tu la connaîtras, mais non autrement. TEST: Donc, touché d’un mouvement de pitié, j’ai écrit ceci ; mais, et afin que je me satisfasse, le feu n’est point transmué avec la matière, comme je l’ai dit ci-dessus. TEST: J’ai bien voulu le dire et en avertir les prudents de ces choses, pour qu’ils ne dépensent pas inutilement leur argent, mais qu’ils sachent auparavant ce qu’ils doivent chercher, et, par ce moyen, parviendront à la vérité de l’Art ; non pas autrement. À Dieu. # FIN DE L’ÉPÎTRE DU FEU PHILOSOPHIQUE -!!! Le Misanthrope, de Molière +!!! Le Misanthrope, de Molière !! TEST: LE MISANTHROPE (1666) TEST: de Molière TEST: Texte établi par Charles Louandre, Charpentier, 1910 (2, pp. 170-239). TEST: PERSONNAGES TEST: Alceste, amant de Célimène, @@ -16555,13 +17835,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… Index: grammalecte-cli.py ================================================================== --- grammalecte-cli.py +++ grammalecte-cli.py @@ -1,6 +1,10 @@ #!/usr/bin/env python3 + +""" +Grammalecte CLI (command line interface) +""" import sys import os.path import argparse import json @@ -71,10 +75,11 @@ sText, lLineSet = txt.createParagraphWithLines(lLine) yield iParagraph, sText, lLineSet def output (sText, hDst=None): + "write in the console or in a file if not null" if not hDst: echo(sText, end="") else: hDst.write(sText) @@ -92,10 +97,11 @@ print("# Error: file <" + spf + "> not found.") return None def main (): + "launch the CLI (command line interface)" xParser = argparse.ArgumentParser() xParser.add_argument("-f", "--file", help="parse file (UTF-8 required!) [on Windows, -f is similar to -ff]", type=str) xParser.add_argument("-ff", "--file_to_file", help="parse file (UTF-8 required!) and create a result file (*.res.txt)", type=str) xParser.add_argument("-owe", "--only_when_errors", help="display results only when there are errors", action="store_true") xParser.add_argument("-j", "--json", help="generate list of errors in JSON (only with option --file or --file_to_file)", action="store_true") @@ -232,14 +238,14 @@ elif sText.startswith("/++ "): for sRule in sText[3:].strip().split(): oGrammarChecker.gce.reactivateRule(sRule) echo("done") elif sText == "/debug" or sText == "/d": - xArgs.debug = not(xArgs.debug) + xArgs.debug = not xArgs.debug echo("debug mode on" if xArgs.debug else "debug mode off") elif sText == "/textformatter" or sText == "/tf": - xArgs.textformatter = not(xArgs.textformatter) + xArgs.textformatter = not xArgs.textformatter echo("textformatter on" if xArgs.debug else "textformatter off") elif sText == "/help" or sText == "/h": echo(_HELP) elif sText == "/lopt" or sText == "/lo": oGrammarChecker.gce.displayOptions("fr") @@ -253,16 +259,16 @@ # reload (todo) pass else: for sParagraph in txt.getParagraph(sText): if xArgs.textformatter: - sText = oTextFormatter.formatText(sText) - sRes = oGrammarChecker.generateParagraph(sText, bEmptyIfNoErrors=xArgs.only_when_errors, nWidth=xArgs.width, bDebug=xArgs.debug) + sText = oTextFormatter.formatText(sParagraph) + sRes = oGrammarChecker.generateParagraph(sParagraph, bEmptyIfNoErrors=xArgs.only_when_errors, nWidth=xArgs.width, bDebug=xArgs.debug) if sRes: echo("\n" + sRes) else: echo("\nNo error found.") sText = _getText(sInputText) if __name__ == '__main__': main() Index: grammalecte-server.py ================================================================== --- grammalecte-server.py +++ grammalecte-server.py @@ -1,10 +1,11 @@ #!/usr/bin/env python3 -import sys -import os.path -import argparse +""" +GRAMMALECTE SERVER +""" + import json import traceback import configparser import time @@ -19,11 +20,11 @@ - +

Grammalecte · Serveur

INFORMATIONS

@@ -49,11 +50,11 @@

Remise à zéro de ses options

[adresse_serveur]:8080/reset_options/fr (POST)

TEST

- +

Analyse

Texte à analyser :

@@ -91,10 +92,11 @@ I'm doomed, but you are not. You can get out of here. """ def getServerOptions (): + "load server options in , returns server options as dictionary" xConfig = configparser.SafeConfigParser() try: xConfig.read("grammalecte-server-options._global.ini") dOpt = xConfig._sections['options'] except: @@ -101,11 +103,12 @@ echo("Options file [grammalecte-server-options._global.ini] not found or not readable") exit() return dOpt -def getConfigOptions (sLang): +def getLangConfigOptions (sLang): + "load options for language , returns grammar checker options as dictionary" xConfig = configparser.SafeConfigParser() try: xConfig.read("grammalecte-server-options." + sLang + ".ini") except: echo("Options file [grammalecte-server-options." + sLang + ".ini] not found or not readable") @@ -118,10 +121,11 @@ exit() return dGCOpt def genUserId (): + "generator: create a user id" i = 0 while True: yield str(i) i += 1 @@ -135,11 +139,11 @@ oTextFormatter = oGrammarChecker.getTextFormatter() gce = oGrammarChecker.getGCEngine() echo("Grammalecte v{}".format(gce.version)) dServerOptions = getServerOptions() - dGCOptions = getConfigOptions("fr") + dGCOptions = getLangConfigOptions("fr") if dGCOptions: gce.setOptions(dGCOptions) dServerGCOptions = gce.getOptions() echo("Grammar options:\n" + " | ".join([ k + ": " + str(v) for k, v in sorted(dServerGCOptions.items()) ])) dUser = {} @@ -148,25 +152,28 @@ app = Bottle() # GET @app.route("/") def mainPage (): + "show main page" if dServerOptions.get("testpage", False) == "True": return HOMEPAGE #return template("main", {}) return SADLIFEOFAMACHINE @app.route("/get_options/fr") def listOptions (): + "show language options as JSON string" sUserId = request.cookies.user_id dOptions = dUser[sUserId]["gc_options"] if sUserId and sUserId in dUser else dServerGCOptions return '{ "values": ' + json.dumps(dOptions) + ', "labels": ' + json.dumps(gce.getOptionsLabels("fr"), ensure_ascii=False) + ' }' # POST @app.route("/gc_text/fr", method="POST") def gcText (): + "parse text sent via POST, show result as a JSON string" #if len(lang) != 2 or lang != "fr": # abort(404, "No grammar checker available for lang “" + str(lang) + "”") bComma = False dOptions = None sError = "" @@ -195,10 +202,11 @@ sJSON += "\n]}\n" return sJSON @app.route("/set_options/fr", method="POST") def setOptions (): + "change options for user_id, returns options as a JSON string" if request.forms.options: sUserId = request.cookies.user_id if request.cookies.user_id else next(userGenerator) dOptions = dUser[sUserId]["gc_options"] if sUserId in dUser else dict(dServerGCOptions) try: dOptions.update(json.loads(request.forms.options)) @@ -210,16 +218,18 @@ return '{"error": "options not registered"}' return '{"error": "no options received"}' @app.route("/reset_options/fr", method="POST") def resetOptions (): + "erase options stored for user_id" if request.cookies.user_id and request.cookies.user_id in dUser: del dUser[request.cookies.user_id] return "done" @app.route("/format_text/fr", method="POST") def formatText (): + "returns text modified via the text formatter" return oTextFormatter.formatText(request.forms.text) #@app.route('/static/') #def server_static (filepath): # return static_file(filepath, root='./views/static') @@ -234,19 +244,19 @@ nNowMinusNHours = int(time.time()) - (int(request.forms.hours) * 60 * 60) for nUserId, dValue in dUser.items(): if dValue["time"] < nNowMinusNHours: del dUser[nUserId] return "done" - else: - return "no" + return "no" except: traceback.print_exc() return "error" # ERROR @app.error(404) def error404 (error): + "show error when error 404" return 'Error 404.
' + str(error) run(app, \ host=dServerOptions.get('host', 'localhost'), \ port=int(dServerOptions.get('port', 8080))) 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,37 +16,39 @@ "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'], [/^&\w+;(?:\w+;|)/, 'HTMLENTITY'], [/^\d\d?h\d\d\b/, 'HOUR'], [/^-?\d+(?:[.,]\d+|)/, 'NUM'], + [/^[%‰+=*/<>⩾⩽-]/, 'SIGN'], [/^[a-zA-Zà-öÀ-Ö0-9ø-ÿØ-ßĀ-ʯfi-st]+(?:[’'`-][a-zA-Zà-öÀ-Ö0-9ø-ÿØ-ßĀ-ʯfi-st]+)*/, 'WORD'] ], "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'], [/^&\w+;(?:\w+;|)/, 'HTMLENTITY'], [/^(?:l|d|n|m|t|s|j|c|ç|lorsqu|puisqu|jusqu|quoiqu|qu)['’`]/i, 'ELPFX'], [/^\d\d?[hm]\d\d\b/, 'HOUR'], - [/^\d+(?:er|nd|e|de|ième|ème|eme)s?\b/, 'ORDINAL'], + [/^\d+(?:ers?|nds?|es?|des?|ièmes?|èmes?|emes?|ᵉʳˢ?|ⁿᵈˢ?|ᵉˢ?|ᵈᵉˢ?)\b/, 'ORDINAL'], [/^-?\d+(?:[.,]\d+|)/, 'NUM'], + [/^[%‰+=*/<>⩾⩽-]/, 'SIGN'], [/^[a-zA-Zà-öÀ-Ö0-9ø-ÿØ-ßĀ-ʯfi-st]+(?:[’'`-][a-zA-Zà-öÀ-Ö0-9ø-ÿØ-ßĀ-ʯfi-st]+)*/, 'WORD'] ] }; @@ -60,36 +62,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; } Index: graphspell/__init__.py ================================================================== --- graphspell/__init__.py +++ graphspell/__init__.py @@ -1,2 +1,11 @@ + +""" +SPELLCHECKER +using a Direct Acyclic Word Graph +with a transducer to retrieve +- lemma of words +- morphologies +with a spell suggestion mechanism +""" from .spellchecker import * Index: graphspell/char_player.py ================================================================== --- graphspell/char_player.py +++ graphspell/char_player.py @@ -1,7 +1,9 @@ -# list of similar chars -# useful for suggestion mechanism +""" +List of similar chars +useful for suggestion mechanism +""" import re import unicodedata @@ -8,10 +10,11 @@ _xTransCharsForSpelling = str.maketrans({ 'ſ': 's', 'ffi': 'ffi', 'ffl': 'ffl', 'ff': 'ff', 'ſt': 'ft', 'fi': 'fi', 'fl': 'fl', 'st': 'st' }) def spellingNormalization (sWord): + "nomalization NFC and removing ligatures" return unicodedata.normalize("NFC", sWord.translate(_xTransCharsForSpelling)) _xTransCharsForSimplification = str.maketrans({ 'à': 'a', 'é': 'e', 'î': 'i', 'ô': 'o', 'û': 'u', 'ÿ': 'i', "y": "i", @@ -19,11 +22,11 @@ 'ä': 'a', 'ê': 'e', 'í': 'i', 'ó': 'o', 'ü': 'u', 'ý': 'i', 'á': 'a', 'ë': 'e', 'ì': 'i', 'ò': 'o', 'ú': 'u', 'ỳ': 'i', 'ā': 'a', 'ē': 'e', 'ī': 'i', 'ō': 'o', 'ū': 'u', 'ȳ': 'i', 'ç': 'c', 'ñ': 'n', 'k': 'q', 'w': 'v', 'œ': 'oe', 'æ': 'ae', - 'ſ': 's', 'ffi': 'ffi', 'ffl': 'ffl', 'ff': 'ff', 'ſt': 'ft', 'fi': 'fi', 'fl': 'fl', 'st': 'st', + 'ſ': 's', 'ffi': 'ffi', 'ffl': 'ffl', 'ff': 'ff', 'ſt': 'ft', 'fi': 'fi', 'fl': 'fl', 'st': 'st', }) def simplifyWord (sWord): "word simplication before calculating distance between words" sWord = sWord.lower().translate(_xTransCharsForSimplification) @@ -92,11 +95,11 @@ "f": "fF", "F": "Ff", "g": "gGjJĵĴ", "G": "GgJjĴĵ", - + "h": "hH", "H": "Hh", "i": "iIîÎïÏyYíÍìÌīĪÿŸ", "I": "IiÎîÏïYyÍíÌìĪīŸÿ", @@ -237,10 +240,11 @@ "Z": ("SS", "ZH"), } def get1toXReplacement (cPrev, cCur, cNext): + "return tuple of replacements for " if cCur in aConsonant and (cPrev in aConsonant or cNext in aConsonant): return () return d1toX.get(cCur, ()) Index: graphspell/dawg.py ================================================================== --- graphspell/dawg.py +++ graphspell/dawg.py @@ -1,15 +1,16 @@ #!python3 -# FSA DICTIONARY BUILDER -# -# by Olivier R. -# License: MPL 2 -# -# This tool encodes lexicon into an indexable binary dictionary -# Input files MUST be encoded in UTF-8. - +""" +FSA DICTIONARY BUILDER + +by Olivier R. +License: MPL 2 + +This tool encodes lexicon into an indexable binary dictionary +Input files MUST be encoded in UTF-8. +""" import sys import os import collections import json @@ -21,10 +22,11 @@ from .progressbar import ProgressBar def readFile (spf): + "generator: read file and return for each line a list of elements separated by a tabulation." print(" < Read lexicon: " + spf) if os.path.isfile(spf): with open(spf, "r", encoding="utf-8") as hSrc: for sLine in hSrc: sLine = sLine.strip() @@ -97,23 +99,23 @@ nTag += 1 dTagOccur[sTag] = dTagOccur.get(sTag, 0) + 1 aEntry.add((sFlex, dAff[sAff], dTag[sTag])) if not aEntry: raise ValueError("# Error. Empty lexicon") - + # Preparing DAWG print(" > Preparing list of words") print(" Filter: " + (sSelectFilterRegex or "[None]")) lVal = lChar + lAff + lTag lWord = [ [dChar[c] for c in sFlex] + [iAff+nChar] + [iTag+nChar+nAff] for sFlex, iAff, iTag in aEntry ] aEntry = None - + # Dictionary of arc values occurrency, to sort arcs of each node dValOccur = dict( [ (dChar[c], dCharOccur[c]) for c in dChar ] \ + [ (dAff[aff]+nChar, dAffOccur[aff]) for aff in dAff ] \ + [ (dTag[tag]+nChar+nAff, dTagOccur[tag]) for tag in dTag ] ) - + self.sFileName = src if type(src) is str else "[None]" self.sLangCode = sLangCode self.sLangName = sLangName self.sDicName = sDicName self.nEntry = len(lWord) @@ -132,15 +134,15 @@ self.nArcVal = len(lVal) self.nTag = self.nArcVal - self.nChar - nAff self.cStemming = cStemming if cStemming == "A": self.funcStemming = st.changeWordWithAffixCode - elif cStemming == "S": + elif cStemming == "S": self.funcStemming = st.changeWordWithSuffixCode else: self.funcStemming = st.noStemming - + # build lWord.sort() oProgBar = ProgressBar(0, len(lWord)) for aEntry in lWord: self.insert(aEntry) @@ -147,20 +149,21 @@ oProgBar.increment(1) oProgBar.done() self.finish() self.countNodes() self.countArcs() - self.sortNodes() # version 2 and 3 + self.sortNodes() # version 2 and 3 self.sortNodeArcs(dValOccur) #self.sortNodeArcs2 (self.oRoot, "") self.displayInfo() # BUILD DAWG def insert (self, aEntry): + "insert a new entry (insertion must be made in alphabetical order)." if aEntry < self.aPreviousEntry: sys.exit("# Error: Words must be inserted in alphabetical order.") - + # find common prefix between word and previous word nCommonPrefix = 0 for i in range(min(len(aEntry), len(self.aPreviousEntry))): if aEntry[i] != self.aPreviousEntry[i]: break @@ -179,11 +182,11 @@ iChar = nCommonPrefix for c in aEntry[nCommonPrefix:]: oNextNode = DawgNode() oNode.arcs[c] = oNextNode self.lUncheckedNodes.append((oNode, c, oNextNode)) - if iChar == (len(aEntry) - 2): + if iChar == (len(aEntry) - 2): oNode.final = True iChar += 1 oNode = oNextNode oNode.final = True self.aPreviousEntry = aEntry @@ -203,54 +206,61 @@ # add the state to the minimized nodes. self.lMinimizedNodes[oChildNode] = oChildNode self.lUncheckedNodes.pop() def countNodes (self): + "count the number of nodes of the whole word graph" self.nNode = len(self.lMinimizedNodes) def countArcs (self): + "count the number of arcs in the whole word graph" self.nArc = 0 for oNode in self.lMinimizedNodes: self.nArc += len(oNode.arcs) - + def sortNodeArcs (self, dValOccur): + "sort arcs of each node according to " print(" > Sort node arcs") self.oRoot.sortArcs(dValOccur) for oNode in self.lMinimizedNodes: oNode.sortArcs(dValOccur) - + def sortNodeArcs2 (self, oNode, cPrevious=""): + "sort arcs of each node depending on the previous char" # recursive function dCharOccur = getCharOrderAfterChar(cPrevious) if dCharOccur: oNode.sortArcs2(dCharOccur, self.lArcVal) for nArcVal, oNextNode in oNode.arcs.items(): self.sortNodeArcs2(oNextNode, self.lArcVal[nArcVal]) def sortNodes (self): + "sort nodes" print(" > Sort nodes") for oNode in self.oRoot.arcs.values(): self._parseNodes(oNode) - + def _parseNodes (self, oNode): # Warning: recursive method if oNode.pos > 0: return oNode.setPos() self.lSortedNodes.append(oNode) for oNextNode in oNode.arcs.values(): - self._parseNodes(oNextNode) - + self._parseNodes(oNextNode) + def lookup (self, sWord): + "return True if is within the word graph (debugging)" oNode = self.oRoot for c in sWord: if self.dChar.get(c, '') not in oNode.arcs: return False oNode = oNode.arcs[self.dChar[c]] return oNode.final def morph (self, sWord): + "return a string of the morphologies of (debugging)" oNode = self.oRoot for c in sWord: if self.dChar.get(c, '') not in oNode.arcs: return '' oNode = oNode.arcs[self.dChar[c]] @@ -265,10 +275,11 @@ s += "]" return s return '' def displayInfo (self): + "display informations about the word graph" print(" * {:<12} {:>16,}".format("Entries:", self.nEntry)) print(" * {:<12} {:>16,}".format("Characters:", self.nChar)) print(" * {:<12} {:>16,}".format("Affixes:", self.nAff)) print(" * {:<12} {:>16,}".format("Tags:", self.nTag)) print(" * {:<12} {:>16,}".format("Arc values:", self.nArcVal)) @@ -275,10 +286,11 @@ print(" * {:<12} {:>16,}".format("Nodes:", self.nNode)) print(" * {:<12} {:>16,}".format("Arcs:", self.nArc)) print(" * {:<12} {:>16}".format("Stemming:", self.cStemming + "FX")) def getArcStats (self): + "return a string with statistics about nodes and arcs" d = {} for oNode in self.lMinimizedNodes: n = len(oNode.arcs) d[n] = d.get(n, 0) + 1 s = " * Nodes:\n" @@ -285,10 +297,11 @@ for n in d: s = s + " {:>9} nodes have {:>3} arcs\n".format(d[n], n) return s def writeInfo (self, sPathFile): + "write informations in file " print(" > Write informations") with open(sPathFile, 'w', encoding='utf-8', newline="\n") as hDst: hDst.write(self.getArcStats()) hDst.write("\n * Values:\n") for i, s in enumerate(self.lArcVal): @@ -394,10 +407,11 @@ if self.lSortedNodes[i].size != nSize: self.lSortedNodes[i].size = nSize bEnd = False def getBinaryAsJSON (self, nCompressionMethod=1, bBinaryDictAsHexString=True): + "return a JSON string containing all necessary data of the dictionary (compressed as a binary string)" self._calculateBinary(nCompressionMethod) byDic = b"" if nCompressionMethod == 1: byDic = self.oRoot.convToBytes1(self.nBytesArc, self.nBytesNodeAddress) for oNode in self.lMinimizedNodes: @@ -436,10 +450,11 @@ # https://github.com/mozilla/addons-linter/issues/1361 "sByDic": byDic.hex() if bBinaryDictAsHexString else [ e for e in byDic ] } def writeAsJSObject (self, spfDst, nCompressionMethod, bInJSModule=False, bBinaryDictAsHexString=True): + "write a file (JSON or JS module) with all the necessary data" if not spfDst.endswith(".json"): spfDst += "."+str(nCompressionMethod)+".json" with open(spfDst, "w", encoding="utf-8", newline="\n") as hDst: if bInJSModule: hDst.write('// JavaScript\n// Generated data (do not edit)\n\n"use strict";\n\nconst dictionary = ') @@ -447,17 +462,19 @@ if bInJSModule: hDst.write(";\n\nexports.dictionary = dictionary;\n") def writeBinary (self, sPathFile, nCompressionMethod, bDebug=False): """ + Save as a binary file. + Format of the binary indexable dictionary: Each section is separated with 4 bytes of \0 - + - Section Header: /grammalecte-fsa/[compression method] * compression method is an ASCII string - + - Section Informations: /[lang code] /[lang name] /[dictionary name] /[date creation] @@ -472,14 +489,14 @@ /[stemming code] * "S" means stems are generated by /suffix_code/, "A" means they are generated by /affix_code/ See defineSuffixCode() and defineAffixCode() for details. "N" means no stemming - + - Section Values: * a list of strings encoded in binary from utf-8, each value separated with a tabulation - + - Section Word Graph (nodes / arcs) * A list of nodes which are a list of arcs with an address of the next node. See DawgNode.convToBytes() for details. """ self._calculateBinary(nCompressionMethod) @@ -520,30 +537,32 @@ def _writeNodes (self, sPathFile, nCompressionMethod): "for debugging only" print(" > Write nodes") with open(sPathFile+".nodes."+str(nCompressionMethod)+".txt", 'w', encoding='utf-8', newline="\n") as hDst: if nCompressionMethod == 1: - hDst.write(self.oRoot.getTxtRepr1(self.nBytesArc, self.nBytesNodeAddress, self.lArcVal)+"\n") + hDst.write(self.oRoot.getTxtRepr1(self.nBytesArc, self.lArcVal)+"\n") #hDst.write( ''.join( [ "%02X " % z for z in self.oRoot.convToBytes1(self.nBytesArc, self.nBytesNodeAddress) ] ).strip() ) for oNode in self.lMinimizedNodes: - hDst.write(oNode.getTxtRepr1(self.nBytesArc, self.nBytesNodeAddress, self.lArcVal)+"\n") + hDst.write(oNode.getTxtRepr1(self.nBytesArc, self.lArcVal)+"\n") if nCompressionMethod == 2: - hDst.write(self.oRoot.getTxtRepr2(self.nBytesArc, self.nBytesNodeAddress, self.lArcVal)+"\n") + hDst.write(self.oRoot.getTxtRepr2(self.nBytesArc, self.lArcVal)+"\n") for oNode in self.lSortedNodes: - hDst.write(oNode.getTxtRepr2(self.nBytesArc, self.nBytesNodeAddress, self.lArcVal)+"\n") + hDst.write(oNode.getTxtRepr2(self.nBytesArc, self.lArcVal)+"\n") if nCompressionMethod == 3: - hDst.write(self.oRoot.getTxtRepr3(self.nBytesArc, self.nBytesNodeAddress, self.nBytesOffset, self.lArcVal)+"\n") + hDst.write(self.oRoot.getTxtRepr3(self.nBytesArc, self.nBytesOffset, self.lArcVal)+"\n") #hDst.write( ''.join( [ "%02X " % z for z in self.oRoot.convToBytes3(self.nBytesArc, self.nBytesNodeAddress, self.nBytesOffset) ] ).strip() ) for oNode in self.lSortedNodes: - hDst.write(oNode.getTxtRepr3(self.nBytesArc, self.nBytesNodeAddress, self.nBytesOffset, self.lArcVal)+"\n") + hDst.write(oNode.getTxtRepr3(self.nBytesArc, self.nBytesOffset, self.lArcVal)+"\n") class DawgNode: + """Node of the word graph""" + NextId = 0 NextPos = 1 # (version 2) - + def __init__ (self): self.i = DawgNode.NextId DawgNode.NextId += 1 self.final = False self.arcs = {} # key: arc value; value: a node @@ -551,19 +570,21 @@ self.pos = 0 # position in the binary dictionary (version 2) self.size = 0 # size of node in bytes (version 3) @classmethod def resetNextId (cls): + "set NextId to 0 " cls.NextId = 0 def setPos (self): # version 2 + "define a position for node (version 2)" self.pos = DawgNode.NextPos DawgNode.NextPos += 1 def __str__ (self): # Caution! this function is used for hashing and comparison! - sFinalChar = "1" if self.final else "0"; + sFinalChar = "1" if self.final else "0" l = [sFinalChar] for (key, node) in self.arcs.items(): l.append(str(key)) l.append(str(node.i)) return "_".join(l) @@ -576,36 +597,40 @@ # 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 sortArcs (self, dValOccur): + "sort arcs of node according to " self.arcs = collections.OrderedDict(sorted(self.arcs.items(), key=lambda t: dValOccur.get(t[0], 0), reverse=True)) def sortArcs2 (self, dValOccur, lArcVal): + "sort arcs of each node depending on the previous char" self.arcs = collections.OrderedDict(sorted(self.arcs.items(), key=lambda t: dValOccur.get(lArcVal[t[0]], 0), reverse=True)) # VERSION 1 ===================================================================================================== def convToBytes1 (self, nBytesArc, nBytesNodeAddress): """ + Convert to bytes (method 1). + Node scheme: - Arc length is defined by nBytesArc - Address length is defined by nBytesNodeAddress - + | Arc | Address of next node | | | | - /---------------\ /---------------\ /---------------\ /---------------\ /---------------\ /---------------\ - | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | - \---------------/ \---------------/ \---------------/ \---------------/ \---------------/ \---------------/ + ┏━━━━━━━━━━━━━━━┓ ┏━━━━━━━━━━━━━━━┓ ┏━━━━━━━━━━━━━━━┓ ┏━━━━━━━━━━━━━━━┓ ┏━━━━━━━━━━━━━━━┓ ┏━━━━━━━━━━━━━━━┓ + ┃ ┃ ┃ ┃ ┃ ┃ ┃ ┃ ┃ ┃ ┃ ┃ ┃ ┃ ┃ ┃ ┃ ┃ ┃ ┃ ┃ ┃ ┃ ┃ ┃ ┃ ┃ ┃ ┃ ┃ ┃ ┃ ┃ ┃ ┃ ┃ ┃ ┃ ┃ ┃ ┃ ┃ ┃ ┃ ┃ ┃ ┃ ┃ ┃ ┃ ┃ ┃ ┃ ┃ + ┗━━━━━━━━━━━━━━━┛ ┗━━━━━━━━━━━━━━━┛ ┗━━━━━━━━━━━━━━━┛ ┗━━━━━━━━━━━━━━━┛ ┗━━━━━━━━━━━━━━━┛ ┗━━━━━━━━━━━━━━━┛ [...] - /---------------\ /---------------\ /---------------\ /---------------\ /---------------\ /---------------\ - | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | - \---------------/ \---------------/ \---------------/ \---------------/ \---------------/ \---------------/ + ┏━━━━━━━━━━━━━━━┓ ┏━━━━━━━━━━━━━━━┓ ┏━━━━━━━━━━━━━━━┓ ┏━━━━━━━━━━━━━━━┓ ┏━━━━━━━━━━━━━━━┓ ┏━━━━━━━━━━━━━━━┓ + ┃ ┃ ┃ ┃ ┃ ┃ ┃ ┃ ┃ ┃ ┃ ┃ ┃ ┃ ┃ ┃ ┃ ┃ ┃ ┃ ┃ ┃ ┃ ┃ ┃ ┃ ┃ ┃ ┃ ┃ ┃ ┃ ┃ ┃ ┃ ┃ ┃ ┃ ┃ ┃ ┃ ┃ ┃ ┃ ┃ ┃ ┃ ┃ ┃ ┃ ┃ ┃ ┃ ┃ + ┗━━━━━━━━━━━━━━━┛ ┗━━━━━━━━━━━━━━━┛ ┗━━━━━━━━━━━━━━━┛ ┗━━━━━━━━━━━━━━━┛ ┗━━━━━━━━━━━━━━━┛ ┗━━━━━━━━━━━━━━━┛ ^ ^ - | | - | | - | \___ if 1, last arc of this node - \_____ if 1, this node is final (only on the first arc) + ┃ ┃ + ┃ ┃ + ┃ ┗━━━ if 1, last arc of this node + ┗━━━━━ if 1, this node is final (only on the first arc) """ nArc = len(self.arcs) nFinalNodeMask = 1 << ((nBytesArc*8)-1) nFinalArcMask = 1 << ((nBytesArc*8)-2) if len(self.arcs) == 0: @@ -621,12 +646,13 @@ if i == nArc: val = val | nFinalArcMask by += val.to_bytes(nBytesArc, byteorder='big') by += self.arcs[arc].addr.to_bytes(nBytesNodeAddress, byteorder='big') return by - - def getTxtRepr1 (self, nBytesArc, nBytesNodeAddress, lVal): + + def getTxtRepr1 (self, nBytesArc, lVal): + "return representation as string of node (method 1)" nArc = len(self.arcs) nFinalNodeMask = 1 << ((nBytesArc*8)-1) nFinalArcMask = 1 << ((nBytesArc*8)-2) s = "i{:_>10} -- #{:_>10}\n".format(self.i, self.addr) if len(self.arcs) == 0: @@ -642,28 +668,30 @@ return s # VERSION 2 ===================================================================================================== def convToBytes2 (self, nBytesArc, nBytesNodeAddress): """ + Convert to bytes (method 2). + Node scheme: - Arc length is defined by nBytesArc - Address length is defined by nBytesNodeAddress - + | Arc | Address of next node | | | | - /---------------\ /---------------\ /---------------\ /---------------\ /---------------\ /---------------\ - | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | - \---------------/ \---------------/ \---------------/ \---------------/ \---------------/ \---------------/ + ┏━━━━━━━━━━━━━━━┓ ┏━━━━━━━━━━━━━━━┓ ┏━━━━━━━━━━━━━━━┓ ┏━━━━━━━━━━━━━━━┓ ┏━━━━━━━━━━━━━━━┓ ┏━━━━━━━━━━━━━━━┓ + ┃ ┃ ┃ ┃ ┃ ┃ ┃ ┃ ┃ ┃ ┃ ┃ ┃ ┃ ┃ ┃ ┃ ┃ ┃ ┃ ┃ ┃ ┃ ┃ ┃ ┃ ┃ ┃ ┃ ┃ ┃ ┃ ┃ ┃ ┃ ┃ ┃ ┃ ┃ ┃ ┃ ┃ ┃ ┃ ┃ ┃ ┃ ┃ ┃ ┃ ┃ ┃ ┃ ┃ + ┗━━━━━━━━━━━━━━━┛ ┗━━━━━━━━━━━━━━━┛ ┗━━━━━━━━━━━━━━━┛ ┗━━━━━━━━━━━━━━━┛ ┗━━━━━━━━━━━━━━━┛ ┗━━━━━━━━━━━━━━━┛ [...] - /---------------\ /---------------\ /---------------\ /---------------\ /---------------\ /---------------\ - | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | - \---------------/ \---------------/ \---------------/ \---------------/ \---------------/ \---------------/ + ┏━━━━━━━━━━━━━━━┓ ┏━━━━━━━━━━━━━━━┓ ┏━━━━━━━━━━━━━━━┓ ┏━━━━━━━━━━━━━━━┓ ┏━━━━━━━━━━━━━━━┓ ┏━━━━━━━━━━━━━━━┓ + ┃ ┃ ┃ ┃ ┃ ┃ ┃ ┃ ┃ ┃ ┃ ┃ ┃ ┃ ┃ ┃ ┃ ┃ ┃ ┃ ┃ ┃ ┃ ┃ ┃ ┃ ┃ ┃ ┃ ┃ ┃ ┃ ┃ ┃ ┃ ┃ ┃ ┃ ┃ ┃ ┃ ┃ ┃ ┃ ┃ ┃ ┃ ┃ ┃ ┃ ┃ ┃ ┃ ┃ + ┗━━━━━━━━━━━━━━━┛ ┗━━━━━━━━━━━━━━━┛ ┗━━━━━━━━━━━━━━━┛ ┗━━━━━━━━━━━━━━━┛ ┗━━━━━━━━━━━━━━━┛ ┗━━━━━━━━━━━━━━━┛ ^ ^ ^ - | | | - | | \_ if 1, caution, no address: next node is the following node - | \___ if 1, last arc of this node - \_____ if 1, this node is final (only on the first arc) + ┃ ┃ ┃ + ┃ ┃ ┗━━ if 1, caution, no address: next node is the following node + ┃ ┗━━━━ if 1, last arc of this node + ┗━━━━━━ if 1, this node is final (only on the first arc) """ nArc = len(self.arcs) nFinalNodeMask = 1 << ((nBytesArc*8)-1) nFinalArcMask = 1 << ((nBytesArc*8)-2) nNextNodeMask = 1 << ((nBytesArc*8)-3) @@ -684,12 +712,13 @@ by += val.to_bytes(nBytesArc, byteorder='big') else: by += val.to_bytes(nBytesArc, byteorder='big') by += self.arcs[arc].addr.to_bytes(nBytesNodeAddress, byteorder='big') return by - - def getTxtRepr2 (self, nBytesArc, nBytesNodeAddress, lVal): + + def getTxtRepr2 (self, nBytesArc, lVal): + "return representation as string of node (method 2)" nArc = len(self.arcs) nFinalNodeMask = 1 << ((nBytesArc*8)-1) nFinalArcMask = 1 << ((nBytesArc*8)-2) nNextNodeMask = 1 << ((nBytesArc*8)-3) s = "i{:_>10} -- #{:_>10}\n".format(self.i, self.addr) @@ -702,41 +731,43 @@ val = val | nFinalNodeMask if i == nArc: val = val | nFinalArcMask if (self.pos + 1) == self.arcs[arc].pos and self.i != 0: val = val | nNextNodeMask - s += " {:<20} {:0>16}\n".format(lVal[arc], bin(val)[2:], "") + s += " {:<20} {:0>16}\n".format(lVal[arc], bin(val)[2:]) else: s += " {:<20} {:0>16} i{:_>10} #{:_>10}\n".format(lVal[arc], bin(val)[2:], self.arcs[arc].i, self.arcs[arc].addr) return s # VERSION 3 ===================================================================================================== def convToBytes3 (self, nBytesArc, nBytesNodeAddress, nBytesOffset): """ + Convert to bytes (method 3). + Node scheme: - Arc length is defined by nBytesArc - Address length is defined by nBytesNodeAddress - Offset length is defined by nBytesOffset - + | Arc | Address of next node or offset to next node | | | | - /---------------\ /---------------\ /---------------\ /---------------\ /---------------\ /---------------\ - |1|0|0| | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | - \---------------/ \---------------/ \---------------/ \---------------/ \---------------/ \---------------/ + ┏━━━━━━━━━━━━━━━┓ ┏━━━━━━━━━━━━━━━┓ ┏━━━━━━━━━━━━━━━┓ ┏━━━━━━━━━━━━━━━┓ ┏━━━━━━━━━━━━━━━┓ ┏━━━━━━━━━━━━━━━┓ + ┃1┃0┃0┃ ┃ ┃ ┃ ┃ ┃ ┃ ┃ ┃ ┃ ┃ ┃ ┃ ┃ ┃ ┃ ┃ ┃ ┃ ┃ ┃ ┃ ┃ ┃ ┃ ┃ ┃ ┃ ┃ ┃ ┃ ┃ ┃ ┃ ┃ ┃ ┃ ┃ ┃ ┃ ┃ ┃ ┃ ┃ ┃ ┃ ┃ ┃ ┃ ┃ ┃ + ┗━━━━━━━━━━━━━━━┛ ┗━━━━━━━━━━━━━━━┛ ┗━━━━━━━━━━━━━━━┛ ┗━━━━━━━━━━━━━━━┛ ┗━━━━━━━━━━━━━━━┛ ┗━━━━━━━━━━━━━━━┛ [...] - /---------------\ /---------------\ /---------------\ - |0|0|1| | | | | | | | | | | | | | | | | | | | | | | | Offsets are shorter than addresses - \---------------/ \---------------/ \---------------/ - /---------------\ /---------------\ /---------------\ /---------------\ /---------------\ /---------------\ - |0|1|0| | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | - \---------------/ \---------------/ \---------------/ \---------------/ \---------------/ \---------------/ + ┏━━━━━━━━━━━━━━━┓ ┏━━━━━━━━━━━━━━━┓ ┏━━━━━━━━━━━━━━━┓ + ┃0┃0┃1┃ ┃ ┃ ┃ ┃ ┃ ┃ ┃ ┃ ┃ ┃ ┃ ┃ ┃ ┃ ┃ ┃ ┃ ┃ ┃ ┃ ┃ ┃ ┃ Offsets are shorter than addresses + ┗━━━━━━━━━━━━━━━┛ ┗━━━━━━━━━━━━━━━┛ ┗━━━━━━━━━━━━━━━┛ + ┏━━━━━━━━━━━━━━━┓ ┏━━━━━━━━━━━━━━━┓ ┏━━━━━━━━━━━━━━━┓ ┏━━━━━━━━━━━━━━━┓ ┏━━━━━━━━━━━━━━━┓ ┏━━━━━━━━━━━━━━━┓ + ┃0┃1┃0┃ ┃ ┃ ┃ ┃ ┃ ┃ ┃ ┃ ┃ ┃ ┃ ┃ ┃ ┃ ┃ ┃ ┃ ┃ ┃ ┃ ┃ ┃ ┃ ┃ ┃ ┃ ┃ ┃ ┃ ┃ ┃ ┃ ┃ ┃ ┃ ┃ ┃ ┃ ┃ ┃ ┃ ┃ ┃ ┃ ┃ ┃ ┃ ┃ ┃ ┃ + ┗━━━━━━━━━━━━━━━┛ ┗━━━━━━━━━━━━━━━┛ ┗━━━━━━━━━━━━━━━┛ ┗━━━━━━━━━━━━━━━┛ ┗━━━━━━━━━━━━━━━┛ ┗━━━━━━━━━━━━━━━┛ ^ ^ ^ - | | | - | | \_ if 1, offset instead of address of next node - | \___ if 1, last arc of this node - \_____ if 1, this node is final (only on the first arc) + ┃ ┃ ┃ + ┃ ┃ ┗━━ if 1, offset instead of address of next node + ┃ ┗━━━━ if 1, last arc of this node + ┗━━━━━━ if 1, this node is final (only on the first arc) """ nArc = len(self.arcs) nFinalNodeMask = 1 << ((nBytesArc*8)-1) nFinalArcMask = 1 << ((nBytesArc*8)-2) nNextNodeMask = 1 << ((nBytesArc*8)-3) @@ -759,12 +790,13 @@ by += (self.arcs[arc].addr-self.addr).to_bytes(nBytesOffset, byteorder='big') else: by += val.to_bytes(nBytesArc, byteorder='big') by += self.arcs[arc].addr.to_bytes(nBytesNodeAddress, byteorder='big') return by - - def getTxtRepr3 (self, nBytesArc, nBytesNodeAddress, nBytesOffset, lVal): + + def getTxtRepr3 (self, nBytesArc, nBytesOffset, lVal): + "return representation as string of node (method 3)" nArc = len(self.arcs) nFinalNodeMask = 1 << ((nBytesArc*8)-1) nFinalArcMask = 1 << ((nBytesArc*8)-2) nNextNodeMask = 1 << ((nBytesArc*8)-3) nMaxOffset = (2 ** (nBytesOffset * 8)) - 1 @@ -794,20 +826,23 @@ "": {} } def addWordToCharDict (sWord): + "for each character of , count how many times it appears after the previous character, and store result in a <_dCharOrder>" cPrevious = "" for cChar in sWord: if cPrevious not in _dCharOrder: _dCharOrder[cPrevious] = {} _dCharOrder[cPrevious][cChar] = _dCharOrder[cPrevious].get(cChar, 0) + 1 cPrevious = cChar def getCharOrderAfterChar (cChar): + "return a dictionary of chars with number of times it appears after character " return _dCharOrder.get(cChar, None) def displayCharOrder (): + "display how many times each character appear after another one" for key, value in _dCharOrder.items(): print("[" + key + "]: ", ", ".join([ c+":"+str(n) for c, n in sorted(value.items(), key=lambda t: t[1], reverse=True) ])) Index: graphspell/echo.py ================================================================== --- graphspell/echo.py +++ graphspell/echo.py @@ -1,9 +1,13 @@ #!python3 -# The most boring yet indispensable function: print! +""" +The most boring yet indispensable function: print! +Because you can print on Windows console without being sure the script won’t crash… +Windows console don’t accept many characters. +""" import sys _CHARMAP = str.maketrans({ 'œ': 'ö', 'Œ': 'Ö', 'ʳ': "r", 'ᵉ': "e", '…': "_", \ @@ -22,8 +26,8 @@ if sys.platform != "win32": print(obj, sep=sep, end=end, file=file, flush=flush) return True try: print(str(obj).translate(_CHARMAP), sep=sep, end=end, file=file, flush=flush) - except: + except Exception: print(str(obj).encode('ascii', 'replace').decode('ascii', 'replace'), sep=sep, end=end, file=file, flush=flush) return True ADDED graphspell/fr.py Index: graphspell/fr.py ================================================================== --- /dev/null +++ graphspell/fr.py @@ -0,0 +1,46 @@ +""" +Default suggestion for 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 @@ -1,8 +1,13 @@ #!python3 -import os +""" +INDEXABLE BINARY DIRECT ACYCLIC WORD GRAPH +Implementation of a spellchecker as a transducer (storing transformation code to get lemma and morphologies) +and a spell suggestion mechanim +""" + import traceback import pkgutil import re from functools import wraps import time @@ -19,10 +24,11 @@ def timethis (func): "decorator for the execution time" @wraps(func) def wrapper (*args, **kwargs): + "something to prevent pylint whining" fStart = time.time() result = func(*args, **kwargs) fEnd = time.time() print(func.__name__, fEnd - fStart) return result @@ -56,11 +62,11 @@ self.aSugg.add(sSugg) if nDist < self.nMinDist: self.nMinDist = nDist self.nDistLimit = min(self.nDistLimit, self.nMinDist+2) - def getSuggestions (self, nSuggLimit=10, nDistLimit=-1): + def getSuggestions (self, nSuggLimit=10): "return a list of suggestions" if self.dSugg[0]: # we sort the better results with the original word self.dSugg[0].sort(key=lambda sSugg: st.distanceDamerauLevenshtein(self.sWord, sSugg)) lRes = self.dSugg.pop(0) @@ -75,10 +81,11 @@ elif self.sWord[0:1].isupper(): lRes = list(map(lambda sSugg: sSugg[0:1].upper()+sSugg[1:], lRes)) # dont’ use <.istitle> return lRes[:nSuggLimit] def reset (self): + "clear data" self.aSugg.clear() self.dSugg.clear() class IBDAWG: @@ -147,11 +154,11 @@ raise ValueError("# Error. Unknown dictionary version: {}".format(self.by[17:18])) try: header, info, values, bdic = self.by.split(b"\0\0\0\0", 3) except Exception: raise Exception - + self.nCompressionMethod = int(self.by[17:18].decode("utf-8")) self.sHeader = header.decode("utf-8") self.lArcVal = values.decode("utf-8").split("\t") self.nArcVal = len(self.lArcVal) self.byDic = bdic @@ -182,10 +189,11 @@ self.__dict__.update(oJSON) self.byDic = binascii.unhexlify(self.sByDic) self.dCharVal = { v: k for k, v in self.dChar.items() } def getInfo (self): + "return string about the IBDAWG" return " Language: {0.sLangName} Lang code: {0.sLangCode} Dictionary name: {0.sDicName}" \ " Compression method: {0.nCompressionMethod:>2} Date: {0.sDate} Stemming: {0.cStemming}FX\n" \ " Arcs values: {0.nArcVal:>10,} = {0.nChar:>5,} characters, {0.nAff:>6,} affixes, {0.nTag:>6,} tags\n" \ " Dictionary: {0.nEntry:>12,} entries, {0.nNode:>11,} nodes, {0.nArc:>11,} arcs\n" \ " Address size: {0.nBytesNodeAddress:>1} bytes, Arc size: {0.nBytesArc:>1} bytes\n".format(self) @@ -194,35 +202,35 @@ "write IBDAWG as a JavaScript object in a JavaScript module" with open(spfDest, "w", encoding="utf-8", newline="\n") as hDst: if bInJSModule: hDst.write('// JavaScript\n// Generated data (do not edit)\n\n"use strict";\n\nconst dictionary = ') hDst.write(json.dumps({ - "sHeader": "/grammalecte-fsa/", - "sLangCode": self.sLangCode, - "sLangName": self.sLangName, - "sDicName": self.sDicName, - "sFileName": self.sFileName, - "sDate": self.sDate, - "nEntry": self.nEntry, - "nChar": self.nChar, - "nAff": self.nAff, - "nTag": self.nTag, - "cStemming": self.cStemming, - "dChar": self.dChar, - "nNode": self.nNode, - "nArc": self.nArc, - "nArcVal": self.nArcVal, - "lArcVal": self.lArcVal, - "nCompressionMethod": self.nCompressionMethod, - "nBytesArc": self.nBytesArc, - "nBytesNodeAddress": self.nBytesNodeAddress, - "nBytesOffset": self.nBytesOffset, - # JavaScript is a pile of shit, so Mozilla’s JS parser don’t like file bigger than 4 Mb! - # So, if necessary, we use an hexadecimal string, that we will convert later in Firefox’s extension. - # https://github.com/mozilla/addons-linter/issues/1361 - "sByDic": self.byDic.hex() if bBinaryDictAsHexString else [ e for e in self.byDic ] - }, ensure_ascii=False)) + "sHeader": "/grammalecte-fsa/", + "sLangCode": self.sLangCode, + "sLangName": self.sLangName, + "sDicName": self.sDicName, + "sFileName": self.sFileName, + "sDate": self.sDate, + "nEntry": self.nEntry, + "nChar": self.nChar, + "nAff": self.nAff, + "nTag": self.nTag, + "cStemming": self.cStemming, + "dChar": self.dChar, + "nNode": self.nNode, + "nArc": self.nArc, + "nArcVal": self.nArcVal, + "lArcVal": self.lArcVal, + "nCompressionMethod": self.nCompressionMethod, + "nBytesArc": self.nBytesArc, + "nBytesNodeAddress": self.nBytesNodeAddress, + "nBytesOffset": self.nBytesOffset, + # JavaScript is a pile of shit, so Mozilla’s JS parser don’t like file bigger than 4 Mb! + # So, if necessary, we use an hexadecimal string, that we will convert later in Firefox’s extension. + # https://github.com/mozilla/addons-linter/issues/1361 + "sByDic": self.byDic.hex() if bBinaryDictAsHexString else [ e for e in self.byDic ] + }, ensure_ascii=False)) if bInJSModule: hDst.write(";\n\nexports.dictionary = dictionary;\n") def isValidToken (self, sToken): "checks if is valid (if there is hyphens in , is split, each part is checked)" @@ -265,11 +273,11 @@ iAddr = 0 for c in sWord: if c not in self.dChar: return False iAddr = self._lookupArcNode(self.dChar[c], iAddr) - if iAddr == None: + if iAddr is None: return False return bool(int.from_bytes(self.byDic[iAddr:iAddr+self.nBytesArc], byteorder='big') & self._finalNodeMask) def getMorph (self, sWord): "retrieves morphologies list, different casing allowed" @@ -344,17 +352,17 @@ self._suggest(oSuggResult, "", nMaxSwitch, nMaxDel, nMaxHardRepl, nMaxJump, nDist, nDeep+1, iAddr, sNewWord, True) # remove last char and go on for sRepl in cp.dFinal1.get(sRemain, ()): self._suggest(oSuggResult, sRepl, nMaxSwitch, nMaxDel, nMaxHardRepl, nMaxJump, nDist, nDeep+1, iAddr, sNewWord, True) #@timethis - def suggest2 (self, sWord, nMaxSugg=10): + def suggest2 (self, sWord, nSuggLimit=10): "returns a set of suggestions for " sWord = cp.spellingNormalization(sWord) sPfx, sWord, sSfx = cp.cut(sWord) oSuggResult = SuggResult(sWord) self._suggest2(oSuggResult) - aSugg = oSuggResult.getSuggestions() + aSugg = oSuggResult.getSuggestions(nSuggLimit) if sSfx or sPfx: # we add what we removed return list(map(lambda sSug: sPfx + sSug + sSfx, aSugg)) return aSugg @@ -407,21 +415,21 @@ "show the path taken by in the graph" sWord = cp.spellingNormalization(sWord) c1 = sWord[0:1] if sWord else " " iPos = -1 n = 0 - print(c1 + ": ", end="") + echo(c1 + ": ", end="") for c2, jAddr in self._getCharArcs(iAddr): - print(c2, end="") + echo(c2, end="") if c2 == sWord[0:1]: iNextNodeAddr = jAddr iPos = n n += 1 if not sWord: return if iPos >= 0: - print("\n "+ " " * iPos + "|") + echo("\n " + " " * iPos + "|") self.drawPath(sWord[1:], iNextNodeAddr) def getSimilarEntries (self, sWord, nSuggLimit=10): "return a list of tuples (similar word, stem, morphology)" if not sWord: @@ -469,29 +477,29 @@ iAddr = 0 for c in sWord: if c not in self.dChar: return [] iAddr = self._lookupArcNode(self.dChar[c], iAddr) - if iAddr == None: + if iAddr is None: return [] - if (int.from_bytes(self.byDic[iAddr:iAddr+self.nBytesArc], byteorder='big') & self._finalNodeMask): + if int.from_bytes(self.byDic[iAddr:iAddr+self.nBytesArc], byteorder='big') & self._finalNodeMask: l = [] nRawArc = 0 while not (nRawArc & self._lastArcMask): iEndArcAddr = iAddr + self.nBytesArc nRawArc = int.from_bytes(self.byDic[iAddr:iEndArcAddr], byteorder='big') nArc = nRawArc & self._arcMask if nArc > self.nChar: - # This value is not a char, this is a stemming code + # This value is not a char, this is a stemming code sStem = ">" + self.funcStemming(sWord, self.lArcVal[nArc]) # Now , we go to the next node and retrieve all following arcs values, all of them are tags 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 [] @@ -500,21 +508,21 @@ iAddr = 0 for c in sWord: if c not in self.dChar: return [] iAddr = self._lookupArcNode(self.dChar[c], iAddr) - if iAddr == None: + if iAddr is None: return [] - if (int.from_bytes(self.byDic[iAddr:iAddr+self.nBytesArc], byteorder='big') & self._finalNodeMask): + if int.from_bytes(self.byDic[iAddr:iAddr+self.nBytesArc], byteorder='big') & self._finalNodeMask: l = [] nRawArc = 0 while not (nRawArc & self._lastArcMask): iEndArcAddr = iAddr + self.nBytesArc nRawArc = int.from_bytes(self.byDic[iAddr:iEndArcAddr], byteorder='big') nArc = nRawArc & self._arcMask if nArc > self.nChar: - # This value is not a char, this is a stemming code + # This value is not a char, this is a stemming code l.append(self.funcStemming(sWord, self.lArcVal[nArc])) iAddr = iEndArcAddr+self.nBytesNodeAddress return l return [] @@ -522,33 +530,33 @@ "looks if is an arc at the node at , if yes, returns address of next node else None" while True: iEndArcAddr = iAddr+self.nBytesArc nRawArc = int.from_bytes(self.byDic[iAddr:iEndArcAddr], byteorder='big') if nVal == (nRawArc & self._arcMask): - # the value we are looking for + # the value we are looking for # we return the address of the next node return int.from_bytes(self.byDic[iEndArcAddr:iEndArcAddr+self.nBytesNodeAddress], byteorder='big') else: # value not found - if (nRawArc & self._lastArcMask): + if nRawArc & self._lastArcMask: return None iAddr = iEndArcAddr+self.nBytesNodeAddress def _getArcs1 (self, iAddr): "generator: return all arcs at as tuples of (nVal, iAddr)" while True: iEndArcAddr = iAddr+self.nBytesArc nRawArc = int.from_bytes(self.byDic[iAddr:iEndArcAddr], byteorder='big') - yield (nRawArc & self._arcMask, int.from_bytes(self.byDic[iEndArcAddr:iEndArcAddr+self.nBytesNodeAddress], byteorder='big')) - if (nRawArc & self._lastArcMask): + yield nRawArc & self._arcMask, int.from_bytes(self.byDic[iEndArcAddr:iEndArcAddr+self.nBytesNodeAddress], byteorder='big') + if nRawArc & self._lastArcMask: break iAddr = iEndArcAddr+self.nBytesNodeAddress def _writeNodes1 (self, spfDest): "for debugging only" print(" > Write binary nodes") - with codecs.open(spfDest, 'w', 'utf-8', newline="\n") as hDst: + with open(spfDest, 'w', 'utf-8', newline="\n") as hDst: iAddr = 0 hDst.write("i{:_>10} -- #{:_>10}\n".format("0", iAddr)) while iAddr < len(self.byDic): iEndArcAddr = iAddr+self.nBytesArc nRawArc = int.from_bytes(self.byDic[iAddr:iEndArcAddr], byteorder='big') @@ -567,21 +575,21 @@ iAddr = 0 for c in sWord: if c not in self.dChar: return [] iAddr = self._lookupArcNode(self.dChar[c], iAddr) - if iAddr == None: + if iAddr is None: return [] - if (int.from_bytes(self.byDic[iAddr:iAddr+self.nBytesArc], byteorder='big') & self._finalNodeMask): + if int.from_bytes(self.byDic[iAddr:iAddr+self.nBytesArc], byteorder='big') & self._finalNodeMask: l = [] nRawArc = 0 while not (nRawArc & self._lastArcMask): iEndArcAddr = iAddr + self.nBytesArc nRawArc = int.from_bytes(self.byDic[iAddr:iEndArcAddr], byteorder='big') nArc = nRawArc & self._arcMask if nArc > self.nChar: - # This value is not a char, this is a stemming code + # This value is not a char, this is a stemming code sStem = ">" + self.funcStemming(sWord, self.lArcVal[nArc]) # Now , we go to the next node and retrieve all following arcs values, all of them are tags if not (nRawArc & self._addrBitMask): iAddr2 = int.from_bytes(self.byDic[iEndArcAddr:iEndArcAddr+self.nBytesNodeAddress], byteorder='big') else: @@ -592,11 +600,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 [] @@ -605,21 +613,21 @@ iAddr = 0 for c in sWord: if c not in self.dChar: return [] iAddr = self._lookupArcNode(self.dChar[c], iAddr) - if iAddr == None: + if iAddr is None: return [] - if (int.from_bytes(self.byDic[iAddr:iAddr+self.nBytesArc], byteorder='big') & self._finalNodeMask): + if int.from_bytes(self.byDic[iAddr:iAddr+self.nBytesArc], byteorder='big') & self._finalNodeMask: l = [] nRawArc = 0 while not (nRawArc & self._lastArcMask): iEndArcAddr = iAddr + self.nBytesArc nRawArc = int.from_bytes(self.byDic[iAddr:iEndArcAddr], byteorder='big') nArc = nRawArc & self._arcMask if nArc > self.nChar: - # This value is not a char, this is a stemming code + # This value is not a char, this is a stemming code l.append(self.funcStemming(sWord, self.lArcVal[nArc])) # Now , we go to the next node if not (nRawArc & self._addrBitMask): iAddr2 = int.from_bytes(self.byDic[iEndArcAddr:iEndArcAddr+self.nBytesNodeAddress], byteorder='big') else: @@ -636,11 +644,11 @@ "looks if is an arc at the node at , if yes, returns address of next node else None" while True: iEndArcAddr = iAddr+self.nBytesArc nRawArc = int.from_bytes(self.byDic[iAddr:iEndArcAddr], byteorder='big') if nVal == (nRawArc & self._arcMask): - # the value we are looking for + # the value we are looking for if not (nRawArc & self._addrBitMask): # we return the address of the next node return int.from_bytes(self.byDic[iEndArcAddr:iEndArcAddr+self.nBytesNodeAddress], byteorder='big') else: # we go to the end of the node @@ -649,18 +657,18 @@ nRawArc = int.from_bytes(self.byDic[iAddr:iAddr+self.nBytesArc], byteorder='big') iAddr += self.nBytesArc + self.nBytesNodeAddress if not (nRawArc & self._addrBitMask) else self.nBytesArc return iAddr else: # value not found - if (nRawArc & self._lastArcMask): + if nRawArc & self._lastArcMask: return None iAddr = iEndArcAddr+self.nBytesNodeAddress if not (nRawArc & self._addrBitMask) else iEndArcAddr def _writeNodes2 (self, spfDest): "for debugging only" print(" > Write binary nodes") - with codecs.open(spfDest, 'w', 'utf-8', newline="\n") as hDst: + with open(spfDest, 'w', 'utf-8', newline="\n") as hDst: iAddr = 0 hDst.write("i{:_>10} -- #{:_>10}\n".format("0", iAddr)) while iAddr < len(self.byDic): iEndArcAddr = iAddr+self.nBytesArc nRawArc = int.from_bytes(self.byDic[iAddr:iEndArcAddr], byteorder='big') @@ -670,11 +678,11 @@ hDst.write(" {:<20} {:0>16} i{:>10} #{:_>10}\n".format(self.lArcVal[nArc], bin(nRawArc)[2:], "?", iNextNodeAddr)) iAddr = iEndArcAddr+self.nBytesNodeAddress else: hDst.write(" {:<20} {:0>16}\n".format(self.lArcVal[nArc], bin(nRawArc)[2:])) iAddr = iEndArcAddr - if (nRawArc & self._lastArcMask): + if nRawArc & self._lastArcMask: hDst.write("\ni{:_>10} -- #{:_>10}\n".format("?", iAddr)) hDst.close() # VERSION 3 def _morph3 (self, sWord): @@ -682,22 +690,22 @@ iAddr = 0 for c in sWord: if c not in self.dChar: return [] iAddr = self._lookupArcNode(self.dChar[c], iAddr) - if iAddr == None: + if iAddr is None: return [] - if (int.from_bytes(self.byDic[iAddr:iAddr+self.nBytesArc], byteorder='big') & self._finalNodeMask): + if int.from_bytes(self.byDic[iAddr:iAddr+self.nBytesArc], byteorder='big') & self._finalNodeMask: l = [] nRawArc = 0 iAddrNode = iAddr while not (nRawArc & self._lastArcMask): iEndArcAddr = iAddr + self.nBytesArc nRawArc = int.from_bytes(self.byDic[iAddr:iEndArcAddr], byteorder='big') nArc = nRawArc & self._arcMask if nArc > self.nChar: - # This value is not a char, this is a stemming code + # This value is not a char, this is a stemming code sStem = ">" + self.funcStemming(sWord, self.lArcVal[nArc]) # Now , we go to the next node and retrieve all following arcs values, all of them are tags if not (nRawArc & self._addrBitMask): iAddr2 = int.from_bytes(self.byDic[iEndArcAddr:iEndArcAddr+self.nBytesNodeAddress], byteorder='big') else: @@ -704,11 +712,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 [] @@ -717,22 +725,22 @@ iAddr = 0 for c in sWord: if c not in self.dChar: return [] iAddr = self._lookupArcNode(self.dChar[c], iAddr) - if iAddr == None: + if iAddr is None: return [] - if (int.from_bytes(self.byDic[iAddr:iAddr+self.nBytesArc], byteorder='big') & self._finalNodeMask): + if int.from_bytes(self.byDic[iAddr:iAddr+self.nBytesArc], byteorder='big') & self._finalNodeMask: l = [] nRawArc = 0 - iAddrNode = iAddr + #iAddrNode = iAddr while not (nRawArc & self._lastArcMask): iEndArcAddr = iAddr + self.nBytesArc nRawArc = int.from_bytes(self.byDic[iAddr:iEndArcAddr], byteorder='big') nArc = nRawArc & self._arcMask if nArc > self.nChar: - # This value is not a char, this is a stemming code + # This value is not a char, this is a stemming code l.append(self.funcStemming(sWord, self.lArcVal[nArc])) iAddr = iEndArcAddr+self.nBytesNodeAddress if not (nRawArc & self._addrBitMask) else iEndArcAddr+self.nBytesOffset return l return [] @@ -741,25 +749,25 @@ iAddrNode = iAddr while True: iEndArcAddr = iAddr+self.nBytesArc nRawArc = int.from_bytes(self.byDic[iAddr:iEndArcAddr], byteorder='big') if nVal == (nRawArc & self._arcMask): - # the value we are looking for + # the value we are looking for if not (nRawArc & self._addrBitMask): return int.from_bytes(self.byDic[iEndArcAddr:iEndArcAddr+self.nBytesNodeAddress], byteorder='big') else: return iAddrNode + int.from_bytes(self.byDic[iEndArcAddr:iEndArcAddr+self.nBytesOffset], byteorder='big') else: # value not found - if (nRawArc & self._lastArcMask): + if nRawArc & self._lastArcMask: return None iAddr = iEndArcAddr+self.nBytesNodeAddress if not (nRawArc & self._addrBitMask) else iEndArcAddr+self.nBytesOffset def _writeNodes3 (self, spfDest): "for debugging only" print(" > Write binary nodes") - with codecs.open(spfDest, 'w', 'utf-8', newline="\n") as hDst: + with open(spfDest, 'w', 'utf-8', newline="\n") as hDst: iAddr = 0 hDst.write("i{:_>10} -- #{:_>10}\n".format("0", iAddr)) while iAddr < len(self.byDic): iEndArcAddr = iAddr+self.nBytesArc nRawArc = int.from_bytes(self.byDic[iAddr:iEndArcAddr], byteorder='big') @@ -770,8 +778,8 @@ iAddr = iEndArcAddr+self.nBytesNodeAddress else: iNextNodeAddr = int.from_bytes(self.byDic[iEndArcAddr:iEndArcAddr+self.nBytesOffset], byteorder='big') hDst.write(" {:<20} {:0>16} i{:>10} +{:_>10}\n".format(self.lArcVal[nArc], bin(nRawArc)[2:], "?", iNextNodeAddr)) iAddr = iEndArcAddr+self.nBytesOffset - if (nRawArc & self._lastArcMask): + if nRawArc & self._lastArcMask: hDst.write("\ni{:_>10} -- #{:_>10}\n".format("?", iAddr)) hDst.close() Index: graphspell/keyboard_chars_proximity.py ================================================================== --- graphspell/keyboard_chars_proximity.py +++ graphspell/keyboard_chars_proximity.py @@ -1,13 +1,18 @@ -# Keyboard chars proximity + +""" +Keyboard chars proximity +""" def getKeyboardMap (sKeyboard): + "return keyboard map as a dictionary of chars" return _dKeyboardMap.get(sKeyboard.lower(), {}) def getKeyboardList (): + "return list of keyboards available" return _dKeyboardMap.keys() _dKeyboardMap = { # keyboards by alphabetical order Index: graphspell/progressbar.py ================================================================== --- graphspell/progressbar.py +++ graphspell/progressbar.py @@ -1,14 +1,17 @@ -# Textual progressbar +""" +Textual progressbar +""" + # by Olivier R. # License: MPL 2 import time class ProgressBar: "Textual progressbar" - + def __init__ (self, nMin=0, nMax=100, nWidth=78): "initiate with minimum nMin to maximum nMax" self.nMin = nMin self.nMax = nMax self.nSpan = nMax - nMin @@ -17,19 +20,19 @@ self.nCurVal = nMin self.startTime = time.time() self._update() def _update (self): - fDone = ((self.nCurVal - self.nMin) / self.nSpan) + fDone = (self.nCurVal - self.nMin) / self.nSpan nAdvance = int(fDone * self.nWidth) - if (nAdvance > self.nAdvance): + if nAdvance > self.nAdvance: self.nAdvance = nAdvance print("\r[ {}{} {}% ] ".format('>'*nAdvance, ' '*(self.nWidth-nAdvance), round(fDone*100)), end="") def increment (self, n=1): "increment value by n (1 by default)" self.nCurVal += n self._update() - + def done (self): "to call when it’s finished" print("\r[ task done in {:.1f} s ] ".format(time.time() - self.startTime)) Index: graphspell/spellchecker.py ================================================================== --- graphspell/spellchecker.py +++ graphspell/spellchecker.py @@ -1,16 +1,17 @@ -# Spellchecker -# Wrapper for the IBDAWG class. -# Useful to check several dictionaries at once. - -# To avoid iterating over a pile of dictionaries, it is assumed that 3 are enough: -# - 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 - - +""" +Spellchecker. +Useful to check several dictionaries at once. + +To avoid iterating over a pile of dictionaries, it is assumed that 3 are enough: +- 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 @@ -20,10 +21,11 @@ "en": "en.bdic" } class SpellChecker (): + "SpellChecker: wrapper for the IBDAWG class" def __init__ (self, sLangCode, sfMainDic="", sfExtendedDic="", sfCommunityDic="", sfPersonalDic=""): "returns True if the main dictionary is loaded" self.sLangCode = sLangCode if not sfMainDic: @@ -34,10 +36,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 @@ -48,23 +57,24 @@ raise Exception(str(e), "Error: <" + str(source) + "> not loaded.") print("Error: <" + str(source) + "> not loaded.") traceback.print_exc() return None - def loadTokenizer (self): + def _loadTokenizer (self): self.oTokenizer = tokenizer.Tokenizer(self.sLangCode) def getTokenizer (self): + "load and return the tokenizer object" if not self.oTokenizer: - self.loadTokenizer() + self._loadTokenizer() return self.oTokenizer def setMainDictionary (self, source): "returns True if the dictionary is loaded" self.oMainDic = self._loadDictionary(source, True) return bool(self.oMainDic) - + def setExtendedDictionary (self, source, bActivate=True): "returns True if the dictionary is loaded" self.oExtendedDic = self._loadDictionary(source) self.bExtendedDic = False if not bActivate else bool(self.oExtendedDic) return bool(self.oExtendedDic) @@ -80,33 +90,68 @@ self.oPersonalDic = self._loadDictionary(source) self.bPersonalDic = False if not bActivate else bool(self.oPersonalDic) return bool(self.oPersonalDic) def activateExtendedDictionary (self): + "activate extended dictionary (if available)" self.bExtendedDic = bool(self.oExtendedDic) def activateCommunityDictionary (self): + "activate community dictionary (if available)" self.bCommunityDic = bool(self.oCommunityDic) def activatePersonalDictionary (self): + "activate personal dictionary (if available)" self.bPersonalDic = bool(self.oPersonalDic) def deactivateExtendedDictionary (self): + "deactivate extended dictionary" self.bExtendedDic = False def deactivateCommunityDictionary (self): + "deactivate community dictionary" self.bCommunityDic = False def deactivatePersonalDictionary (self): + "deactivate personal dictionary" self.bPersonalDic = False + + # Default suggestions + + def loadSuggestions (self, sLangCode): + "load default suggestion module for " + try: + suggest = importlib.import_module("."+sLangCode, "graphspell") + except ImportError: + print("No suggestion module for language <"+sLangCode+">") + return + self.dDefaultSugg = suggest.dSugg + + + # Storage + + def activateStorage (self): + "store all lemmas and morphologies retrieved from the word graph" + self.bStorage = True + + def deactivateStorage (self): + "stop storing all lemmas and morphologies retrieved from the word graph" + self.bStorage = False + + def clearStorage (self): + "clear all stored data" + self._dLemmas.clear() + self._dMorphologies.clear() + # parse text functions def parseParagraph (self, sText, bSpellSugg=False): + "return a list of tokens where token value doesn’t exist in the word graph" if not self.oTokenizer: - self.loadTokenizer() + self._loadTokenizer() aSpellErrs = [] for dToken in self.oTokenizer.genTokens(sText): if dToken['sType'] == "WORD" and not self.isValidToken(dToken['sValue']): if bSpellSugg: dToken['aSuggestions'] = [] @@ -114,12 +159,14 @@ dToken['aSuggestions'].extend(lSugg) aSpellErrs.append(dToken) return aSpellErrs def countWordsOccurrences (self, sText, bByLemma=False, bOnlyUnknownWords=False, dWord={}): + """count word occurrences. + can be used to cumulate count from several texts.""" if not self.oTokenizer: - self.loadTokenizer() + self._loadTokenizer() for dToken in self.oTokenizer.genTokens(sText): if dToken['sType'] == "WORD": if bOnlyUnknownWords: if not self.isValidToken(dToken['sValue']): dWord[dToken['sValue']] = dWord.get(dToken['sValue'], 0) + 1 @@ -149,11 +196,11 @@ "checks if sWord is valid (different casing tested if the first letter is a capital)" if self.oMainDic.isValid(sWord): return True if self.bExtendedDic and self.oExtendedDic.isValid(sWord): return True - if self.bCommunityDic and self.oCommunityDic.isValid(sToken): + if self.bCommunityDic and self.oCommunityDic.isValid(sWord): return True if self.bPersonalDic and self.oPersonalDic.isValid(sWord): return True return False @@ -161,33 +208,52 @@ "checks if sWord is in dictionary as is (strict verification)" if self.oMainDic.lookup(sWord): return True if self.bExtendedDic and self.oExtendedDic.lookup(sWord): return True - if self.bCommunityDic and self.oCommunityDic.lookup(sToken): + if self.bCommunityDic and self.oCommunityDic.lookup(sWord): return True if self.bPersonalDic and self.oPersonalDic.lookup(sWord): 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: @@ -202,10 +268,11 @@ yield from self.oCommunityDic.select(sFlexPattern, sTagsPattern) if self.bPersonalDic: yield from self.oPersonalDic.select(sFlexPattern, sTagsPattern) def drawPath (self, sWord): + "draw the path taken by within the word graph: display matching nodes and their arcs" self.oMainDic.drawPath(sWord) if self.bExtendedDic: print("-----") self.oExtendedDic.drawPath(sWord) if self.bCommunityDic: Index: graphspell/str_transform.py ================================================================== --- graphspell/str_transform.py +++ graphspell/str_transform.py @@ -1,25 +1,32 @@ #!python3 +""" +Operations on strings: +- calculate distance between two strings +- transform strings with transformation codes +""" + #### DISTANCE CALCULATIONS def longestCommonSubstring (s1, s2): + "longest common substring" # http://en.wikipedia.org/wiki/Longest_common_substring_problem # http://en.wikibooks.org/wiki/Algorithm_implementation/Strings/Longest_common_substring - M = [ [0]*(1+len(s2)) for i in range(1+len(s1)) ] - longest, x_longest = 0, 0 + lMatrix = [ [0]*(1+len(s2)) for i in range(1+len(s1)) ] + nLongest, nLongestX = 0, 0 for x in range(1, 1+len(s1)): for y in range(1, 1+len(s2)): if s1[x-1] == s2[y-1]: - M[x][y] = M[x-1][y-1] + 1 - if M[x][y] > longest: - longest = M[x][y] - x_longest = x + lMatrix[x][y] = lMatrix[x-1][y-1] + 1 + if lMatrix[x][y] > nLongest: + nLongest = lMatrix[x][y] + nLongestX = x else: - M[x][y] = 0 - return s1[x_longest-longest : x_longest] + lMatrix[x][y] = 0 + return s1[nLongestX-nLongest : nLongestX] def distanceDamerauLevenshtein (s1, s2): "distance of Damerau-Levenshtein between and " # https://fr.wikipedia.org/wiki/Distance_de_Damerau-Levenshtein @@ -54,11 +61,11 @@ i1, i2 = 0, 0 # Cursors for each string nLargestCS = 0 # Largest common substring nLocalCS = 0 # Local common substring nTrans = 0 # Number of transpositions ('ab' vs 'ba') lOffset = [] # Offset pair array, for computing the transpositions - + while i1 < nLen1 and i2 < nLen2: if s1[i1] == s2[i2]: nLocalCS += 1 # Check if current match is a transposition bTrans = False @@ -103,10 +110,11 @@ nLargestCS += nLocalCS return round(max(nLen1, nLen2) - nLargestCS + nTrans) def showDistance (s1, s2): + "display Damerau-Levenshtein distance and Sift4 distance between and " print("Damerau-Levenshtein: " + s1 + "/" + s2 + " = " + distanceDamerauLevenshtein(s1, s2)) print("Sift4:" + s1 + "/" + s2 + " = " + distanceSift4(s1, s2)) @@ -114,23 +122,27 @@ #### STEMMING OPERATIONS ## No stemming def noStemming (sFlex, sStem): + "return " return sStem -def rebuildWord (sFlex, cmd1, cmd2): - if cmd1 == "_": +def rebuildWord (sFlex, sCode1, sCode2): + """ Change with codes (each inserts a char at a defined possition). + + """ + if sCode1 == "_": + return sFlex + n, c = sCode1.split(":") + sFlex = sFlex[:n] + c + sFlex[n:] + if sCode2 == "_": return sFlex - n, c = cmd1.split(":") - s = s[:n] + c + s[n:] - if cmd2 == "_": - return s - n, c = cmd2.split(":") - return s[:n] + c + s[n:] - - + n, c = sCode2.split(":") + return sFlex[:n] + c + sFlex[n:] + + ## Define affixes for stemming # Note: 48 is the ASCII code for "0" @@ -150,14 +162,15 @@ jSfx = 0 for i in range(min(len(sFlex), len(sStem))): if sFlex[i] != sStem[i]: break jSfx += 1 - return chr(len(sFlex)-jSfx+48) + sStem[jSfx:] + return chr(len(sFlex)-jSfx+48) + sStem[jSfx:] def changeWordWithSuffixCode (sWord, sSfxCode): + "apply transformation code on and return the result string" if sSfxCode == "0": return sWord return sWord[:-(ord(sSfxCode[0])-48)] + sSfxCode[1:] if sSfxCode[0] != '0' else sWord + sSfxCode[1:] @@ -167,11 +180,11 @@ """ Returns a string defining how to get stem from flexion. Examples: "0" if stem = flexion "stem" if no common substring "n(pfx)/m(sfx)" with n and m: chars with numeric meaning, "0" = 0, "1" = 1, ... ":" = 10, etc. (See ASCII table.) Says how many letters to strip from flexion. - pfx [optional]: string to add before the flexion + pfx [optional]: string to add before the flexion sfx [optional]: string to add after the flexion """ if sFlex == sStem: return "0" # is stem a substring of flexion? @@ -189,13 +202,13 @@ return chr(n+48) + sPfx + "/" + chr(m+48) + sSfx return sStem def changeWordWithAffixCode (sWord, sAffCode): + "apply transformation code on and return the result string" if sAffCode == "0": return sWord if '/' not in sAffCode: return sAffCode sPfxCode, sSfxCode = sAffCode.split('/') - sWord = sPfxCode[1:] + sWord[(ord(sPfxCode[0])-48):] + sWord = sPfxCode[1:] + sWord[(ord(sPfxCode[0])-48):] return sWord[:-(ord(sSfxCode[0])-48)] + sSfxCode[1:] if sSfxCode[0] != '0' else sWord + sSfxCode[1:] - Index: graphspell/tokenizer.py ================================================================== --- graphspell/tokenizer.py +++ graphspell/tokenizer.py @@ -1,49 +1,62 @@ -# Very simple tokenizer +""" +Very simple tokenizer +using regular expressions +""" import re _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+\])', r'(?P\d\d?h\d\d\b)', r'(?P-?\d+(?:[.,]\d+))', + r'(?P[%‰+=*/<>⩾⩽-])', r"(?P\w+(?:[’'`-]\w+)*)" ), "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+\])', r"(?P(?:l|d|n|m|t|s|j|c|ç|lorsqu|puisqu|jusqu|quoiqu|qu)['’`])", - r'(?P\d+(?:er|nd|e|de|ième|ème|eme)\b)', + r'(?P\d+(?:ers?|nds?|es?|des?|ièmes?|èmes?|emes?|ᵉʳˢ?|ⁿᵈˢ?|ᵉˢ?|ᵈᵉˢ?)\b)', r'(?P\d\d?h\d\d\b)', r'(?P-?\d+(?:[.,]\d+|))', + r'(?P[%‰+=*/<>⩾⩽-])', r"(?P\w+(?:[’'`-]\w+)*)" ) } class Tokenizer: + "Tokenizer: transforms a text in a list of tokens" def __init__ (self, sLang): 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): + "generator: tokenize " + i = 0 + 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 @@ -1,11 +1,14 @@ #!/usr/bin/env python3 # coding: UTF-8 + +""" +Grammalecte builder +""" import sys import os -import subprocess import re import zipfile import traceback import configparser import datetime @@ -15,40 +18,43 @@ import json import platform from distutils import dir_util, file_util -import dialog_bundled +#import dialog_bundled import compile_rules import helpers import lex_build sWarningMessage = "The content of this folder is generated by code and replaced at each build.\n" def getConfig (sLang): + "load config.ini in at gc_lang/, returns xConfigParser object" xConfig = configparser.SafeConfigParser() xConfig.optionxform = str try: - xConfig.read("gc_lang/" + sLang + "/config.ini", encoding="utf-8") - except: + xConfig.read_file(open("gc_lang/" + sLang + "/config.ini", "r", encoding="utf-8")) + except FileNotFoundError: print("# Error. Can’t read config file [" + sLang + "]") exit() return xConfig def createOptionsLabelProperties (dOptLbl): + "create content for .properties files (LibreOffice)" sContent = "" for sOpt, tLabel in dOptLbl.items(): sContent += sOpt + "=" + tLabel[0] + "\n" if tLabel[1]: sContent += "hlp_" + sOpt + "=" + tLabel[1] + "\n" return sContent def createDialogOptionsXDL (dVars): + "create bundled dialog options file .xdl (LibreOffice)" sFixedline = '\n' sCheckbox = '\n' iTabIndex = 1 nPosY = 5 nWidth = 240 @@ -133,17 +139,17 @@ if bInstall: print("> installation in Writer") if dVars.get('unopkg', False): cmd = '"'+os.path.abspath(dVars.get('unopkg')+'" add -f '+spfZip) print(cmd) - #subprocess.run(cmd) os.system(cmd) else: print("# Error: path and filename of unopkg not set in config.ini") def createServerOptions (sLang, dOptData): + "create file options for Grammalecte server" with open("grammalecte-server-options."+sLang+".ini", "w", encoding="utf-8", newline="\n") as hDst: hDst.write("# Server options. Lang: " + sLang + "\n\n[gc_options]\n") for sSection, lOpt in dOptData["lStructOpt"]: hDst.write("\n########## " + dOptData["dOptLabel"][sLang].get(sSection, sSection + "[no label found]")[0] + " ##########\n") for lLineOpt in lOpt: @@ -164,10 +170,11 @@ hZip.write(spf) hZip.writestr("setup.py", helpers.fileFile("gc_lang/fr/setup.py", dVars)) def copyGrammalectePyPackageInZipFile (hZip, spLangPack, sAddPath=""): + "copy Grammalecte Python package in zip file" for sf in os.listdir("grammalecte"): if not os.path.isdir("grammalecte/"+sf): hZip.write("grammalecte/"+sf, sAddPath+"grammalecte/"+sf) for sf in os.listdir("grammalecte/graphspell"): if not os.path.isdir("grammalecte/graphspell/"+sf): @@ -179,10 +186,11 @@ if not os.path.isdir(spLangPack+"/"+sf): hZip.write(spLangPack+"/"+sf, sAddPath+spLangPack+"/"+sf) def create (sLang, xConfig, bInstallOXT, bJavaScript): + "make Grammalecte for project " oNow = datetime.datetime.now() print("============== MAKE GRAMMALECTE [{0}] at {1.hour:>2} h {1.minute:>2} min {1.second:>2} s ==============".format(sLang, oNow)) #### READ CONFIGURATION print("> read configuration...") @@ -228,10 +236,11 @@ # 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("\n") createOXT(spLang, dVars, xConfig._sections['oxt'], spLangPack, bInstallOXT) createServerOptions(sLang, dVars) createPackageZip(sLang, dVars, spLangPack) @@ -250,11 +259,11 @@ # options data struct dVars["dOptJavaScript"] = json.dumps(list(dVars["dOptJavaScript"].items())) dVars["dOptFirefox"] = json.dumps(list(dVars["dOptFirefox"].items())) dVars["dOptThunderbird"] = json.dumps(list(dVars["dOptThunderbird"].items())) - + # create folder spLangPack = "grammalecte-js/"+sLang helpers.createCleanFolder(spLangPack) # create files @@ -273,20 +282,21 @@ helpers.copyAndFileTemplate(spLang+"/modules-js/"+sf, spLangPack+"/"+sf, dVars) print(sf, end=", ") print() try: - build_module = importlib.import_module("gc_lang."+sLang+".build") + buildjs = importlib.import_module("gc_lang."+sLang+".build") except ImportError: print("# No complementary builder in folder gc_lang/"+sLang) else: - build_module.build(sLang, dVars, spLangPack) + buildjs.build(sLang, dVars, spLangPack) return dVars['version'] def copyGraphspellCore (bJavaScript=False): + "copy Graphspell package in Grammalecte package" helpers.createCleanFolder("grammalecte/graphspell") dir_util.mkpath("grammalecte/graphspell/_dictionaries") for sf in os.listdir("graphspell"): if not os.path.isdir("graphspell/"+sf): file_util.copy_file("graphspell/"+sf, "grammalecte/graphspell") @@ -301,10 +311,11 @@ file_util.copy_file("graphspell-js/"+sf, "grammalecte-js/graphspell") helpers.copyAndFileTemplate("graphspell-js/"+sf, "grammalecte-js/graphspell/"+sf, dVars) def copyGraphspellDictionaries (dVars, bJavaScript=False, bExtendedDict=False, bCommunityDict=False, bPersonalDict=False): + "copy requested Graphspell dictionaries in Grammalecte package" dVars["dic_main_filename_py"] = "" dVars["dic_main_filename_js"] = "" dVars["dic_extended_filename_py"] = "" dVars["dic_extended_filename_js"] = "" dVars["dic_community_filename_py"] = "" @@ -333,16 +344,17 @@ dVars['dic_main_filename_py'] = dVars['dic_default_filename_py'] + ".bdic" dVars['dic_main_filename_js'] = dVars['dic_default_filename_js'] + ".json" def buildDictionary (dVars, sType, bJavaScript=False): + "build binary dictionary for Graphspell from lexicons" if sType == "main": spfLexSrc = dVars['lexicon_src'] - l_sfDictDst = dVars['dic_filenames'].split(",") - l_sDicName = dVars['dic_name'].split(",") - l_sFilter = dVars['dic_filter'].split(",") - for sfDictDst, sDicName, sFilter in zip(l_sfDictDst, l_sDicName, l_sFilter): + lSfDictDst = dVars['dic_filenames'].split(",") + lDicName = dVars['dic_name'].split(",") + lFilter = dVars['dic_filter'].split(",") + for sfDictDst, sDicName, sFilter in zip(lSfDictDst, lDicName, lFilter): lex_build.build(spfLexSrc, dVars['lang'], dVars['lang_name'], sfDictDst, bJavaScript, sDicName, sFilter, dVars['stemming_method'], int(dVars['fsa_method'])) else: if sType == "extended": spfLexSrc = dVars['lexicon_extended_src'] sfDictDst = dVars['dic_extended_filename'] @@ -358,10 +370,11 @@ lex_build.build(spfLexSrc, dVars['lang'], dVars['lang_name'], sfDictDst, bJavaScript, sDicName, "", dVars['stemming_method'], int(dVars['fsa_method'])) def main (): + "build Grammalecte with requested options" print("Python: " + sys.version) xParser = argparse.ArgumentParser() xParser.add_argument("lang", type=str, nargs='+', help="lang project to generate (name of folder in /lang)") xParser.add_argument("-b", "--build_data", help="launch build_data.py (part 1 and 2)", action="store_true") xParser.add_argument("-bb", "--build_data_before", help="launch build_data.py (only part 1: before dictionary building)", action="store_true") @@ -403,29 +416,29 @@ xArgs.add_community_dictionary = False if not dVars["lexicon_personal_src"]: xArgs.add_personal_dictionary = False # build data - build_data_module = None + databuild = None if xArgs.build_data_before or xArgs.build_data_after: # lang data try: - build_data_module = importlib.import_module("gc_lang."+sLang+".build_data") + databuild = importlib.import_module("gc_lang."+sLang+".build_data") except ImportError: print("# Error. Couldn’t import file build_data.py in folder gc_lang/"+sLang) - if build_data_module and xArgs.build_data_before: - build_data_module.before('gc_lang/'+sLang, dVars, xArgs.javascript) + if databuild and xArgs.build_data_before: + databuild.before('gc_lang/'+sLang, dVars, xArgs.javascript) if xArgs.dict: buildDictionary(dVars, "main", xArgs.javascript) if xArgs.add_extended_dictionary: buildDictionary(dVars, "extended", xArgs.javascript) if xArgs.add_community_dictionary: buildDictionary(dVars, "community", xArgs.javascript) if xArgs.add_personal_dictionary: buildDictionary(dVars, "personal", xArgs.javascript) - if build_data_module and xArgs.build_data_after: - build_data_module.after('gc_lang/'+sLang, dVars, xArgs.javascript) + if databuild and xArgs.build_data_after: + databuild.after('gc_lang/'+sLang, dVars, xArgs.javascript) # copy dictionaries from Graphspell copyGraphspellDictionaries(dVars, xArgs.javascript, xArgs.add_extended_dictionary, xArgs.add_community_dictionary, xArgs.add_personal_dictionary) # make @@ -446,16 +459,15 @@ unittest.TextTestRunner().run(xTestSuite) if xArgs.perf or xArgs.perf_memo: hDst = open("./gc_lang/"+sLang+"/perf_memo.txt", "a", encoding="utf-8", newline="\n") if xArgs.perf_memo else None tests.perf(sVersion, hDst) - # Firefox - if False: - # obsolete - with helpers.cd("_build/xpi/"+sLang): - spfFirefox = dVars['win_fx_dev_path'] if platform.system() == "Windows" else dVars['linux_fx_dev_path'] - os.system('jpm run -b "' + spfFirefox + '"') + # Firefox (obsolete) + #if False: + # with helpers.cd("_build/xpi/"+sLang): + # spfFirefox = dVars['win_fx_dev_path'] if platform.system() == "Windows" else dVars['linux_fx_dev_path'] + # os.system('jpm run -b "' + spfFirefox + '"') if xArgs.web_ext or xArgs.firefox: with helpers.cd("_build/webext/"+sLang): if xArgs.lint_web_ext: os.system(r'web-ext lint -o text') Index: misc/grammalecte.sublime-syntax ================================================================== --- misc/grammalecte.sublime-syntax +++ misc/grammalecte.sublime-syntax @@ -24,10 +24,23 @@ # Bookmarks - match: '^!!.*|^\[\+\+\].*' scope: bookmark + # Bookmarks + - match: '^GRAPH_NAME:.*' + scope: bookmark + + # Graph + - match: '^@@@@GRAPH: *(\w+) *' + scope: graphline + captures: + 1: string.graphname + + - match: '^@@@@(?:END_GRAPH *| *)' + scope: graphline + # 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 +48,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|stem|textarea0?\w*|before0?\w*|after0?\w*|word|option|define|select|exclude|analysex?|tag_|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,19 +60,31 @@ 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 + + - match: '/(\w+)/' + scope: rule.options + captures: + 1: rule.optionname + # Definitions and options - match: '^OPT(?:GROUP|LANG|PRIORITY)/|^OPTSOFTWARE:' scope: options.command - match: '^OPT(?:LABEL|)/' @@ -84,20 +109,48 @@ 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 + - match: '/(\d*)>>' + scope: keyword.tag + captures: + 1: keyword.tag.group + + + # Tokens + - match: '(>)\w+' + scope: string.lemma + captures: + 1: keyword.valid + + - match: '(~)(?!(?:\d+(?::\d+|)|)>>)[^\s]+' + scope: string.regex + captures: + 1: keyword.valid + + - match: '(@)([^@][^\s¬]+)' + scope: string.morph + captures: + 1: keyword.valid + 2: string.morph.pattern + + - match: '(¬)(\S+)' + scope: string.morph + captures: + 1: keyword.invalid + 2: string.morph.antipattern # Escaped chars - match: '\\(?:\d+|w|d|b|n|s|t)' scope: constant.character.escape @@ -108,11 +161,11 @@ # Example errors - match: '{{.+?}}' scope: message.error # special chars - - match: '[@=*^?!:+<>]' + - match: '[@=*^?¿!:+<>~]' scope: keyword.other - match: '\(\?(?:[:=!]|#A0F0FF background #0050A0 + + name + Graphline + scope + graphline + settings + + foreground + hsl(0, 100%, 80%) + background + hsl(0, 100%, 20% + fontStyle + bold + + name String scope string @@ -233,10 +248,40 @@ #602020 fontStyle bold + + name + Keyword tag + scope + keyword.tag + settings + + foreground + #FF70FF + background + #602060 + fontStyle + bold + + + + name + Keyword tag group + scope + keyword.tag.group + settings + + foreground + #F0B0F0 + background + #602060 + fontStyle + bold + + name Keyword textprocessor scope keyword.textprocessor @@ -291,10 +336,41 @@ foreground #A0A0A0 + + name + Keyword Valid + scope + keyword.valid + settings + + fontStyle + bold + foreground + hsl(150, 100%, 80%) + background + hsl(150, 100%, 20%) + + + + name + Keyword Invalid + scope + keyword.invalid + settings + + fontStyle + bold + foreground + hsl(0, 100%, 80%) + background + hsl(0, 100%, 20%) + + + name Rule options scope rule.options @@ -344,10 +420,21 @@ italic foreground #A0A0A0 + + name + Rule name + scope + rule.rulename2 + settings + + foreground + #F0D080 + + name Rule priority scope rule.priority @@ -356,10 +443,63 @@ foreground #F06060 + + name + String lemma + scope + string.lemma + settings + + foreground + hsl(210, 100%, 80%) + background + hsl(210, 100%, 15%) + + + + name + String regex + scope + string.regex + settings + + foreground + hsl(60, 100%, 80%) + background + hsl(60, 100%, 10%) + + + + name + String morph pattern + scope + string.morph.pattern + settings + + foreground + hsl(150, 80%, 90%) + background + hsl(150, 80%, 10%) + + + + name + String morph antipattern + scope + string.morph.antipattern + settings + + foreground + hsl(0, 80%, 90%) + background + hsl(0, 80%, 10%) + + + name JavaScript Dollar scope