Index: compile_rules.py
==================================================================
--- compile_rules.py
+++ compile_rules.py
@@ -2,10 +2,11 @@
 import re
 import traceback
 import json
 
 import compile_rules_js_convert as jsconv
+import compile_rules_graph as crg
 
 
 dDEF = {}
 lFUNCTIONS = []
 
@@ -27,35 +28,35 @@
     s = re.sub(r"isRealStart0 *\(\)", 'before0("^ *$")', s)
     s = re.sub(r"isEnd *\(\)", 'after("^ *$|^,")', s)
     s = re.sub(r"isRealEnd *\(\)", 'after("^ *$")', s)
     s = re.sub(r"isEnd0 *\(\)", 'after0("^ *$|^,")', s)
     s = re.sub(r"isRealEnd0 *\(\)", 'after0("^ *$")', s)
-    s = re.sub(r"(select|exclude)[(][\\](\d+)", '\\1(dDA, m.start(\\2), m.group(\\2)', s)
-    s = re.sub(r"define[(][\\](\d+)", 'define(dDA, m.start(\\1)', s)
+    s = re.sub(r"(select|exclude)[(][\\](\d+)", '\\1(dTokenPos, m.start(\\2), m.group(\\2)', s)
+    s = re.sub(r"define[(][\\](\d+)", 'define(dTokenPos, m.start(\\1)', s)
     s = re.sub(r"(morph|morphex|displayInfo)[(][\\](\d+)", '\\1((m.start(\\2), m.group(\\2))', s)
-    s = re.sub(r"(morph|morphex|displayInfo)[(]", '\\1(dDA, ', s)
+    s = re.sub(r"(morph|morphex|displayInfo)[(]", '\\1(dTokenPos, ', s)
     s = re.sub(r"(sugg\w+|switch\w+)\(@", '\\1(m.group(i[4])', s)
     s = re.sub(r"word\(\s*1\b", 'nextword1(s, m.end()', s)                                  # word(1)
     s = re.sub(r"word\(\s*-1\b", 'prevword1(s, m.start()', s)                               # word(-1)
     s = re.sub(r"word\(\s*(\d)", 'nextword(s, m.end(), \\1', s)                             # word(n)
     s = re.sub(r"word\(\s*-(\d)", 'prevword(s, m.start(), \\1', s)                          # word(-n)
     s = re.sub(r"before\(\s*", 'look(s[:m.start()], ', s)                                   # before(s)
     s = re.sub(r"after\(\s*", 'look(s[m.end():], ', s)                                      # after(s)
     s = re.sub(r"textarea\(\s*", 'look(s, ', s)                                             # textarea(s)
-    s = re.sub(r"before_chk1\(\s*", 'look_chk1(dDA, s[:m.start()], 0, ', s)                 # before_chk1(s)
-    s = re.sub(r"after_chk1\(\s*", 'look_chk1(dDA, s[m.end():], m.end(), ', s)              # after_chk1(s)
-    s = re.sub(r"textarea_chk1\(\s*", 'look_chk1(dDA, s, 0, ', s)                           # textarea_chk1(s)
+    s = re.sub(r"before_chk1\(\s*", 'look_chk1(dTokenPos, s[:m.start()], 0, ', s)           # before_chk1(s)
+    s = re.sub(r"after_chk1\(\s*", 'look_chk1(dTokenPos, s[m.end():], m.end(), ', s)        # after_chk1(s)
+    s = re.sub(r"textarea_chk1\(\s*", 'look_chk1(dTokenPos, s, 0, ', s)                     # textarea_chk1(s)
     s = re.sub(r"/0", 'sx[m.start():m.end()]', s)                                           # /0
     s = re.sub(r"before0\(\s*", 'look(sx[:m.start()], ', s)                                 # before0(s)
     s = re.sub(r"after0\(\s*", 'look(sx[m.end():], ', s)                                    # after0(s)
     s = re.sub(r"textarea0\(\s*", 'look(sx, ', s)                                           # textarea0(s)
-    s = re.sub(r"before0_chk1\(\s*", 'look_chk1(dDA, sx[:m.start()], 0, ', s)               # before0_chk1(s)
-    s = re.sub(r"after0_chk1\(\s*", 'look_chk1(dDA, sx[m.end():], m.end(), ', s)            # after0_chk1(s)
-    s = re.sub(r"textarea0_chk1\(\s*", 'look_chk1(dDA, sx, 0, ', s)                         # textarea0_chk1(s)
-    s = re.sub(r"isEndOfNG\(\s*\)", 'isEndOfNG(dDA, s[m.end():], m.end())', s)              # isEndOfNG(s)
-    s = re.sub(r"isNextNotCOD\(\s*\)", 'isNextNotCOD(dDA, s[m.end():], m.end())', s)        # isNextNotCOD(s)
-    s = re.sub(r"isNextVerb\(\s*\)", 'isNextVerb(dDA, s[m.end():], m.end())', s)            # isNextVerb(s)
+    s = re.sub(r"before0_chk1\(\s*", 'look_chk1(dTokenPos, sx[:m.start()], 0, ', s)         # before0_chk1(s)
+    s = re.sub(r"after0_chk1\(\s*", 'look_chk1(dTokenPos, sx[m.end():], m.end(), ', s)      # after0_chk1(s)
+    s = re.sub(r"textarea0_chk1\(\s*", 'look_chk1(dTokenPos, sx, 0, ', s)                   # textarea0_chk1(s)
+    s = re.sub(r"isEndOfNG\(\s*\)", 'isEndOfNG(dTokenPos, s[m.end():], m.end())', s)        # isEndOfNG(s)
+    s = re.sub(r"isNextNotCOD\(\s*\)", 'isNextNotCOD(dTokenPos, s[m.end():], m.end())', s)  # isNextNotCOD(s)
+    s = re.sub(r"isNextVerb\(\s*\)", 'isNextVerb(dTokenPos, s[m.end():], m.end())', s)      # isNextVerb(s)
     s = re.sub(r"\bspell *[(]", '_oSpellChecker.isValid(', s)
     s = re.sub(r"[\\](\d+)", 'm.group(\\1)', s)
     return s
 
 
@@ -110,13 +111,21 @@
 def createRule (s, nIdLine, sLang, bParagraph, dOptPriority):
     "returns rule as list [option name, regex, bCaseInsensitive, identifier, list of actions]"
     global dJSREGEXES
     global nRULEWITHOUTNAME
 
-    #### OPTIONS
     sLineId = str(nIdLine) + ("p" if bParagraph else "s")
     sRuleId = sLineId
+
+    #### GRAPH CALL
+    if s.startswith("@@@@"):
+        if bParagraph:
+            print("Error. Graph call can’t be made only after the first pass (sentence by sentence)")
+            exit()
+        return ["@@@@", s[4:], sLineId]
+
+    #### OPTIONS
     sOption = False         # False or [a-z0-9]+ name
     nPriority = 4           # Default is 4, value must be between 0 and 9
     tGroups = None          # code for groups positioning (only useful for JavaScript)
     cCaseMode = 'i'         # i: case insensitive,  s: case sensitive,  u: uppercasing allowed
     cWordLimitLeft = '['    # [: word limit, <: no specific limit
@@ -343,12 +352,13 @@
 
 
 def _calcRulesStats (lRules):
     d = {'=':0, '~': 0, '-': 0, '>': 0}
     for aRule in lRules:
-        for aAction in aRule[6]:
-            d[aAction[1]] = d[aAction[1]] + 1
+        if aRule[0] != "@@@@":
+            for aAction in aRule[6]:
+                d[aAction[1]] = d[aAction[1]] + 1
     return (d, len(lRules))
 
 
 def displayStats (lParagraphRules, lSentenceRules):
     print("  {:>18} {:>18} {:>18} {:>18}".format("DISAMBIGUATOR", "TEXT PROCESSOR", "GRAMMAR CHECKING", "REGEX"))
@@ -432,51 +442,73 @@
         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)
+        # 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:
@@ -517,19 +549,19 @@
     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"
+            sParams = "s, sx, m, dTokenPos, 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"
+            sParams = "s, m, dTokenPos"
         else:
             print("# Unknown function type in [" + sFuncName + "]")
             continue
         sPyCallables += "def {} ({}):\n".format(sFuncName, sParams)
         sPyCallables += "    return " + sReturn + "\n"
@@ -549,7 +581,11 @@
           "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)
+
+    # compile graph rules
+    d2 = crg.make(lGraphRule, dDEF, sLang, bJavaScript)
+    d.update(d2)
 
     return d

ADDED   compile_rules_graph.py
Index: compile_rules_graph.py
==================================================================
--- /dev/null
+++ compile_rules_graph.py
@@ -0,0 +1,377 @@
+# Create a Direct Acyclic Rule Graph (DARG)
+
+import re
+import traceback
+import json
+
+import darg
+
+
+dACTIONS = {}
+dFUNCTIONS = {}
+
+
+def prepareFunction (s, bTokenValue=False):
+    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], dTags', 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 i, sToken in enumerate(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):
+    # 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):
+    for i in range(len(dPos), 0, -1):
+        sText = sText.replace("\\"+str(i), "\\"+str(dPos[i]))
+    return sText
+
+
+def checkTokenNumbers (sText, sActionId, nToken):
+    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):
+    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):
+    # 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<action>[-~=/])(?P<start>\\d+|)(?P<end>:\\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:
+        try:
+            iStartAction = dPos[iStartAction]
+            iEndAction = dPos[iEndAction]
+        except:
+            print("# Error. Wrong groups in: " + sActionId)
+
+    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 <define> 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
+    d = {
+        "graph_callables": sPyCallables,
+        "rules_graphs": dAllGraph,
+        "rules_actions": dACTIONS
+    }
+    return d

Index: compile_rules_js_convert.py
==================================================================
--- compile_rules_js_convert.py
+++ compile_rules_js_convert.py
@@ -117,10 +117,13 @@
     return (sRegex, lNegLookBeforeRegex)
 
 
 def pyRuleToJS (lRule, dJSREGEXES, sWORDLIMITLEFT):
     lRuleJS = copy.deepcopy(lRule)
+    # graph rules
+    if lRuleJS[0] == "@@@@":
+        return lRuleJS
     del lRule[-1] # tGroups positioning codes are useless for Python
     # error messages
     for aAction in lRuleJS[6]:
         if aAction[1] == "-":
             aAction[2] = aAction[2].replace(" ", " ") # nbsp --> nnbsp
@@ -132,25 +135,31 @@
 
 
 def writeRulesToJSArray (lRules):
     sArray = "[\n"
     for sOption, aRuleGroup in lRules:
-        sArray += '  ["' + sOption + '", [\n'  if sOption  else  "  [false, [\n"
-        for sRegex, bCaseInsensitive, sLineId, sRuleId, nPriority, lActions, aGroups, aNegLookBehindRegex in aRuleGroup:
-            sArray += '    [' + sRegex + ", "
-            sArray += "true, " if bCaseInsensitive  else "false, "
-            sArray += '"' + sLineId + '", '
-            sArray += '"' + sRuleId + '", '
-            sArray += str(nPriority) + ", "
-            sArray += json.dumps(lActions, ensure_ascii=False) + ", "
-            sArray += json.dumps(aGroups, ensure_ascii=False) + ", "
-            sArray += json.dumps(aNegLookBehindRegex, ensure_ascii=False) + "],\n"
-        sArray += "  ]],\n"
+        if sOption != "@@@@":
+            sArray += '  ["' + sOption + '", [\n'  if sOption  else  "  [false, [\n"
+            for sRegex, bCaseInsensitive, sLineId, sRuleId, nPriority, lActions, aGroups, aNegLookBehindRegex in aRuleGroup:
+                sArray += '    [' + sRegex + ", "
+                sArray += "true, " if bCaseInsensitive  else "false, "
+                sArray += '"' + sLineId + '", '
+                sArray += '"' + sRuleId + '", '
+                sArray += str(nPriority) + ", "
+                sArray += json.dumps(lActions, ensure_ascii=False) + ", "
+                sArray += json.dumps(aGroups, ensure_ascii=False) + ", "
+                sArray += json.dumps(aNegLookBehindRegex, ensure_ascii=False) + "],\n"
+            sArray += "  ]],\n"
+        else:
+            sArray += '  ["' + sOption + '", [\n'
+            for sGraphName, sLineId in aRuleGroup:
+                sArray += '    ["' + sGraphName + '", "' + sLineId + '"],\n"'
+            sArray += "  ]],\n"
     sArray += "]"
     return sArray
 
 
 def groupsPositioningCodeToList (sGroupsPositioningCode):
     if not sGroupsPositioningCode:
         return None
     return [ int(sCode)  if sCode.isdigit() or (sCode[0:1] == "-" and sCode[1:].isdigit())  else sCode \
              for sCode in sGroupsPositioningCode.split(",") ]

ADDED   darg.py
Index: darg.py
==================================================================
--- /dev/null
+++ darg.py
@@ -0,0 +1,188 @@
+#!python3
+
+# RULE GRAPH BUILDER
+#
+# by Olivier R.
+# License: MPL 2
+
+
+import json
+import time
+import traceback
+
+from graphspell.progressbar import ProgressBar
+
+
+
+class DARG:
+    """DIRECT ACYCLIC RULE GRAPH"""
+    # This code is inspired from Steve Hanov’s DAWG, 2011. (http://stevehanov.ca/blog/index.php?id=115)
+
+    def __init__ (self, lRule, sLangCode):
+        print("===== Direct Acyclic Rule Graph - Minimal Acyclic Finite State Automaton =====")
+
+        # Preparing DARG
+        print(" > Preparing list of tokens")
+        self.sLangCode = sLangCode
+        self.nRule = len(lRule)
+        self.aPreviousRule = []
+        Node.resetNextId()
+        self.oRoot = Node()
+        self.lUncheckedNodes = []  # list of nodes that have not been checked for duplication.
+        self.lMinimizedNodes = {}  # list of unique nodes that have been checked for duplication.
+        self.nNode = 0
+        self.nArc = 0
+        
+        # build
+        lRule.sort()
+        oProgBar = ProgressBar(0, len(lRule))
+        for aRule in lRule:
+            self.insert(aRule)
+            oProgBar.increment(1)
+        oProgBar.done()
+        self.finish()
+        self.countNodes()
+        self.countArcs()
+        self.displayInfo()
+
+    # BUILD DARG
+    def insert (self, aRule):
+        if aRule < self.aPreviousRule:
+            sys.exit("# Error: tokens must be inserted in order.")
+    
+        # find common prefix between word and previous word
+        nCommonPrefix = 0
+        for i in range(min(len(aRule), len(self.aPreviousRule))):
+            if aRule[i] != self.aPreviousRule[i]:
+                break
+            nCommonPrefix += 1
+
+        # Check the lUncheckedNodes for redundant nodes, proceeding from last
+        # one down to the common prefix size. Then truncate the list at that point.
+        self._minimize(nCommonPrefix)
+
+        # add the suffix, starting from the correct node mid-way through the graph
+        if len(self.lUncheckedNodes) == 0:
+            oNode = self.oRoot
+        else:
+            oNode = self.lUncheckedNodes[-1][2]
+
+        iToken = nCommonPrefix
+        for sToken in aRule[nCommonPrefix:]:
+            oNextNode = Node()
+            oNode.dArcs[sToken] = oNextNode
+            self.lUncheckedNodes.append((oNode, sToken, oNextNode))
+            if iToken == (len(aRule) - 2): 
+                oNode.bFinal = True
+            iToken += 1
+            oNode = oNextNode
+        oNode.bFinal = True
+        self.aPreviousRule = aRule
+
+    def finish (self):
+        "minimize unchecked nodes"
+        self._minimize(0)
+
+    def _minimize (self, downTo):
+        # proceed from the leaf up to a certain point
+        for i in range( len(self.lUncheckedNodes)-1, downTo-1, -1 ):
+            oNode, sToken, oChildNode = self.lUncheckedNodes[i]
+            if oChildNode in self.lMinimizedNodes:
+                # replace the child with the previously encountered one
+                oNode.dArcs[sToken] = self.lMinimizedNodes[oChildNode]
+            else:
+                # add the state to the minimized nodes.
+                self.lMinimizedNodes[oChildNode] = oChildNode
+            self.lUncheckedNodes.pop()
+
+    def countNodes (self):
+        self.nNode = len(self.lMinimizedNodes)
+
+    def countArcs (self):
+        self.nArc = 0
+        for oNode in self.lMinimizedNodes:
+            self.nArc += len(oNode.dArcs)
+
+    def displayInfo (self):
+        print(" * {:<12} {:>16,}".format("Rules:", self.nRule))
+        print(" * {:<12} {:>16,}".format("Nodes:", self.nNode))
+        print(" * {:<12} {:>16,}".format("Arcs:", self.nArc))
+
+    def createGraph (self):
+        dGraph = { 0: self.oRoot.getNodeAsDict() }
+        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()))
+        return dGraph
+
+
+
+class Node:
+    NextId = 0
+    
+    def __init__ (self):
+        self.i = Node.NextId
+        Node.NextId += 1
+        self.bFinal = False
+        self.dArcs = {}          # key: arc value; value: a node
+
+    @classmethod
+    def resetNextId (cls):
+        cls.NextId = 0
+
+    def __str__ (self):
+        # Caution! this function is used for hashing and comparison!
+        cFinal = "1"  if self.bFinal  else "0"
+        l = [cFinal]
+        for (key, oNode) in self.dArcs.items():
+            l.append(str(key))
+            l.append(str(oNode.i))
+        return "_".join(l)
+
+    def __hash__ (self):
+        # Used as a key in a python dictionary.
+        return self.__str__().__hash__()
+
+    def __eq__ (self, other):
+        # Used as a key in a python dictionary.
+        # Nodes are equivalent if they have identical arcs, and each identical arc leads to identical states.
+        return self.__str__() == other.__str__()        
+
+    def getNodeAsDict (self):
+        "returns the node as a dictionary structure"
+        dNode = {}
+        dReValue = {}
+        dReMorph = {}
+        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["<re_value>"] = dReValue
+        if dReMorph:
+            dNode["<re_morph>"] = dReMorph
+        if dLemma:
+            dNode["<lemmas>"] = dLemma
+        if dMeta:
+            dNode["<meta>"] = dMeta
+        if dRule:
+            dNode["<rules>"] = dRule
+        #if self.bFinal:
+        #    dNode["<final>"] = 1
+        return dNode

Index: gc_core/js/lang_core/gc_engine.js
==================================================================
--- gc_core/js/lang_core/gc_engine.js
+++ gc_core/js/lang_core/gc_engine.js
@@ -37,11 +37,10 @@
 // data
 let _sAppContext = "";                                  // what software is running
 let _dOptions = null;
 let _aIgnoredRules = new Set();
 let _oSpellChecker = null;
-let _dAnalyses = new Map();                             // cache for data from dictionary
 
 
 var gc_engine = {
 
     //// Informations
@@ -327,10 +326,11 @@
             } else {
                 _oSpellChecker = new SpellChecker("${lang}", sPath, "${dic_main_filename_js}", "${dic_extended_filename_js}", "${dic_community_filename_js}", "${dic_personal_filename_js}");
             }
             _sAppContext = sContext;
             _dOptions = gc_options.getOptions(sContext).gl_shallowCopy();     // duplication necessary, to be able to reset to default
+            _oSpellChecker.activateStorage();
         }
         catch (e) {
             helpers.logerror(e);
         }
     },
@@ -376,39 +376,30 @@
     // for debugging: info of word
     if (!aWord) {
         helpers.echo("> nothing to find");
         return true;
     }
-    if (!_dAnalyses.has(aWord[1]) && !_storeMorphFromFSA(aWord[1])) {
-        helpers.echo("> not in FSA");
+    let lMorph = _oSpellChecker.getMorph(aWord[1]);
+    if (lMorph.length === 0) {
+        helpers.echo("> not in dictionary");
         return true;
     }
     if (dDA.has(aWord[0])) {
         helpers.echo("DA: " + dDA.get(aWord[0]));
     }
-    helpers.echo("FSA: " + _dAnalyses.get(aWord[1]));
+    helpers.echo("FSA: " + lMorph);
     return true;
 }
 
-function _storeMorphFromFSA (sWord) {
-    // retrieves morphologies list from _oSpellChecker -> _dAnalyses
-    //helpers.echo("register: "+sWord + " " + _oSpellChecker.getMorph(sWord).toString())
-    _dAnalyses.set(sWord, _oSpellChecker.getMorph(sWord));
-    return !!_dAnalyses.get(sWord);
-}
-
 function morph (dDA, aWord, sPattern, bStrict=true, bNoWord=false) {
     // analyse a tuple (position, word), return true if sPattern in morphologies (disambiguation on)
     if (!aWord) {
         //helpers.echo("morph: noword, returns " + bNoWord);
         return bNoWord;
     }
     //helpers.echo("aWord: "+aWord.toString());
-    if (!_dAnalyses.has(aWord[1]) && !_storeMorphFromFSA(aWord[1])) {
-        return false;
-    }
-    let lMorph = dDA.has(aWord[0]) ? dDA.get(aWord[0]) : _dAnalyses.get(aWord[1]);
+    let lMorph = dDA.has(aWord[0]) ? dDA.get(aWord[0]) : _oSpellChecker.getMorph(aWord[1]);
     //helpers.echo("lMorph: "+lMorph.toString());
     if (lMorph.length === 0) {
         return false;
     }
     //helpers.echo("***");
@@ -423,14 +414,11 @@
     if (!aWord) {
         //helpers.echo("morph: noword, returns " + bNoWord);
         return bNoWord;
     }
     //helpers.echo("aWord: "+aWord.toString());
-    if (!_dAnalyses.has(aWord[1]) && !_storeMorphFromFSA(aWord[1])) {
-        return false;
-    }
-    let lMorph = dDA.has(aWord[0]) ? dDA.get(aWord[0]) : _dAnalyses.get(aWord[1]);
+    let lMorph = dDA.has(aWord[0]) ? dDA.get(aWord[0]) : _oSpellChecker.getMorph(aWord[1]);
     //helpers.echo("lMorph: "+lMorph.toString());
     if (lMorph.length === 0) {
         return false;
     }
     //helpers.echo("***");
@@ -442,41 +430,32 @@
     return lMorph.some(s  =>  (s.search(sPattern) !== -1));
 }
 
 function analyse (sWord, sPattern, bStrict=true) {
     // analyse a word, return true if sPattern in morphologies (disambiguation off)
-    if (!_dAnalyses.has(sWord) && !_storeMorphFromFSA(sWord)) {
+    let lMorph = _oSpellChecker.getMorph(sWord);
+    if (lMorph.length === 0) {
         return false;
     }
     if (bStrict) {
-        return _dAnalyses.get(sWord).every(s  =>  (s.search(sPattern) !== -1));
+        return lMorph.every(s  =>  (s.search(sPattern) !== -1));
     }
-    return _dAnalyses.get(sWord).some(s  =>  (s.search(sPattern) !== -1));
+    return lMorph.some(s  =>  (s.search(sPattern) !== -1));
 }
 
 function analysex (sWord, sPattern, sNegPattern) {
     // analyse a word, returns True if not sNegPattern in word morphologies and sPattern in word morphologies (disambiguation off)
-    if (!_dAnalyses.has(sWord) && !_storeMorphFromFSA(sWord)) {
+    let lMorph = _oSpellChecker.getMorph(sWord);
+    if (lMorph.length === 0) {
         return false;
     }
     // check negative condition
-    if (_dAnalyses.get(sWord).some(s  =>  (s.search(sNegPattern) !== -1))) {
+    if (lMorph.some(s  =>  (s.search(sNegPattern) !== -1))) {
         return false;
     }
     // search sPattern
-    return _dAnalyses.get(sWord).some(s  =>  (s.search(sPattern) !== -1));
-}
-
-function stem (sWord) {
-    // returns a list of sWord's stems
-    if (!sWord) {
-        return [];
-    }
-    if (!_dAnalyses.has(sWord) && !_storeMorphFromFSA(sWord)) {
-        return [];
-    }
-    return _dAnalyses.get(sWord).map( s => s.slice(1, s.indexOf(" ")) );
+    return lMorph.some(s  =>  (s.search(sPattern) !== -1));
 }
 
 
 //// functions to get text outside pattern scope
 
@@ -565,19 +544,17 @@
         return true;
     }
     if (dDA.has(nPos)) {
         return true;
     }
-    if (!_dAnalyses.has(sWord) && !_storeMorphFromFSA(sWord)) {
+    let lMorph = _oSpellChecker.getMorph(sWord);
+    if (lMorph.length === 0  ||  lMorph.length === 1) {
         return true;
     }
-    if (_dAnalyses.get(sWord).length === 1) {
-        return true;
-    }
-    let lSelect = _dAnalyses.get(sWord).filter( sMorph => sMorph.search(sPattern) !== -1 );
+    let lSelect = lMorph.filter( sMorph => sMorph.search(sPattern) !== -1 );
     if (lSelect.length > 0) {
-        if (lSelect.length != _dAnalyses.get(sWord).length) {
+        if (lSelect.length != lMorph.length) {
             dDA.set(nPos, lSelect);
         }
     } else if (lDefault) {
         dDA.set(nPos, lDefaul);
     }
@@ -589,19 +566,17 @@
         return true;
     }
     if (dDA.has(nPos)) {
         return true;
     }
-    if (!_dAnalyses.has(sWord) && !_storeMorphFromFSA(sWord)) {
+    let lMorph = _oSpellChecker.getMorph(sWord);
+    if (lMorph.length === 0  ||  lMorph.length === 1) {
         return true;
     }
-    if (_dAnalyses.get(sWord).length === 1) {
-        return true;
-    }
-    let lSelect = _dAnalyses.get(sWord).filter( sMorph => sMorph.search(sPattern) === -1 );
+    let lSelect = lMorph.filter( sMorph => sMorph.search(sPattern) === -1 );
     if (lSelect.length > 0) {
-        if (lSelect.length != _dAnalyses.get(sWord).length) {
+        if (lSelect.length != lMorph.length) {
             dDA.set(nPos, lSelect);
         }
     } else if (lDefault) {
         dDA.set(nPos, lDefault);
     }

Index: gc_core/py/lang_core/gc_engine.py
==================================================================
--- gc_core/py/lang_core/gc_engine.py
+++ gc_core/py/lang_core/gc_engine.py
@@ -10,10 +10,23 @@
 
 from ..graphspell.spellchecker import SpellChecker
 from ..graphspell.echo import echo
 from . import gc_options
 
+from ..graphspell.tokenizer import Tokenizer
+from .gc_rules_graph import dAllGraph, dRule
+
+try:
+    # LibreOffice / OpenOffice
+    from com.sun.star.linguistic2 import SingleProofreadingError
+    from com.sun.star.text.TextMarkupType import PROOFREADING
+    from com.sun.star.beans import PropertyValue
+    #import lightproof_handler_${implname} as opt
+    _bWriterError = True
+except ImportError:
+    _bWriterError = False
+
 
 __all__ = [ "lang", "locales", "pkg", "name", "version", "author", \
             "load", "parse", "getSpellChecker", \
             "setOption", "setOptions", "getOptions", "getDefaultOptions", "getOptionsLabels", "resetOptions", "displayOptions", \
             "ignoreRule", "resetIgnoreRules", "reactivateRule", "listRules", "displayRules" ]
@@ -31,30 +44,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"):
+    global _oSpellChecker
+    global _sAppContext
+    global _dOptions
+    global _oTokenizer
+    global _createRegexError
+    global _createTokenError
+    try:
+        _oSpellChecker = SpellChecker("${lang}", "${dic_main_filename_py}", "${dic_extended_filename_py}", "${dic_community_filename_py}", "${dic_personal_filename_py}")
+        _sAppContext = sContext
+        _dOptions = dict(gc_options.getOptions(sContext))   # duplication necessary, to be able to reset to default
+        _oTokenizer = _oSpellChecker.getTokenizer()
+        _oSpellChecker.activateStorage()
+        _createRegexError = _createRegexWriterError  if _bWriterError  else _createRegexDictError
+    except:
+        traceback.print_exc()
+
+
+def _getRules (bParagraph):
+    try:
+        if not bParagraph:
+            return _rules.lSentenceRules
+        return _rules.lParagraphRules
+    except:
+        _loadRules()
+    if not bParagraph:
+        return _rules.lSentenceRules
+    return _rules.lParagraphRules
+
+
+def _loadRules ():
+    from . import gc_rules
+    global _rules
+    _rules = gc_rules
+    # compile rules regex
+    for sOption, lRuleGroup in chain(_rules.lParagraphRules, _rules.lSentenceRules):
+        if sOption != "@@@@":
+            for aRule in lRuleGroup:
+                try:
+                    aRule[0] = re.compile(aRule[0])
+                except:
+                    echo("Bad regular expression in # " + str(aRule[2]))
+                    aRule[0] = "(?i)<Grammalecte>"
 
 
 #### 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 +142,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,17 +237,14 @@
         if bUppercase and m.group(iGroup)[0:1].isupper():
             xErr.aSuggestions = tuple(map(str.capitalize, m.expand(sRepl).split("|")))
         else:
             xErr.aSuggestions = tuple(m.expand(sRepl).split("|"))
     # Message
-    if sMsg[0:1] == "=":
-        sMessage = globals()[sMsg[1:]](s, m)
-    else:
-        sMessage = m.expand(sMsg)
+    sMessage = globals()[sMsg[1:]](s, m)  if sMsg[0:1] == "="  else  m.expand(sMsg)
     xErr.aShortComment = sMessage   # sMessage.split("|")[0]     # in context menu
     xErr.aFullComment = sMessage   # sMessage.split("|")[-1]    # in dialog
-    if bIdRule:
+    if bShowRuleId:
         xErr.aShortComment += "  # " + sLineId + " # " + sRuleId
     # URL
     if sURL:
         p = PropertyValue()
         p.Name = "FullCommentURL"
@@ -177,11 +253,11 @@
     else:
         xErr.aProperties = ()
     return xErr
 
 
-def _createDictError (s, sx, sRepl, nOffset, m, iGroup, sLineId, sRuleId, bUppercase, sMsg, sURL, bIdRule, sOption, bContext):
+def _createRegexDictError (s, sx, sRepl, nOffset, m, iGroup, sLineId, sRuleId, bUppercase, sMsg, sURL, bShowRuleId, sOption, bContext):
     "error as a dictionary"
     dErr = {}
     dErr["nStart"] = nOffset + m.start(iGroup)
     dErr["nEnd"] = nOffset + m.end(iGroup)
     dErr["sLineId"] = sLineId
@@ -194,25 +270,21 @@
             if bUppercase and m.group(iGroup)[0:1].isupper():
                 dErr["aSuggestions"] = list(map(str.capitalize, sugg.split("|")))
             else:
                 dErr["aSuggestions"] = sugg.split("|")
         else:
-            dErr["aSuggestions"] = ()
+            dErr["aSuggestions"] = []
     elif sRepl == "_":
-        dErr["aSuggestions"] = ()
+        dErr["aSuggestions"] = []
     else:
         if bUppercase and m.group(iGroup)[0:1].isupper():
             dErr["aSuggestions"] = list(map(str.capitalize, m.expand(sRepl).split("|")))
         else:
             dErr["aSuggestions"] = m.expand(sRepl).split("|")
     # Message
-    if sMsg[0:1] == "=":
-        sMessage = globals()[sMsg[1:]](s, m)
-    else:
-        sMessage = m.expand(sMsg)
-    dErr["sMessage"] = sMessage
-    if bIdRule:
+    dErr["sMessage"] = globals()[sMsg[1:]](s, m)  if sMsg[0:1] == "="  else  m.expand(sMsg)
+    if bShowRuleId:
         dErr["sMessage"] += "  # " + sLineId + " # " + sRuleId
     # URL
     dErr["URL"] = sURL  if sURL  else ""
     # Context
     if bContext:
@@ -220,28 +292,26 @@
         dErr['sBefore'] = sx[max(0,m.start(iGroup)-80):m.start(iGroup)]
         dErr['sAfter'] = sx[m.end(iGroup):m.end(iGroup)+80]
     return dErr
 
 
-def _rewrite (s, sRepl, iGroup, m, bUppercase):
-    "text processor: write sRepl in s at iGroup position"
+def _rewrite (sSentence, sRepl, iGroup, m, bUppercase):
+    "text processor: write <sRepl> in <sSentence> at <iGroup> position"
     nLen = m.end(iGroup) - m.start(iGroup)
     if sRepl == "*":
         sNew = " " * nLen
-    elif sRepl == ">" or sRepl == "_" or sRepl == "~":
+    elif sRepl == "_":
         sNew = sRepl + " " * (nLen-1)
-    elif sRepl == "@":
-        sNew = "@" * nLen
     elif sRepl[0:1] == "=":
-        sNew = globals()[sRepl[1:]](s, m)
+        sNew = globals()[sRepl[1:]](sSentence, m)
         sNew = sNew + " " * (nLen-len(sNew))
         if bUppercase and m.group(iGroup)[0:1].isupper():
             sNew = sNew.capitalize()
     else:
         sNew = m.expand(sRepl)
         sNew = sNew + " " * (nLen-len(sNew))
-    return s[0:m.start(iGroup)] + sNew + s[m.end(iGroup):]
+    return sSentence[0:m.start(iGroup)] + sNew + sSentence[m.end(iGroup):]
 
 
 def ignoreRule (sRuleId):
     _aIgnoredRules.add(sRuleId)
 
@@ -261,45 +331,21 @@
             zFilter = re.compile(sFilter)
         except:
             echo("# Error. List rules: wrong regex.")
             sFilter = None
     for sOption, lRuleGroup in chain(_getRules(True), _getRules(False)):
-        for _, _, sLineId, sRuleId, _, _ in lRuleGroup:
-            if not sFilter or zFilter.search(sRuleId):
-                yield (sOption, sLineId, sRuleId)
+        if sOption != "@@@@":
+            for _, _, sLineId, sRuleId, _, _ in lRuleGroup:
+                if not sFilter or zFilter.search(sRuleId):
+                    yield (sOption, sLineId, sRuleId)
 
 
 def displayRules (sFilter=None):
     echo("List of rules. Filter: << " + str(sFilter) + " >>")
     for sOption, sLineId, sRuleId in listRules(sFilter):
         echo("{:<10} {:<10} {}".format(sOption, sLineId, sRuleId))
 
-
-#### init
-
-try:
-    # LibreOffice / OpenOffice
-    from com.sun.star.linguistic2 import SingleProofreadingError
-    from com.sun.star.text.TextMarkupType import PROOFREADING
-    from com.sun.star.beans import PropertyValue
-    #import lightproof_handler_${implname} as opt
-    _createError = _createWriterError
-except ImportError:
-    _createError = _createDictError
-
-
-def load (sContext="Python"):
-    global _oSpellChecker
-    global _sAppContext
-    global _dOptions
-    try:
-        _oSpellChecker = SpellChecker("${lang}", "${dic_main_filename_py}", "${dic_extended_filename_py}", "${dic_community_filename_py}", "${dic_personal_filename_py}")
-        _sAppContext = sContext
-        _dOptions = dict(gc_options.getOptions(sContext))   # duplication necessary, to be able to reset to default
-    except:
-        traceback.print_exc()
-
 
 def setOption (sOpt, bVal):
     if sOpt in _dOptions:
         _dOptions[sOpt] = bVal
 
@@ -334,100 +380,58 @@
 
 
 def getSpellChecker ():
     return _oSpellChecker
 
-
-def _getRules (bParagraph):
-    try:
-        if not bParagraph:
-            return _rules.lSentenceRules
-        return _rules.lParagraphRules
-    except:
-        _loadRules()
-    if not bParagraph:
-        return _rules.lSentenceRules
-    return _rules.lParagraphRules
-
-
-def _loadRules ():
-    from . import gc_rules
-    global _rules
-    _rules = gc_rules
-    # compile rules regex
-    for lRuleGroup in chain(_rules.lParagraphRules, _rules.lSentenceRules):
-        for rule in lRuleGroup[1]:
-            try:
-                rule[0] = re.compile(rule[0])
-            except:
-                echo("Bad regular expression in # " + str(rule[2]))
-                rule[0] = "(?i)<Grammalecte>"
-
 
 def _getPath ():
     return os.path.join(os.path.dirname(sys.modules[__name__].__file__), __name__ + ".py")
 
 
 
 #### common functions
-
-# common regexes
-_zEndOfSentence = re.compile('([.?!:;…][ .?!… »”")]*|.$)')
-_zBeginOfParagraph = re.compile("^\W*")
-_zEndOfParagraph = re.compile("\W*$")
-_zNextWord = re.compile(" +(\w[\w-]*)")
-_zPrevWord = re.compile("(\w[\w-]*) +$")
-
 
 def option (sOpt):
     "return True if option sOpt is active"
     return _dOptions.get(sOpt, False)
 
 
-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)
     if bStrict:
         return all(p.search(s)  for s in lMorph)
     return any(p.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):
         return False
     # search sPattern
@@ -435,46 +439,41 @@
     return any(p.search(s)  for s in lMorph)
 
 
 def analyse (sWord, sPattern, bStrict=True):
     "analyse a word, return True if sPattern in morphologies (disambiguation off)"
-    if sWord not in _dAnalyses and not _storeMorphFromFSA(sWord):
-        return False
-    if not _dAnalyses[sWord]:
+    lMorph = _oSpellChecker.getMorph(sWord)
+    if not lMorph:
         return False
     p = re.compile(sPattern)
     if bStrict:
-        return all(p.search(s)  for s in _dAnalyses[sWord])
-    return any(p.search(s)  for s in _dAnalyses[sWord])
+        return all(p.search(s)  for s in lMorph)
+    return any(p.search(s)  for s in lMorph)
 
 
 def analysex (sWord, sPattern, sNegPattern):
     "analyse a word, returns True if not sNegPattern in word morphologies and sPattern in word morphologies (disambiguation off)"
-    if sWord not in _dAnalyses and not _storeMorphFromFSA(sWord):
+    lMorph = _oSpellChecker.getMorph(sWord)
+    if not lMorph:
         return False
     # check negative condition
     np = re.compile(sNegPattern)
-    if any(np.search(s)  for s in _dAnalyses[sWord]):
+    if any(np.search(s)  for s in lMorph):
         return False
     # search sPattern
     p = re.compile(sPattern)
-    return any(p.search(s)  for s in _dAnalyses[sWord])
-
-
-def stem (sWord):
-    "returns a list of sWord's stems"
-    if not sWord:
-        return []
-    if sWord not in _dAnalyses and not _storeMorphFromFSA(sWord):
-        return []
-    return [ s[1:s.find(" ")]  for s in _dAnalyses[sWord] ]
+    return any(p.search(s)  for s in lMorph)
+
 
 
 ## functions to get text outside pattern scope
 
 # warning: check compile_rules.py to understand how it works
 
+_zNextWord = re.compile(r" +(\w[\w-]*)")
+_zPrevWord = re.compile(r"(\w[\w-]*) +$")
+
 def nextword (s, iStart, n):
     "get the nth word of the input string or empty string"
     m = re.match("(?: +[\\w%-]+){" + str(n-1) + "} +([\\w%-]+)", s[iStart:])
     if not m:
         return None
@@ -512,11 +511,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 +522,563 @@
         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):
+    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):
+    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):
+    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:
+
+    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):
+        self.sSentence = sSentence
+        self.lToken = list(_oTokenizer.genTokens(sSentence, True))
+
+    def _getNextMatchingNodes (self, dToken, dGraph, dNode, bDebug=False):
+        "generator: return nodes where <dToken> “values” match <dNode> 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 "<lemmas>" in dNode:
+            for sLemma in _oSpellChecker.getLemma(dToken["sValue"]):
+                if sLemma in dNode["<lemmas>"]:
+                    if bDebug:
+                        print("  MATCH: >" + sLemma)
+                    yield dGraph[dNode["<lemmas>"][sLemma]]
+        # regex value arcs
+        if "<re_value>" in dNode:
+            for sRegex in dNode["<re_value>"]:
+                if "¬" not in sRegex:
+                    # no anti-pattern
+                    if re.search(sRegex, dToken["sValue"]):
+                        if bDebug:
+                            print("  MATCH: ~" + sRegex)
+                        yield dGraph[dNode["<re_value>"][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["<re_value>"][sRegex]]
+        # regex morph arcs
+        if "<re_morph>" in dNode:
+            for sRegex in dNode["<re_morph>"]:
+                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["<re_morph>"][sRegex]]
+                else:
+                    # there is an anti-pattern
+                    sPattern, sNegPattern = sRegex.split("¬", 1)
+                    if sNegPattern == "*":
+                        # all morphologies must match with <sPattern>
+                        if sPattern and all(re.search(sPattern, sMorph)  for sMorph in _oSpellChecker.getMorph(dToken["sValue"])):
+                            if bDebug:
+                                print("  MATCH: @" + sRegex)
+                            yield dGraph[dNode["<re_morph>"][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["<re_morph>"][sRegex]]
+        # meta arc (for token type)
+        if "<meta>" in dNode:
+            for sMeta in dNode["<meta>"]:
+                # not regex here, we just search if <dNode["sType"]> exists within <sMeta>
+                if sMeta == "*":
+                    if bDebug:
+                        print("  MATCH: *" + sMeta)
+                    yield dGraph[dNode["<meta>"]["*"]]
+                elif "¬" in sMeta:
+                    if dNode["sType"] not in sMeta:
+                        if bDebug:
+                            print("  MATCH: *" + sMeta)
+                        yield dGraph[dNode["<meta>"][sMeta]]
+                elif dNode["sType"] in sMeta:
+                    if bDebug:
+                        print("  MATCH: *" + sMeta)
+                    yield dGraph[dNode["<meta>"][sMeta]]
+
+
+    def parse (self, dGraph, dPriority, sCountry="${country_default}", dOptions=None, bShowRuleId=False, bDebug=False, bContext=False):
+        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 "<rules>" in dPointer["dNode"]:
+                    bChange, dErr = self._executeActions(dGraph, dPointer["dNode"]["<rules>"], 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("-", 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("~", sRuleId)
+                                bChange = True
+                            elif cActionType == "=":
+                                # disambiguation
+                                globals()[sWhat](self.lToken, nTokenOffset)
+                                if bDebug:
+                                    print("=", sRuleId)
+                            elif cActionType == ">":
+                                # we do nothing, this test is just a condition to apply all following actions
+                                if bDebug:
+                                    print(">", sRuleId)
+                                pass
+                            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:
+            p = PropertyValue()
+            p.Name = "FullCommentURL"
+            p.Value = sURL
+            xErr.aProperties = (p,)
+        else:
+            xErr.aProperties = ()
+        return xErr
+
+    def _createDictError (self, sSugg, nTokenOffset, iFirstToken, nStart, nEnd, sLineId, sRuleId, bUppercase, sMsg, sURL, bShowRuleId, sOption, bContext):
+        "error as a dictionary"
+        dErr = {}
+        dErr["nStart"] = nStart
+        dErr["nEnd"] = nEnd
+        dErr["sLineId"] = sLineId
+        dErr["sRuleId"] = sRuleId
+        dErr["sType"] = sOption  if sOption  else "notype"
+        # suggestions
+        if sSugg[0:1] == "=":
+            sSugg = globals()[sSugg[1:]](self.lToken, 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 <nTokenRewriteStart> and <nTokenRewriteEnd> 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 <sNegPattern> not in morphologies and <sPattern> 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 <sNegPattern> not in morphologies and <sPattern> 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 <dToken> according to <sPattern>, 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 <dToken> according to <sPattern>, 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 <dToken>, 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}

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,5 @@
+# generated code, do not edit
+
+dAllGraph = ${rules_graphs}
+
+dRule = ${rules_actions}

ADDED   gc_core/py/lang_core/gc_sentence.py
Index: gc_core/py/lang_core/gc_sentence.py
==================================================================
--- /dev/null
+++ gc_core/py/lang_core/gc_sentence.py
@@ -0,0 +1,237 @@
+# Sentence checker
+
+from ..graphspell.tokenizer import Tokenizer
+from .gc_rules_graph import dGraph
+
+
+oTokenizer = Tokenizer("${lang}")
+
+
+class TokenSentence:
+
+    def __init__ (self, sSentence, sSentence0, nOffset):
+        self.sSentence = sSentence
+        self.sSentence0 = sSentence0
+        self.nOffset = nOffset
+        self.lToken = list(oTokenizer.genTokens())
+
+    def parse (self):
+        dErr = {}
+        lPointer = []
+        for dToken in self.lToken:
+            for i, dPointer in enumerate(lPointer):
+                bValid = False
+                for dNode in self._getNextMatchingNodes(dToken, dPointer["dNode"]):
+                    dPointer["nOffset"] = dToken["i"]
+                    dPointer["dNode"] = dNode
+                    bValid = True
+                if not bValid:
+                    del lPointer[i]
+            for dNode in self._getNextMatchingNodes(dToken, dGraph):
+                lPointer.append({"nOffset": 0, "dNode": dNode})
+            for dPointer in lPointer:
+                if "<rules>" in dPointer["dNode"]:
+                    for dNode in dGraph[dPointer["dNode"]["<rules>"]]:
+                        dErr = self._executeActions(dNode, nOffset)
+        return dErr
+
+    def _getNextMatchingNodes (self, dToken, dNode):
+        # token value
+        if dToken["sValue"] in dNode:
+            yield dGraph[dNode[dToken["sValue"]]]
+        # token lemmas
+        for sLemma in dToken["lLemma"]:
+            if sLemma in dNode:
+                yield dGraph[dNode[sLemma]]
+        # universal arc
+        if "*" in dNode:
+            yield dGraph[dNode["*"]]
+        # regex arcs
+        if "~" in dNode:
+            for sRegex in dNode["~"]:
+                for sMorph in dToken["lMorph"]:
+                    if re.search(sRegex, sMorph):
+                        yield dGraph[dNode["~"][sRegex]]
+
+    def _executeActions (self, dNode, nOffset):
+        for sLineId, nextNodeKey in dNode.items():
+            for sArc in dGraph[nextNodeKey]:
+                bCondMemo = None
+                sFuncCond, cActionType, sWhat, *eAct = dRule[sArc]
+                # action in lActions: [ condition, action type, replacement/suggestion/action[, iGroupStart, iGroupEnd[, message, URL]] ]
+                try:
+                    bCondMemo = not sFuncCond or globals()[sFuncCond](self, sCountry, bCondMemo)
+                    if bCondMemo:
+                        if cActionType == "-":
+                            # grammar error
+                            nErrorStart = nSentenceOffset + m.start(eAct[0])
+                            nErrorEnd = nSentenceOffset + m.start(eAct[1])
+                            if nErrorStart not in dErrs or nPriority > dPriority[nErrorStart]:
+                                dErrs[nErrorStart] = _createError(self, sWhat, nErrorStart, nErrorEnd, sLineId, bUppercase, eAct[2], eAct[3], bIdRule, sOption, bContext)
+                                dPriority[nErrorStart] = nPriority
+                        elif cActionType == "~":
+                            # text processor
+                            self._rewrite(sWhat, nErrorStart, nErrorEnd)
+                        elif cActionType == "@":
+                            # jump
+                            self._jump(sWhat)
+                        elif cActionType == "=":
+                            # disambiguation
+                            globals()[sWhat](self.lToken)
+                        elif cActionType == ">":
+                            # we do nothing, this test is just a condition to apply all following actions
+                            pass
+                        else:
+                            print("# error: unknown action at " + sLineId)
+                    elif cActionType == ">":
+                        break
+                except Exception as e:
+                    raise Exception(str(e), "# " + sLineId + " # " + sRuleId)
+
+    def _createWriterError (self):
+        d = {}
+        return d
+
+    def _createDictError (self):
+        d = {}
+        return d
+
+    def _rewrite (self, sWhat, nErrorStart, nErrorEnd):
+        "text processor: rewrite tokens between <nErrorStart> and <nErrorEnd> position"
+        lTokenValue = sWhat.split("|")
+        if len(lTokenValue) != (nErrorEnd - nErrorStart + 1):
+            print("Error. Text processor: number of replacements != number of tokens.")
+            return
+        for i, sValue in zip(range(nErrorStart, nErrorEnd+1), lTokenValue):
+            self.lToken[i]["sValue"] = sValue
+
+    def _jump (self, sWhat):
+        try:
+            nFrom, nTo = sWhat.split(">")
+            self.lToken[int(nFrom)]["iJump"] = int(nTo)
+        except:
+            print("# Error. Jump failed: ", sWhat)
+            traceback.print_exc()
+            return
+
+
+#### Analyse tokens
+
+def g_morph (dToken, sPattern, bStrict=True):
+    "analyse a token, return True if <sPattern> in morphologies"
+    if "lMorph" in dToken:
+        lMorph = dToken["lMorph"]
+    else:
+        if dToken["sValue"] not in _dAnalyses and not _storeMorphFromFSA(dToken["sValue"]):
+            return False
+        if not _dAnalyses[dToken["sValue"]]:
+            return False
+        lMorph = _dAnalyses[dToken["sValue"]]
+    zPattern = re.compile(sPattern)
+    if bStrict:
+        return all(zPattern.search(sMorph)  for sMorph in lMorph)
+    return any(zPattern.search(sMorph)  for sMorph in lMorph)
+
+def g_morphex (dToken, sPattern, sNegPattern):
+    "analyse a token, return True if <sNegPattern> not in morphologies and <sPattern> in morphologies"
+    if "lMorph" in dToken:
+        lMorph = dToken["lMorph"]
+    else:
+        if dToken["sValue"] not in _dAnalyses and not _storeMorphFromFSA(dToken["sValue"]):
+            return False
+        if not _dAnalyses[dToken["sValue"]]:
+            return False
+        lMorph = _dAnalyses[dToken["sValue"]]
+    # check negative condition
+    zNegPattern = re.compile(sNegPattern)
+    if any(zNegPattern.search(sMorph)  for sMorph in lMorph):
+        return False
+    # search sPattern
+    zPattern = re.compile(sPattern)
+    return any(zPattern.search(sMorph)  for sMorph in lMorph)
+
+def g_analyse (dToken, sPattern, bStrict=True):
+    "analyse a token, return True if <sPattern> in morphologies (disambiguation off)"
+    if dToken["sValue"] not in _dAnalyses and not _storeMorphFromFSA(dToken["sValue"]):
+        return False
+    if not _dAnalyses[dToken["sValue"]]:
+        return False
+    zPattern = re.compile(sPattern)
+    if bStrict:
+        return all(zPattern.search(sMorph)  for sMorph in _dAnalyses[dToken["sValue"]])
+    return any(zPattern.search(sMorph)  for sMorph in _dAnalyses[dToken["sValue"]])
+
+
+def g_analysex (dToken, sPattern, sNegPattern):
+    "analyse a token, return True if <sNegPattern> not in morphologies and <sPattern> in morphologies (disambiguation off)"
+    if dToken["sValue"] not in _dAnalyses and not _storeMorphFromFSA(dToken["sValue"]):
+        return False
+    if not _dAnalyses[dToken["sValue"]]:
+        return False
+    # check negative condition
+    zNegPattern = re.compile(sNegPattern)
+    if any(zNegPattern.search(sMorph)  for sMorph in _dAnalyses[dToken["sValue"]]):
+        return False
+    # search sPattern
+    zPattern = re.compile(sPattern)
+    return any(zPattern.search(sMorph)  for sMorph in _dAnalyses[dToken["sValue"]])
+
+
+#### Go outside the rule scope
+
+def g_nextToken (i):
+    pass
+
+def g_prevToken (i):
+    pass
+
+def g_look ():
+    pass
+
+def g_lookAndCheck ():
+    pass
+
+
+#### Disambiguator
+
+def g_select (dToken, sPattern, lDefault=None):
+    "select morphologies for <dToken> according to <sPattern>, always return True"
+    if dToken["sValue"] not in _dAnalyses and not _storeMorphFromFSA(dToken["sValue"]):
+        return True
+    if len(_dAnalyses[dToken["sValue"]]) == 1:
+        return True
+    lMorph = dToken["lMorph"] or _dAnalyses[dToken["sValue"]]
+    lSelect = [ sMorph  for sMorph in lMorph  if re.search(sPattern, sMorph) ]
+    if lSelect:
+        if len(lSelect) != len(lMorph):
+            dToken["lMorph"] = lSelect
+    elif lDefault:
+        dToken["lMorph"] = lDefault
+    return True
+
+
+def g_exclude (dToken, sPattern, lDefault=None):
+    "select morphologies for <dToken> according to <sPattern>, always return True"
+    if dToken["sValue"] not in _dAnalyses and not _storeMorphFromFSA(dToken["sValue"]):
+        return True
+    if len(_dAnalyses[dToken["sValue"]]) == 1:
+        return True
+    lMorph = dToken["lMorph"] or _dAnalyses[dToken["sValue"]]
+    lSelect = [ sMorph  for sMorph in lMorph  if not re.search(sPattern, sMorph) ]
+    if lSelect:
+        if len(lSelect) != len(lMorph):
+            dToken["lMorph"] = lSelect
+    elif lDefault:
+        dToken["lMorph"] = lDefault
+    return True
+
+
+def g_define (dToken, lMorph):
+    "set morphologies of <dToken>, always return True"
+    dToken["lMorph"] = lMorph
+    return True
+
+
+#### CALLABLES (generated code)
+
+${graph_callables}

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,64 @@
+# 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)
+        pas
+        jamais
+        point
+        guère
+        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
@@ -56,11 +56,11 @@
 
 
 def getSimil (sWord, sMorph, bSubst=False):
     if ":V" not in sMorph:
         return set()
-    sInfi = sMorph[1:sMorph.find(" ")]
+    sInfi = sMorph[1:sMorph.find("/")]
     aSugg = set()
     tTags = _getTags(sInfi)
     if tTags:
         if not bSubst:
             # we suggest conjugated forms

Index: gc_lang/fr/modules/gce_analyseur.py
==================================================================
--- gc_lang/fr/modules/gce_analyseur.py
+++ gc_lang/fr/modules/gce_analyseur.py
@@ -15,63 +15,58 @@
         return "nous"
     if s2 == "vous":
         return "vous"
     if s2 == "eux":
         return "ils"
-    if s2 == "elle" or s2 == "elles":
-        # We don’t check if word exists in _dAnalyses, for it is assumed it has been done before
-        if cr.mbNprMasNotFem(_dAnalyses.get(s1, False)):
+    if s2 == "elle" or s2 == "elles":    
+        if cr.mbNprMasNotFem(_oSpellChecker.getMorph(s1)):
             return "ils"
         # si épicène, indéterminable, mais OSEF, le féminin l’emporte
         return "elles"
     return s1 + " et " + s2
 
 
 def apposition (sWord1, sWord2):
     "returns True if nom + nom (no agreement required)"
-    # We don’t check if word exists in _dAnalyses, for it is assumed it has been done before
-    return cr.mbNomNotAdj(_dAnalyses.get(sWord2, False)) and cr.mbPpasNomNotAdj(_dAnalyses.get(sWord1, False))
+    return cr.mbNomNotAdj(_oSpellChecker.getMorph(sWord2)) and cr.mbPpasNomNotAdj(_oSpellChecker.getMorph(sWord1))
 
 
 def isAmbiguousNAV (sWord):
     "words which are nom|adj and verb are ambiguous (except être and avoir)"
-    if sWord not in _dAnalyses and not _storeMorphFromFSA(sWord):
+    lMorph = _oSpellChecker.getMorph(sWord)
+    if not cr.mbNomAdj(lMorph) or sWord == "est":
         return False
-    if not cr.mbNomAdj(_dAnalyses[sWord]) or sWord == "est":
-        return False
-    if cr.mbVconj(_dAnalyses[sWord]) and not cr.mbMG(_dAnalyses[sWord]):
+    if cr.mbVconj(lMorph) and not cr.mbMG(lMorph):
         return True
     return False
 
 
 def isAmbiguousAndWrong (sWord1, sWord2, sReqMorphNA, sReqMorphConj):
     "use it if sWord1 won’t be a verb; word2 is assumed to be True via isAmbiguousNAV"
-    # We don’t check if word exists in _dAnalyses, for it is assumed it has been done before
-    a2 = _dAnalyses.get(sWord2, None)
+    a2 = _oSpellChecker.getMorph(sWord2)
     if not a2:
         return False
     if cr.checkConjVerb(a2, sReqMorphConj):
         # verb word2 is ok
         return False
-    a1 = _dAnalyses.get(sWord1, None)
+    a1 = _oSpellChecker.getMorph(sWord1)
     if not a1:
         return False
     if cr.checkAgreement(a1, a2) and (cr.mbAdj(a2) or cr.mbAdj(a1)):
         return False
     return True
 
 
 def isVeryAmbiguousAndWrong (sWord1, sWord2, sReqMorphNA, sReqMorphConj, bLastHopeCond):
     "use it if sWord1 can be also a verb; word2 is assumed to be True via isAmbiguousNAV"
-    # We don’t check if word exists in _dAnalyses, for it is assumed it has been done before
-    a2 = _dAnalyses.get(sWord2, None)
+    a2 = _oSpellChecker.getMorph(sWord2)
     if not a2:
         return False
     if cr.checkConjVerb(a2, sReqMorphConj):
         # verb word2 is ok
         return False
-    a1 = _dAnalyses.get(sWord1, None)
+    a1 = _oSpellChecker.getMorph(sWord1)
     if not a1:
         return False
     if cr.checkAgreement(a1, a2) and (cr.mbAdj(a2) or cr.mbAdjNb(a1)):
         return False
     # now, we know there no agreement, and conjugation is also wrong
@@ -82,15 +77,14 @@
         return True
     return False
 
 
 def checkAgreement (sWord1, sWord2):
-    # We don’t check if word exists in _dAnalyses, for it is assumed it has been done before
-    a2 = _dAnalyses.get(sWord2, None)
+    a2 = _oSpellChecker.getMorph(sWord2)
     if not a2:
         return True
-    a1 = _dAnalyses.get(sWord1, None)
+    a1 = _oSpellChecker.getMorph(sWord1)
     if not a1:
         return True
     return cr.checkAgreement(a1, a2)
 
 

Index: gc_lang/fr/modules/gce_suggestions.py
==================================================================
--- gc_lang/fr/modules/gce_suggestions.py
+++ gc_lang/fr/modules/gce_suggestions.py
@@ -7,17 +7,17 @@
 
 ## Verbs
 
 def suggVerb (sFlex, sWho, funcSugg2=None):
     aSugg = set()
-    for sStem in stem(sFlex):
+    for sStem in _oSpellChecker.getLemma(sFlex):
         tTags = conj._getTags(sStem)
         if tTags:
             # we get the tense
             aTense = set()
-            for sMorph in _dAnalyses.get(sFlex, []): # we don’t check if word exists in _dAnalyses, for it is assumed it has been done before
-                for m in re.finditer(">"+sStem+" .*?(:(?:Y|I[pqsf]|S[pq]|K|P))", sMorph):
+            for sMorph in _oSpellChecker.getMorph(sFlex):
+                for m in re.finditer(">"+sStem+"/.*?(:(?:Y|I[pqsf]|S[pq]|K|P))", sMorph):
                     # stem must be used in regex to prevent confusion between different verbs (e.g. sauras has 2 stems: savoir and saurer)
                     if m:
                         if m.group(1) == ":Y":
                             aTense.add(":Ip")
                             aTense.add(":Iq")
@@ -40,11 +40,11 @@
     return ""
 
 
 def suggVerbPpas (sFlex, sWhat=None):
     aSugg = set()
-    for sStem in stem(sFlex):
+    for sStem in _oSpellChecker.getLemma(sFlex):
         tTags = conj._getTags(sStem)
         if tTags:
             if not sWhat:
                 aSugg.add(conj._getConjWithTags(sStem, tTags, ":PQ", ":Q1"))
                 aSugg.add(conj._getConjWithTags(sStem, tTags, ":PQ", ":Q2"))
@@ -83,21 +83,21 @@
     return ""
 
 
 def suggVerbTense (sFlex, sTense, sWho):
     aSugg = set()
-    for sStem in stem(sFlex):
+    for sStem in _oSpellChecker.getLemma(sFlex):
         if conj.hasConj(sStem, sTense, sWho):
             aSugg.add(conj.getConj(sStem, sTense, sWho))
     if aSugg:
         return "|".join(aSugg)
     return ""
 
 
 def suggVerbImpe (sFlex):
     aSugg = set()
-    for sStem in stem(sFlex):
+    for sStem in _oSpellChecker.getLemma(sFlex):
         tTags = conj._getTags(sStem)
         if tTags:
             if conj._hasConjWithTags(tTags, ":E", ":2s"):
                 aSugg.add(conj._getConjWithTags(sStem, tTags, ":E", ":2s"))
             if conj._hasConjWithTags(tTags, ":E", ":1p"):
@@ -108,11 +108,11 @@
         return "|".join(aSugg)
     return ""
 
 
 def suggVerbInfi (sFlex):
-    return "|".join([ sStem  for sStem in stem(sFlex)  if conj.isVerb(sStem) ])
+    return "|".join([ sStem  for sStem in _oSpellChecker.getLemma(sFlex)  if conj.isVerb(sStem) ])
 
 
 _dQuiEst = { "je": ":1s", "j’": ":1s", "j’en": ":1s", "j’y": ":1s", \
              "tu": ":2s", "il": ":3s", "on": ":3s", "elle": ":3s", "nous": ":1p", "vous": ":2p", "ils": ":3p", "elles": ":3p" }
 _lIndicatif = [":Ip", ":Iq", ":Is", ":If"]
@@ -131,11 +131,11 @@
     if not sWho:
         if sSuj[0:1].islower(): # pas un pronom, ni un nom propre
             return ""
         sWho = ":3s"
     aSugg = set()
-    for sStem in stem(sFlex):
+    for sStem in _oSpellChecker.getLemma(sFlex):
         tTags = conj._getTags(sStem)
         if tTags:
             for sTense in lMode:
                 if conj._hasConjWithTags(tTags, sTense, sWho):
                     aSugg.add(conj._getConjWithTags(sStem, tTags, sTense, sWho))
@@ -147,13 +147,14 @@
 ## Nouns and adjectives
 
 def suggPlur (sFlex, sWordToAgree=None):
     "returns plural forms assuming sFlex is singular"
     if sWordToAgree:
-        if sWordToAgree not in _dAnalyses and not _storeMorphFromFSA(sWordToAgree):
+        lMorph = _oSpellChecker.getMorph(sFlex)
+        if not lMorph:
             return ""
-        sGender = cr.getGender(_dAnalyses.get(sWordToAgree, []))
+        sGender = cr.getGender(lMorph)
         if sGender == ":m":
             return suggMasPlur(sFlex)
         elif sGender == ":f":
             return suggFemPlur(sFlex)
     aSugg = set()
@@ -191,13 +192,12 @@
     return ""
 
 
 def suggMasSing (sFlex, bSuggSimil=False):
     "returns masculine singular forms"
-    # we don’t check if word exists in _dAnalyses, for it is assumed it has been done before
     aSugg = set()
-    for sMorph in _dAnalyses.get(sFlex, []):
+    for sMorph in _oSpellChecker.getMorph(sFlex):
         if not ":V" in sMorph:
             # not a verb
             if ":m" in sMorph or ":e" in sMorph:
                 aSugg.add(suggSing(sFlex))
             else:
@@ -219,13 +219,12 @@
     return ""
 
 
 def suggMasPlur (sFlex, bSuggSimil=False):
     "returns masculine plural forms"
-    # we don’t check if word exists in _dAnalyses, for it is assumed it has been done before
     aSugg = set()
-    for sMorph in _dAnalyses.get(sFlex, []):
+    for sMorph in _oSpellChecker.getMorph(sFlex):
         if not ":V" in sMorph:
             # not a verb
             if ":m" in sMorph or ":e" in sMorph:
                 aSugg.add(suggPlur(sFlex))
             else:
@@ -250,13 +249,12 @@
     return ""
 
 
 def suggFemSing (sFlex, bSuggSimil=False):
     "returns feminine singular forms"
-    # we don’t check if word exists in _dAnalyses, for it is assumed it has been done before
     aSugg = set()
-    for sMorph in _dAnalyses.get(sFlex, []):
+    for sMorph in _oSpellChecker.getMorph(sFlex):
         if not ":V" in sMorph:
             # not a verb
             if ":f" in sMorph or ":e" in sMorph:
                 aSugg.add(suggSing(sFlex))
             else:
@@ -276,13 +274,12 @@
     return ""
 
 
 def suggFemPlur (sFlex, bSuggSimil=False):
     "returns feminine plural forms"
-    # we don’t check if word exists in _dAnalyses, for it is assumed it has been done before
     aSugg = set()
-    for sMorph in _dAnalyses.get(sFlex, []):
+    for sMorph in _oSpellChecker.getMorph(sFlex):
         if not ":V" in sMorph:
             # not a verb
             if ":f" in sMorph or ":e" in sMorph:
                 aSugg.add(suggPlur(sFlex))
             else:
@@ -301,33 +298,32 @@
         return "|".join(aSugg)
     return ""
 
 
 def hasFemForm (sFlex):
-    for sStem in stem(sFlex):
+    for sStem in _oSpellChecker.getLemma(sFlex):
         if mfsp.isFemForm(sStem) or conj.hasConj(sStem, ":PQ", ":Q3"):
             return True
     if phonet.hasSimil(sFlex, ":f"):
         return True
     return False
 
 
 def hasMasForm (sFlex):
-    for sStem in stem(sFlex):
+    for sStem in _oSpellChecker.getLemma(sFlex):
         if mfsp.isFemForm(sStem) or conj.hasConj(sStem, ":PQ", ":Q1"):
             # what has a feminine form also has a masculine form
             return True
     if phonet.hasSimil(sFlex, ":m"):
         return True
     return False
 
 
 def switchGender (sFlex, bPlur=None):
-    # we don’t check if word exists in _dAnalyses, for it is assumed it has been done before
     aSugg = set()
     if bPlur == None:
-        for sMorph in _dAnalyses.get(sFlex, []):
+        for sMorph in _oSpellChecker.getMorph(sFlex):
             if ":f" in sMorph:
                 if ":s" in sMorph:
                     aSugg.add(suggMasSing(sFlex))
                 elif ":p" in sMorph:
                     aSugg.add(suggMasPlur(sFlex))
@@ -338,17 +334,17 @@
                     aSugg.add(suggFemPlur(sFlex))
                 else:
                     aSugg.add(suggFemSing(sFlex))
                     aSugg.add(suggFemPlur(sFlex))
     elif bPlur:
-        for sMorph in _dAnalyses.get(sFlex, []):
+        for sMorph in _oSpellChecker.getMorph(sFlex):
             if ":f" in sMorph:
                 aSugg.add(suggMasPlur(sFlex))
             elif ":m" in sMorph:
                 aSugg.add(suggFemPlur(sFlex))
     else:
-        for sMorph in _dAnalyses.get(sFlex, []):
+        for sMorph in _oSpellChecker.getMorph(sFlex):
             if ":f" in sMorph:
                 aSugg.add(suggMasSing(sFlex))
             elif ":m" in sMorph:
                 aSugg.add(suggFemSing(sFlex))
     if aSugg:
@@ -355,13 +351,12 @@
         return "|".join(aSugg)
     return ""
 
 
 def switchPlural (sFlex):
-    # we don’t check if word exists in _dAnalyses, for it is assumed it has been done before
     aSugg = set()
-    for sMorph in _dAnalyses.get(sFlex, []):
+    for sMorph in _oSpellChecker.getMorph(sFlex):
         if ":s" in sMorph:
             aSugg.add(suggPlur(sFlex))
         elif ":p" in sMorph:
             aSugg.add(suggSing(sFlex))
     if aSugg:
@@ -373,13 +368,12 @@
     return phonet.hasSimil(sWord, sPattern)
 
 
 def suggSimil (sWord, sPattern=None, bSubst=False):
     "return list of words phonetically similar to sWord and whom POS is matching sPattern"
-    # we don’t check if word exists in _dAnalyses, for it is assumed it has been done before
     aSugg = phonet.selectSimil(sWord, sPattern)
-    for sMorph in _dAnalyses.get(sWord, []):
+    for sMorph in _oSpellChecker.getMorph(sWord):
         aSugg.update(conj.getSimil(sWord, sMorph, bSubst))
         break
     if aSugg:
         return "|".join(aSugg)
     return ""
@@ -392,12 +386,11 @@
         return "ce|cet"
     return "ce"
 
 
 def suggLesLa (sWord):
-    # we don’t check if word exists in _dAnalyses, for it is assumed it has been done before
-    if any( ":p" in sMorph  for sMorph in _dAnalyses.get(sWord, []) ):
+    if any( ":p" in sMorph  for sMorph in _oSpellChecker.getMorph(sWord) ):
         return "les|la"
     return "la"
 
 
 _zBinary = re.compile("^[01]+$")

Index: gc_lang/fr/rules.grx
==================================================================
--- gc_lang/fr/rules.grx
+++ gc_lang/fr/rules.grx
@@ -202,10 +202,11 @@
 !!
 !! 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,11 +218,14 @@
 DEF: w1             \w+
 DEF: w2             \w\w+
 DEF: w3             \w\w\w+
 DEF: w4             \w\w\w\w+
 
-
+# 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]
 
 
 !!
 !!
 !!
@@ -390,11 +394,10 @@
     https?://[\w./?&!%=+*"'@$#-]+ <<- ~>> *
 __<i](p_URL2)__
     ((?:{w_1}[.])*)({w_2})([.](?:com|net|org|info|fr|ca|be|ch|i[ot]|co[.]uk|tk|es|jp|zh|ru|us|nl|xyz)) @@0,**,$
     <<- ~1>> *
     <<- ~2>> =\2.capitalize()
-    <<- =>> define(\2, [":MP:e:i"])
     <<- ~3>> *
 
 # Numéro de chapitre
 __<i>(p_chapitre)__
     ^\d+[.][\d.-]* <<- ~>> *
@@ -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
@@ -1238,11 +1241,11 @@
 !!!! Redondances                                                                                    
 !!
 !!
 __[i]/redon1(redondances_paragraphe)__
     ({w_4})[  ,.;!?:].*[  ](\1)  @@0,$
-    <<- not morph(\1, ":(?:G|V0)|>(?:t(?:antôt|emps|rès)|loin|souvent|parfois|quelquefois|côte|petit|même) ", False) and not \1[0].isupper()
+    <<- not morph(\1, ":(?:G|V0)|>(?:t(?:antôt|emps|rès)|loin|souvent|parfois|quelquefois|côte|petit|même)/", False) and not \1[0].isupper()
     -2>> _                                                      # Dans ce paragraphe, répétition de « \1 » (à gauche).
     <<- __also__ -1>> _                                         # Dans ce paragraphe, répétition de « \1 » (à droite).
 
 TEST: __redon1__ Tu es son {{avenir}}. Et lui aussi est ton {{avenir}}.
 TEST: __redon1__ Car parfois il y en a. Mais parfois il n’y en a pas.
@@ -1509,15 +1512,15 @@
 
 __<i]/tu(tu_t_euphonique_incorrect)__
     ([-–—− ]t(?:[’' ][-–—−]?|[-–—−][’' ]?))(ils?|elles?|on|tu)  @@0,$
     <<- re.search("(?i)^(?:ils|elles|tu)$", \2) -1>> -      # 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-
 __<i]/tu(tu_t_euphonique_superflu)__
     [td]([- ]t[-’' ])(?:il|elle|on)  @@1
     <<- -1>> -                                              # 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-
 __<i]/tu(tu_t_euphonique_manquant)__
     [aec](-(il|elle|on))  @@1,2  <<- -1>> -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-
@@ -1759,11 +1762,11 @@
 
 
 # est-ce … ?
 __[i]/tu(tu_est_ce)__
     (?<![cCdDlL][’'])(est ce) ({w_2})  @@0,$
-    <<- morphex(\2, ":", ":N.*:[me]:[si]|>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}} ?
@@ -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 "
 
@@ -1941,11 +1944,11 @@
 TEST: Tape-toi Patrick.
 
 
 __[u]/virg(virgule_après_verbe_COD)__
     l(?:es?|a) ({w_2}(?:[ei]r|re)) ([A-ZÉÂÔÈ][\w-]+)  @@w,$
-    <<- morph(\1, ":Y", False) and morph(\2, ":M", False) and not morph(word(-1), ">à ", False, False)
+    <<- morph(\1, ":Y", False) and morph(\2, ":M", False) and not morph(word(-1), ">à/", False, False)
     -1>> \1,                                                                                        # Une virgule est probablement souhaitable.
 
 TEST: Tu vas les {{donner}} Rachel.
 TEST: Il va la {{tuer}} Paul.
 TEST: Cependant les promesses n’engagent que ceux qui les croient, comme aimait à le dire Jacques Chirac.
@@ -1964,11 +1967,11 @@
 
 !!!! A / À: accentuation la préposition en début de phrase                                          
 
 __<s]/typo(typo_À_début_phrase1)__
     ^ *(A) (?!t[’-](?:ils?|elles?|on))({w_2})  @@*,$
-    <<- morphex(\2, ":[GNAY]", ":(?:Q|3s)|>(?: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.
 __<s>/typo(typo_À_début_phrase2)__
     ^ *(A) [ldnms]’  @@*  <<- -1>> À                                                                # S’il s’agit de la préposition « à », il faut accentuer la majuscule.
 __<s>/typo(typo_À_début_phrase3)__
@@ -2002,15 +2005,15 @@
 !!!
 
 # mots grammaticaux
 __[i](d_dans)__
     dans
-    <<- not morph(word(-1), ":D.*:p|>[a-z]+ièmes ", False, False) =>> select(\0, ":R")
+    <<- not morph(word(-1), ":D.*:p|>[a-z]+ièmes/", False, False) =>> select(\0, ":R")
 
 __[i](d_ton_son)__
     (\w+) ([ts]on)  @@0,$
-    <<- morph(\1, ">(?:le|ce[st]?|ton|mon|son|quel(?:que|)s?|[nv]otre|un|leur|ledit|dudit) ") =>> exclude(\2, ":D")
+    <<- morph(\1, ">(?:le|ce[st]?|ton|mon|son|quel(?:que|)s?|[nv]otre|un|leur|ledit|dudit)/") =>> exclude(\2, ":D")
 
 # Pronoms le/la/les
 __[i](d_je_le_la_les)__
     je (l(?:e(?:ur|s|)|a)) @@$                  <<- not morph(word(-1), ":1s", False, False) =>> select(\1, ":Oo")
 __[i](d_tu_le_la_les)__
@@ -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
@@ -2204,11 +2207,11 @@
 
 TEST: __ocr__ on poirautait, {{cotte}} mariée n’arrivait pas à se décider.
 
 
 # Comme / Gomme
-__[s]/ocr(ocr_comme)__      Gomme <<- not morph(word(1), ">(?:et|o[uù]) ") ->> Comme                # Erreur de numérisation ?
+__[s]/ocr(ocr_comme)__      Gomme <<- not morph(word(1), ">(?:et|o[uù])/") ->> Comme                # Erreur de numérisation ?
 
 TEST: __ocr__ {{Gomme}} il était sage à cette époque-là !
 
 
 # Comment / Gomment
@@ -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
@@ -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.
@@ -2832,11 +2835,11 @@
 
 
 # avoir été
 __[i]/bs(bs_avoir_été_chez)__
     (?<!l’)({avoir}) été chez  @@0
-    <<- not re.search("(?i)^avoir$", \1) and morph(\1, ">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}}
@@ -2929,49 +2932,49 @@
 !!
 !!!! Pléonasmes                                                                                     
 !!
 !!
 
-__[i]/pleo(pleo_abolir)__               (abol\w+) (?:absolument|entièrement|compl[èé]tement|totalement) @@0 <<- morph(\1, ">abolir ", False) ->> \1         # Pléonasme.
-__[i]/pleo(pleo_acculer)__              (accul\w+) aux? pieds? du mur @@0 <<- morph(\1, ">acculer ", False) ->> \1                                          # Pléonasme.
-__[i]/pleo(pleo_achever)__              (ach[eè]v\w+) (?:absolument|entièrement|compl[èé]tement|totalement) @@0 <<- morph(\1, ">achever ", False) ->> \1    # Pléonasme.
+__[i]/pleo(pleo_abolir)__               (abol\w+) (?:absolument|entièrement|compl[èé]tement|totalement) @@0 <<- morph(\1, ">abolir/", False) ->> \1         # Pléonasme.
+__[i]/pleo(pleo_acculer)__              (accul\w+) aux? pieds? du mur @@0 <<- morph(\1, ">acculer/", False) ->> \1                                          # Pléonasme.
+__[i]/pleo(pleo_achever)__              (ach[eè]v\w+) (?:absolument|entièrement|compl[èé]tement|totalement) @@0 <<- morph(\1, ">achever/", False) ->> \1    # Pléonasme.
 __[i]/pleo(pleo_en_cours)__             actuellement en cours <<- not after(r" +de?\b") ->> en cours                                            # Pléonasme.
 __[i]/pleo(pleo_en_train_de)__          (actuellement en train) d(?:e(?! nuit)|’{w_2}) @@0 <<- -1>> en train                                    # Pléonasme.
 __[i]/pleo(pleo_ajouter)__              (ajout\w+) en plus @@0 <<- ->> \1                                                                       # Pléonasme.
 __[i]/pleo(pleo_apanage)__              (apanages?) exclusifs? @@0 <<- ->> \1                                                                   # Pléonasme.
 __[i]/pleo(pleo_applaudir)__            (applaudi\w+) des deux mains @@0 <<- ->> \1                                                             # Pléonasme.
 __[i]/pleo(pleo_aujourd_hui)__          au jour d’aujourd’hui <<- ->> aujourd’hui                                                               # Pléonasme.
-__[i]/pleo(pleo_avancer)__              (avan[cç]\w+) en avant @@0 <<- morph(\1, ">avancer ", False) ->> \1                                     # Pléonasme.
+__[i]/pleo(pleo_avancer)__              (avan[cç]\w+) en avant @@0 <<- morph(\1, ">avancer/", False) ->> \1                                     # Pléonasme.
 __[i]/pleo(pleo_s_avérer)__             s’av([éè]r\w+) vrai(e?s?) @@4,$ <<- ->> s’av\1 exact\2                                                  # Pléonasme.
 __[i]/pleo(pleo_avéré)__                (avérée?s?) vraie?s? @@0 <<- ->> \1                                                                     # Pléonasme.
 __[i]/pleo(pleo_avenir)__               avenir devant (?:lui|[mts]oi|eux|[nv]ous) <<- morph(word(-1), ":A|>un", False) ->> avenir               # Pléonasme.
 __[i]/pleo(pleo_bourrasque)__           (bourrasques?) de vent @@0 <<- ->> \1                                                                   # Pléonasme.
 __[i]/pleo(pleo_car_en_effet)__         car en effet <<- ->> car|en effet                                                                       # Pléonasme.
 __[i]/pleo(pleo_cirrhose)__             (cirrhoses?) du foie @@0 <<- ->> \1                                                                     # Pléonasme.
-__[i]/pleo(pleo_collaborer)__           (collabor\w+) ensemble @@0 <<- morph(\1, ">collaborer ", False) ->> \1                                  # Pléonasme.
+__[i]/pleo(pleo_collaborer)__           (collabor\w+) ensemble @@0 <<- morph(\1, ">collaborer/", False) ->> \1                                  # Pléonasme.
 __[i]/pleo(pleo_comme_par_exemple)__    comme par exemple <<- ->> comme|par exemple                                                             # Pléonasme.
-__[i]/pleo(pleo_comparer)__             (compar\w+) entre (?:eux|elles) @@0 <<- morph(\1, ">comparer ", False) ->> \1                           # Pléonasme.
-__[i]/pleo(pleo_contraindre)__          (contrai\w+) malgré (?:soi|eux|lui|moi|elle|toi) @@0 <<- morph(\1, ">contraindre ", False) ->> \1       # Pléonasme.
+__[i]/pleo(pleo_comparer)__             (compar\w+) entre (?:eux|elles) @@0 <<- morph(\1, ">comparer/", False) ->> \1                           # Pléonasme.
+__[i]/pleo(pleo_contraindre)__          (contrai\w+) malgré (?:soi|eux|lui|moi|elle|toi) @@0 <<- morph(\1, ">contraindre/", False) ->> \1       # Pléonasme.
 __[i]/pleo(pleo_descendre)__            (descend\w+) en bas(?! de) @@0 <<- ->> \1                                                               # Pléonasme.
 __[i]/pleo(pleo_dessiner)__             (dessin\w+) un dessin @@0 <<- ->> \1                                                                    # Pléonasme.
 __[i]/pleo(pleo_dorénavant)__           à (?:partir|compter) de dorénavant <<- ->> dorénavant|à partir de maintenant                            # Pléonasme.
 __[i]/pleo(pleo_donc_par_conséquent)__  donc par conséquent <<- ->> donc|par conséquent|c’est pourquoi                                          # Pléonasme.
-__[i]/pleo(pleo_enchevêtrer)__          (enchevêtr\w+) les uns dans les autres @@0 <<- morph(\1, ">enchevêtrer ", False) ->> \1                 # Pléonasme.
-__[i]/pleo(pleo_entraider)__            (entraid\w+) (?:mutuellement|les uns les autres) @@0 <<- morph(\1, ">entraider ", False) ->> \1         # Pléonasme.
+__[i]/pleo(pleo_enchevêtrer)__          (enchevêtr\w+) les uns dans les autres @@0 <<- morph(\1, ">enchevêtrer/", False) ->> \1                 # Pléonasme.
+__[i]/pleo(pleo_entraider)__            (entraid\w+) (?:mutuellement|les uns les autres) @@0 <<- morph(\1, ">entraider/", False) ->> \1         # Pléonasme.
 __[i]/pleo(pleo_entraide)__             (entraides?) mutuelles? @@0 <<- ->> \1                                                                  # Pléonasme.
 __[i]/pleo(pleo_erreur)__               (erreurs?) involontaires? @@0 <<- ->> \1                                                                # Pléonasme.
 __[i]/pleo(pleo_étape)__                (étapes?) intermédiaires? @@0 <<- ->> \1                                                                # Pléonasme.
 __[i]/pleo(pleo_hasard)__               (hasards?) imprévus? @@0 <<- ->> \1                                                                     # Pléonasme.
 __[i]/pleo(pleo_hémorragie)__           (hémorragies?) de sang @@0 <<- ->> \1                                                                   # Pléonasme.
-__[i]/pleo(pleo_joindre)__              (join\w+) ensemble @@0 <<- morph(\1, ">joindre ") ->> \1|mettre ensemble                                # Pléonasme.
+__[i]/pleo(pleo_joindre)__              (join\w+) ensemble @@0 <<- morph(\1, ">joindre/") ->> \1|mettre ensemble                                # Pléonasme.
 __[i]/pleo(pleo_lever)__                lever debout <<- ->> lever                                                                              # Pléonasme.
 __[i]/pleo(pleo_mais_qqch)__            mais (cependant|pourtant|toutefois) @@5 <<- ->> mais|cependant|pourtant|toutefois                       # Pléonasme.
 __[i]/pleo(pleo_marche)__               (marches?) à pieds? @@0 <<- ->> \1                                                                      # Pléonasme.
 __[i]/pleo(pleo_méandre)__              (méandres?) sinueux @@0 <<- ->> \1                                                                      # Pléonasme.
 __[i]/pleo(pleo_media)__                (m[eé]dias?) d’informations? @@0 <<- ->> \1                                                             # Pléonasme.
 __[i]/pleo(pleo_monopole)__             (monopoles?) exclusifs? @@0 <<- ->> \1                                                                  # Pléonasme.
-__[i]/pleo(pleo_monter)__               (mont\w+) en haut(?! d[eu’]) @@0 <<- morph(\1, ">monter ", False) ->> \1                                # Pléonasme.
+__[i]/pleo(pleo_monter)__               (mont\w+) en haut(?! d[eu’]) @@0 <<- morph(\1, ">monter/", False) ->> \1                                # Pléonasme.
 __[i]/pleo(pleo_opportunité)__          (opportunités?) à saisir @@0 <<- ->> \1                                                                 # Pléonasme.
 __[i]/pleo(pleo_orage)__                (orages?) électriques? @@0 <<- ->> \1                                                                   # Pléonasme.
 __[i]/pleo(pleo_jumelles)__             paires? de jumelles? <<- ->> jumelles                                                                   # Pléonasme.
 __[i]/pleo(pleo_panacée)__              (panacées?) universelles? @@0 <<- ->> \1|remède universel                                               # Pléonasme.
 __[i]/pleo(pleo_perspective)__          (perspectives?) d’avenir @@0 <<- ->> \1                                                                 # Pléonasme.
@@ -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
@@ -3282,19 +3285,19 @@
 TEST: plus d’une sont parties aussi vite qu’elles étaient venues
 
 
 __[i]/conf(conf_il_on_pas_verbe)__
     (?<!t’)(?:il|on) (?:l’|l(?:es?|a|eur|ui) +|[nv]ous +|)({w_2}) @@$
-    <<- morphex(\1, ":", ":(?:[123][sp]|O[onw]|X)|ou ") and morphex(word(-1), ":", ":3s", True)
+    <<- morphex(\1, ":", ":(?:[123][sp]|O[onw]|X)|>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)__
     (?<!t’)ils (?:l’|l(?:es?|a|eur|ui) +|[nv]ous +|)({w_2}) @@$
-    <<- morphex(\1, ":", ":(?:[123][sp]|O[onw]|X)|ou ") and morphex(word(-1), ":", ":3p", True)
+    <<- morphex(\1, ":", ":(?:[123][sp]|O[onw]|X)|>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}}
 
@@ -3340,11 +3343,11 @@
 
  
 __[i]/conf(conf_très_verbe)__
     très +(?!envie)({w_2})  @@$
     <<- morphex(\1, ":(?:Y|[123][sp])", ":[AQW]") -1>> =suggSimil(\1, ":[AW]", True)                # Incohérence avec « très » : « \1 » n’est ni un adjectif, ni un participe passé, ni un adverbe.
-    <<- morph(\1, ">jeûne ", False) -1>> =\1.replace("û", "u")                                      # Confusion. Le jeûne est une privation de nourriture.|https://fr.wiktionary.org/wiki/jeune
+    <<- morph(\1, ">jeûne/", False) -1>> =\1.replace("û", "u")                                      # Confusion. Le jeûne est une privation de nourriture.|https://fr.wiktionary.org/wiki/jeune
 
 TEST: Il est très {{cite}}.
 TEST: très {{suivit}} par ce détective
 TEST: il était très {{habille}}
 TEST: Très {{jeûne}}, elle a su qu’elle ne voulait pas d’une vie ordinaire.
@@ -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 :
@@ -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,21 +3687,21 @@
 __[i]/conf(conf_ceux_ce_être)__
     (ceux) (?:ne |)(?:sont|serai(?:en|)[ts]?|f[uû](?:ren|)t|n’(?!ayant|étant)\w+) @@0
     <<- -1>> ce                                                             # Confusion.|http://www.intellego.fr/soutien-scolaire-6eme/aide-scolaire-francais/ce-ceux-ou-se/3829
 __[s]/conf(conf_ce_ne_être_doit)__
     ([sS]e) n(?:e |’)({être}|d[eouû]\w+|p[oeuû]\w+)  @@0,$
-    <<- morph(\2, ">(?:être|pouvoir|devoir) .*:3s", False)
+    <<- morph(\2, ">(?:être|pouvoir|devoir)/.*:3s", False)
     -1>> ce                                                                 # Confusion probable.|http://bdl.oqlf.gouv.qc.ca/bdl/gabarit_bdl.asp?id=2440
 __[i]/conf(conf_ce_ne)__
     (ceux) ne ({w_2}) @@0,$
     <<- morphex(\2, ":[123]s", ":P") -1>> ce                                # Confusion.|http://www.intellego.fr/soutien-scolaire-6eme/aide-scolaire-francais/ce-ceux-ou-se/3829
 __[i]/conf(conf_ce_nom1)__
     (se) ({w1}) @@0,3
-    <<- morphex(\2, ":[NAQ]", ":([123][sp]|Y|P|Q)|>l[ea]? ") -1>> ce        # Confusion. Ce chien, ce chat… Se demander, se croire…
+    <<- morphex(\2, ":[NAQ]", ":([123][sp]|Y|P|Q)|>l[ea]?/") -1>> ce        # Confusion. Ce chien, ce chat… Se demander, se croire…
 __[i]/conf(conf_ce_nom2)__
     (ceux) (?!l[aà] |qu[ie]? )({w_2}) @@0,$
-    <<- morphex(\2, ":N.*:s", ":(?:A.*:[pi]|P|R)|>autour ") -1>> ce         # Confusion probable.|http://www.intellego.fr/soutien-scolaire-6eme/aide-scolaire-francais/ce-ceux-ou-se/3829
+    <<- morphex(\2, ":N.*:s", ":(?:A.*:[pi]|P|R)|>autour/") -1>> ce         # Confusion probable.|http://www.intellego.fr/soutien-scolaire-6eme/aide-scolaire-francais/ce-ceux-ou-se/3829
 
 TEST: il ne {{ce}} compte pas parmi eux
 TEST: il ne {{ç’}}avançait jamais sans avoir pesé toutes les conséquences
 TEST: {{Se}} seraient des histoires.
 TEST: {{se}} seraient des jours heureux.
@@ -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.
@@ -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.
 
@@ -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,14 +4238,14 @@
 
 
 # prêt / près / pré
 __[i]/conf(conf_prêt_à)__
     (près) à ({w_2})  @@0,$
-    <<- not before("(?i)(?:peu|de|au plus) $") and morph(\2, ":Y|>(?:tout|les?|la) ") -1>> prêt|prêts       # Confusion. Être près de (faire) quelque chose. Prêt à faire quelque chose.
+    <<- not before("(?i)(?:peu|de|au plus) $") and morph(\2, ":Y|>(?:tout|les?|la)/") -1>> prêt|prêts       # Confusion. Être près de (faire) quelque chose. Prêt à faire quelque chose.
 __[i]/conf(conf_près_de)__
     (prêts?) d(?:e +|’)({w_1}) @@0,$
-    <<- morph(\2, ":(?:Y|M[12P])|>(?:en|y|les?) ", False) -1>> près                                 # Confusion. Être près de (faire) quelque chose. Prêt à faire quelque chose.
+    <<- morph(\2, ":(?:Y|M[12P])|>(?:en|y|les?)/", False) -1>> près                                 # Confusion. Être près de (faire) quelque chose. Prêt à faire quelque chose.
 __[i]/conf(conf_près)__         de(?: plus|puis) (prêts?)  @@$ <<- -1>> près                        # Confusion. Être prêt(e) à faire quelque chose. Être près de quelque chose.
 __[i]/conf(conf_très_près)__    très (pr(?:êt|é)s?) @@$ <<- -1>> près                               # Confusion probable. Pour évoquer la proximité, utilisez :
 
 TEST: ils se sont approchés très {{prêts}}.
 TEST: Je suis si {{prêt}} d’y arriver.
@@ -4251,11 +4254,11 @@
 
 
 # quand / quant / qu’en
 __[i]/conf(conf_quant_à)__
     (?<![dD]e )(quand) (?:à|aux?)  @@0
-    <<- not morph(word(-1), ">(?:arriver|venir|à|revenir|partir|aller) ")
+    <<- not morph(word(-1), ">(?:arriver|venir|à|revenir|partir|aller)/")
         and not(\0.endswith("à") and after("^ +[mts]on tour[, ]")) -1>> quant                           # Confusion probable. Quand = à quel moment. Quant à = à propos de.
 __[i]/conf(conf_quand1)__   quant(?! à| aux?| est[ -]il d(?:es?|u) ) <<- ->> quand                  # Confusion. Quand = à quel moment. Quant à = à propos de.
 __[i]/conf(conf_qu_en1)__   (quan[dt]) est[ -]il d(?:es?|u) @@0 <<- -1>> qu’en                      # Confusion. Ce qu’il en est de… → Qu’en est-il de… ?
 __[i]/conf(conf_qu_en2)__   (quan[dt]) ({w_2}ant) @@0,$ <<- morph(\2, ":P", False) -1>> qu’en       # Confusion probable.
 __[i]/conf(conf_quand2)__
@@ -4292,12 +4295,12 @@
 TEST: {{qu’elle}} emmerdeuse.
 
 
 __[i]/conf(conf_qu_elle_verbe)__
     (quelles?) +({w_1})  @@0,$
-    <<- \2.islower() and (morphex(\2, ":V|>(?:ne?|me?|te?|se?|[nv]ous|l(?:e|a|es|ui|leur|)|en|y) ", ":[NA].*:[fe]|>(?:plus|moins)") or \2 == "t" or \2 == "s")
-        and not (morph(\2, ">(?:pouvoir|devoir|en)", False) and morph(word(1), ":V0e", False)) >>>
+    <<- \2.islower() and (morphex(\2, ":V|>(?:ne?|me?|te?|se?|[nv]ous|l(?:e|a|es|ui|leur|)|en|y)/", ":[NA].*:[fe]|>(?:plus|moins)") or \2 == "t" or \2 == "s")
+        and not (morph(\2, ">(?:pouvoir|devoir|en)/", False) and morph(word(1), ":V0e", False)) >>>
     <<- \1.endswith("e") and not morph(\2, ":V0e", False) 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) >>>
@@ -4356,12 +4359,12 @@
 __[i]/conf(conf_me_te_se_son)!6__
     [mts]e (son)  @@3
     <<- -1>> sont                   # Confusion : “son” est un déterminant ou un nom masculin. Le verbe “être” à la 3ᵉ personne du pluriel s’écrit “sont”.
 __[i]/conf(conf_son_qqch)__
     (sont) ({w_2})  @@0,$
-    <<- morphex(\2, ":[NA].*:[me]:s|>[aeéiîou].* :[NA].*:f:s", ":[GW]")
-        and morphex(word(-1), ":V|>(?:à|avec|chez|dès|contre|devant|derrière|en|par|pour|sans|sur) ", ":[NA].*:[pi]|>(?:ils|elles|vous|nous|leur|lui|[nmts]e) ", True)
+    <<- morphex(\2, ":[NA].*:[me]:s|>[aeéiîou].*/:[NA].*:f:s", ":[GW]")
+        and morphex(word(-1), ":V|>(?:à|avec|chez|dès|contre|devant|derrière|en|par|pour|sans|sur)/", ":[NA].*:[pi]|>(?:ils|elles|vous|nous|leur|lui|[nmts]e)/", True)
         and not before(r"(?i)\bce que? |[mts]’en +$")
     -1>> son                        # Confusion : “sont” est le verbe “être” à la 3ᵉ personne du pluriel. Pour le déterminant, écrivez “son”.
 __[i]/conf(conf_qui_sont_les)__
     (?:qu[ie]|comment|pourquoi) +(son) @@$
     <<- morph(word(1), ":[DR]", False, True) -1>> sont      # Confusion probable : “son” est un déterminant ou un nom masculin. Le verbe “être” à la 3ᵉ personne du pluriel s’écrit “sont”.
@@ -4393,10 +4396,38 @@
     <<- 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
 
 TEST: Je suis {{sur}} de Patrick.
 
+__[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.
+
 
 # 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.
@@ -4437,11 +4468,11 @@
 __[i]/conf(conf_sur_la_bonne_voie)__        sur la bonne (voix) @@$     <<- -1>> voie               # Confusion.|http://fr.wiktionary.org/wiki/voix
 __[i]/conf(conf_en_voie_de)__
     en (voix) d(?:e (?:développement|disparition|guérison|résorption)|’(?:acquisition|achèvement|extinction|obtention))  @@3
     <<- -1>> voie                                                                                   # Confusion.|http://fr.wiktionary.org/wiki/voie
 __[i]/conf(conf_ouvrir_la_voix)__
-    (ouv\w+) +la (voix) (?:à|aux?)  @@0,w <<- morph(\1, ">ouvrir ", False) -2>> voie                # Confusion.|http://fr.wiktionary.org/wiki/voie
+    (ouv\w+) +la (voix) (?:à|aux?)  @@0,w <<- morph(\1, ">ouvrir/", False) -2>> voie                # Confusion.|http://fr.wiktionary.org/wiki/voie
 __[i]/conf(conf_par_voie_de_conséquence)__  par (voix) de conséquence   @@4 <<- -1>> voie           # Confusion.|http://fr.wiktionary.org/wiki/voie
 __[i]/conf(conf_voie_adj)__
     (voix) (?:abdominale|anale|biliaire|carrossable|communale|express|interdite|intramusculaire|intraveineuse|piétonne|principale|prioritaire|privée|publique|déserte|romaine|appienne|flaminienne|ferrée|ferroviaire|lactée|lacrymale|aérienne|maritime|fluviale|terrestre|navigable|détournée|déviée|buccale|digestive|urinaire|respiratoire|parallèle|administrative|diplomatique|gouvernementale|législative|hiérarchique|rectiligne|sinueuse|souterraine|urbaine)s? @@0
     <<- -1>> voie                                                                                   # Confusion.|http://fr.wiktionary.org/wiki/voie
 
@@ -4454,17 +4485,17 @@
 
 # voire / voir
 __[i]/conf(conf_voir_voire)__
     (voir) ({w_2}) @@0,$
     <<- not re.search("^(?:grand|petit|rouge)$", \2) and morphex(\2, ":A", ":[NGM]") and not \2.istitle()
-        and not before(r"(?i)\b[ndmts](?:e |’(?:en |y ))(?:pas |jamais |) *$") and not morph(word(-1), ":O[os]|>(?:[ndmts]e|falloir|pouvoir|savoir|de) ", False)
+        and not before(r"(?i)\b[ndmts](?:e |’(?:en |y ))(?:pas |jamais |) *$") and not morph(word(-1), ":O[os]|>(?:[ndmts]e|falloir|pouvoir|savoir|de)/", False)
     -1>> voire 
     # Confusion probable : “voir” est un verbe concernant la perception visuelle. Pour signifier “et même possiblement”, écrivez :|https://fr.wiktionary.org/wiki/voire
 
 __[i]/conf(conf_voire_voir)__
     voire
-    <<- morph(word(-1), ":Cs|>(?:ni|et|sans|pour|falloir|[pv]ouvoir|aller) ", True, False) ->> voir
+    <<- morph(word(-1), ":Cs|>(?:ni|et|sans|pour|falloir|[pv]ouvoir|aller)/", True, False) ->> voir
     # Confusion probable : “voire” signifie “et même possiblement”. Pour le verbe, écrivez “voir”.|https://fr.wiktionary.org/wiki/voire
 
 TEST: Elles sont fatiguées, {{voir}} épuisées.
 TEST: Ce serait pour aider, ainsi que {{voire}} l’avancement du projet.
 TEST: Elles vont voir rouge en apprenant cet échec.
@@ -4496,11 +4527,11 @@
     (j’(?:en +|y +|))({w_1})  @@0,$
     <<- morphex(\2, ":", ":(?:[123][sp]|O[onw])")
     -2>> =suggSimil(\2, ":1s", False)                                                               # Incohérence avec « \1 » : « \2 » devrait être un verbe.
 __[i]/conf(conf_ne_qqch)__
     (n(?:e +|’))({w_1})  @@0,$
-    <<- morphex(\2, ":", ":(?:[123][sp]|Y|P|O[onw]|X)|>(?:[lmtsn]|surtout|guère|presque|même|tout|parfois|vraiment|réellement|justement) ") and not re.search("(?i)-(?:ils?|elles?|[nv]ous|je|tu|on|ce)$", \2)
+    <<- morphex(\2, ":", ":(?:[123][sp]|Y|P|O[onw]|X)|>(?:[lmtsn]|surtout|guère|presque|même|tout|parfois|vraiment|réellement|justement)/") and not re.search("(?i)-(?:ils?|elles?|[nv]ous|je|tu|on|ce)$", \2)
     -2>> =suggSimil(\2, ":(?:[123][sp]|Oo|Y)", False)                                               # Incohérence avec « \1 » : « \2 » devrait être un verbe ou un pronom personnel objet.
 __[i]/conf(conf_n_y_en_qqch)__
     (n’(?:en|y)) ({w_1})  @@0,$
     <<- morphex(\2, ":", ":(?:[123][sp]|Y|P|O[onw]|X)") and not re.search("(?i)-(?:ils?|elles?|[nv]ous|je|tu|on|ce)$", \2)
     -2>> =suggSimil(\2, ":(?:[123][sp]|Y)", False)                                                  # Incohérence avec « \1 » : « \2 » devrait être un verbe.
@@ -4509,27 +4540,27 @@
     <<- morphex(\2, ":", ":(?:[123][sp]|Y|P|O[onw]|X)") and not re.search("(?i)-(?:ils?|elles?|[nv]ous|je|tu|on|ce)$", \2)
     -2>> =suggSimil(\2, ":(?:[123][sp]|Y)", False)                                                  # Incohérence avec « \1 » : « \2 » devrait être un verbe.
 __[i]/conf(conf_me_te_se_qqch)__
     ([mts]e +(?:les? |la |l’|))(?!voi(?:là|ci))({w_1})  @@0,$
     <<- not re.search("(?i)^se que?", \0)
-        and morphex(\2, ":", ":(?:[123][sp]|Y|P|Oo)|>[lmts] ") and not re.search("(?i)-(?:ils?|elles?|[nv]ous|je|tu|on|ce)$", \2)
+        and morphex(\2, ":", ":(?:[123][sp]|Y|P|Oo)|>[lmts]/") and not re.search("(?i)-(?:ils?|elles?|[nv]ous|je|tu|on|ce)$", \2)
     -2>> =suggSimil(\2, ":(?:[123][sp]|Oo|Y)", False)                                               # Incohérence avec « \1 » : « \2 » devrait être un verbe ou un pronom personnel objet.
 __[i]/conf(conf_m_t_s_y_en_qqch)__
     ([mts]’(?:en|y)) (?!voilà)({w_1})  @@0,$
-    <<- morphex(\2, ":", ":(?:[123][sp]|Y|P|X|Oo)|rien ") and not re.search("(?i)-(?:ils?|elles?|[nv]ous|je|tu|on|ce)$", \2)
+    <<- morphex(\2, ":", ":(?:[123][sp]|Y|P|X|Oo)|>rien/") and not re.search("(?i)-(?:ils?|elles?|[nv]ous|je|tu|on|ce)$", \2)
     -2>> =suggSimil(\2, ":(?:[123][sp]|Y)", False)                                                  # Incohérence avec « \1 » : « \2 » devrait être un verbe.
 __[i]/conf(conf_m_s_qqch)__
     ([ms]’)({w_1})  @@0,2
-    <<- morphex(\2, ":", ":(?:[123][sp]|Y|P)|>(?:en|y|ils?) ") and not re.search("(?i)-(?:ils?|elles?|[nv]ous|je|tu|on|ce)$", \2)
+    <<- morphex(\2, ":", ":(?:[123][sp]|Y|P)|>(?:en|y|ils?)/") and not re.search("(?i)-(?:ils?|elles?|[nv]ous|je|tu|on|ce)$", \2)
     -2>> =suggSimil(\2, ":(?:[123][sp]|Y)", False)                                                  # Incohérence avec « \1 » : « \2 » devrait être un verbe.
 __[i]/conf(conf_t_qqch)__
     (t’)({w_1})  @@0,2
-    <<- morphex(\2, ":", ":(?:[123][sp]|Y|P)|>(?:en|y|ils?|elles?) ") and not re.search("(?i)-(?:ils?|elles?|[nv]ous|je|tu|on|ce)$", \2)
+    <<- morphex(\2, ":", ":(?:[123][sp]|Y|P)|>(?:en|y|ils?|elles?)/") and not re.search("(?i)-(?:ils?|elles?|[nv]ous|je|tu|on|ce)$", \2)
     -2>> =suggSimil(\2, ":(?:[123][sp]|Y)", False)                                                  # Incohérence avec « \1 » : « \2 » devrait être un verbe.
 __[i]/conf(conf_c_ç_qqch)__
     ([cç]’)({w_1})  @@0,2
-    <<- morphex(\2, ":", ":[123][sp]|>(?:en|y|que?) ") and not re.search("(?i)-(?:ils?|elles?|[nv]ous|je|tu|on|dire)$", \2)
+    <<- morphex(\2, ":", ":[123][sp]|>(?:en|y|que?)/") and not re.search("(?i)-(?:ils?|elles?|[nv]ous|je|tu|on|dire)$", \2)
     -2>> =suggSimil(\2, ":3s", False)                                                               # Incohérence avec « \1 » : « \2 » devrait être un verbe.
 
 TEST: ne l’{{oubli}} pas
 TEST: elle ne la {{croix}} pas
 TEST: ils me les {{laissés}}.
@@ -4554,53 +4585,162 @@
 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.
 
+
+#__[i](p_notre_père_qui_es_au_cieux)__   notre père (qui est? aux cieux) @@11 <<- ~1>> *
+
+
+@@@@
+@@@@
+@@@@
+@@@@
+@@@@GRAPH: graphe1                                                                                  
+@@@@
+@@@@
+@@@@
+@@@@
+
+__p_notre_père_qui_es_au_cieux__
+    notre père qui [es|est] aux cieux
+        <<- ~4>> !
+        <<- ~3:0>> _
+
 
 !!
 !!
 !!!! Formes verbales sans sujet                                                                     
 !!
 !!
+
+__tag_sujets__
+    [je|j’]
+    [moi|moi-même] qui
+    [moi|moi-même] [seul|seule]
+        <<- />> 1s
+
+    tu
+    t’  @: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$)
+        <<- 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
+        <<- 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
+        <<- 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$)
+        <<- 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
+        <<- 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
+        <<- 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
+        <<- 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$)
+        <<- 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)
+        <<- 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)
+        <<- 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)
+        <<- 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)
+        <<- 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)
+        <<- 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)
+        <<- 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 +4751,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
@@ -4640,15 +4777,29 @@
 !!
 !!!! 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]
+        <<- -3>> prévu                                                                              # Invariable. Implicitement, \1 que ce qui était prévu.
+
+    [plus|moins|aussi]  **  que  [prévue|prévus|prévues]
+        <<- -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]
+        <<- -5>> prévu                                                                              # Invariable. Implicitement, \1 \2 \3 que ce qui était prévu.
+
+    comme [annoncés|annoncée|annoncées]
+        <<- -2>> annoncé                                                                            # Invariable. Implicitement, comme ce qui était annoncé.
+
+    comme [convenus|convenue|convenues]
+        <<- -2>> convenu                                                                            # Invariable. Implicitement, comme ce qui était convenu.
+
+    comme [prévue|prévus|prévues]
+        <<- -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
@@ -4664,97 +4815,129 @@
 !!
 !!!! 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__
+    [<start>|,]  tout [deux|trois]
+        <<- -2>> tous                                                                               # Locution pronominale : « tous deux ».|https://fr.wiktionary.org/wiki/tous_deux
+
+    tout [mes|tes|ses|ces|nos|vos|leurs|ceux|celles]
+        <<- not morph(<1, ">(?:d[eu]|avant|après|malgré)/") -1>> tous                               # Erreur d’accord probable avec « \2 ».
+
+    tout les @:¬:(?:3s|Oo)
+        <<- 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__
+    [<start>|,]     tous    [des|mes|tes|ses|ces]   @:[NA].*:f¬:[me]
+    [<start>|,]     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__
+    [<start>|,]     toutes  [des|mes|tes|ses|ces]   @:[NA].*:m¬:[fe]
+    [<start>|,]     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__
+    [<start>|,] 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__
+    [<start>|,] 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__
+    [<start>|,] 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__
+    [<start>|,] 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
@@ -4767,64 +4950,126 @@
 !!
 !!!! 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__
+    [<start>|,]  je             [le|la|l’|les|me|m’|te|t’|se|s’|nous|vous|lui|leur]     @:1s¬:(?:Oo|X)  [pas|rien|jamais|guère|point]
+    [<start>|,]  tu             [le|la|l’|les|me|m’|te|t’|se|s’|nous|vous|lui|leur]     @:2s¬:(?:Oo|X)  [pas|rien|jamais|guère|point]
+    [<start>|,]  [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]
+    [<start>|,]  nous           [le|la|l’|les|me|m’|te|t’|se|s’|nous|vous|lui|leur]     @:1p¬:(?:Oo|X)  [pas|rien|jamais|guère|point]
+    [<start>|,]  vous           [le|la|l’|les|me|m’|te|t’|se|s’|nous|vous|lui|leur]     @:2p¬:(?:Oo|X)  [pas|rien|jamais|guère|point]
+    [<start>|,]  [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.
+
+    [<start>|,]  [je|j’]        [en|y]                                                  @:1s¬:(?:Oo|X)  [pas|rien|jamais|guère|point]
+    [<start>|,]  tu             [en|y]                                                  @:2s¬:(?:Oo|X)  [pas|rien|jamais|guère|point]
+    [<start>|,]  [il|elle|on]   [en|y]                                                  @:3s¬:(?:Oo|X)  [pas|rien|jamais|guère|point]
+    [<start>|,]  nous           [en|y]                                                  @:1p¬:(?:Oo|X)  [pas|rien|jamais|guère|point]
+    [<start>|,]  vous           [en|y]                                                  @:2p¬:(?:Oo|X)  [pas|rien|jamais|guère|point]
+    [<start>|,]  [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.
+
+    [<start>|,]  je             [me|m’|te|t’|se|s’|nous|vous]   [le|la|l’|les|en|y]     @:1s¬:(?:Oo|X)  [pas|rien|jamais|guère|point]
+    [<start>|,]  tu             [me|m’|te|t’|se|s’|nous|vous]   [le|la|l’|les|en|y]     @:2s¬:(?:Oo|X)  [pas|rien|jamais|guère|point]
+    [<start>|,]  [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]
+    [<start>|,]  nous           [me|m’|te|t’|se|s’|nous|vous]   [le|la|l’|les|en|y]     @:1p¬:(?:Oo|X)  [pas|rien|jamais|guère|point]
+    [<start>|,]  vous           [me|m’|te|t’|se|s’|nous|vous]   [le|la|l’|les|en|y]     @:2p¬:(?:Oo|X)  [pas|rien|jamais|guère|point]
+    [<start>|,]  [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]
+    [<start>|,]  je             [le|la|l’|les]                  [lui|leur|en|y]         @:1s¬:(?:Oo|X)  [pas|rien|jamais|guère|point]
+    [<start>|,]  tu             [le|la|l’|les]                  [lui|leur|en|y]         @:2s¬:(?:Oo|X)  [pas|rien|jamais|guère|point]
+    [<start>|,]  [il|elle|on]   [le|la|l’|les]                  [lui|leur|en|y]         @:3s¬:(?:Oo|X)  [pas|rien|jamais|guère|point]
+    [<start>|,]  nous           [le|la|l’|les]                  [lui|leur|en|y]         @:1p¬:(?:Oo|X)  [pas|rien|jamais|guère|point]
+    [<start>|,]  vous           [le|la|l’|les]                  [lui|leur|en|y]         @:2p¬:(?:Oo|X)  [pas|rien|jamais|guère|point]
+    [<start>|,]  [ils|elles]    [le|la|l’|les]                  [lui|leur|en|y]         @:3p¬:(?:Oo|X)  [pas|rien|jamais|guère|point]
+    [<start>|,]  je             [lui|leur]                      en                      @:1s¬:(?:Oo|X)  [pas|rien|jamais|guère|point]
+    [<start>|,]  tu             [lui|leur]                      en                      @:2s¬:(?:Oo|X)  [pas|rien|jamais|guère|point]
+    [<start>|,]  [il|elle|on]   [lui|leur]                      en                      @:3s¬:(?:Oo|X)  [pas|rien|jamais|guère|point]
+    [<start>|,]  nous           [lui|leur]                      en                      @:1p¬:(?:Oo|X)  [pas|rien|jamais|guère|point]
+    [<start>|,]  vous           [lui|leur]                      en                      @:2p¬:(?:Oo|X)  [pas|rien|jamais|guère|point]
+    [<start>|,]  [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.
+
+    [<start>|,]  [je|j’]        @>[aeéiouœ].*:1s¬:(?:Oo|X)              [pas|rien|jamais|guère|point]
+    [<start>|,]  tu             @>[aeéiouœ].*:2s¬:(?:Oo|X)              [pas|rien|jamais|guère|point]
+    [<start>|,]  [il|elle|on]   @>[aeéiouœ].*:3s¬:(?:Oo|X)              [pas|rien|jamais|guère|point]
+    [<start>|,]  nous           @>[aeéiouœ].*:1p¬:(?:Oo|X)              [pas|rien|jamais|guère|point]
+    [<start>|,]  vous           @>[aeéiouœ].*:2p¬:(?:Oo|X)              [pas|rien|jamais|guère|point]
+    [<start>|,]  [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.
+
+    [<start>|,]  je             @>[bcdfgjklmnpqrstvwxz].*:1s¬:(?:Oo|X)  [pas|rien|jamais|guère|point]
+    [<start>|,]  tu             @>[bcdfgjklmnpqrstvwxz].*:2s¬:(?:Oo|X)  [pas|rien|jamais|guère|point]
+    [<start>|,]  [il|elle|on]   @>[bcdfgjklmnpqrstvwxz].*:3s¬:(?:Oo|X)  [pas|rien|jamais|guère|point]
+    [<start>|,]  nous           @>[bcdfgjklmnpqrstvwxz].*:1p¬:(?:Oo|X)  [pas|rien|jamais|guère|point]
+    [<start>|,]  vous           @>[bcdfgjklmnpqrstvwxz].*:2p¬:(?:Oo|X)  [pas|rien|jamais|guère|point]
+    [<start>|,]  [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                                                                                      
 !!
 !!
 
-__[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 [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 [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 [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 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 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 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 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 [pas|rien|guère|point]                   [m’|t’|s’|nous|vous|les|lui|leur|l’]  [en|y]            (@:[VNA]¬:(?:Y|W|X|O[ow])|>que?/)
+    ne [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 [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 jamais ?[rien|plus|trop|beaucoup]¿       [m’|t’|s’|nous|vous|les|lui|leur|l’]  [en|y]            (@:[VNA]¬:(?:Y|W|X|O[ow])|>que?/)
+    ne jamais ?[rien|plus]¿ non plus            [m’|t’|s’|nous|vous|les|lui|leur|l’]  [en|y]            (@:[VNA]¬:(?:Y|W|X|O[ow])|>que?/)
+    ne 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 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 [pas|rien|guère|point]                   [me|te|nous|vous] [le|la|les]                           (@:[VNA]¬:(?:Y|W|X|O[ow])|>que?/)
+    ne [pas|rien|guère|point] [trop|beaucoup]   [me|te|nous|vous] [le|la|les]                           (@:[VNA]¬:(?:Y|W|X|O[ow])|>que?/)
+    ne [pas|rien|guère|point] non plus          [me|te|nous|vous] [le|la|les]                           (@:[VNA]¬:(?:Y|W|X|O[ow])|>que?/)
+    ne jamais ?[rien|plus|trop|beaucoup]¿       [me|te|nous|vous] [le|la|les]                           (@:[VNA]¬:(?:Y|W|X|O[ow])|>que?/)
+    ne jamais ?[rien|plus]¿ non plus            [me|te|nous|vous] [le|la|les]                           (@:[VNA]¬:(?:Y|W|X|O[ow])|>que?/)
+    ne plus ?[jamais|rien|guère|trop|beaucoup]¿ [me|te|nous|vous] [le|la|les]                           (@:[VNA]¬:(?:Y|W|X|O[ow])|>que?/)
+    ne plus ?[jamais|rien|guère]¿ non plus      [me|te|nous|vous] [le|la|les]                           (@:[VNA]¬:(?:Y|W|X|O[ow])|>que?/)
+    ne [pas|rien|guère|point]                   [le|la|les] [lui|leur]                                  (@:[VNA]¬:(?:Y|W|X|O[ow])|>que?/)
+    ne [pas|rien|guère|point] [trop|beaucoup]   [le|la|les] [lui|leur]                                  (@:[VNA]¬:(?:Y|W|X|O[ow])|>que?/)
+    ne [pas|rien|guère|point] non plus          [le|la|les] [lui|leur]                                  (@:[VNA]¬:(?:Y|W|X|O[ow])|>que?/)
+    ne jamais ?[rien|plus|trop|beaucoup]¿       [le|la|les] [lui|leur]                                  (@:[VNA]¬:(?:Y|W|X|O[ow])|>que?/)
+    ne jamais ?[rien|plus]¿ non plus            [le|la|les] [lui|leur]                                  (@:[VNA]¬:(?:Y|W|X|O[ow])|>que?/)
+    ne plus ?[jamais|rien|guère|trop|beaucoup]¿ [le|la|les] [lui|leur]                                  (@:[VNA]¬:(?:Y|W|X|O[ow])|>que?/)
+    ne 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.
+
+    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,15 +5076,15 @@
 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__
+    [<start>|,]  [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
 
 
 
 !!!
@@ -4846,286 +5091,1155 @@
 !!!
 !!! 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}  [,|<end>]
+    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_loc_de_début_phrase)__  ^ *(?:et(?: puis|)|puis|car|mais|or donc|m’est avis que) <<- ~>> *
-__[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_de_plus)__              ^ *de plus <<- ~>> *
-__[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__
+    <start>  car
+    <start>  de plus
+    <start>  et  ?puis¿
+    <start>  mais
+    <start>  m’  est   avis  [que|qu’|qu]
+    <start>  or  donc
+    <start>  puis
+    [<start>|,] 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           [<end>|,|@:[VXG]¬>qui]
+        <<- ~1:6>> *
+
+    [après|avant|avec|pour|contre|sans|envers|chez|d’|D’|malgré|selon] on ne sait [qui|quoi]        [<end>|,|@:[VXG]¬>qui]
+        <<- ~1:5>> *
+
+    [après|avant|avec|pour|contre|sans|envers|chez|de|en|malgré|selon] tout un chacun               [<end>|,|@:[VXG]¬>qui]
+    [après|avant|avec|pour|contre|sans|envers|chez|de|en|malgré|selon] tout le monde                [<end>|,|@:[VXG]¬>qui]
+        <<- ~1:4>> *
+
+    [après|avant|avec|pour|contre|sans|envers|chez|de|en|malgré] tout ça                            [<end>|,|@:[VXG]¬>qui]
+    [après|avant|avec|pour|contre|sans|envers|chez|de|en|malgré|selon] [vous|nous] autres           [<end>|,|@:[VXG]¬>qui]
+        <<- ~1:3>> *
+
+    [après|avant|avec|pour|contre|sans|envers|chez|de|d’|D’|en|malgré|selon]  [autrui|quelqu’un|quelqu’une]     [<end>|,|@:[VXG]¬>qui]
+    [après|avant|avec|envers|chez|malgré|selon]                               {pronom_obj}                      [<end>|,|@:[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]    [<end>|,|@:[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]    [<end>|,|@:[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]  [<end>|,|@:[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) <<- ~>> *
+
+
+__[i](p_qui_loc_tps)__          qui (ce (?:jour|matin|après-midi|soir)-là|cette (?:nuit|matinée|soirée)-là) @@4 <<- ~1>> *    
+
+
+    
+
 
 TEST: ils vont et viennent, toujours {{cotes a cotes}}…
 TEST: Nous irons {{tours à tours}} chercher du bois.
 TEST: Ma thèse en 180 secondes.
 
@@ -5140,10 +6254,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>> *
@@ -5165,10 +6281,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 +6326,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,11 +6413,11 @@
 __[i](p_motion_de)__                    motions? (de (?:blâme|censure|défiance)) @@$ <<- ~1>> *
 __[i](p_noix_de)__                      noix (de (?:cajou|p[ée]can|coco|lavage|muscade|veau|macadamia)) @@$ <<- ~1>> *
 __[i](p_nu_comme_un_ver)__              nue?s? (comme (?:un ver|des vers)) @@$ <<- ~1>> *
 __[i](p_numéro)__
     numéro (un|deux|trois|quatre|cinq|six|sept|huit|neuf|dix(?:-sept|-huit|-neuf|)|onze|douze|treize|quatorze|quinze|seize|vingt|trente|quarante|cinquante|soixante(?:-dix|)|quatre-vingt(?:-dix|)|cent|mille|\d+) @@$
-    <<- before(r"\b[lL]a +$") =>> define(\0, [">numéro :N:f:s"])
+    <<- before(r"\b[lL]a +$") =>> define(\0, [">numéro/:N:f:s"])
     <<- ~1>> *
 __[i](p_oiseau_de)__                    oiseaux? (de (?:malheur|nuit|proie|mauvais augure)) @@$ <<- ~1>> * 
 __[i](p_onde_de_choc)__                 ondes? (de choc) @@$ <<- ~1>> *
 __[i](p_orge)__                         orge (perlé|mondé|carré) @@$ <<- ~1>> *
 __[i](p_noire_comme)__                  noire?s? (comme (?:la nuit|une nuit sans lune)) @@$ <<- ~1>> *
@@ -5446,15 +6570,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 +6588,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 +6678,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>> *
@@ -5587,19 +6711,22 @@
 __[i](p_les_xxx_les_plus_adj)__
     (?:[lmts]es|nos|vos|leurs) ({w_2}) (les plus) ({w_2})  @@w,w,$
     <<- morphex(\1, ":[NAQ].*:[pi]", ":[123][sp]") and morph(\3, ":A.*:[pi]", False) ~2>> * 
 __[i](p_le_plus_le_moins)__
     (le (?:plus|moins)) ({w_2})  @@0,$
-    <<- morph(\2, ":A", ":([me]:[si]|G)") and morph(word(-1), ">(?:avoir|être) :V", False) ~1>> *
+    <<- morph(\2, ":A", ":([me]:[si]|G)") and morph(word(-1), ">(?:avoir|être)/:V", False) ~1>> *
 __[i](p_bien_sûr)__
     bien sûr(?! de) <<- ~>> *
 __[i](p_bien_mal_fort_adj_adv)__
     (bien|mal|(?:fort|super) (?:bien|mal)|fort) +({w_2})  @@0,$
     <<- morph(\2, ":[AW]") ~1>> *
 __[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 +6845,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}}.
@@ -5901,11 +7028,11 @@
 
 !!!! Redondances dans la phrase                                                                     
  
 __[i]/redon2(redondances_phrase)__
     ({w_4})[ ,].* (\1)  @@0,$
-    <<- not morph(\1, ":(?:G|V0)|>même ", False) -2>> _             # Dans cette phrase, répétition de « \1 » (à gauche).
+    <<- not morph(\1, ":(?:G|V0)|>même/", False) -2>> _             # Dans cette phrase, répétition de « \1 » (à gauche).
     <<- __also__ -1>> _                                             # Dans cette phrase, répétition de « \1 » (à droite).
 
 TEST: __redon2__ Quelle {{imposture}}, c’est d’un ennui, c’est une {{imposture}}.
 TEST: __redon2__ ils sont là côte à côte.
 TEST: __redon2__ Tu avances petit à petit, et tu réussis.
@@ -5956,15 +7083,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 +7174,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 +7284,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.
 __<i]/gn(gn_leur_accord3)__
     ^ *(leur) +({w_2})  @@*,$
     <<- morphex(\2, ":[NAQ].*:p", ":[siGW]") -1>> leurs                                             # Accord de nombre erroné avec « \1 ».
@@ -6196,11 +7323,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,11 +7612,11 @@
 
 
 ##  trouver ça/ceci/cela + adj
 __[i]/gn(gn_trouver_ça_adj)__
     (trouv\w+) +(ça|ce(?:ci|la)) +({w_2})  @@0,w,$
-    <<- morph(\1, ">trouver ", False) and morphex(\3, ":A.*:(?:f|m:p)", ":(?:G|3[sp]|M[12P])")
+    <<- morph(\1, ">trouver/", False) and morphex(\3, ":A.*:(?:f|m:p)", ":(?:G|3[sp]|M[12P])")
     -3>> =suggMasSing(@)                                                                            # Trouver \2 + [adjectif] : l’adjectif s’accorde avec “\2” (au masculin singulier).
 
 TEST: ils trouvent ça de plus en plus {{idiots}}              ->> idiot
 
 
@@ -6622,15 +7749,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 +7778,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}}
@@ -6683,15 +7810,15 @@
 __[i]/gn(gn_2m_le_après_et_ou_de)__
     (?:et|ou) +(le) +({w_2}) +({w_2})  @@w,w,$
     <<- morph(\1, ":D", False) >>>
     <<- not \3.startswith("seul")
         and morphex(\2, ":[NAQ].*:[me]", ":(?:B|G|V0|f)") and morph(\3, ":[NAQ].*:f")
-        and not apposition(\2, \3) and not morph(word(-1), ":[NAQ]|>(?:et|ou) ", False, False)
+        and not apposition(\2, \3) and not morph(word(-1), ":[NAQ]|>(?:et|ou)/", False, False)
     -3>> =suggMasSing(@, True)                                                                      # Accord de genre erroné : « \2 » est masculin, « \3 » est féminin.
     <<- not \3.startswith("seul")
         and morphex(\2, ":[NAQ].*:[si]", ":G") and morphex(\3, ":[NAQ].*:p", ":[GWsi]")
-        and not apposition(\2, \3) and not morph(word(-1), ":[NAQ]|>(?:et|ou) ", False, False)
+        and not apposition(\2, \3) and not morph(word(-1), ":[NAQ]|>(?:et|ou)/", False, False)
     -3>> =suggMasSing(@)                                                                            # Accord de nombre erroné avec « \2 » : « \3 » devrait être au singulier.    
 
 TEST: le test très {{cons}} qu’on a passé hier.
 TEST: c’était le chien {{perdue}} des voisins.
 TEST: viens vite ou le pari {{imperdables}} sera moins facile…
@@ -6709,15 +7836,15 @@
     -2>> =suggMasSing(@)                                                                            # Accord de nombre erroné avec « \1 » : « \2 » devrait être au singulier.
 __[i]/gn(gn_2m_det_mas_sing_après_et_ou_de)__
     (?:et|ou|de) +(?:cet?|quel|au|ledit) +({w_2}) +({w_2})  @@w,$
     <<- not \2.startswith("seul")
         and morphex(\1, ":[NAQ].*:[me]", ":(?:B|G|V0|f)") and morph(\2, ":[NAQ].*:f")
-        and not apposition(\1, \2) and not morph(word(-1), ":[NAQ]|>(?:et|ou) ", False, False)
+        and not apposition(\1, \2) and not morph(word(-1), ":[NAQ]|>(?:et|ou)/", False, False)
     -2>> =suggMasSing(@, True)                                                                      # Accord de genre erroné : « \1 » est masculin, « \2 » est féminin.
     <<- not \2.startswith("seul")
         and morphex(\1, ":[NAQ].*:[si]", ":G") and morphex(\2, ":[NAQ].*:p", ":[GWsi]")
-        and not apposition(\1, \2) and not morph(word(-1), ":[NAQ]|>(?:et|ou) ", False, False)
+        and not apposition(\1, \2) and not morph(word(-1), ":[NAQ]|>(?:et|ou)/", False, False)
     -2>> =suggMasSing(@)                                                                            # Accord de nombre erroné avec « \1 » : « \2 » devrait être au singulier.    
 
 TEST: cet outil {{terribles}} qu’il a dans les mains
 TEST: J’aimerais connaître de quel parti {{gauchistes}} on parle.
 
@@ -6734,16 +7861,16 @@
     -2>> =suggMasSing(@)                                                                            # Accord de nombre erroné avec « \1 » : « \2 » devrait être au singulier.
 __[i]/gn(gn_2m_mon_ton_son_après_et_ou_de)__
     (?:et|ou|de) +[mts]on +({w_2}) +({w_2})  @@w,$
     <<- not \2.startswith("seul")
         and morphex(\1, ":[NAQ].*:m", ":(?:B|G|e|V0|f)") and morph(\2, ":[NAQ].*:f")
-        and not apposition(\1, \2) and not morph(word(-1), ":[NAQ]|>(?:et|ou) ", False, False)
+        and not apposition(\1, \2) and not morph(word(-1), ":[NAQ]|>(?:et|ou)/", False, False)
     -2>> =suggMasSing(@, True)                                                                      # Accord de genre erroné : « \1 » est masculin, « \2 » est féminin.
     <<- not \2.startswith("seul")
         and morphex(\1, ":[NAQ].*:[si]", ":G") and morphex(\2, ":[NAQ].*:p", ":[GWsi]")
         and not apposition(\1, \2)
-        and not morph(word(-1), ":[NAQ]|>(?:et|ou) ", False, False)
+        and not morph(word(-1), ":[NAQ]|>(?:et|ou)/", False, False)
     -2>> =suggMasSing(@)                                                                            # Accord de nombre erroné avec « \1 » : « \2 » devrait être au singulier.    
 
 TEST: il brandissait avec fougue son drapeau {{déchirés}}
 TEST: comment osez-vous médire de mon héritage {{glorieuse}}
 
@@ -6762,15 +7889,15 @@
 __[i]/gn(gn_2m_la_après_et_ou_de)__
     (?:et|ou|de) +(la) +({w_2}) +({w_2})  @@w,w,$
     <<- morph(\1, ":D", False) >>>
     <<- \2 != "fois" and not \3.startswith("seul")
         and morphex(\2, ":[NAQ].*:[fe]", ":(?:B|G|V0|m)") and morph(\3, ":[NAQ].*:m")
-        and not apposition(\2, \3) and not morph(word(-1), ":[NAQ]|>(?:et|ou) ", False, False)
+        and not apposition(\2, \3) and not morph(word(-1), ":[NAQ]|>(?:et|ou)/", False, False)
     -3>> =suggFemSing(@, True)                                                                      # Accord de genre erroné : « \2 » est féminin, « \3 » est masculin.
     <<- not \3.startswith("seul")
         and morphex(\2, ":[NAQ].*:[si]", ":G") and morphex(\3, ":[NAQ].*:p", ":[GWsi]")
-        and not apposition(\2, \3) and not morph(word(-1), ":[NAQ]|>(?:et|ou) ", False, False)
+        and not apposition(\2, \3) and not morph(word(-1), ":[NAQ]|>(?:et|ou)/", False, False)
     -3>> =suggFemSing(@)                                                                            # Accord de nombre erroné avec « \2 » : « \3 » devrait être au singulier.
 
 TEST: La plus grande {{cinglé}}.
 TEST: il imaginait de la pluie {{noir}} tombant sur une terre dévastée.
 
@@ -6787,15 +7914,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 +7944,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 +7974,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 +8002,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 +8032,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 +8062,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 +8092,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 +8113,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}}
@@ -7260,11 +8387,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 +8407,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 +8469,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 +8494,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 +8525,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 +8621,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.
@@ -7532,19 +8659,19 @@
 !!
 
 # à / a
 __[i]/conf(conf_suite_à)__
     suite (a) ({w1}) @@w,$
-    <<- morph(\2, ":D|>[ld] ", False) and isStart() -1>> à          # Confusion : “a” est une forme conjuguée du verbe “avoir”. Pour la préposition, écrivez “à”.
+    <<- morph(\2, ":D|>[ld]/", False) and isStart() -1>> à          # Confusion : “a” est une forme conjuguée du verbe “avoir”. Pour la préposition, écrivez “à”.
 
 TEST: Suite {{a}} ces folies, nous rentrâmes chez nous.
 TEST: il s’avère que, suite {{a}} d’horribles complications, nous renonçâmes.
 
 
 __[i]/conf(conf_pronom_à_l_air)__
     (?:tout|ça|ce(?:ci|la)) (à) l’air +({w_2})  @@w,$
-    <<- morphex(\2, ":[AR]", ">libre ") and morph(word(-1), ":Cs", False, True)
+    <<- morphex(\2, ":[AR]", ">libre/") and morph(word(-1), ":Cs", False, True)
     -1>> a                                                          # Confusion probable : “à” est une préposition. Pour le verbe “avoir”, écrivez “a”.
 
 TEST: lorsque tout {{à}} l’air fini, c’est trompeur.
 TEST: Tout {{à}} l’air complètement foutu…
 TEST: Ça {{à}} l’air génial.
@@ -7579,21 +8706,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 +8740,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,14 +8764,14 @@
 
 # amende / amande
 __[i]/conf(conf_yeux_en_amande)__
     yeux en (amendes?) @@$ <<- -1>> amande                                                          # Confusion. Une amende est une peine.|http://www.cnrtl.fr/lexicographie/amende
 __[i]/conf(conf_à_l_amende)__
-    (m\w+) à (l’amande) @@0,$ <<- morph(\1, ">mettre ", False) -2>> l’amende                        # Confusion. L’amande est un fruit.
+    (m\w+) à (l’amande) @@0,$ <<- morph(\1, ">mettre/", False) -2>> l’amende                        # Confusion. L’amande est un fruit.
 __[i]/conf(conf_faire_amende_honorable)__
     (f\w+)(?:-(?:je|tu|ils?|[nv]ous|elles?)|) +(amandes? honorables?) @@0,$
-    <<- morph(\1, ">faire ", False) -2>> amende honorable                                           # Confusion. L’amande est un fruit.
+    <<- morph(\1, ">faire/", False) -2>> amende honorable                                           # Confusion. L’amande est un fruit.
 
 TEST: Avec ses beaux yeux en {{amendes}} nul ne peut lui résister.
 TEST: Nous avons déconné, nous avons été mis à {{l’amande}}.
 TEST: Ces gens-là ne feront jamais {{amande honorable}}.
 
@@ -7680,11 +8807,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 +8854,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 +8939,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 +8967,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 +9056,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 +9090,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 +9107,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 +9144,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 +9160,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 +9182,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.
@@ -8127,11 +9259,11 @@
     (mauvaise|bonne) (fois)  @@0,$
     <<- not ( \1 == "bonne" and before(r"(?i)\bune +$") and after("(?i)^ +pour toute") ) 
     -2>> foi                                                                                        # Confusion probable.|http://fr.wiktionary.org/wiki/foi
 __[i]/conf(conf_faire_perdre_donner_foi)__
     ((?:f[aieî]|perd|donn|[ae])\w*) (fois) @@0,$
-    <<- morph(\1, ">(?:faire|perdre|donner|avoir) ", False) -2>> foi                                      # Confusion probable.|http://fr.wiktionary.org/wiki/foi
+    <<- morph(\1, ">(?:faire|perdre|donner|avoir)/", False) -2>> foi                                      # Confusion probable.|http://fr.wiktionary.org/wiki/foi
 
 TEST: C’est une personne de bonne {{fois}}.
 TEST: Mais il a perdu {{fois}} en l’avenir.
 
 
@@ -8281,17 +9413,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 +9446,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 +9464,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 +9636,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 +9650,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 +9671,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}}.
@@ -8696,11 +9828,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 +9890,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}}.
 
 
@@ -8792,11 +9924,11 @@
     -1>> soi                                                                                        # Confusion probable.
 __[i]/conf(conf_quel_que_soit2)__
     quel(?:le|)s? que (soi(?:es?|)) @@$ <<- -1>> soit|soient                                        # Confusion probable. 
 __[i]/conf(conf_soi_même1)__
     (soi[tes]s? mêmes?) @@$
-    <<- morph(word(-1), ":[YQ]|>(?:avec|contre|par|pour|sur) ", False, True) -1>> soi-même          # Confusion probable : moi-même, toi-même, lui-même, elle-même, soi-même, elles-mêmes, eux-mêmes.
+    <<- morph(word(-1), ":[YQ]|>(?:avec|contre|par|pour|sur)/", False, True) -1>> soi-même          # Confusion probable : moi-même, toi-même, lui-même, elle-même, soi-même, elles-mêmes, eux-mêmes.
 __[i]/conf(conf_soi_même2)__
     soi[tes]s?-mêmes? <<- ->> soi-même                                                              # Confusion : moi-même, toi-même, lui-même, elle-même, soi-même, elles-mêmes, eux-mêmes.
 
 TEST: chez {{soit}}, c’est presque toujours mieux.
 TEST: ce n’est pas la philosophie en {{soit}} qui est problématique
@@ -8815,45 +9947,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 +9971,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 +9989,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 +10011,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”.
@@ -9078,11 +10186,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.
@@ -9217,11 +10325,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 +10357,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 +10377,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,11 +10418,11 @@
 TEST: Le vieux cocher avait mission
 
 
 __[i]/infi(infi_adjectifs_pluriels)__
     ^ *(?:[lmtsc]es|[nv]os|leurs|quel(?:le|)s) ({w_1}[sxz]) +({w_2}er)  @@w,$
-    <<- morphex(\1, ":N.*:[pi]", ":G") and morphex(\2, ":Y", ">aller |:M") and isNextVerb()
+    <<- morphex(\1, ":N.*:[pi]", ":G") and morphex(\2, ":Y", ">aller/|:M") and isNextVerb()
     -2>> =suggVerbPpas(\2, ":p")                                                    # Confusion probable : “\2” est à verbe à l’infinitif. Pour l’adjectif, écrivez :
 
 TEST: les documents {{scanner}} ne sont pas lisibles.
 TEST: tes doutes {{remâcher}} deviennent difficiles à vivre.
 
@@ -9350,11 +10458,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 +10479,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 +10533,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() ~>> *
@@ -9469,11 +10577,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.
@@ -9548,11 +10656,11 @@
 
 
 ## soit / soie / soi
 __[i]/conf(conf_aller_de_soi)__
     ({aller}) +de (soi[tes])  @@0,$
-    <<- morph(\1, ">aller", False) and not after(" soit ") -2>> soi                                 # Confusion.|https://fr.wiktionary.org/wiki/aller_de_soi
+    <<- morph(\1, ">aller/", False) and not after(" soit ") -2>> soi                                # Confusion.|https://fr.wiktionary.org/wiki/aller_de_soi
 
 TEST: cela ne va pas de {{soit}}.
 
 
 
@@ -9559,11 +10667,11 @@
 !!!! Adverbes après verbe                                                                           
 
 # fort
 __[i]/sgpl(sgpl_verbe_fort)__
     ({w_2}) +(forts)  @@0,$
-    <<- morphex(\1, ":V", ":[AN].*:[me]:[pi]|>(?:être|sembler|devenir|re(?:ster|devenir)|para[îi]tre|appara[îi]tre) .*:(?:[123]p|P|Q)|>(?:affirmer|trouver|croire|désirer|estime|préférer|penser|imaginer|voir|vouloir|aimer|adorer|souhaiter) ")
+    <<- morphex(\1, ":V", ":[AN].*:[me]:[pi]|>(?:être|sembler|devenir|re(?:ster|devenir)|para[îi]tre|appara[îi]tre)/.*:(?:[123]p|P|Q)|>(?:affirmer|trouver|croire|désirer|estime|préférer|penser|imaginer|voir|vouloir|aimer|adorer|souhaiter)/")
         and not morph(word(1), ":A.*:[me]:[pi]", False)
     -2>> fort                                               # Confusion probable. S’il s’agit ici de l’adverbe “fort” (équivalent de “fortement”), écrivez-le au singulier.
 
 TEST: ces emmerdeurs crient bien trop {{forts}}
 TEST: ces animaux paraissent forts, mais ils sont faibles.
@@ -9626,11 +10734,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 +10746,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 +10761,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 +10777,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
 
@@ -9707,34 +10815,34 @@
 !!
 !!
 
 __[i]/conj(conj_se_conf_être_avoir)__
     (s’)(?:en +|y+ |)({avoir})  @@0,$
-    <<- morph(\2, ">avoir ", False) >>>
+    <<- morph(\2, ">avoir/", False) >>>
     <<- morph(\2, ":3p", False) -2>> sont|étaient|seront|seraient                                   # Confusion. Sous sa forme pronominale, un verbe s’emploie avec l’auxilaire “être”, non “avoir”.
     <<- __else__ -2>> est|était|sera|serait                                                         # Confusion. Sous sa forme pronominale, un verbe s’emploie avec l’auxilaire “être”, non “avoir”.
 
 TEST: s’en {{ait}} trop
 
 
 __[i]/conj(conj_je_me_conf_être_avoir)__
     je m’(?:en +|y+ |)({avoir})  @@$
-    <<- morph(\1, ">avoir ", False) -1>> suis|étais|serai|serais                                    # Confusion. Sous sa forme pronominale, un verbe s’emploie avec l’auxilaire “être”, non “avoir”.
+    <<- morph(\1, ">avoir/", False) -1>> suis|étais|serai|serais                                    # Confusion. Sous sa forme pronominale, un verbe s’emploie avec l’auxilaire “être”, non “avoir”.
 
 __[i]/conj(conj_tu_te_conf_être_avoir)__
     tu t’(?:en +|y+ |)({avoir})  @@$
-    <<- morph(\1, ">avoir ", False) and not morph(word(-1), ":V0", False, False)
+    <<- morph(\1, ">avoir/", False) and not morph(word(-1), ":V0", False, False)
     -1>> es|étais|seras|serais                                                                      # Confusion. Sous sa forme pronominale, un verbe s’emploie avec l’auxilaire “être”, non “avoir”.
 
 __[i]/conj(conj_nous_nous_conf_être_avoir)__
     (nous) nous (?:en +|y+ |)({avoir})  @@0,$
-    <<- morph(\2, ">avoir ", False) and isStart() -2>> sommes|étions|serons|serions                 # Confusion possible. Sous sa forme pronominale, un verbe s’emploie avec l’auxilaire “être”, non “avoir”.
+    <<- morph(\2, ">avoir/", False) and isStart() -2>> sommes|étions|serons|serions                 # Confusion possible. Sous sa forme pronominale, un verbe s’emploie avec l’auxilaire “être”, non “avoir”.
     <<- __also__ -1>> nous,                                                                         # S’il ne s’agit pas d’une locution pronominale, mettez une virgule pour séparer les personnes que vous désignez du sujet.
 
 __[i]/conj(conj_vous_vous_conf_être_avoir)__
     (vous) vous (?:en +|y+ |)({avoir})  @@0,$
-    <<- morph(\2, ">avoir ", False) and isStart() -2>> êtes|étiez|serez|seriez                      # Confusion possible. Sous sa forme pronominale, un verbe s’emploie avec l’auxilaire “être”, non “avoir”.
+    <<- morph(\2, ">avoir/", False) and isStart() -2>> êtes|étiez|serez|seriez                      # Confusion possible. Sous sa forme pronominale, un verbe s’emploie avec l’auxilaire “être”, non “avoir”.
     <<- __also__ -1>> vous,                                                                         # S’il ne s’agit pas d’une locution pronominale, mettez une virgule pour séparer les personnes que vous désignez du sujet.
 
 TEST: je m’y {{avais}} habitué.
 TEST: tu t’{{avais}} donné du temps pour finir ton mémoire.
 TEST: Ce qu’il a tu t’a donné la nausée.
@@ -9769,54 +10877,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.
@@ -9842,11 +10950,11 @@
 !!
 !!
 
 __[i]/ppas(ppas_me_te_laisser_adj)__
     ([mt]e|l[ae]) +(laiss\w*) +({w_3})  @@0,w,$
-    <<- morph(\2, ">laisser ", False) and  morphex(\3, ":[AQ].*:p", ":(?:[YG]|[AQ].*:[is])")
+    <<- morph(\2, ">laisser/", False) and  morphex(\3, ":[AQ].*:p", ":(?:[YG]|[AQ].*:[is])")
     -3>> =suggSing(@)                                                                               # Accord avec « \1 » : « \3 » devrait être au singulier.
 
 TEST: Elle te laisse {{épuisés}} par la tâche.
 TEST: Ils la laissèrent {{malades}}.
 TEST: Ils la laissent prendre le train.
@@ -9855,11 +10963,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.
@@ -9875,11 +10983,11 @@
 !!
 !!
 
 __[i]/ppas(ppas_je_verbe)__
     j(?:e +|’(?:y +|en +|))(?:ne +|n’|)((?:s[oue]|étai|fus|dev|re(?:dev|st)|par)\w*|a(?:ie?|vais|urais?) +été|eus(?:se|) +été) +({w_2})  @@w,$
-    <<- (morph(\1, ">(?:être|sembler|devenir|re(?:ster|devenir)|para[îi]tre) ", False) or \1.endswith(" été")) and morphex(\2, ":[NAQ].*:p", ":[GWYsi]")
+    <<- (morph(\1, ">(?:être|sembler|devenir|re(?:ster|devenir)|para[îi]tre)/", False) or \1.endswith(" été")) and morphex(\2, ":[NAQ].*:p", ":[GWYsi]")
     -2>> =suggSing(@)                                                        # Accord avec le sujet « je » : « \2 » devrait être au singulier.
 
 TEST: j’étais {{perdus}}                                                          ->> perdu
 TEST: j’aurais été {{perdus}} sans toi                                            ->> perdu
 TEST: je n’étais pas {{perdus}}                                                   ->> perdu
@@ -9894,21 +11002,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 +11026,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 +11045,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 +11059,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 +11083,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 +11122,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
@@ -10058,11 +11166,11 @@
 __[i](p_risque_d_être)__
     risqu\w+ +(d’)être @@* <<- ~1>> *
 
 __[i]/ppas(ppas_je_verbe_être)__
     j(?:e|’(?:y|en)) +(?:ne +|n’|)((?:p[aeouûr]|s(?:embl|ouhait)|cr[ouû]|d[eouûéiî]|estim|i(?:magin|r)|v(?:[eo]u|a)|a(?:ffirm|im|dor|ll)|risqu)\w*) +(?:être|avoir été) +({w_2}) @@w,$
-    <<- morph(\1, ">(?:sembler|para[îi]tre|pouvoir|penser|préférer|croire|d(?:evoir|éclarer|ésirer|étester|ire)|vouloir|affirmer|aimer|adorer|souhaiter|estimer|imaginer|risquer|aller) ", False)
+    <<- morph(\1, ">(?:sembler|para[îi]tre|pouvoir|penser|préférer|croire|d(?:evoir|éclarer|ésirer|étester|ire)|vouloir|affirmer|aimer|adorer|souhaiter|estimer|imaginer|risquer|aller)/", False)
         and morphex(\2, ":[NAQ].*:p", ":[GWYsi]")
     -2>> =suggSing(@)                                                        # Accord avec le sujet « je » : « \2 » devrait être au singulier.
 
 TEST: Je ne peux pas être {{méchants}}.
 TEST: j’aurais vraiment été {{tentés}}
@@ -10071,11 +11179,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 +11190,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 +11203,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 +11214,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 +11225,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)__
     (?<![nN][oO][uU][sS] )nous +(?: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,$
-    <<- 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)
+    <<- 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}}
@@ -10236,19 +11344,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 +11364,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.
 
 
@@ -10289,27 +11397,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}} ?
@@ -10352,37 +11460,37 @@
 !!
 !!
 
 __[i]/ppas(ppas_je_me_verbe)__
     je +(?:ne +|)me +((?:s[eauû]|montr|pens|rév|v[oiîe])\w+) +({w_2})  @@w,$
-    <<- morph(\1, ">(?:montrer|penser|révéler|savoir|sentir|voir|vouloir) ", False) and morphex(\2, ":[NAQ].*:p", ":[GWYsi]")
+    <<- morph(\1, ">(?:montrer|penser|révéler|savoir|sentir|voir|vouloir)/", False) and morphex(\2, ":[NAQ].*:p", ":[GWYsi]")
     -2>> =suggSing(@)                                                        # Accord avec le sujet « je » : « \2 » devrait être au singulier.
 
 TEST: je me savais {{implacables}} avec eux
 
 
 __[i]/ppas(ppas_tu_te_verbe)__
     tu +(?:ne +|)te +((?:s[eauû]|montr|pens|rév|v[oiîe])\w+) +({w_2})  @@w,$
-    <<- morph(\1, ">(?:montrer|penser|révéler|savoir|sentir|voir|vouloir) ", False) and morphex(\2, ":[NAQ].*:p", ":[GWYsi]")
+    <<- morph(\1, ">(?:montrer|penser|révéler|savoir|sentir|voir|vouloir)/", False) and morphex(\2, ":[NAQ].*:p", ":[GWYsi]")
     -2>> =suggSing(@)                                                        # Accord avec le sujet « je » : « \2 » devrait être au singulier.
 
 TEST: quand tu te montres {{infaillibles}}
 
 
 __[i]/ppas(ppas_il_se_verbe)__
     (il|ce|ce qui|celui +qui|ça +qui|lui +qui|celui-(?:ci|là)|quiconque|lequel) +(?:ne +|)se +((?:s[eauû]|montr|pens|rév|v[oiîe])\w+) +({w_2})  @@0,w,$
-    <<- morph(\2, ">(?:montrer|penser|révéler|savoir|sentir|voir|vouloir) ", False)
+    <<- morph(\2, ">(?:montrer|penser|révéler|savoir|sentir|voir|vouloir)/", False)
         and (morphex(\3, ":[NAQ].*:p", ":[GWsi]") or morphex(\3, ":[NAQ].*:f", ":[GWYme]"))
         and (not re.search("^(?:celui-(?:ci|là)|lequel)$", \1) or not morph(word(-1), ":R", False, False))
     -3>> =suggMasSing(@)                                                     # Accord avec le sujet « \1 » : « \3 » devrait être au masculin singulier.
 
 TEST: lequel se veut {{imbattables}} ?
 
 
 __[i]/ppas(ppas_elle_se_verbe)__
     (elle|celle-(?:ci|là)|laquelle) +(?:ne +|)se +((?:s[eauû]|montr|pens|rév|v[oiîe])\w+) +({w_2})  @@0,w,$
-    <<- morph(\2, ">(?:montrer|penser|révéler|savoir|sentir|voir|vouloir) ", False)
+    <<- morph(\2, ">(?:montrer|penser|révéler|savoir|sentir|voir|vouloir)/", False)
         and (morphex(\3, ":[NAQ].*:p", ":[GWsi]") or morphex(\3, ":[NAQ].*:m", ":[GWYfe]"))
         and not morph(word(-1), ":R", False, False)
     -3>> =suggFemSing(@)                                                     # Accord avec le sujet « \1 » : « \3 » devrait être au féminin singulier.
 
 TEST: Elle se sait plus {{fortes}} qu’eux tous.
@@ -10389,84 +11497,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}}
@@ -10498,15 +11606,15 @@
 
 #__[i]/conj__  fait(s|e|es) ({w1}) <<- morph(\2, ":V") and not morph(\2, ":Y")
 #   ->> fait \1                      # Le participe passé de faire reste au masculin singulier s’il est suivi par un verbe à l’infinitif.
 
 __[i](p_les_avoir_fait_vinfi)__
-    les ({avoir}) +(fait) +(?:[mts](?:e +|’)|)({infi}) @@w,w,$ <<- morph(\1, ">avoir ", False) and morph(\3, ":Y", False) ~2>> _
+    les ({avoir}) +(fait) +(?:[mts](?:e +|’)|)({infi}) @@w,w,$ <<- morph(\1, ">avoir/", False) and morph(\3, ":Y", False) ~2>> _
 
 __[i]/ppas(ppas_pronom_avoir)__
     (?:j’|je |tu |ils? |elles? |on |et )(?:ne +|n’|l(?:ui|eur) +|)({avoir}) +({w_2})  @@w,$
-    <<- not re.search("(?i)^(?:barre|confiance|cours|envie|peine|prise|crainte|cure|affaire|hâte|force|recours)$", \2) and morph(word(-1), ">(?:comme|et|lorsque?|mais|o[uù]|puisque?|qu(?:oique?|i|and)|si(?:non|)) ", False, True)
+    <<- not re.search("(?i)^(?:barre|confiance|cours|envie|peine|prise|crainte|cure|affaire|hâte|force|recours)$", \2) and morph(word(-1), ">(?:comme|et|lorsque?|mais|o[uù]|puisque?|qu(?:oique?|i|and)|si(?:non|))/", False, True)
         and morph(\1, ":V0a", False) and not \2.isupper() and morphex(\2, ":(?:[123][sp]|Q.*:[fp])", ":(?:G|W|Q.*:m:[si])")
     -2>> =suggMasSing(@)
     # Ce verbe devrait être un participe passé au masculin singulier.|http://fr.wikipedia.org/wiki/Accord_du_participe_pass%C3%A9_en_fran%C3%A7ais
 
 TEST: ils leur avaient {{donnés}} du fil à retordre.
@@ -10518,11 +11626,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 +11638,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 +11689,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 +11737,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.
@@ -10872,11 +11980,11 @@
     <<- morph(\2, ":(?:[123][sp]|P)", False) =>> select(\2,":(?:[123][sp]|P)")
     <<- ~1>> *
 __<i](p_premier_ne_pro_per_obj2)__
     ^( *ne (?:[mt]’|l(?:ui|eur) )en) ({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>> *
 __<i](p_premier_ne_pro_per_obj3)__
     ^( *ne (?:[mt]e|[nv]ous) (?:les?|la|en)) ({w_2})  @@0,$
     <<- morph(\2, ":(?:[123][sp]|P)", False) =>> select(\2,":(?:[123][sp]|P)")
     <<- ~1>> *
 __<i](p_premier_ne_pro_per_obj4)__
@@ -10884,19 +11992,19 @@
     <<- morph(\2, ":(?:[123][sp]|P)", False) =>> select(\2,":(?:[123][sp]|P)")
     <<- ~1>> *
 __<i>(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>> *
 __<i>(p_premier_ne_pro_per_obj6)__
     ^( *ne l’)({w_2})  @@0,$
     <<- morph(\2, ":(?:[123][sp]|P)", False) =>> select(\2,":(?:[123][sp]|P)")
     <<- ~1>> *
 __<i>(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.
@@ -10950,19 +12058,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?))  @@$
@@ -11084,11 +12193,11 @@
 TEST: deux fois par an, souligne le Dr Assouline
 
 
 __[i]/imp(imp_laisser_le_la_les_infi)__
     ((laiss\w+) l(?:es|a)) +({w_2})  @@0,0,$
-    <<- morph(\2, ">laisser .*: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 +12206,11 @@
 TEST: le coût humain de la guerre qu’il a laissé les submerger.
 
 
 __<i]/imp(imp_apostrophe_m_t_en)__
     ([ -][mt])-en @@0
-    <<- not (\0.endswith("t-en") and before(r"(?i)\bva$") and morph(word(1), ">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
 
 
@@ -11281,11 +12390,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)__
@@ -11351,11 +12460,11 @@
 
 #### CONFUSION veillez/veuillez                                                                     
 
 __[i]/conf(conf_veillez2)__
     (veuillez) +à +(ne|{infi})  @@0,$
-    <<- isStart() and morph(\2, ":Y|>ne ", False) -1>> veillez          # Confusion probable : “veuillez” est une forme conjuguée du verbe “vouloir”.|http://bdl.oqlf.gouv.qc.ca/bdl/gabarit_bdl.asp?id=1939
+    <<- isStart() and morph(\2, ":Y|>ne/", False) -1>> veillez          # Confusion probable : “veuillez” est une forme conjuguée du verbe “vouloir”.|http://bdl.oqlf.gouv.qc.ca/bdl/gabarit_bdl.asp?id=1939
 
 TEST: {{Veuillez}} à ne pas tomber dans ce piège.
 TEST: Et {{veuillez}} surtout à ouvrir grand les yeux.
 TEST: {{Veuillez}}, s’il vous plaît, à prendre vos médicaments.
 TEST: Veuillez à nouveau faire attention à ce problème.
@@ -11363,11 +12472,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.
@@ -11405,11 +12514,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 +12528,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 +12545,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 +12562,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.
 
 
@@ -11491,11 +12600,11 @@
 !!
 
 ## 1sg
 __[i]/conj(conj_j)__
     j’({w_1})  @@2
-    <<- morphex(\1, ":V", ":1s|>(?:en|y) ") >>>
+    <<- morphex(\1, ":V", ":1s|>(?:en|y)/") >>>
     <<- \1 == "est" or \1 == "es" -1>> ai|aie|suis          # Conjugaison erronée. Confusion probable entre “être” et “avoir”. Accord avec « \1 ». Le verbe devrait être à la 1ʳᵉ personne du singulier.
     <<- __else__ -1>> =suggVerb(@, ":1s")                   # Conjugaison erronée. Accord avec « je ». Le verbe devrait être à la 1ʳᵉ personne du singulier.
 __[i]/conj(conj_je)__
     (je) +({w_1})  @@0,$
     <<- morphex(\2, ":V", ":(?:1s|G)") and not (morph(\2, ":[PQ]", False) and morph(word(-1), ":V0.*:1s", False, False)) >>>
@@ -11601,11 +12710,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)
@@ -11905,11 +13014,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 +13132,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)")
@@ -12210,11 +13319,11 @@
     ({w_1}s) tu  @@0
     <<- morphex(\1, ":V.*:2s", ":[GNW]") and not before(r"(?i)\b(?:je|tu) +$") and morphex(word(1), ":", ":2s", True)
     ->> \1-tu                                                                                       # Forme interrogative ? Mettez un trait d’union.
 __[i]/inte(inte_union_il_on)__
     ({w_2}[td]) (?:il|on)  @@0
-    <<- morphex(\1, ":V.*:3s", ":[GNW]") and not before(r"(?i)\b(?:ce|il|elle|on) +$") and morphex(word(1), ":", ":3s|>y ", True)
+    <<- morphex(\1, ":V.*:3s", ":[GNW]") and not before(r"(?i)\b(?:ce|il|elle|on) +$") and morphex(word(1), ":", ":3s|>y/", True)
     ->> =\0.replace(" ", "-")                                                                       # Forme interrogative ? Mettez un trait d’union.
 __[i]/inte(inte_union_elle)__
     (?<![cC]’)({w_2}[td]) elle  @@0
     <<- morphex(\1, ":V.*:3s", ":[GNW]") and not before(r"(?i)\b(?:ce|il|elle|on) +$") and morphex(word(1), ":", ":3s", True)
     ->> \1-elle                                                                                     # Forme interrogative ? Mettez un trait d’union.
@@ -12222,11 +13331,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)__
     (?<![cC]’)({w_1}nt) (?:ils|elles)  @@0
     <<- morphex(\1, ":V.*:3p", ":[GNW]") and not before(r"(?i)\b(?:ce|ils|elles) +$") and morphex(word(1), ":", ":3p", True)
     ->> =\0.replace(" ", "-")                                                                       # Forme interrogative ? Mettez un trait d’union.
@@ -12294,20 +13403,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à ?
 
@@ -12326,46 +13435,58 @@
 
 !!!! 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                                                                                  
 !!
 !!
 
 # 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__
+    [<start>|,]  [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 +13495,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__
+    [<start>|,]  si     [j’|J’|t’|T’]  @:[SK]¬:(?:G|V0|I)
+    [<start>|,]  si     @:(?:Os|M)     @:[SK]¬:(?:G|V0|I)
+    [<start>|,]  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,76 +13536,123 @@
 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__
+    [<start>|,]  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.
+
+    [<start>|,]  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__
+    <start>  [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.
+
+    <start>  j’  @:S¬:[GIK]|V0a.*:Sq:1s
+        <<- /vmode/ -3>> =suggVerbMode(\3, ":I", "je")                                              # Ce verbe ne devrait pas être au subjonctif.
+
+    <start>  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                                                                                       
+@@@@
 
 
 !!
 !!
 !!
@@ -16555,13 +17721,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: 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 <cCur>"
     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 <spf> 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 <dValOccur>"
         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 <sWord> 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 <sWord> (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 <sPathFile>"
         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 <dValOccur>"
         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 <sWord>, 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 <cChar>"
     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 <sToken> is valid (if there is hyphens in <sToken>, <sToken> 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>"
         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 <sWord> 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 <nVal> is an arc at the node at <iAddr>, 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 <iAddr> 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 <nVal> is an arc at the node at <iAddr>, 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 <sLangCode>"
+        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.
+           <dWord> 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 <sWord> 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 <s1> and <s2>"
     # 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 <s1> and <s2>"
     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 <sStem>"
     return sStem
 
-def rebuildWord (sFlex, cmd1, cmd2):
-    if cmd1 == "_":
+def rebuildWord (sFlex, sCode1, sCode2):
+    """ Change <sFlex> with codes (each inserts a char at a defined possition).
+        <I forgot what purpose it has…>
+    """
+    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 <sSfxCode> on <sWord> 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 <sAffCode> on <sWord> 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<FOLDERUNIX>/(?: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<FOLDERWIN>[a-zA-Z]:\\(?:Program Files(?: [(]x86[)]|)|[\w.()]+)(?:\\[\w.()-]+)*)',
-            r'(?P<PUNC>[.,?!:;…«»“”"()/·]+)',
+            r'(?P<PUNC>[][,.;:!?…«»“”‘’"(){}·–—])',
             r'(?P<ACRONYM>[A-Z][.][A-Z][.](?:[A-Z][.])*)',
             r'(?P<LINK>(?:https?://|www[.]|\w+[@.]\w\w+[@.])\w[\w./?&!%=+*"\'@$#-]+)',
             r'(?P<HASHTAG>[#@][\w-]+)',
             r'(?P<HTML><\w+.*?>|</\w+ *>)',
             r'(?P<PSEUDOHTML>\[/?\w+\])',
             r'(?P<HOUR>\d\d?h\d\d\b)',
             r'(?P<NUM>-?\d+(?:[.,]\d+))',
+            r'(?P<SIGN>[%‰+=*/<>⩾⩽-])',
             r"(?P<WORD>\w+(?:[’'`-]\w+)*)"
         ),
     "fr":
         (
             r'(?P<FOLDERUNIX>/(?: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<FOLDERWIN>[a-zA-Z]:\\(?:Program Files(?: [(]x86[)]|)|[\w.()]+)(?:\\[\w.()-]+)*)',
-            r'(?P<PUNC>[.,?!:;…«»“”"()/·]+)',
+            r'(?P<PUNC>[][,.;:!?…«»“”‘’"(){}·–—])',
             r'(?P<ACRONYM>[A-Z][.][A-Z][.](?:[A-Z][.])*)',
             r'(?P<LINK>(?:https?://|www[.]|\w+[@.]\w\w+[@.])\w[\w./?&!%=+*"\'@$#-]+)',
             r'(?P<HASHTAG>[#@][\w-]+)',
             r'(?P<HTML><\w+.*?>|</\w+ *>)',
             r'(?P<PSEUDOHTML>\[/?\w+\])',
             r"(?P<ELPFX>(?:l|d|n|m|t|s|j|c|ç|lorsqu|puisqu|jusqu|quoiqu|qu)['’`])",
-            r'(?P<ORDINAL>\d+(?:er|nd|e|de|ième|ème|eme)\b)',
+            r'(?P<ORDINAL>\d+(?:ers?|nds?|es?|des?|ièmes?|èmes?|emes?|ᵉʳˢ?|ⁿᵈˢ?|ᵉˢ?|ᵈᵉˢ?)\b)',
             r'(?P<HOUR>\d\d?h\d\d\b)',
             r'(?P<NUM>-?\d+(?:[.,]\d+|))',
+            r'(?P<SIGN>[%‰+=*/<>⩾⩽-])',
             r"(?P<WORD>\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 <sText>"
+        i = 0
+        if bStartEndToken:
+            yield { "i": 0, "sType": "INFO", "sValue": "<start>", "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": "<end>", "nStart": iEnd, "nEnd": iEnd }

Index: make.py
==================================================================
--- make.py
+++ make.py
@@ -228,10 +228,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)

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]|)__|</?js>'
       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: '\(\?(?:[:=!]|<!)|[(|)]'
       scope: keyword.parenthesis
 

Index: misc/grammalecte.tmTheme
==================================================================
--- misc/grammalecte.tmTheme
+++ misc/grammalecte.tmTheme
@@ -66,10 +66,25 @@
 				<string>#A0F0FF</string>
 				<key>background</key>
 				<string>#0050A0</string>
 			</dict>
 		</dict>
+		<dict>
+			<key>name</key>
+			<string>Graphline</string>
+			<key>scope</key>
+			<string>graphline</string>
+			<key>settings</key>
+			<dict>
+				<key>foreground</key>
+				<string>hsl(0, 100%, 80%)</string>
+				<key>background</key>
+				<string>hsl(0, 100%, 20%</string>
+				<key>fontStyle</key>
+				<string>bold</string>
+			</dict>
+		</dict>
 		<dict>
 			<key>name</key>
 			<string>String</string>
 			<key>scope</key>
 			<string>string</string>
@@ -233,10 +248,40 @@
 				<string>#602020</string>
 				<key>fontStyle</key>
 				<string>bold</string>
 			</dict>
 		</dict>
+		<dict>
+			<key>name</key>
+			<string>Keyword tag</string>
+			<key>scope</key>
+			<string>keyword.tag</string>
+			<key>settings</key>
+			<dict>
+				<key>foreground</key>
+				<string>#FF70FF</string>
+				<key>background</key>
+				<string>#602060</string>
+				<key>fontStyle</key>
+				<string>bold</string>
+			</dict>
+		</dict>
+		<dict>
+			<key>name</key>
+			<string>Keyword tag group</string>
+			<key>scope</key>
+			<string>keyword.tag.group</string>
+			<key>settings</key>
+			<dict>
+				<key>foreground</key>
+				<string>#F0B0F0</string>
+				<key>background</key>
+				<string>#602060</string>
+				<key>fontStyle</key>
+				<string>bold</string>
+			</dict>
+		</dict>
 		<dict>
 			<key>name</key>
 			<string>Keyword textprocessor</string>
 			<key>scope</key>
 			<string>keyword.textprocessor</string>
@@ -291,10 +336,41 @@
 				<key>foreground</key>
 				<string>#A0A0A0</string>
 			</dict>
 		</dict>
 
+		<dict>
+			<key>name</key>
+			<string>Keyword Valid</string>
+			<key>scope</key>
+			<string>keyword.valid</string>
+			<key>settings</key>
+			<dict>
+				<key>fontStyle</key>
+				<string>bold</string>
+				<key>foreground</key>
+				<string>hsl(150, 100%, 80%)</string>
+				<key>background</key>
+				<string>hsl(150, 100%, 20%)</string>
+			</dict>
+		</dict>
+		<dict>
+			<key>name</key>
+			<string>Keyword Invalid</string>
+			<key>scope</key>
+			<string>keyword.invalid</string>
+			<key>settings</key>
+			<dict>
+				<key>fontStyle</key>
+				<string>bold</string>
+				<key>foreground</key>
+				<string>hsl(0, 100%, 80%)</string>
+				<key>background</key>
+				<string>hsl(0, 100%, 20%)</string>
+			</dict>
+		</dict>
+
 		<dict>
 			<key>name</key>
 			<string>Rule options</string>
 			<key>scope</key>
 			<string>rule.options</string>
@@ -344,10 +420,21 @@
 				<string>italic</string>
 				<key>foreground</key>
 				<string>#A0A0A0</string>
 			</dict>
 		</dict>
+		<dict>
+			<key>name</key>
+			<string>Rule name</string>
+			<key>scope</key>
+			<string>rule.rulename2</string>
+			<key>settings</key>
+			<dict>
+				<key>foreground</key>
+				<string>#F0D080</string>
+			</dict>
+		</dict>
 		<dict>
 			<key>name</key>
 			<string>Rule priority</string>
 			<key>scope</key>
 			<string>rule.priority</string>
@@ -356,10 +443,63 @@
 				<key>foreground</key>
 				<string>#F06060</string>
 			</dict>
 		</dict>
 		
+		<dict>
+			<key>name</key>
+			<string>String lemma</string>
+			<key>scope</key>
+			<string>string.lemma</string>
+			<key>settings</key>
+			<dict>
+				<key>foreground</key>
+				<string>hsl(210, 100%, 80%)</string>
+				<key>background</key>
+				<string>hsl(210, 100%, 15%)</string>
+			</dict>
+		</dict>
+		<dict>
+			<key>name</key>
+			<string>String regex</string>
+			<key>scope</key>
+			<string>string.regex</string>
+			<key>settings</key>
+			<dict>
+				<key>foreground</key>
+				<string>hsl(60, 100%, 80%)</string>
+				<key>background</key>
+				<string>hsl(60, 100%, 10%)</string>
+			</dict>
+		</dict>
+		<dict>
+			<key>name</key>
+			<string>String morph pattern</string>
+			<key>scope</key>
+			<string>string.morph.pattern</string>
+			<key>settings</key>
+			<dict>
+				<key>foreground</key>
+				<string>hsl(150, 80%, 90%)</string>
+				<key>background</key>
+				<string>hsl(150, 80%, 10%)</string>
+			</dict>
+		</dict>
+		<dict>
+			<key>name</key>
+			<string>String morph antipattern</string>
+			<key>scope</key>
+			<string>string.morph.antipattern</string>
+			<key>settings</key>
+			<dict>
+				<key>foreground</key>
+				<string>hsl(0, 80%, 90%)</string>
+				<key>background</key>
+				<string>hsl(0, 80%, 10%)</string>
+			</dict>
+		</dict>
+
 
 		<dict>
 			<key>name</key>
 			<string>JavaScript Dollar</string>
 			<key>scope</key>