Grammalecte  Changes On Branch c8734856cf5ae6bf

Changes In Branch mtok Through [c8734856cf] Excluding Merge-Ins

This is equivalent to a diff from f831e2b562 to c8734856cf

2021-03-16
18:13
[fr] ajustements: locutions adverbiales check-in: 66c9bda313 user: olr tags: fr, mtok
18:12
[core] gc engine: fix bug for multi-token detection check-in: c8734856cf user: olr tags: core, mtok
2021-03-13
11:34
[fr] ajustements check-in: b62d7a4807 user: olr tags: fr, mtok
2021-03-10
20:22
[fr] faux positif check-in: 0915b69239 user: olr tags: trunk, fr
2021-03-09
17:20
[misc] SublimeText syntaxic color check-in: 126b183b9d user: olr tags: misc, mtok
07:23
[fr] ajustements check-in: f831e2b562 user: olr tags: trunk, fr
2021-03-06
23:10
[fr] ajustements check-in: 199d498ccf user: olr tags: trunk, fr

Modified compile_rules_graph.py from [82cd1181fb] to [523cbe02bb].

34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
def rewriteCode (sCode):
    "convert simple code syntax to a string of Python code"
    if sCode[0:1] == "=":
        sCode = sCode[1:]
    sCode = sCode.replace("__also__", "bCondMemo")
    sCode = sCode.replace("__else__", "not bCondMemo")
    sCode = sCode.replace("sContext", "_sAppContext")
    sCode = re.sub(r"\b(morph0?|morphVC|value|tag|meta|info)[(]\\(\d+)", 'g_\\1(lToken[nTokenOffset+\\2]', sCode)
    sCode = re.sub(r"\b(morph0?|morphVC|value|tag|meta|info)[(]\\-(\d+)", 'g_\\1(lToken[nLastToken-\\2+1]', sCode)
    sCode = re.sub(r"\b(select|define|definefrom|rewrite|addmorph|setmeta)[(][\\](\d+)", 'g_\\1(lToken[nTokenOffset+\\2]', sCode)
    sCode = re.sub(r"\b(select|define|definefrom|rewrite|addmorph|setmeta)[(][\\]-(\d+)", 'g_\\1(lToken[nLastToken-\\2+1]', sCode)
    sCode = re.sub(r"\b(agreement|suggAgree)[(][\\](\d+), *[\\](\d+)", 'g_\\1(lToken[nTokenOffset+\\2], lToken[nTokenOffset+\\3]', sCode)
    sCode = re.sub(r"\b(agreement|suggAgree)[(][\\](\d+), *[\\]-(\d+)", 'g_\\1(lToken[nTokenOffset+\\2], lToken[nLastToken-\\3+1]', sCode)
    sCode = re.sub(r"\b(agreement|suggAgree)[(][\\]-(\d+), *[\\](\d+)", 'g_\\1(lToken[nLastToken-\\2+1], lToken[nTokenOffset+\\3]', sCode)
    sCode = re.sub(r"\b(agreement|suggAgree)[(][\\]-(\d+), *[\\]-(\d+)", 'g_\\1(lToken[nLastToken-\\2+1], lToken[nLastToken-\\3+1]', sCode)
    sCode = re.sub(r"\b(tagbefore|tagafter)[(][\\](\d+)", 'g_\\1(lToken[nTokenOffset+\\2], dTags', sCode)







|
|







34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
def rewriteCode (sCode):
    "convert simple code syntax to a string of Python code"
    if sCode[0:1] == "=":
        sCode = sCode[1:]
    sCode = sCode.replace("__also__", "bCondMemo")
    sCode = sCode.replace("__else__", "not bCondMemo")
    sCode = sCode.replace("sContext", "_sAppContext")
    sCode = re.sub(r"\b(morph[0x]?|morphVC|value|tag|meta|info)[(]\\(\d+)", 'g_\\1(lToken[nTokenOffset+\\2]', sCode)
    sCode = re.sub(r"\b(morph[0x]?|morphVC|value|tag|meta|info)[(]\\-(\d+)", 'g_\\1(lToken[nLastToken-\\2+1]', sCode)
    sCode = re.sub(r"\b(select|define|definefrom|rewrite|addmorph|setmeta)[(][\\](\d+)", 'g_\\1(lToken[nTokenOffset+\\2]', sCode)
    sCode = re.sub(r"\b(select|define|definefrom|rewrite|addmorph|setmeta)[(][\\]-(\d+)", 'g_\\1(lToken[nLastToken-\\2+1]', sCode)
    sCode = re.sub(r"\b(agreement|suggAgree)[(][\\](\d+), *[\\](\d+)", 'g_\\1(lToken[nTokenOffset+\\2], lToken[nTokenOffset+\\3]', sCode)
    sCode = re.sub(r"\b(agreement|suggAgree)[(][\\](\d+), *[\\]-(\d+)", 'g_\\1(lToken[nTokenOffset+\\2], lToken[nLastToken-\\3+1]', sCode)
    sCode = re.sub(r"\b(agreement|suggAgree)[(][\\]-(\d+), *[\\](\d+)", 'g_\\1(lToken[nLastToken-\\2+1], lToken[nTokenOffset+\\3]', sCode)
    sCode = re.sub(r"\b(agreement|suggAgree)[(][\\]-(\d+), *[\\]-(\d+)", 'g_\\1(lToken[nLastToken-\\2+1], lToken[nLastToken-\\3+1]', sCode)
    sCode = re.sub(r"\b(tagbefore|tagafter)[(][\\](\d+)", 'g_\\1(lToken[nTokenOffset+\\2], dTags', sCode)
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
        if m:
            sOption = m.group(1)
            sAction = sAction[m.end():].strip()
        if nPriority == -1:
            nPriority = self.dOptPriority.get(sOption, 4)

        # valid action?
        m = re.search(r"(?P<action>[-=~/!>])(?P<start>-?\d+\.?|)(?P<end>:\.?-?\d+|)(?P<casing>:|)>>", sAction)
        if not m:
            print("\n# Error. No action found at: ", sLineId, sActionId)
            exit()

        # Condition
        sCondition = sAction[:m.start()].strip()
        if sCondition:







|







283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
        if m:
            sOption = m.group(1)
            sAction = sAction[m.end():].strip()
        if nPriority == -1:
            nPriority = self.dOptPriority.get(sOption, 4)

        # valid action?
        m = re.search(r"(?P<action>[-=~/!>&])(?P<start>-?\d+\.?|)(?P<end>:\.?-?\d+|)(?P<casing>:|)>>", sAction)
        if not m:
            print("\n# Error. No action found at: ", sLineId, sActionId)
            exit()

        # Condition
        sCondition = sAction[:m.start()].strip()
        if sCondition:
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
        checkTokenNumbers(sCondition, sActionId, nToken)    # check tokens in condition
        checkTokenNumbers(sAction, sActionId, nToken)       # check tokens in action

        if cAction == ">":
            ## no action, break loop if condition is False
            return [sLineId, sOption, sCondition, cAction, ""]

        if not sAction and cAction != "!":
            print(f"\n# Error in action at line <{sLineId}/{sActionId}>:  This action is empty.")
            exit()

        if sAction[0:1] != "=" and cAction != "=":
            checkIfThereIsCode(sAction, sActionId)

        if cAction == "-":







|







371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
        checkTokenNumbers(sCondition, sActionId, nToken)    # check tokens in condition
        checkTokenNumbers(sAction, sActionId, nToken)       # check tokens in action

        if cAction == ">":
            ## no action, break loop if condition is False
            return [sLineId, sOption, sCondition, cAction, ""]

        if not sAction and cAction not in "!#":
            print(f"\n# Error in action at line <{sLineId}/{sActionId}>:  This action is empty.")
            exit()

        if sAction[0:1] != "=" and cAction != "=":
            checkIfThereIsCode(sAction, sActionId)

        if cAction == "-":
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
                nToken = sAction.count("|") + 1
                if iStartAction > 0 and iEndAction > 0:
                    if (iEndAction - iStartAction + 1) != nToken:
                        print(f"\n# Error in action at line <{sLineId}/{sActionId}>: numbers of modified tokens modified.")
                elif iStartAction < 0 or iEndAction < 0 and iStartAction != iEndAction:
                    print(f"\n# Warning in action at line <{sLineId}/{sActionId}>: rewriting with possible token position modified.")
            return [sLineId, sOption, sCondition, cAction, sAction, iStartAction, iEndAction, bCaseSensitivity]
        if cAction in "!/":
            ## tags
            return [sLineId, sOption, sCondition, cAction, sAction, iStartAction, iEndAction]
        if cAction == "=":
            ## disambiguator
            sAction = self.createFunction("da", sAction)
            return [sLineId, sOption, sCondition, cAction, sAction]
        print("\n# Unknown action at ", sLineId, sActionId)







|







402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
                nToken = sAction.count("|") + 1
                if iStartAction > 0 and iEndAction > 0:
                    if (iEndAction - iStartAction + 1) != nToken:
                        print(f"\n# Error in action at line <{sLineId}/{sActionId}>: numbers of modified tokens modified.")
                elif iStartAction < 0 or iEndAction < 0 and iStartAction != iEndAction:
                    print(f"\n# Warning in action at line <{sLineId}/{sActionId}>: rewriting with possible token position modified.")
            return [sLineId, sOption, sCondition, cAction, sAction, iStartAction, iEndAction, bCaseSensitivity]
        if cAction in "!/&":
            ## tags
            return [sLineId, sOption, sCondition, cAction, sAction, iStartAction, iEndAction]
        if cAction == "=":
            ## disambiguator
            sAction = self.createFunction("da", sAction)
            return [sLineId, sOption, sCondition, cAction, sAction]
        print("\n# Unknown action at ", sLineId, sActionId)
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
        elif sLine.startswith("        ||"):
            # tokens line continuation
            iPrevLine, sPrevLine = lTokenLine[-1]
            lTokenLine[-1] = [iPrevLine, sPrevLine + " " + sLine.strip()[2:]]
        elif sLine.startswith("        <<- "):
            # actions
            lActions.append([iLine, sLine[12:].strip()])
            if not re.search(r"[-=~/!>](?:-?\d\.?(?::\.?-?\d+|)|):?>>", sLine):
                bActionBlock = True
        elif sLine.startswith("        && "):
            # action message
            iPrevLine, sPrevLine = lActions[-1]
            lActions[-1] = [iPrevLine, sPrevLine + sLine]
        elif sLine.startswith("        ") and bActionBlock:
            # action line continuation
            iPrevLine, sPrevLine = lActions[-1]
            lActions[-1] = [iPrevLine, sPrevLine + " " + sLine.strip()]
            if re.search(r"[-=~/!>](?:-?\d\.?(?::\.?-?\d+|)|):?>>", sLine):
                bActionBlock = False
        elif re.match("[  ]*$", sLine):
            # empty line to end merging
            if not lTokenLine:
                continue
            if bActionBlock or not lActions:
                print("# Error. No action found at line:", iLine)







|









|







541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
        elif sLine.startswith("        ||"):
            # tokens line continuation
            iPrevLine, sPrevLine = lTokenLine[-1]
            lTokenLine[-1] = [iPrevLine, sPrevLine + " " + sLine.strip()[2:]]
        elif sLine.startswith("        <<- "):
            # actions
            lActions.append([iLine, sLine[12:].strip()])
            if not re.search(r"[-=~/!>&](?:-?\d\.?(?::\.?-?\d+|)|):?>>", sLine):
                bActionBlock = True
        elif sLine.startswith("        && "):
            # action message
            iPrevLine, sPrevLine = lActions[-1]
            lActions[-1] = [iPrevLine, sPrevLine + sLine]
        elif sLine.startswith("        ") and bActionBlock:
            # action line continuation
            iPrevLine, sPrevLine = lActions[-1]
            lActions[-1] = [iPrevLine, sPrevLine + " " + sLine.strip()]
            if re.search(r"[-=~/!>&](?:-?\d\.?(?::\.?-?\d+|)|):?>>", sLine):
                bActionBlock = False
        elif re.match("[  ]*$", sLine):
            # empty line to end merging
            if not lTokenLine:
                continue
            if bActionBlock or not lActions:
                print("# Error. No action found at line:", iLine)

Modified darg.py from [f98928fa4d] to [6cee0c2543].

212
213
214
215
216
217
218
219
220
221

222
223
224
225
226
227
228
229
230
231


232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249


250
251
252
253
254
255
256
        # 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 = {}   # regex for token values
        dReMorph = {}   # regex for morph
        dMorph = {}     # simple search in morph

        dLemma = {}
        dPhonet = {}
        dMeta = {}
        dTag = {}
        dRule = {}
        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:
                dMorph[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:
                dPhonet[sArc[1:]] = oNode.__hash__()
            elif sArc.startswith("*") and len(sArc) > 1:
                dMeta[sArc[1:]] = oNode.__hash__()
            elif sArc.startswith("/") and len(sArc) > 1:
                dTag[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 dMorph:
            dNode["<morph>"] = dMorph
        if dLemma:
            dNode["<lemmas>"] = dLemma
        if dPhonet:
            dNode["<phonet>"] = dPhonet
        if dTag:







|
|
|
>










>
>


















>
>







212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
        # 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 = {}       # regex for token values
        dReMorph = {}       # regex for morph
        dMorph = {}         # simple search in morph
        dReMultiMorph = {}  # regex for morph in multi-tokens
        dLemma = {}
        dPhonet = {}
        dMeta = {}
        dTag = {}
        dRule = {}
        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:
                dMorph[sArc[1:]] = oNode.__hash__()
            elif sArc.startswith("&") and len(sArc) > 1:
                dReMultiMorph[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:
                dPhonet[sArc[1:]] = oNode.__hash__()
            elif sArc.startswith("*") and len(sArc) > 1:
                dMeta[sArc[1:]] = oNode.__hash__()
            elif sArc.startswith("/") and len(sArc) > 1:
                dTag[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 dReMultiMorph:
            dNode["<re_mmorph>"] = dReMultiMorph
        if dMorph:
            dNode["<morph>"] = dMorph
        if dLemma:
            dNode["<lemmas>"] = dLemma
        if dPhonet:
            dNode["<phonet>"] = dPhonet
        if dTag:

Modified gc_core/js/lang_core/gc_engine.js from [04cf68e8aa] to [28d62d5f91].

177
178
179
180
181
182
183



184
185
186
187
188
189
190
            s += `#${dToken["i"]}\t${dToken["nStart"]}:${dToken["nEnd"]}\t${dToken["sValue"]}\t${dToken["sType"]}`;
            if (dToken.hasOwnProperty("lMorph")) {
                s += "\t" + dToken["lMorph"].toString();
            }
            if (dToken.hasOwnProperty("aTags")) {
                s += "\t" + dToken["aTags"].toString();
            }



            s += "\n";
        }
        return s;
    }

    parse (sCountry="${country_default}", bDebug=false, dOptions=null, bContext=false, bFullInfo=false) {
        // analyses <sText> and returns an iterable of errors or (with option <bFullInfo>) a list of sentences with tokens and errors







>
>
>







177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
            s += `#${dToken["i"]}\t${dToken["nStart"]}:${dToken["nEnd"]}\t${dToken["sValue"]}\t${dToken["sType"]}`;
            if (dToken.hasOwnProperty("lMorph")) {
                s += "\t" + dToken["lMorph"].toString();
            }
            if (dToken.hasOwnProperty("aTags")) {
                s += "\t" + dToken["aTags"].toString();
            }
            if (dToken.hasOwnProperty("nMultiStartTo")) {
                s += "\t>>" + dToken["nMultiStartTo"].toString();
            }
            s += "\n";
        }
        return s;
    }

    parse (sCountry="${country_default}", bDebug=false, dOptions=null, bContext=false, bFullInfo=false) {
        // analyses <sText> and returns an iterable of errors or (with option <bFullInfo>) a list of sentences with tokens and errors
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
                }
            }
            else if (!sOption || option(sOption)) {
                for (let [zRegex, bUppercase, sLineId, sRuleId, nPriority, lActions, lGroups, lNegLookBefore] of lRuleGroup) {
                    if (!gc_engine.aIgnoredRules.has(sRuleId)) {
                        while ((m = zRegex.gl_exec2(sText, lGroups, lNegLookBefore)) !== null) {
                            let bCondMemo = null;
                            for (let [sFuncCond, cActionType, sWhat, ...eAct] of lActions) {
                                // action in lActions: [ condition, action type, replacement/suggestion/action[, iGroup[, message, URL]] ]
                                try {
                                    bCondMemo = (!sFuncCond || gc_functions[sFuncCond](sText, sText0, m, this.dTokenPos, sCountry, bCondMemo));
                                    if (bCondMemo) {
                                        switch (cActionType) {
                                            case "-":
                                                // grammar error
                                                //console.log("-> error detected in " + sLineId + "\nzRegex: " + zRegex.source);
                                                let nErrorStart = nOffset + m.start[eAct[0]];
                                                if (!this.dError.has(nErrorStart) || nPriority > this.dErrorPriority.get(nErrorStart)) {
                                                    this.dError.set(nErrorStart, this._createErrorFromRegex(sText, sText0, sWhat, nOffset, m, eAct[0], sLineId, sRuleId, bUppercase, eAct[1], eAct[2], bShowRuleId, sOption, bContext));
                                                    this.dErrorPriority.set(nErrorStart, nPriority);
                                                    this.dSentenceError.set(nErrorStart, this.dError.get(nErrorStart));
                                                }
                                                break;
                                            case "~":
                                                // text processor
                                                //console.log("-> text processor by " + sLineId + "\nzRegex: " + zRegex.source);
                                                sText = this.rewriteText(sText, sWhat, eAct[0], m, bUppercase);
                                                bChange = true;
                                                if (bDebug) {
                                                    console.log("~ " + sText + "  -- " + m[eAct[0]] + "  # " + sLineId);
                                                }
                                                break;
                                            case "=":
                                                // disambiguation
                                                //console.log("-> disambiguation by " + sLineId + "\nzRegex: " + zRegex.source);
                                                gc_functions[sWhat](sText, m, this.dTokenPos);
                                                if (bDebug) {
                                                    console.log("= " + m[0] + "  # " + sLineId, "\nDA:", this.dTokenPos);
                                                }
                                                break;
                                            case ">":
                                                // we do nothing, this test is just a condition to apply all following actions
                                                break;







|










|







|








|







300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
                }
            }
            else if (!sOption || option(sOption)) {
                for (let [zRegex, bUppercase, sLineId, sRuleId, nPriority, lActions, lGroups, lNegLookBefore] of lRuleGroup) {
                    if (!gc_engine.aIgnoredRules.has(sRuleId)) {
                        while ((m = zRegex.gl_exec2(sText, lGroups, lNegLookBefore)) !== null) {
                            let bCondMemo = null;
                            for (let [sFuncCond, cActionType, sAction, ...eAct] of lActions) {
                                // action in lActions: [ condition, action type, replacement/suggestion/action[, iGroup[, message, URL]] ]
                                try {
                                    bCondMemo = (!sFuncCond || gc_functions[sFuncCond](sText, sText0, m, this.dTokenPos, sCountry, bCondMemo));
                                    if (bCondMemo) {
                                        switch (cActionType) {
                                            case "-":
                                                // grammar error
                                                //console.log("-> error detected in " + sLineId + "\nzRegex: " + zRegex.source);
                                                let nErrorStart = nOffset + m.start[eAct[0]];
                                                if (!this.dError.has(nErrorStart) || nPriority > this.dErrorPriority.get(nErrorStart)) {
                                                    this.dError.set(nErrorStart, this._createErrorFromRegex(sText, sText0, sAction, nOffset, m, eAct[0], sLineId, sRuleId, bUppercase, eAct[1], eAct[2], bShowRuleId, sOption, bContext));
                                                    this.dErrorPriority.set(nErrorStart, nPriority);
                                                    this.dSentenceError.set(nErrorStart, this.dError.get(nErrorStart));
                                                }
                                                break;
                                            case "~":
                                                // text processor
                                                //console.log("-> text processor by " + sLineId + "\nzRegex: " + zRegex.source);
                                                sText = this.rewriteText(sText, sAction, eAct[0], m, bUppercase);
                                                bChange = true;
                                                if (bDebug) {
                                                    console.log("~ " + sText + "  -- " + m[eAct[0]] + "  # " + sLineId);
                                                }
                                                break;
                                            case "=":
                                                // disambiguation
                                                //console.log("-> disambiguation by " + sLineId + "\nzRegex: " + zRegex.source);
                                                gc_functions[sAction](sText, m, this.dTokenPos);
                                                if (bDebug) {
                                                    console.log("= " + m[0] + "  # " + sLineId, "\nDA:", this.dTokenPos);
                                                }
                                                break;
                                            case ">":
                                                // we do nothing, this test is just a condition to apply all following actions
                                                break;
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
        }
        if (bDebug) {
            console.log("UPDATE:");
            console.log(this.asString());
        }
    }

    * _getMatches (oGraph, oToken, oNode, bKeep=false) {
        // generator: return matches where <oToken> “values” match <oNode> arcs
        try {
            let bTokenFound = false;
            // token value
            if (oNode.hasOwnProperty(oToken["sValue"])) {
                yield [" ", oToken["sValue"], oNode[oToken["sValue"]]];
                bTokenFound = true;







|







390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
        }
        if (bDebug) {
            console.log("UPDATE:");
            console.log(this.asString());
        }
    }

    * _getNextNodes (oGraph, oToken, oNode, bKeep=false) {
        // generator: return matches where <oToken> “values” match <oNode> arcs
        try {
            let bTokenFound = false;
            // token value
            if (oNode.hasOwnProperty(oToken["sValue"])) {
                yield [" ", oToken["sValue"], oNode[oToken["sValue"]]];
                bTokenFound = true;
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601








602
603
604
605
606
607
608
609

610
611
612
613
614
615
616
617
618
619
620
621

622
623
624
625








626
627
628
629
630
631
632
            }
            if (!bTokenFound && bKeep) {
                yield [null, "", -1];
            }
            // JUMP
            // Warning! Recurssion!
            if (oNode.hasOwnProperty("<>")) {
                yield* this._getMatches(oGraph, oToken, oGraph[oNode["<>"]], bKeep=true);
            }
        }
        catch (e) {
            console.error(e);
        }
    }

    parseGraph (oGraph, sCountry="${country_default}", dOptions=null, bShowRuleId=false, bDebug=false, bContext=false) {
        // parse graph with tokens from the text and execute actions encountered
        let lPointer = [];
        let bTagAndRewrite = false;
        try {
            for (let [iToken, oToken] of this.lTokens.entries()) {
                if (bDebug) {
                    console.log("TOKEN: " + oToken["sValue"]);
                }
                // check arcs for each existing pointer
                let lNextPointer = [];
                for (let oPointer of lPointer) {








                    for (let [cActionType, sMatch, iNode] of this._getMatches(oGraph, oToken, oGraph[oPointer["iNode"]])) {
                        if (cActionType === null) {
                            lNextPointer.push(oPointer);
                            continue;
                        }
                        if (bDebug) {
                            console.log("  MATCH: " + cActionType + sMatch);
                        }

                        lNextPointer.push({ "iToken1": oPointer["iToken1"], "iNode": iNode });
                    }
                }
                lPointer = lNextPointer;
                // check arcs of first nodes
                for (let [cActionType, sMatch, iNode] of this._getMatches(oGraph, oToken, oGraph[0])) {
                    if (cActionType === null) {
                        continue;
                    }
                    if (bDebug) {
                        console.log("  MATCH: " + cActionType + sMatch);
                    }

                    lPointer.push({ "iToken1": iToken, "iNode": iNode });
                }
                // check if there is rules to check for each pointer
                for (let oPointer of lPointer) {








                    if (oGraph[oPointer["iNode"]].hasOwnProperty("<rules>")) {
                        let bChange = this._executeActions(oGraph, oGraph[oPointer["iNode"]]["<rules>"], oPointer["iToken1"]-1, iToken, dOptions, sCountry, bShowRuleId, bDebug, bContext);
                        if (bChange) {
                            bTagAndRewrite = true;
                        }
                    }
                }







|









|







|
|
>
>
>
>
>
>
>
>
|
|
|



|

>
|


|

|
|



|

>
|


|
>
>
>
>
>
>
>
>







578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
            }
            if (!bTokenFound && bKeep) {
                yield [null, "", -1];
            }
            // JUMP
            // Warning! Recurssion!
            if (oNode.hasOwnProperty("<>")) {
                yield* this._getNextNodes(oGraph, oToken, oGraph[oNode["<>"]], bKeep=true);
            }
        }
        catch (e) {
            console.error(e);
        }
    }

    parseGraph (oGraph, sCountry="${country_default}", dOptions=null, bShowRuleId=false, bDebug=false, bContext=false) {
        // parse graph with tokens from the text and execute actions encountered
        let lPointers = [];
        let bTagAndRewrite = false;
        try {
            for (let [iToken, oToken] of this.lTokens.entries()) {
                if (bDebug) {
                    console.log("TOKEN: " + oToken["sValue"]);
                }
                // check arcs for each existing pointer
                let lNextPointers = [];
                for (let oPointer of lPointers) {
                    if (oPointer["nMultiEnd"] != -1) {
                        if (oToken["i"] <= oPointer["nMultiEnd"]) {
                            lNextPointers.push(oPointer);
                        }
                        if (oToken["i"] != oPointer["nMultiEnd"]) {
                            continue;
                        }
                    }
                    for (let [cNodeType, sMatch, iNode] of this._getNextNodes(oGraph, oToken, oGraph[oPointer["iNode"]])) {
                        if (cNodeType === null) {
                            lNextPointers.push(oPointer);
                            continue;
                        }
                        if (bDebug) {
                            console.log("  MATCH: " + cNodeType + sMatch);
                        }
                        let nMultiEnd = (cNodeType != "&") ? -1 : dToken["nMultiStartTo"];
                        lNextPointers.push({ "iToken1": oPointer["iToken1"], "iNode": iNode, "nMultiEnd": nMultiEnd });
                    }
                }
                lPointers = lNextPointers;
                // check arcs of first nodes
                for (let [cNodeType, sMatch, iNode] of this._getNextNodes(oGraph, oToken, oGraph[0])) {
                    if (cNodeType === null) {
                        continue;
                    }
                    if (bDebug) {
                        console.log("  MATCH: " + cNodeType + sMatch);
                    }
                    let nMultiEnd = (cNodeType != "&") ? -1 : dToken["nMultiStartTo"];
                    lPointers.push({ "iToken1": iToken, "iNode": iNode, "nMultiEnd": nMultiEnd });
                }
                // check if there is rules to check for each pointer
                for (let oPointer of lPointers) {
                    if (oPointer["nMultiEnd"] != -1) {
                        if (oToken["i"] < oPointer["nMultiEnd"]) {
                            continue;
                        }
                        if (oToken["i"] >= oPointer["nMultiEnd"]) {
                            oPointer["nMultiEnd"] = -1;
                        }
                    }
                    if (oGraph[oPointer["iNode"]].hasOwnProperty("<rules>")) {
                        let bChange = this._executeActions(oGraph, oGraph[oPointer["iNode"]]["<rules>"], oPointer["iToken1"]-1, iToken, dOptions, sCountry, bShowRuleId, bDebug, bContext);
                        if (bChange) {
                            bTagAndRewrite = true;
                        }
                    }
                }
649
650
651
652
653
654
655
656
657
658
659
660
661

662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752



















753
754
755
756
757
758
759
760
        for (let [sLineId, nextNodeKey] of Object.entries(oNode)) {
            let bCondMemo = null;
            for (let sRuleId of oGraph[nextNodeKey]) {
                try {
                    if (bDebug) {
                        console.log("   >TRY: " + sRuleId + " " + sLineId);
                    }
                    let [_, sOption, sFuncCond, cActionType, sWhat, ...eAct] = gc_rules_graph.dRule[sRuleId];
                    // Suggestion    [ option, condition, "-", replacement/suggestion/action, iTokenStart, iTokenEnd, cStartLimit, cEndLimit, bCaseSvty, nPriority, sMessage, iURL ]
                    // TextProcessor [ option, condition, "~", replacement/suggestion/action, iTokenStart, iTokenEnd, bCaseSvty ]
                    // Disambiguator [ option, condition, "=", replacement/suggestion/action ]
                    // Tag           [ option, condition, "/", replacement/suggestion/action, iTokenStart, iTokenEnd ]
                    // Immunity      [ option, condition, "!", "",                            iTokenStart, iTokenEnd ]

                    // Test          [ option, condition, ">", "" ]
                    if (!sOption || dOptions.gl_get(sOption, false)) {
                        bCondMemo = !sFuncCond || gc_functions[sFuncCond](this.lTokens, nTokenOffset, nLastToken, sCountry, bCondMemo, this.dTags, this.sSentence, this.sSentence0);
                        if (bCondMemo) {
                            if (cActionType == "-") {
                                // grammar error
                                let [iTokenStart, iTokenEnd, cStartLimit, cEndLimit, bCaseSvty, nPriority, sMessage, iURL] = eAct;
                                let nTokenErrorStart = (iTokenStart > 0) ? nTokenOffset + iTokenStart : nLastToken + iTokenStart;
                                if (!this.lTokens[nTokenErrorStart].hasOwnProperty("sImmunity") || (this.lTokens[nTokenErrorStart]["sImmunity"] != "*" && !this.lTokens[nTokenErrorStart]["sImmunity"].includes(sOption))) {
                                    let nTokenErrorEnd = (iTokenEnd > 0) ? nTokenOffset + iTokenEnd : nLastToken + iTokenEnd;
                                    let nErrorStart = this.nOffsetWithinParagraph + ((cStartLimit == "<") ? this.lTokens[nTokenErrorStart]["nStart"] : this.lTokens[nTokenErrorStart]["nEnd"]);
                                    let nErrorEnd = this.nOffsetWithinParagraph + ((cEndLimit == ">") ? this.lTokens[nTokenErrorEnd]["nEnd"] : this.lTokens[nTokenErrorEnd]["nStart"]);
                                    if (!this.dError.has(nErrorStart) || nPriority > this.dErrorPriority.gl_get(nErrorStart, -1)) {
                                        this.dError.set(nErrorStart, this._createErrorFromTokens(sWhat, nTokenOffset, nLastToken, nTokenErrorStart, nErrorStart, nErrorEnd, sLineId, sRuleId, bCaseSvty,
                                                                                                 sMessage, gc_rules_graph.dURL[iURL], bShowRuleId, sOption, bContext));
                                        this.dErrorPriority.set(nErrorStart, nPriority);
                                        this.dSentenceError.set(nErrorStart, this.dError.get(nErrorStart));
                                        if (bDebug) {
                                            console.log("    NEW_ERROR: ",  this.dError.get(nErrorStart));
                                        }
                                    }
                                }
                            }
                            else if (cActionType == "~") {
                                // text processor
                                let nTokenStart = (eAct[0] > 0) ? nTokenOffset + eAct[0] : nLastToken + eAct[0];
                                let nTokenEnd = (eAct[1] > 0) ? nTokenOffset + eAct[1] : nLastToken + eAct[1];
                                this._tagAndPrepareTokenForRewriting(sWhat, nTokenStart, nTokenEnd, nTokenOffset, nLastToken, eAct[2], bDebug);
                                bChange = true;
                                if (bDebug) {
                                    console.log(`    TEXT_PROCESSOR: [${this.lTokens[nTokenStart]["sValue"]}:${this.lTokens[nTokenEnd]["sValue"]}]  > ${sWhat}`);
                                }
                            }
                            else if (cActionType == "=") {
                                // disambiguation
                                gc_functions[sWhat](this.lTokens, nTokenOffset, nLastToken);
                                if (bDebug) {
                                    console.log(`    DISAMBIGUATOR: (${sWhat})  [${this.lTokens[nTokenOffset+1]["sValue"]}:${this.lTokens[nLastToken]["sValue"]}]`);
                                }
                            }
                            else if (cActionType == ">") {
                                // we do nothing, this test is just a condition to apply all following actions
                                if (bDebug) {
                                    console.log("    COND_OK");
                                }
                            }
                            else if (cActionType == "/") {
                                // Tag
                                let nTokenStart = (eAct[0] > 0) ? nTokenOffset + eAct[0] : nLastToken + eAct[0];
                                let nTokenEnd = (eAct[1] > 0) ? nTokenOffset + eAct[1] : nLastToken + eAct[1];
                                for (let i = nTokenStart; i <= nTokenEnd; i++) {
                                    if (this.lTokens[i].hasOwnProperty("aTags")) {
                                        this.lTokens[i]["aTags"].add(...sWhat.split("|"))
                                    } else {
                                        this.lTokens[i]["aTags"] = new Set(sWhat.split("|"));
                                    }
                                }
                                if (bDebug) {
                                    console.log(`    TAG:  ${sWhat} > [${this.lTokens[nTokenStart]["sValue"]}:${this.lTokens[nTokenEnd]["sValue"]}]`);
                                }
                                for (let sTag of sWhat.split("|")) {
                                    if (!this.dTags.has(sTag)) {
                                        this.dTags.set(sTag, [nTokenStart, nTokenEnd]);
                                    } else {
                                        this.dTags.set(sTag, [Math.min(nTokenStart, this.dTags.get(sTag)[0]), Math.max(nTokenEnd, this.dTags.get(sTag)[1])]);
                                    }
                                }
                            }
                            else if (cActionType == "!") {
                                // immunity
                                if (bDebug) {
                                    console.log("    IMMUNITY: " + sLineId + " / " + sRuleId);
                                }
                                let nTokenStart = (eAct[0] > 0) ? nTokenOffset + eAct[0] : nLastToken + eAct[0];
                                let nTokenEnd = (eAct[1] > 0) ? nTokenOffset + eAct[1] : nLastToken + eAct[1];
                                let sImmunity = sWhat || "*";
                                if (nTokenEnd - nTokenStart == 0) {
                                    this.lTokens[nTokenStart]["sImmunity"] = sImmunity;
                                    let nErrorStart = this.nOffsetWithinParagraph + this.lTokens[nTokenStart]["nStart"];
                                    if (this.dError.has(nErrorStart)) {
                                        this.dError.delete(nErrorStart);
                                    }
                                } else {
                                    for (let i = nTokenStart;  i <= nTokenEnd;  i++) {
                                        this.lTokens[i]["sImmunity"] = sImmunity;
                                        let nErrorStart = this.nOffsetWithinParagraph + this.lTokens[i]["nStart"];
                                        if (this.dError.has(nErrorStart)) {
                                            this.dError.delete(nErrorStart);
                                        }
                                    }
                                }



















                            } else {
                                console.log("# error: unknown action at " + sLineId);
                            }
                        }
                        else if (cActionType == ">") {
                            if (bDebug) {
                                console.log("    COND_BREAK");
                            }







|





>













|













|


|




|

|














|

|



|

|














|















>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
|







670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
        for (let [sLineId, nextNodeKey] of Object.entries(oNode)) {
            let bCondMemo = null;
            for (let sRuleId of oGraph[nextNodeKey]) {
                try {
                    if (bDebug) {
                        console.log("   >TRY: " + sRuleId + " " + sLineId);
                    }
                    let [_, sOption, sFuncCond, cActionType, sAction, ...eAct] = gc_rules_graph.dRule[sRuleId];
                    // Suggestion    [ option, condition, "-", replacement/suggestion/action, iTokenStart, iTokenEnd, cStartLimit, cEndLimit, bCaseSvty, nPriority, sMessage, iURL ]
                    // TextProcessor [ option, condition, "~", replacement/suggestion/action, iTokenStart, iTokenEnd, bCaseSvty ]
                    // Disambiguator [ option, condition, "=", replacement/suggestion/action ]
                    // Tag           [ option, condition, "/", replacement/suggestion/action, iTokenStart, iTokenEnd ]
                    // Immunity      [ option, condition, "!", "",                            iTokenStart, iTokenEnd ]
                    // Immunity      [ option, condition, "&", "",                            iTokenStart, iTokenEnd ]
                    // Test          [ option, condition, ">", "" ]
                    if (!sOption || dOptions.gl_get(sOption, false)) {
                        bCondMemo = !sFuncCond || gc_functions[sFuncCond](this.lTokens, nTokenOffset, nLastToken, sCountry, bCondMemo, this.dTags, this.sSentence, this.sSentence0);
                        if (bCondMemo) {
                            if (cActionType == "-") {
                                // grammar error
                                let [iTokenStart, iTokenEnd, cStartLimit, cEndLimit, bCaseSvty, nPriority, sMessage, iURL] = eAct;
                                let nTokenErrorStart = (iTokenStart > 0) ? nTokenOffset + iTokenStart : nLastToken + iTokenStart;
                                if (!this.lTokens[nTokenErrorStart].hasOwnProperty("sImmunity") || (this.lTokens[nTokenErrorStart]["sImmunity"] != "*" && !this.lTokens[nTokenErrorStart]["sImmunity"].includes(sOption))) {
                                    let nTokenErrorEnd = (iTokenEnd > 0) ? nTokenOffset + iTokenEnd : nLastToken + iTokenEnd;
                                    let nErrorStart = this.nOffsetWithinParagraph + ((cStartLimit == "<") ? this.lTokens[nTokenErrorStart]["nStart"] : this.lTokens[nTokenErrorStart]["nEnd"]);
                                    let nErrorEnd = this.nOffsetWithinParagraph + ((cEndLimit == ">") ? this.lTokens[nTokenErrorEnd]["nEnd"] : this.lTokens[nTokenErrorEnd]["nStart"]);
                                    if (!this.dError.has(nErrorStart) || nPriority > this.dErrorPriority.gl_get(nErrorStart, -1)) {
                                        this.dError.set(nErrorStart, this._createErrorFromTokens(sAction, nTokenOffset, nLastToken, nTokenErrorStart, nErrorStart, nErrorEnd, sLineId, sRuleId, bCaseSvty,
                                                                                                 sMessage, gc_rules_graph.dURL[iURL], bShowRuleId, sOption, bContext));
                                        this.dErrorPriority.set(nErrorStart, nPriority);
                                        this.dSentenceError.set(nErrorStart, this.dError.get(nErrorStart));
                                        if (bDebug) {
                                            console.log("    NEW_ERROR: ",  this.dError.get(nErrorStart));
                                        }
                                    }
                                }
                            }
                            else if (cActionType == "~") {
                                // text processor
                                let nTokenStart = (eAct[0] > 0) ? nTokenOffset + eAct[0] : nLastToken + eAct[0];
                                let nTokenEnd = (eAct[1] > 0) ? nTokenOffset + eAct[1] : nLastToken + eAct[1];
                                this._tagAndPrepareTokenForRewriting(sAction, nTokenStart, nTokenEnd, nTokenOffset, nLastToken, eAct[2], bDebug);
                                bChange = true;
                                if (bDebug) {
                                    console.log(`    TEXT_PROCESSOR: [${this.lTokens[nTokenStart]["sValue"]}:${this.lTokens[nTokenEnd]["sValue"]}]  > ${sAction}`);
                                }
                            }
                            else if (cActionType == "=") {
                                // disambiguation
                                gc_functions[sAction](this.lTokens, nTokenOffset, nLastToken);
                                if (bDebug) {
                                    console.log(`    DISAMBIGUATOR: (${sAction})  [${this.lTokens[nTokenOffset+1]["sValue"]}:${this.lTokens[nLastToken]["sValue"]}]`);
                                }
                            }
                            else if (cActionType == ">") {
                                // we do nothing, this test is just a condition to apply all following actions
                                if (bDebug) {
                                    console.log("    COND_OK");
                                }
                            }
                            else if (cActionType == "/") {
                                // Tag
                                let nTokenStart = (eAct[0] > 0) ? nTokenOffset + eAct[0] : nLastToken + eAct[0];
                                let nTokenEnd = (eAct[1] > 0) ? nTokenOffset + eAct[1] : nLastToken + eAct[1];
                                for (let i = nTokenStart; i <= nTokenEnd; i++) {
                                    if (this.lTokens[i].hasOwnProperty("aTags")) {
                                        this.lTokens[i]["aTags"].add(...sAction.split("|"))
                                    } else {
                                        this.lTokens[i]["aTags"] = new Set(sAction.split("|"));
                                    }
                                }
                                if (bDebug) {
                                    console.log(`    TAG:  ${sAction} > [${this.lTokens[nTokenStart]["sValue"]}:${this.lTokens[nTokenEnd]["sValue"]}]`);
                                }
                                for (let sTag of sAction.split("|")) {
                                    if (!this.dTags.has(sTag)) {
                                        this.dTags.set(sTag, [nTokenStart, nTokenEnd]);
                                    } else {
                                        this.dTags.set(sTag, [Math.min(nTokenStart, this.dTags.get(sTag)[0]), Math.max(nTokenEnd, this.dTags.get(sTag)[1])]);
                                    }
                                }
                            }
                            else if (cActionType == "!") {
                                // immunity
                                if (bDebug) {
                                    console.log("    IMMUNITY: " + sLineId + " / " + sRuleId);
                                }
                                let nTokenStart = (eAct[0] > 0) ? nTokenOffset + eAct[0] : nLastToken + eAct[0];
                                let nTokenEnd = (eAct[1] > 0) ? nTokenOffset + eAct[1] : nLastToken + eAct[1];
                                let sImmunity = sAction || "*";
                                if (nTokenEnd - nTokenStart == 0) {
                                    this.lTokens[nTokenStart]["sImmunity"] = sImmunity;
                                    let nErrorStart = this.nOffsetWithinParagraph + this.lTokens[nTokenStart]["nStart"];
                                    if (this.dError.has(nErrorStart)) {
                                        this.dError.delete(nErrorStart);
                                    }
                                } else {
                                    for (let i = nTokenStart;  i <= nTokenEnd;  i++) {
                                        this.lTokens[i]["sImmunity"] = sImmunity;
                                        let nErrorStart = this.nOffsetWithinParagraph + this.lTokens[i]["nStart"];
                                        if (this.dError.has(nErrorStart)) {
                                            this.dError.delete(nErrorStart);
                                        }
                                    }
                                }
                            }
                            else if (cActionType == "&") {
                                // multi-tokens
                                let nTokenStart = (eAct[0] > 0) ? nTokenOffset + eAct[0] : nLastToken + eAct[0];
                                let nTokenEnd = (eAct[1] > 0) ? nTokenOffset + eAct[1] : nLastToken + eAct[1];
                                let oMultiToken = {
                                    "nTokenStart": nTokenStart,
                                    "nTokenEnd": nTokenEnd,
                                    "lTokens": this.lTokens.slice(nTokenStart, nTokenEnd+1),
                                    "lMorph": (sAction) ? sAction.split("|") : [":HM"]
                                }
                                this.lTokens[nTokenStart]["nMultiStartTo"] = this.lTokens[nTokenEnd]["i"];
                                this.lTokens[nTokenEnd]["nMultiEndFrom"] = this.lTokens[nTokenStart]["i"];
                                this.lTokens[nTokenStart]["oMultiToken"] = oMultiToken;
                                this.lTokens[nTokenEnd]["oMultiToken"] = oMultiToken;
                                if (bDebug) {
                                    console.log(`"    MULTI-TOKEN: ${sAction}  [${this.lTokens[nTokenStart]["sValue"]}:${this.lTokens[nTokenEnd]["sValue"]}]`);
                                }
                            }
                            else {
                                console.log("# error: unknown action at " + sLineId);
                            }
                        }
                        else if (cActionType == ">") {
                            if (bDebug) {
                                console.log("    COND_BREAK");
                            }
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
            sNew = sRepl.gl_expand(m);
            sNew = sNew + " ".repeat(ln-sNew.length);
        }
        //console.log(sText+"\nstart: "+m.start[iGroup]+" end:"+m.end[iGroup]);
        return sText.slice(0, m.start[iGroup]) + sNew + sText.slice(m.end[iGroup]);
    }

    _tagAndPrepareTokenForRewriting (sWhat, nTokenRewriteStart, nTokenRewriteEnd, nTokenOffset, nLastToken, bCaseSvty, bDebug) {
        // text processor: rewrite tokens between <nTokenRewriteStart> and <nTokenRewriteEnd> position
        if (sWhat === "*") {
            // purge text
            if (nTokenRewriteEnd - nTokenRewriteStart == 0) {
                this.lTokens[nTokenRewriteStart]["bToRemove"] = true;
            } else {
                for (let i = nTokenRewriteStart;  i <= nTokenRewriteEnd;  i++) {
                    this.lTokens[i]["bToRemove"] = true;
                }
            }
        }
        else if (sWhat === "␣") {
            // merge tokens
            this.lTokens[nTokenRewriteStart]["nMergeUntil"] = nTokenRewriteEnd;
        }
        else if (sWhat.startsWith("␣")) {
            sWhat = this._expand(sWhat, nTokenOffset, nLastToken);
            this.lTokens[nTokenRewriteStart]["nMergeUntil"] = nTokenRewriteEnd;
            this.lTokens[nTokenRewriteStart]["sMergedValue"] = sWhat.slice(1);
        }
        else if (sWhat === "_") {
            // neutralized token
            if (nTokenRewriteEnd - nTokenRewriteStart == 0) {
                this.lTokens[nTokenRewriteStart]["sNewValue"] = "_";
            } else {
                for (let i = nTokenRewriteStart;  i <= nTokenRewriteEnd;  i++) {
                    this.lTokens[i]["sNewValue"] = "_";
                }
            }
        }
        else {
            if (sWhat.startsWith("=")) {
                sWhat = gc_functions[sWhat.slice(1)](this.lTokens, nTokenOffset, nLastToken);
            } else {
                sWhat = this._expand(sWhat, nTokenOffset, nLastToken);
            }
            let bUppercase = bCaseSvty && this.lTokens[nTokenRewriteStart]["sValue"].slice(0,1).gl_isUpperCase();
            if (nTokenRewriteEnd - nTokenRewriteStart == 0) {
                // one token
                if (bUppercase) {
                    sWhat = sWhat.gl_toCapitalize();
                }
                this.lTokens[nTokenRewriteStart]["sNewValue"] = sWhat;
            }
            else {
                // several tokens
                let lTokenValue = sWhat.split("|");
                if (lTokenValue.length != (nTokenRewriteEnd - nTokenRewriteStart + 1)) {
                    if (bDebug) {
                        console.log("Error. Text processor: number of replacements != number of tokens.");
                    }
                    return;
                }
                let j = 0;







|

|









|



|
|

|

|










|
|

|





|

|



|







915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
            sNew = sRepl.gl_expand(m);
            sNew = sNew + " ".repeat(ln-sNew.length);
        }
        //console.log(sText+"\nstart: "+m.start[iGroup]+" end:"+m.end[iGroup]);
        return sText.slice(0, m.start[iGroup]) + sNew + sText.slice(m.end[iGroup]);
    }

    _tagAndPrepareTokenForRewriting (sAction, nTokenRewriteStart, nTokenRewriteEnd, nTokenOffset, nLastToken, bCaseSvty, bDebug) {
        // text processor: rewrite tokens between <nTokenRewriteStart> and <nTokenRewriteEnd> position
        if (sAction === "*") {
            // purge text
            if (nTokenRewriteEnd - nTokenRewriteStart == 0) {
                this.lTokens[nTokenRewriteStart]["bToRemove"] = true;
            } else {
                for (let i = nTokenRewriteStart;  i <= nTokenRewriteEnd;  i++) {
                    this.lTokens[i]["bToRemove"] = true;
                }
            }
        }
        else if (sAction === "␣") {
            // merge tokens
            this.lTokens[nTokenRewriteStart]["nMergeUntil"] = nTokenRewriteEnd;
        }
        else if (sAction.startsWith("␣")) {
            sAction = this._expand(sAction, nTokenOffset, nLastToken);
            this.lTokens[nTokenRewriteStart]["nMergeUntil"] = nTokenRewriteEnd;
            this.lTokens[nTokenRewriteStart]["sMergedValue"] = sAction.slice(1);
        }
        else if (sAction === "_") {
            // neutralized token
            if (nTokenRewriteEnd - nTokenRewriteStart == 0) {
                this.lTokens[nTokenRewriteStart]["sNewValue"] = "_";
            } else {
                for (let i = nTokenRewriteStart;  i <= nTokenRewriteEnd;  i++) {
                    this.lTokens[i]["sNewValue"] = "_";
                }
            }
        }
        else {
            if (sAction.startsWith("=")) {
                sAction = gc_functions[sAction.slice(1)](this.lTokens, nTokenOffset, nLastToken);
            } else {
                sAction = this._expand(sAction, nTokenOffset, nLastToken);
            }
            let bUppercase = bCaseSvty && this.lTokens[nTokenRewriteStart]["sValue"].slice(0,1).gl_isUpperCase();
            if (nTokenRewriteEnd - nTokenRewriteStart == 0) {
                // one token
                if (bUppercase) {
                    sAction = sAction.gl_toCapitalize();
                }
                this.lTokens[nTokenRewriteStart]["sNewValue"] = sAction;
            }
            else {
                // several tokens
                let lTokenValue = sAction.split("|");
                if (lTokenValue.length != (nTokenRewriteEnd - nTokenRewriteStart + 1)) {
                    if (bDebug) {
                        console.log("Error. Text processor: number of replacements != number of tokens.");
                    }
                    return;
                }
                let j = 0;

Modified gc_core/js/lang_core/gc_functions.js from [d31e5f26d5] to [ed16810cd6].

198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
        if (sValues.includes(sValue)) {
            return true;
        }
    }
    return false;
}

function g_morph (oToken, sPattern, sNegPattern="", nLeft=null, nRight=null, bMemorizeMorph=true) {
    // analyse a token, return True if <sNegPattern> not in morphologies and <sPattern> in morphologies
    let lMorph;
    if (oToken.hasOwnProperty("lMorph")) {
        lMorph = oToken["lMorph"];
    }
    else {
        if (nLeft !== null) {
            let sValue = (nRight !== null) ? oToken["sValue"].slice(nLeft, nRight) : oToken["sValue"].slice(nLeft);
            lMorph = gc_engine.oSpellChecker.getMorph(sValue);
            if (bMemorizeMorph) {
                oToken["lMorph"] = lMorph;
            }
        } else {
            lMorph = gc_engine.oSpellChecker.getMorph(oToken["sValue"]);
        }
    }
    if (lMorph.length == 0) {
        return false;
    }







|









<
<
<







198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214



215
216
217
218
219
220
221
        if (sValues.includes(sValue)) {
            return true;
        }
    }
    return false;
}

function g_morph (oToken, sPattern, sNegPattern="", nLeft=null, nRight=null) {
    // analyse a token, return True if <sNegPattern> not in morphologies and <sPattern> in morphologies
    let lMorph;
    if (oToken.hasOwnProperty("lMorph")) {
        lMorph = oToken["lMorph"];
    }
    else {
        if (nLeft !== null) {
            let sValue = (nRight !== null) ? oToken["sValue"].slice(nLeft, nRight) : oToken["sValue"].slice(nLeft);
            lMorph = gc_engine.oSpellChecker.getMorph(sValue);



        } else {
            lMorph = gc_engine.oSpellChecker.getMorph(oToken["sValue"]);
        }
    }
    if (lMorph.length == 0) {
        return false;
    }
234
235
236
237
238
239
240

























241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
            }
        }
    }
    // search sPattern
    return lMorph.some(sMorph  =>  (sMorph.search(sPattern) !== -1));
}


























function g_morph0 (oToken, sPattern, sNegPattern="", nLeft=null, nRight=null, bMemorizeMorph=true) {
    // analyse a token, return True if <sNegPattern> not in morphologies and <sPattern> in morphologies
    let lMorph;
    if (nLeft !== null) {
        let sValue = (nRight !== null) ? oToken["sValue"].slice(nLeft, nRight) : oToken["sValue"].slice(nLeft);
        lMorph = gc_engine.oSpellChecker.getMorph(sValue);
        if (bMemorizeMorph) {
            oToken["lMorph"] = lMorph;
        }
    } else {
        lMorph = gc_engine.oSpellChecker.getMorph(oToken["sValue"]);
    }
    if (lMorph.length == 0) {
        return false;
    }
    // check negative condition







>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
|





<
<
<







231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268



269
270
271
272
273
274
275
            }
        }
    }
    // search sPattern
    return lMorph.some(sMorph  =>  (sMorph.search(sPattern) !== -1));
}

function g_morphx (oToken, sPattern, sNegPattern="") {
    // analyse a multi-token, return True if <sNegPattern> not in morphologies and <sPattern> in morphologies
    if (!oToken.hasOwnProperty("oMultiToken")) {
        return false;
    }
    let lMorph = oToken["oMultiToken"]["lMorph"];
    if (lMorph.length == 0) {
        return false;
    }
    // check negative condition
    if (sNegPattern) {
        if (sNegPattern == "*") {
            // all morph must match sPattern
            return lMorph.every(sMorph  =>  (sMorph.search(sPattern) !== -1));
        }
        else {
            if (lMorph.some(sMorph  =>  (sMorph.search(sNegPattern) !== -1))) {
                return false;
            }
        }
    }
    // search sPattern
    return lMorph.some(sMorph  =>  (sMorph.search(sPattern) !== -1));
}

function g_morph0 (oToken, sPattern, sNegPattern="", nLeft=null, nRight=null) {
    // analyse a token, return True if <sNegPattern> not in morphologies and <sPattern> in morphologies
    let lMorph;
    if (nLeft !== null) {
        let sValue = (nRight !== null) ? oToken["sValue"].slice(nLeft, nRight) : oToken["sValue"].slice(nLeft);
        lMorph = gc_engine.oSpellChecker.getMorph(sValue);



    } else {
        lMorph = gc_engine.oSpellChecker.getMorph(oToken["sValue"]);
    }
    if (lMorph.length == 0) {
        return false;
    }
    // check negative condition

Modified gc_core/py/lang_core/gc_engine.py from [2b36e73536] to [db2ab5bd90].

250
251
252
253
254
255
256


257
258
259
260
261
262
263
        s += "now:      " + self.sSentence  + "\n"
        for dToken in self.lTokens:
            s += '#{i}\t{nStart}:{nEnd}\t{sValue}\t{sType}'.format(**dToken)
            if "lMorph" in dToken:
                s += "\t" + str(dToken["lMorph"])
            if "aTags" in dToken:
                s += "\t" + str(dToken["aTags"])


            s += "\n"
        #for nPos, dToken in self.dTokenPos.items():
        #    s += "{}\t{}\n".format(nPos, dToken)
        return s

    def parse (self, sCountry="${country_default}", bDebug=False, dOptions=None, bContext=False, bFullInfo=False):
        "analyses <sText> and returns an iterable of errors or (with option <bFullInfo>) paragraphs errors and sentences with tokens and errors"







>
>







250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
        s += "now:      " + self.sSentence  + "\n"
        for dToken in self.lTokens:
            s += '#{i}\t{nStart}:{nEnd}\t{sValue}\t{sType}'.format(**dToken)
            if "lMorph" in dToken:
                s += "\t" + str(dToken["lMorph"])
            if "aTags" in dToken:
                s += "\t" + str(dToken["aTags"])
            if "nMultiStartTo" in dToken:
                s += "\t>>" + str(dToken["nMultiStartTo"])
            s += "\n"
        #for nPos, dToken in self.dTokenPos.items():
        #    s += "{}\t{}\n".format(nPos, dToken)
        return s

    def parse (self, sCountry="${country_default}", bDebug=False, dOptions=None, bContext=False, bFullInfo=False):
        "analyses <sText> and returns an iterable of errors or (with option <bFullInfo>) paragraphs errors and sentences with tokens and errors"
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
                        sText = self.parseGraph(_rules_graph.dAllGraph[sGraphName], sCountry, dOptions, bShowRuleId, bDebug, bContext)
            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(sText):
                            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 getattr(gc_functions, sFuncCond)(sText, sText0, m, self.dTokenPos, sCountry, bCondMemo)
                                    if bCondMemo:
                                        if bDebug:
                                            echo("RULE: " + sLineId)
                                        if cActionType == "-":
                                            # grammar error
                                            nErrorStart = nOffset + m.start(eAct[0])
                                            if nErrorStart not in self.dError or nPriority > self.dErrorPriority.get(nErrorStart, -1):
                                                self.dError[nErrorStart] = self._createErrorFromRegex(sText, sText0, sWhat, nOffset, m, eAct[0], sLineId, sRuleId, bUppercase, eAct[1], eAct[2], bShowRuleId, sOption, bContext)
                                                self.dErrorPriority[nErrorStart] = nPriority
                                                self.dSentenceError[nErrorStart] = self.dError[nErrorStart]
                                        elif cActionType == "~":
                                            # text processor
                                            sText = self.rewriteText(sText, sWhat, eAct[0], m, bUppercase)
                                            bChange = True
                                            if bDebug:
                                                echo("~ " + sText + "  -- " + m.group(eAct[0]) + "  # " + sLineId)
                                        elif cActionType == "=":
                                            # disambiguation
                                            if not bParagraph:
                                                getattr(gc_functions, sWhat)(sText, m, self.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)







|










|




|






|







340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
                        sText = self.parseGraph(_rules_graph.dAllGraph[sGraphName], sCountry, dOptions, bShowRuleId, bDebug, bContext)
            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(sText):
                            bCondMemo = None
                            for sFuncCond, cActionType, sAction, *eAct in lActions:
                                # action in lActions: [ condition, action type, replacement/suggestion/action[, iGroup[, message, URL]] ]
                                try:
                                    bCondMemo = not sFuncCond or getattr(gc_functions, sFuncCond)(sText, sText0, m, self.dTokenPos, sCountry, bCondMemo)
                                    if bCondMemo:
                                        if bDebug:
                                            echo("RULE: " + sLineId)
                                        if cActionType == "-":
                                            # grammar error
                                            nErrorStart = nOffset + m.start(eAct[0])
                                            if nErrorStart not in self.dError or nPriority > self.dErrorPriority.get(nErrorStart, -1):
                                                self.dError[nErrorStart] = self._createErrorFromRegex(sText, sText0, sAction, nOffset, m, eAct[0], sLineId, sRuleId, bUppercase, eAct[1], eAct[2], bShowRuleId, sOption, bContext)
                                                self.dErrorPriority[nErrorStart] = nPriority
                                                self.dSentenceError[nErrorStart] = self.dError[nErrorStart]
                                        elif cActionType == "~":
                                            # text processor
                                            sText = self.rewriteText(sText, sAction, eAct[0], m, bUppercase)
                                            bChange = True
                                            if bDebug:
                                                echo("~ " + sText + "  -- " + m.group(eAct[0]) + "  # " + sLineId)
                                        elif cActionType == "=":
                                            # disambiguation
                                            if not bParagraph:
                                                getattr(gc_functions, sAction)(sText, m, self.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)
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
                dToken["aTags"] = self.dTokenPos[dToken["nStart"]]["aTags"]
        self.lTokens = lNewTokens
        self.dTokenPos = { dToken["nStart"]: dToken  for dToken in self.lTokens  if dToken["sType"] != "INFO" }
        if bDebug:
            echo("UPDATE:")
            echo(self)

    def _getMatches (self, dGraph, dToken, dNode, bKeep=False):
        "generator: return matches where <dToken> “values” match <dNode> arcs"
        bTokenFound = False
        # token value
        if dToken["sValue"] in dNode:
            yield (" ", dToken["sValue"], dNode[dToken["sValue"]])
            bTokenFound = True
        if dToken["sValue"][0:2].istitle(): # we test only 2 first chars, to match words such as "Laissez-les", "Crève-cœur".







|







396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
                dToken["aTags"] = self.dTokenPos[dToken["nStart"]]["aTags"]
        self.lTokens = lNewTokens
        self.dTokenPos = { dToken["nStart"]: dToken  for dToken in self.lTokens  if dToken["sType"] != "INFO" }
        if bDebug:
            echo("UPDATE:")
            echo(self)

    def _getNextNodes (self, dGraph, dToken, dNode, bKeep=False):
        "generator: return matches where <dToken> “values” match <dNode> arcs"
        bTokenFound = False
        # token value
        if dToken["sValue"] in dNode:
            yield (" ", dToken["sValue"], dNode[dToken["sValue"]])
            bTokenFound = True
        if dToken["sValue"][0:2].istitle(): # we test only 2 first chars, to match words such as "Laissez-les", "Crève-cœur".
505
506
507
508
509
510
511

























512
513
514
515
516
517
518
                                        bTokenFound = True
                            else:
                                if sNegPattern and any(re.search(sNegPattern, sMorph)  for sMorph in lMorph):
                                    continue
                                if not sPattern or any(re.search(sPattern, sMorph)  for sMorph in lMorph):
                                    yield ("@", sRegex, dNode["<re_morph>"][sRegex])
                                    bTokenFound = True

























        # token tags
        if "aTags" in dToken and "<tags>" in dNode:
            for sTag in dToken["aTags"]:
                if sTag in dNode["<tags>"]:
                    yield ("/", sTag, dNode["<tags>"][sTag])
                    bTokenFound = True
        # meta arc (for token type)







>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>







507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
                                        bTokenFound = True
                            else:
                                if sNegPattern and any(re.search(sNegPattern, sMorph)  for sMorph in lMorph):
                                    continue
                                if not sPattern or any(re.search(sPattern, sMorph)  for sMorph in lMorph):
                                    yield ("@", sRegex, dNode["<re_morph>"][sRegex])
                                    bTokenFound = True
            # regex multi morph arcs
            if "<re_mmorph>" in dNode:
                if "nMultiStartTo" in dToken:
                    lMorph = dToken["dMultiToken"]["lMorph"]
                    for sRegex in dNode["<re_mmorph>"]:
                        if "¬" not in sRegex:
                            # no anti-pattern
                            if any(re.search(sRegex, sMorph)  for sMorph in lMorph):
                                yield ("&", sRegex, dNode["<re_mmorph>"][sRegex])
                                bTokenFound = True
                        else:
                            # there is an anti-pattern
                            sPattern, sNegPattern = sRegex.split("¬", 1)
                            if sNegPattern == "*":
                                # all morphologies must match with <sPattern>
                                if sPattern:
                                    if all(re.search(sPattern, sMorph)  for sMorph in lMorph):
                                        yield ("&", sRegex, dNode["<re_mmorph>"][sRegex])
                                        bTokenFound = True
                            else:
                                if sNegPattern and any(re.search(sNegPattern, sMorph)  for sMorph in lMorph):
                                    continue
                                if not sPattern or any(re.search(sPattern, sMorph)  for sMorph in lMorph):
                                    yield ("&", sRegex, dNode["<re_mmorph>"][sRegex])
                                    bTokenFound = True
        # token tags
        if "aTags" in dToken and "<tags>" in dNode:
            for sTag in dToken["aTags"]:
                if sTag in dNode["<tags>"]:
                    yield ("/", sTag, dNode["<tags>"][sTag])
                    bTokenFound = True
        # meta arc (for token type)
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545





546
547
548
549
550
551

552
553
554
555
556
557
558
559

560
561
562
563



564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589

590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660

















661
662
663
664
665
666
667
                        yield ("*", sMeta, dNode["<meta>"][sMeta])
                        bTokenFound = True
        if not bTokenFound and bKeep:
            yield (None, "", -1)
        # JUMP
        # Warning! Recursion!
        if "<>" in dNode:
            yield from self._getMatches(dGraph, dToken, dGraph[dNode["<>"]], bKeep=True)

    def parseGraph (self, dGraph, sCountry="${country_default}", dOptions=None, bShowRuleId=False, bDebug=False, bContext=False):
        "parse graph with tokens from the text and execute actions encountered"
        lPointer = []
        bTagAndRewrite = False
        for iToken, dToken in enumerate(self.lTokens):
            if bDebug:
                echo("TOKEN: " + dToken["sValue"])
            # check arcs for each existing pointer
            lNextPointer = []
            for dPointer in lPointer:





                for cActionType, sMatch, iNode in self._getMatches(dGraph, dToken, dGraph[dPointer["iNode"]]):
                    if cActionType is None:
                        lNextPointer.append(dPointer)
                        continue
                    if bDebug:
                        echo("  MATCH: " + cActionType + sMatch)

                    lNextPointer.append({ "iToken1": dPointer["iToken1"], "iNode": iNode })
            lPointer = lNextPointer
            # check arcs of first nodes
            for cActionType, sMatch, iNode in self._getMatches(dGraph, dToken, dGraph[0]):
                if cActionType is None:
                    continue
                if bDebug:
                    echo("  MATCH: " + cActionType + sMatch)

                lPointer.append({ "iToken1": iToken, "iNode": iNode })
            # check if there is rules to check for each pointer
            for dPointer in lPointer:
                #if bDebug:



                #    echo("+", dPointer)
                if "<rules>" in dGraph[dPointer["iNode"]]:
                    bChange = self._executeActions(dGraph, dGraph[dPointer["iNode"]]["<rules>"], dPointer["iToken1"]-1, iToken, dOptions, sCountry, bShowRuleId, bDebug, bContext)
                    if bChange:
                        bTagAndRewrite = True
        if bTagAndRewrite:
            self.rewriteFromTags(bDebug)
        if bDebug:
            echo(self)
        return self.sSentence

    def _executeActions (self, dGraph, dNode, nTokenOffset, nLastToken, dOptions, sCountry, bShowRuleId, bDebug, bContext):
        "execute actions found in the DARG"
        bChange = False
        for sLineId, nextNodeKey in dNode.items():
            bCondMemo = None
            for sRuleId in dGraph[nextNodeKey]:
                try:
                    if bDebug:
                        echo("   >TRY: " + sRuleId + " " + sLineId)
                    _, sOption, sFuncCond, cActionType, sWhat, *eAct = _rules_graph.dRule[sRuleId]
                    # Suggestion    [ option, condition, "-", replacement/suggestion/action, iTokenStart, iTokenEnd, cStartLimit, cEndLimit, bCaseSvty, nPriority, sMessage, iURL ]
                    # TextProcessor [ option, condition, "~", replacement/suggestion/action, iTokenStart, iTokenEnd, bCaseSvty ]
                    # Disambiguator [ option, condition, "=", replacement/suggestion/action ]
                    # Tag           [ option, condition, "/", replacement/suggestion/action, iTokenStart, iTokenEnd ]
                    # Immunity      [ option, condition, "!", option,                        iTokenStart, iTokenEnd ]

                    # Test          [ option, condition, ">", "" ]
                    if not sOption or dOptions.get(sOption, False):
                        bCondMemo = not sFuncCond or getattr(gc_functions, sFuncCond)(self.lTokens, nTokenOffset, nLastToken, sCountry, bCondMemo, self.dTags, self.sSentence, self.sSentence0)
                        if bCondMemo:
                            if cActionType == "-":
                                # grammar error
                                iTokenStart, iTokenEnd, cStartLimit, cEndLimit, bCaseSvty, nPriority, sMessage, iURL = eAct
                                nTokenErrorStart = nTokenOffset + iTokenStart  if iTokenStart > 0  else nLastToken + iTokenStart
                                if "sImmunity" not in self.lTokens[nTokenErrorStart] or (self.lTokens[nTokenErrorStart]["sImmunity"] != "*" and sOption not in self.lTokens[nTokenErrorStart]["sImmunity"]):
                                    nTokenErrorEnd = nTokenOffset + iTokenEnd  if iTokenEnd > 0  else nLastToken + iTokenEnd
                                    nErrorStart = self.nOffsetWithinParagraph + (self.lTokens[nTokenErrorStart]["nStart"] if cStartLimit == "<"  else self.lTokens[nTokenErrorStart]["nEnd"])
                                    nErrorEnd = self.nOffsetWithinParagraph + (self.lTokens[nTokenErrorEnd]["nEnd"] if cEndLimit == ">"  else self.lTokens[nTokenErrorEnd]["nStart"])
                                    if nErrorStart not in self.dError or nPriority > self.dErrorPriority.get(nErrorStart, -1):
                                        self.dError[nErrorStart] = self._createErrorFromTokens(sWhat, nTokenOffset, nLastToken, nTokenErrorStart, nErrorStart, nErrorEnd, sLineId, sRuleId, bCaseSvty, \
                                                                                               sMessage, _rules_graph.dURL.get(iURL, ""), bShowRuleId, sOption, bContext)
                                        self.dErrorPriority[nErrorStart] = nPriority
                                        self.dSentenceError[nErrorStart] = self.dError[nErrorStart]
                                        if bDebug:
                                            echo("    NEW_ERROR: {}".format(self.dError[nErrorStart]))
                            elif cActionType == "~":
                                # text processor
                                nTokenStart = nTokenOffset + eAct[0]  if eAct[0] > 0  else nLastToken + eAct[0]
                                nTokenEnd = nTokenOffset + eAct[1]  if eAct[1] > 0  else nLastToken + eAct[1]
                                self._tagAndPrepareTokenForRewriting(sWhat, nTokenStart, nTokenEnd, nTokenOffset, nLastToken, eAct[2], bDebug)
                                bChange = True
                                if bDebug:
                                    echo("    TEXT_PROCESSOR: [{}:{}]  > {}".format(self.lTokens[nTokenStart]["sValue"], self.lTokens[nTokenEnd]["sValue"], sWhat))
                            elif cActionType == "=":
                                # disambiguation
                                getattr(gc_functions, sWhat)(self.lTokens, nTokenOffset, nLastToken)
                                if bDebug:
                                    echo("    DISAMBIGUATOR: ({})  [{}:{}]".format(sWhat, self.lTokens[nTokenOffset+1]["sValue"], self.lTokens[nLastToken]["sValue"]))
                            elif cActionType == ">":
                                # we do nothing, this test is just a condition to apply all following actions
                                if bDebug:
                                    echo("    COND_OK")
                            elif cActionType == "/":
                                # Tag
                                nTokenStart = nTokenOffset + eAct[0]  if eAct[0] > 0  else nLastToken + eAct[0]
                                nTokenEnd = nTokenOffset + eAct[1]  if eAct[1] > 0  else nLastToken + eAct[1]
                                for i in range(nTokenStart, nTokenEnd+1):
                                    if "aTags" in self.lTokens[i]:
                                        self.lTokens[i]["aTags"].update(sWhat.split("|"))
                                    else:
                                        self.lTokens[i]["aTags"] = set(sWhat.split("|"))
                                if bDebug:
                                    echo("    TAG: {} >  [{}:{}]".format(sWhat, self.lTokens[nTokenStart]["sValue"], self.lTokens[nTokenEnd]["sValue"]))
                                for sTag in sWhat.split("|"):
                                    if sTag not in self.dTags:
                                        self.dTags[sTag] = [nTokenStart, nTokenEnd]
                                    else:
                                        self.dTags[sTag][0] = min(nTokenStart, self.dTags[sTag][0])
                                        self.dTags[sTag][1] = max(nTokenEnd, self.dTags[sTag][1])
                            elif cActionType == "!":
                                # immunity
                                if bDebug:
                                    echo("    IMMUNITY: " + sLineId + " / " + sRuleId)
                                nTokenStart = nTokenOffset + eAct[0]  if eAct[0] > 0  else nLastToken + eAct[0]
                                nTokenEnd = nTokenOffset + eAct[1]  if eAct[1] > 0  else nLastToken + eAct[1]
                                sImmunity = sWhat or "*"
                                if nTokenEnd - nTokenStart == 0:
                                    self.lTokens[nTokenStart]["sImmunity"] = sImmunity
                                    nErrorStart = self.nOffsetWithinParagraph + self.lTokens[nTokenStart]["nStart"]
                                    if nErrorStart in self.dError:
                                        del self.dError[nErrorStart]
                                else:
                                    for i in range(nTokenStart, nTokenEnd+1):
                                        self.lTokens[i]["sImmunity"] = sImmunity
                                        nErrorStart = self.nOffsetWithinParagraph + self.lTokens[i]["nStart"]
                                        if nErrorStart in self.dError:
                                            del self.dError[nErrorStart]

















                            else:
                                echo("# error: unknown action at " + sLineId)
                        elif cActionType == ">":
                            if bDebug:
                                echo("    COND_BREAK")
                            break
                except Exception as e:







|



|





|
|
>
>
>
>
>
|
|
|


|
>
|
|

|
|


|
>
|

|
|
>
>
>
|



















|





>













|









|


|


|

|










|

|

|
|











|











>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>







554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
                        yield ("*", sMeta, dNode["<meta>"][sMeta])
                        bTokenFound = True
        if not bTokenFound and bKeep:
            yield (None, "", -1)
        # JUMP
        # Warning! Recursion!
        if "<>" in dNode:
            yield from self._getNextNodes(dGraph, dToken, dGraph[dNode["<>"]], bKeep=True)

    def parseGraph (self, dGraph, sCountry="${country_default}", dOptions=None, bShowRuleId=False, bDebug=False, bContext=False):
        "parse graph with tokens from the text and execute actions encountered"
        lPointers = []
        bTagAndRewrite = False
        for iToken, dToken in enumerate(self.lTokens):
            if bDebug:
                echo("TOKEN: " + dToken["sValue"])
            # check arcs for each existing pointer
            lNextPointers = []
            for dPointer in lPointers:
                if dPointer["nMultiEnd"] != -1:
                    if dToken["i"] <= dPointer["nMultiEnd"]:
                        lNextPointers.append(dPointer)
                    if dToken["i"] != dPointer["nMultiEnd"]:
                        continue
                for cNodeType, sMatch, iNode in self._getNextNodes(dGraph, dToken, dGraph[dPointer["iNode"]]):
                    if cNodeType is None:
                        lNextPointers.append(dPointer)
                        continue
                    if bDebug:
                        echo("  MATCH: " + cNodeType + sMatch)
                    nMultiEnd = -1  if cNodeType != "&"  else dToken["nMultiStartTo"]
                    lNextPointers.append({ "iToken1": dPointer["iToken1"], "iNode": iNode, "nMultiEnd": nMultiEnd })
            lPointers = lNextPointers
            # check arcs of first nodes
            for cNodeType, sMatch, iNode in self._getNextNodes(dGraph, dToken, dGraph[0]):
                if cNodeType is None:
                    continue
                if bDebug:
                    echo("  MATCH: " + cNodeType + sMatch)
                nMultiEnd = -1  if cNodeType != "&"  else dToken["nMultiStartTo"]
                lPointers.append({ "iToken1": iToken, "iNode": iNode, "nMultiEnd": nMultiEnd })
            # check if there is rules to check for each pointer
            for dPointer in lPointers:
                if dPointer["nMultiEnd"] != -1:
                    if dToken["i"] < dPointer["nMultiEnd"]:
                        continue
                    if dToken["i"] >= dPointer["nMultiEnd"]:
                        dPointer["nMultiEnd"] = -1
                if "<rules>" in dGraph[dPointer["iNode"]]:
                    bChange = self._executeActions(dGraph, dGraph[dPointer["iNode"]]["<rules>"], dPointer["iToken1"]-1, iToken, dOptions, sCountry, bShowRuleId, bDebug, bContext)
                    if bChange:
                        bTagAndRewrite = True
        if bTagAndRewrite:
            self.rewriteFromTags(bDebug)
        if bDebug:
            echo(self)
        return self.sSentence

    def _executeActions (self, dGraph, dNode, nTokenOffset, nLastToken, dOptions, sCountry, bShowRuleId, bDebug, bContext):
        "execute actions found in the DARG"
        bChange = False
        for sLineId, nextNodeKey in dNode.items():
            bCondMemo = None
            for sRuleId in dGraph[nextNodeKey]:
                try:
                    if bDebug:
                        echo("   >TRY: " + sRuleId + " " + sLineId)
                    _, sOption, sFuncCond, cActionType, sAction, *eAct = _rules_graph.dRule[sRuleId]
                    # Suggestion    [ option, condition, "-", replacement/suggestion/action, iTokenStart, iTokenEnd, cStartLimit, cEndLimit, bCaseSvty, nPriority, sMessage, iURL ]
                    # TextProcessor [ option, condition, "~", replacement/suggestion/action, iTokenStart, iTokenEnd, bCaseSvty ]
                    # Disambiguator [ option, condition, "=", replacement/suggestion/action ]
                    # Tag           [ option, condition, "/", replacement/suggestion/action, iTokenStart, iTokenEnd ]
                    # Immunity      [ option, condition, "!", option,                        iTokenStart, iTokenEnd ]
                    # Multi-token   [ option, condition, "&", morphologies,                  iTokenStart, iTokenEnd ]
                    # Test          [ option, condition, ">", "" ]
                    if not sOption or dOptions.get(sOption, False):
                        bCondMemo = not sFuncCond or getattr(gc_functions, sFuncCond)(self.lTokens, nTokenOffset, nLastToken, sCountry, bCondMemo, self.dTags, self.sSentence, self.sSentence0)
                        if bCondMemo:
                            if cActionType == "-":
                                # grammar error
                                iTokenStart, iTokenEnd, cStartLimit, cEndLimit, bCaseSvty, nPriority, sMessage, iURL = eAct
                                nTokenErrorStart = nTokenOffset + iTokenStart  if iTokenStart > 0  else nLastToken + iTokenStart
                                if "sImmunity" not in self.lTokens[nTokenErrorStart] or (self.lTokens[nTokenErrorStart]["sImmunity"] != "*" and sOption not in self.lTokens[nTokenErrorStart]["sImmunity"]):
                                    nTokenErrorEnd = nTokenOffset + iTokenEnd  if iTokenEnd > 0  else nLastToken + iTokenEnd
                                    nErrorStart = self.nOffsetWithinParagraph + (self.lTokens[nTokenErrorStart]["nStart"] if cStartLimit == "<"  else self.lTokens[nTokenErrorStart]["nEnd"])
                                    nErrorEnd = self.nOffsetWithinParagraph + (self.lTokens[nTokenErrorEnd]["nEnd"] if cEndLimit == ">"  else self.lTokens[nTokenErrorEnd]["nStart"])
                                    if nErrorStart not in self.dError or nPriority > self.dErrorPriority.get(nErrorStart, -1):
                                        self.dError[nErrorStart] = self._createErrorFromTokens(sAction, nTokenOffset, nLastToken, nTokenErrorStart, nErrorStart, nErrorEnd, sLineId, sRuleId, bCaseSvty, \
                                                                                               sMessage, _rules_graph.dURL.get(iURL, ""), bShowRuleId, sOption, bContext)
                                        self.dErrorPriority[nErrorStart] = nPriority
                                        self.dSentenceError[nErrorStart] = self.dError[nErrorStart]
                                        if bDebug:
                                            echo("    NEW_ERROR: {}".format(self.dError[nErrorStart]))
                            elif cActionType == "~":
                                # text processor
                                nTokenStart = nTokenOffset + eAct[0]  if eAct[0] > 0  else nLastToken + eAct[0]
                                nTokenEnd = nTokenOffset + eAct[1]  if eAct[1] > 0  else nLastToken + eAct[1]
                                self._tagAndPrepareTokenForRewriting(sAction, nTokenStart, nTokenEnd, nTokenOffset, nLastToken, eAct[2], bDebug)
                                bChange = True
                                if bDebug:
                                    echo("    TEXT_PROCESSOR: [{}:{}]  > {}".format(self.lTokens[nTokenStart]["sValue"], self.lTokens[nTokenEnd]["sValue"], sAction))
                            elif cActionType == "=":
                                # disambiguation
                                getattr(gc_functions, sAction)(self.lTokens, nTokenOffset, nLastToken)
                                if bDebug:
                                    echo("    DISAMBIGUATOR: ({})  [{}:{}]".format(sAction, self.lTokens[nTokenOffset+1]["sValue"], self.lTokens[nLastToken]["sValue"]))
                            elif cActionType == ">":
                                # we do nothing, this test is just a condition to apply all following actions
                                if bDebug:
                                    echo("    COND_OK")
                            elif cActionType == "/":
                                # Tag
                                nTokenStart = nTokenOffset + eAct[0]  if eAct[0] > 0  else nLastToken + eAct[0]
                                nTokenEnd = nTokenOffset + eAct[1]  if eAct[1] > 0  else nLastToken + eAct[1]
                                for i in range(nTokenStart, nTokenEnd+1):
                                    if "aTags" in self.lTokens[i]:
                                        self.lTokens[i]["aTags"].update(sAction.split("|"))
                                    else:
                                        self.lTokens[i]["aTags"] = set(sAction.split("|"))
                                if bDebug:
                                    echo("    TAG: {} >  [{}:{}]".format(sAction, self.lTokens[nTokenStart]["sValue"], self.lTokens[nTokenEnd]["sValue"]))
                                for sTag in sAction.split("|"):
                                    if sTag not in self.dTags:
                                        self.dTags[sTag] = [nTokenStart, nTokenEnd]
                                    else:
                                        self.dTags[sTag][0] = min(nTokenStart, self.dTags[sTag][0])
                                        self.dTags[sTag][1] = max(nTokenEnd, self.dTags[sTag][1])
                            elif cActionType == "!":
                                # immunity
                                if bDebug:
                                    echo("    IMMUNITY: " + sLineId + " / " + sRuleId)
                                nTokenStart = nTokenOffset + eAct[0]  if eAct[0] > 0  else nLastToken + eAct[0]
                                nTokenEnd = nTokenOffset + eAct[1]  if eAct[1] > 0  else nLastToken + eAct[1]
                                sImmunity = sAction or "*"
                                if nTokenEnd - nTokenStart == 0:
                                    self.lTokens[nTokenStart]["sImmunity"] = sImmunity
                                    nErrorStart = self.nOffsetWithinParagraph + self.lTokens[nTokenStart]["nStart"]
                                    if nErrorStart in self.dError:
                                        del self.dError[nErrorStart]
                                else:
                                    for i in range(nTokenStart, nTokenEnd+1):
                                        self.lTokens[i]["sImmunity"] = sImmunity
                                        nErrorStart = self.nOffsetWithinParagraph + self.lTokens[i]["nStart"]
                                        if nErrorStart in self.dError:
                                            del self.dError[nErrorStart]
                            elif cActionType == "&":
                                # multi-tokens
                                nTokenStart = nTokenOffset + eAct[0]  if eAct[0] > 0  else nLastToken + eAct[0]
                                nTokenEnd = nTokenOffset + eAct[1]  if eAct[1] > 0  else nLastToken + eAct[1]
                                dMultiToken = {
                                    "nTokenStart": nTokenStart,
                                    "nTokenEnd": nTokenEnd,
                                    "lTokens": self.lTokens[nTokenStart:nTokenEnd+1],
                                    "lMorph": sAction.split("|")  if sAction else  [":HM"]
                                }
                                self.lTokens[nTokenStart]["nMultiStartTo"] = self.lTokens[nTokenEnd]["i"]
                                self.lTokens[nTokenEnd]["nMultiEndFrom"] = self.lTokens[nTokenStart]["i"]
                                self.lTokens[nTokenStart]["dMultiToken"] = dMultiToken
                                self.lTokens[nTokenEnd]["dMultiToken"] = dMultiToken
                                if bDebug:
                                    echo("    MULTI-TOKEN: ({})  [{}:{}]".format(sAction, self.lTokens[nTokenStart]["sValue"], self.lTokens[nTokenEnd]["sValue"]))
                                #print(dMultiToken)
                            else:
                                echo("# error: unknown action at " + sLineId)
                        elif cActionType == ">":
                            if bDebug:
                                echo("    COND_BREAK")
                            break
                except Exception as e:
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
            if bUppercase and m.group(iGroup)[0:1].isupper():
                sNew = sNew.capitalize()
        else:
            sNew = m.expand(sRepl)
            sNew = sNew + " " * (nLen-len(sNew))
        return sText[0:m.start(iGroup)] + sNew + sText[m.end(iGroup):]

    def _tagAndPrepareTokenForRewriting (self, sWhat, nTokenRewriteStart, nTokenRewriteEnd, nTokenOffset, nLastToken, bCaseSvty, bDebug):
        "text processor: rewrite tokens between <nTokenRewriteStart> and <nTokenRewriteEnd> position"
        if sWhat == "*":
            # purge text
            if nTokenRewriteEnd - nTokenRewriteStart == 0:
                self.lTokens[nTokenRewriteStart]["bToRemove"] = True
            else:
                for i in range(nTokenRewriteStart, nTokenRewriteEnd+1):
                    self.lTokens[i]["bToRemove"] = True
        elif sWhat == "␣":
            # merge tokens
            self.lTokens[nTokenRewriteStart]["nMergeUntil"] = nTokenRewriteEnd
        elif sWhat.startswith("␣"):
            sWhat = self._expand(sWhat, nTokenOffset, nLastToken)
            self.lTokens[nTokenRewriteStart]["nMergeUntil"] = nTokenRewriteEnd
            self.lTokens[nTokenRewriteStart]["sMergedValue"] = sWhat[1:]
        elif sWhat == "_":
            # neutralized token
            if nTokenRewriteEnd - nTokenRewriteStart == 0:
                self.lTokens[nTokenRewriteStart]["sNewValue"] = "_"
            else:
                for i in range(nTokenRewriteStart, nTokenRewriteEnd+1):
                    self.lTokens[i]["sNewValue"] = "_"
        else:
            if sWhat.startswith("="):
                sWhat = getattr(gc_functions, sWhat[1:])(self.lTokens, nTokenOffset, nLastToken)
            else:
                sWhat = self._expand(sWhat, nTokenOffset, nLastToken)
            bUppercase = bCaseSvty and self.lTokens[nTokenRewriteStart]["sValue"][0:1].isupper()
            if nTokenRewriteEnd - nTokenRewriteStart == 0:
                # one token
                if bUppercase:
                    sWhat = sWhat[0:1].upper() + sWhat[1:]
                self.lTokens[nTokenRewriteStart]["sNewValue"] = sWhat
            else:
                # several tokens
                lTokenValue = sWhat.split("|")
                if len(lTokenValue) != (nTokenRewriteEnd - nTokenRewriteStart + 1):
                    if bDebug:
                        echo("Error. Text processor: number of replacements != number of tokens.")
                    return
                for i, sValue in zip(range(nTokenRewriteStart, nTokenRewriteEnd+1), lTokenValue):
                    if not sValue or sValue == "*":
                        self.lTokens[i]["bToRemove"] = True







|

|






|


|
|

|
|







|
|

|




|
|


|







826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
            if bUppercase and m.group(iGroup)[0:1].isupper():
                sNew = sNew.capitalize()
        else:
            sNew = m.expand(sRepl)
            sNew = sNew + " " * (nLen-len(sNew))
        return sText[0:m.start(iGroup)] + sNew + sText[m.end(iGroup):]

    def _tagAndPrepareTokenForRewriting (self, sAction, nTokenRewriteStart, nTokenRewriteEnd, nTokenOffset, nLastToken, bCaseSvty, bDebug):
        "text processor: rewrite tokens between <nTokenRewriteStart> and <nTokenRewriteEnd> position"
        if sAction == "*":
            # purge text
            if nTokenRewriteEnd - nTokenRewriteStart == 0:
                self.lTokens[nTokenRewriteStart]["bToRemove"] = True
            else:
                for i in range(nTokenRewriteStart, nTokenRewriteEnd+1):
                    self.lTokens[i]["bToRemove"] = True
        elif sAction == "␣":
            # merge tokens
            self.lTokens[nTokenRewriteStart]["nMergeUntil"] = nTokenRewriteEnd
        elif sAction.startswith("␣"):
            sAction = self._expand(sAction, nTokenOffset, nLastToken)
            self.lTokens[nTokenRewriteStart]["nMergeUntil"] = nTokenRewriteEnd
            self.lTokens[nTokenRewriteStart]["sMergedValue"] = sAction[1:]
        elif sAction == "_":
            # neutralized token
            if nTokenRewriteEnd - nTokenRewriteStart == 0:
                self.lTokens[nTokenRewriteStart]["sNewValue"] = "_"
            else:
                for i in range(nTokenRewriteStart, nTokenRewriteEnd+1):
                    self.lTokens[i]["sNewValue"] = "_"
        else:
            if sAction.startswith("="):
                sAction = getattr(gc_functions, sAction[1:])(self.lTokens, nTokenOffset, nLastToken)
            else:
                sAction = self._expand(sAction, nTokenOffset, nLastToken)
            bUppercase = bCaseSvty and self.lTokens[nTokenRewriteStart]["sValue"][0:1].isupper()
            if nTokenRewriteEnd - nTokenRewriteStart == 0:
                # one token
                if bUppercase:
                    sAction = sAction[0:1].upper() + sAction[1:]
                self.lTokens[nTokenRewriteStart]["sNewValue"] = sAction
            else:
                # several tokens
                lTokenValue = sAction.split("|")
                if len(lTokenValue) != (nTokenRewriteEnd - nTokenRewriteStart + 1):
                    if bDebug:
                        echo("Error. Text processor: number of replacements != number of tokens.")
                    return
                for i, sValue in zip(range(nTokenRewriteStart, nTokenRewriteEnd+1), lTokenValue):
                    if not sValue or sValue == "*":
                        self.lTokens[i]["bToRemove"] = True

Modified gc_core/py/lang_core/gc_functions.py from [069bca6c44] to [8b75051fd9].

184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217





















218
219
220
221
222
223
224
225
226
227
228
229
230
            return True
        sValue = sValue.lower()
        if sValue in sValues:
            return True
    return False


def g_morph (dToken, sPattern, sNegPattern="", nLeft=None, nRight=None, bMemorizeMorph=True):
    "analyse a token, return True if <sNegPattern> not in morphologies and <sPattern> in morphologies"
    if "lMorph" in dToken:
        lMorph = dToken["lMorph"]
    else:
        if nLeft is not None:
            lMorph = _oSpellChecker.getMorph(dToken["sValue"][slice(nLeft, nRight)])
            if bMemorizeMorph:
                dToken["lMorph"] = 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)
        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_morph0 (dToken, sPattern, sNegPattern="", nLeft=None, nRight=None, bMemorizeMorph=True):
    "analyse a token, return True if <sNegPattern> not in morphologies and <sPattern> in morphologies (disambiguation off)"
    if nLeft is not None:
        lMorph = _oSpellChecker.getMorph(dToken["sValue"][slice(nLeft, nRight)])
        if bMemorizeMorph:
            dToken["lMorph"] = lMorph
    else:
        lMorph = _oSpellChecker.getMorph(dToken["sValue"])
    if not lMorph:
        return False
    # check negative condition
    if sNegPattern:
        if sNegPattern == "*":







|






<
<


















>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
|



<
<







184
185
186
187
188
189
190
191
192
193
194
195
196
197


198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240


241
242
243
244
245
246
247
            return True
        sValue = sValue.lower()
        if sValue in sValues:
            return True
    return False


def g_morph (dToken, sPattern, sNegPattern="", nLeft=None, nRight=None):
    "analyse a token, return True if <sNegPattern> not in morphologies and <sPattern> in morphologies"
    if "lMorph" in dToken:
        lMorph = dToken["lMorph"]
    else:
        if nLeft is not None:
            lMorph = _oSpellChecker.getMorph(dToken["sValue"][slice(nLeft, nRight)])


        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)
        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_morphx (dToken, sPattern, sNegPattern=""):
    "analyse a multi-token, return True if <sNegPattern> not in morphologies and <sPattern> in morphologies"
    if not "dMultiToken" in dToken:
        return False
    lMorph = dToken["dMultiToken"]["lMorph"]
    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)
        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_morph0 (dToken, sPattern, sNegPattern="", nLeft=None, nRight=None):
    "analyse a token, return True if <sNegPattern> not in morphologies and <sPattern> in morphologies (disambiguation off)"
    if nLeft is not None:
        lMorph = _oSpellChecker.getMorph(dToken["sValue"][slice(nLeft, nRight)])


    else:
        lMorph = _oSpellChecker.getMorph(dToken["sValue"])
    if not lMorph:
        return False
    # check negative condition
    if sNegPattern:
        if sNegPattern == "*":

Modified gc_lang/fr/modules-js/cregex.js from [c1ba0b4cea] to [2376d50b6d].

78
79
80
81
82
83
84
85




86
87
88
89
90
91
92
    _zNPf: new RegExp(":(?:M[12P]|T):f"),
    _zNPe: new RegExp(":(?:M[12P]|T):e"),


    ///// FONCTIONS

    getLemmaOfMorph: function (sMorph) {
        return this._zLemma.exec(sMorph)[1];




    },

    agreement: function (l1, l2) {
        // returns True if agreement in gender and number is possible between morphologies <l1> and <l2>
        let [sGender1, sNumber1] = this.getGenderNumber(l1);
        let [sGender2, sNumber2] = this.getGenderNumber(l2);
        if (sNumber1 !== ":i" && sNumber2 !== ":i" && sNumber1 !== sNumber2) {







|
>
>
>
>







78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
    _zNPf: new RegExp(":(?:M[12P]|T):f"),
    _zNPe: new RegExp(":(?:M[12P]|T):e"),


    ///// FONCTIONS

    getLemmaOfMorph: function (sMorph) {
        let m = this._zLemma.exec(sMorph);
        if (m) {
            return m[1];
        }
        return "";
    },

    agreement: function (l1, l2) {
        // returns True if agreement in gender and number is possible between morphologies <l1> and <l2>
        let [sGender1, sNumber1] = this.getGenderNumber(l1);
        let [sGender2, sNumber2] = this.getGenderNumber(l2);
        if (sNumber1 !== ":i" && sNumber2 !== ":i" && sNumber1 !== sNumber2) {

Modified gc_lang/fr/modules-js/gce_analyseur.js from [7da47c7f30] to [233e133202].

9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
        if (oToken["sValue"].includes("-t-")) {
            nEnd = nEnd - 2;
        }
        else if (oToken["sValue"].search(/-l(?:es?|a)-(?:[mt]oi|nous|leur)$|(?:[nv]ous|lui|leur)-en$/) != -1) {
            nEnd = oToken["sValue"].slice(0,nEnd).lastIndexOf("-");
        }
    }
    return g_morph(oToken, sPattern, sNegPattern, 0, nEnd, false);
}

function apposition (sWord1, sWord2) {
    // returns true if nom + nom (no agreement required)
    return sWord2.length < 2 || (cregex.mbNomNotAdj(gc_engine.oSpellChecker.getMorph(sWord2)) && cregex.mbPpasNomNotAdj(gc_engine.oSpellChecker.getMorph(sWord1)));
}








|







9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
        if (oToken["sValue"].includes("-t-")) {
            nEnd = nEnd - 2;
        }
        else if (oToken["sValue"].search(/-l(?:es?|a)-(?:[mt]oi|nous|leur)$|(?:[nv]ous|lui|leur)-en$/) != -1) {
            nEnd = oToken["sValue"].slice(0,nEnd).lastIndexOf("-");
        }
    }
    return g_morph(oToken, sPattern, sNegPattern, 0, nEnd);
}

function apposition (sWord1, sWord2) {
    // returns true if nom + nom (no agreement required)
    return sWord2.length < 2 || (cregex.mbNomNotAdj(gc_engine.oSpellChecker.getMorph(sWord2)) && cregex.mbPpasNomNotAdj(gc_engine.oSpellChecker.getMorph(sWord1)));
}

Modified gc_lang/fr/modules/cregex.py from [d34aee52e1] to [412242b1dc].

78
79
80
81
82
83
84
85



86
87
88
89
90
91
92
NPe = re.compile(":(?:M[12P]|T):e")


#### FONCTIONS

def getLemmaOfMorph (s):
    "return lemma in morphology <s>"
    return Lemma.search(s).group(1)




def agreement (l1, l2):
    "returns True if agreement in gender and number is possible between morphologies <l1> and <l2>"
    sGender1, sNumber1 = getGenderNumber(l1)
    sGender2, sNumber2 = getGenderNumber(l2)
    if sNumber1 != ":i" and sNumber2 != ":i" and sNumber1 != sNumber2:
        return False







|
>
>
>







78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
NPe = re.compile(":(?:M[12P]|T):e")


#### FONCTIONS

def getLemmaOfMorph (s):
    "return lemma in morphology <s>"
    m = Lemma.search(s)
    if m:
        return m.group(1)
    return ""

def agreement (l1, l2):
    "returns True if agreement in gender and number is possible between morphologies <l1> and <l2>"
    sGender1, sNumber1 = getGenderNumber(l1)
    sGender2, sNumber2 = getGenderNumber(l2)
    if sNumber1 != ":i" and sNumber2 != ":i" and sNumber1 != sNumber2:
        return False

Modified gc_lang/fr/modules/gce_analyseur.py from [af51223cf8] to [edfe6ed977].

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
#### GRAMMAR CHECKING ENGINE PLUGIN: Parsing functions for French language

from . import cregex as cr


def g_morphVC (dToken, sPattern, sNegPattern=""):
    "lance la fonction g_morph() sur la première partie d’un verbe composé (ex: vient-il)"
    nEnd = dToken["sValue"].rfind("-")
    if dToken["sValue"].count("-") > 1:
        if "-t-" in dToken["sValue"]:
            nEnd = nEnd - 2
        elif re.search("-l(?:es?|a)-(?:[mt]oi|nous|leur)$|(?:[nv]ous|lui|leur)-en$", dToken["sValue"]):
            nEnd = dToken["sValue"][0:nEnd].rfind("-")
    return g_morph(dToken, sPattern, sNegPattern, 0, nEnd, False)


def apposition (sWord1, sWord2):
    "returns True if nom + nom (no agreement required)"
    return len(sWord2) < 2 or (cr.mbNomNotAdj(_oSpellChecker.getMorph(sWord2)) and cr.mbPpasNomNotAdj(_oSpellChecker.getMorph(sWord1)))















|







1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
#### GRAMMAR CHECKING ENGINE PLUGIN: Parsing functions for French language

from . import cregex as cr


def g_morphVC (dToken, sPattern, sNegPattern=""):
    "lance la fonction g_morph() sur la première partie d’un verbe composé (ex: vient-il)"
    nEnd = dToken["sValue"].rfind("-")
    if dToken["sValue"].count("-") > 1:
        if "-t-" in dToken["sValue"]:
            nEnd = nEnd - 2
        elif re.search("-l(?:es?|a)-(?:[mt]oi|nous|leur)$|(?:[nv]ous|lui|leur)-en$", dToken["sValue"]):
            nEnd = dToken["sValue"][0:nEnd].rfind("-")
    return g_morph(dToken, sPattern, sNegPattern, 0, nEnd)


def apposition (sWord1, sWord2):
    "returns True if nom + nom (no agreement required)"
    return len(sWord2) < 2 or (cr.mbNomNotAdj(_oSpellChecker.getMorph(sWord2)) and cr.mbPpasNomNotAdj(_oSpellChecker.getMorph(sWord1)))


Modified gc_lang/fr/rules.grx from [16699b2371] to [01d6ef4e84].

428
429
430
431
432
433
434

435
436
437
438
439
440
441
    [)]\b(?![s¹²³⁴⁵⁶⁷⁸⁹⁰]\b)
        <<- not before("\\((?:[rR][eéEÉ]|[qQ][uU]’|[nNmMtTsSdDlL]’)$") ->> ") "                               && Il manque un espace après la parenthèse.
__<s>/typo(typo_parenthèse_ouvrante_collée)__
    \b[(](?=[^)][^)][^)])
        <<- ->> " ("                                                                                && Il manque un espace avant la parenthèse.

TEST: C’est au fond du couloir{{(}}celui du deuxième étage{{)}}qu’il se trouve.     ->> " (|||) "

TEST: (a + b)²
TEST: il faut (re)former tout ça.
TEST: il (n’)est (qu’)ingénieur


# Points et espaces
__<s>/typo(typo_point_entre_deux_espaces)__      [  ][.](?=[  ])  <<- ->> .                         && Pas d’espace avant un point.







>







428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
    [)]\b(?![s¹²³⁴⁵⁶⁷⁸⁹⁰]\b)
        <<- not before("\\((?:[rR][eéEÉ]|[qQ][uU]’|[nNmMtTsSdDlL]’)$") ->> ") "                               && Il manque un espace après la parenthèse.
__<s>/typo(typo_parenthèse_ouvrante_collée)__
    \b[(](?=[^)][^)][^)])
        <<- ->> " ("                                                                                && Il manque un espace avant la parenthèse.

TEST: C’est au fond du couloir{{(}}celui du deuxième étage{{)}}qu’il se trouve.     ->> " (|||) "
TEST: de gain différentiel 𝐴 (𝑉ᵣ = 𝐴·𝑣H{{)}}et associé                              ->> ") "
TEST: (a + b)²
TEST: il faut (re)former tout ça.
TEST: il (n’)est (qu’)ingénieur


# Points et espaces
__<s>/typo(typo_point_entre_deux_espaces)__      [  ][.](?=[  ])  <<- ->> .                         && Pas d’espace avant un point.
1558
1559
1560
1561
1562
1563
1564























































1565
1566
1567
1568
1569
1570
1571

TEST: « Je suis donc perdu ? », dit Paul.
TEST: “C’est bon !”, croit savoir Marie.
TEST: “Parce que… ?” finit par demander Paul.
TEST: « Dans quel pays sommes-nous ? » demanda un manifestant.



























































!!
!!
!!
!!
!!







>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>







1559
1560
1561
1562
1563
1564
1565
1566
1567
1568
1569
1570
1571
1572
1573
1574
1575
1576
1577
1578
1579
1580
1581
1582
1583
1584
1585
1586
1587
1588
1589
1590
1591
1592
1593
1594
1595
1596
1597
1598
1599
1600
1601
1602
1603
1604
1605
1606
1607
1608
1609
1610
1611
1612
1613
1614
1615
1616
1617
1618
1619
1620
1621
1622
1623
1624
1625
1626
1627

TEST: « Je suis donc perdu ? », dit Paul.
TEST: “C’est bon !”, croit savoir Marie.
TEST: “Parce que… ?” finit par demander Paul.
TEST: « Dans quel pays sommes-nous ? » demanda un manifestant.


!!!! Purge des références aux notes                                                               !!

# les références aux notes
__<s>(p_exposants)__
    [¹²³⁴⁵⁶⁷⁸⁹⁰]+
        <<- ~>> *

__[i](p_références_aux_notes)__
    ({w_2})(\d+)  @@0,$
        <<- not morph(\0, ":") and morph(\1, ":") ~2>> *

TEST: POLITIQUESOCIÉTÉÉCONOMIEMONDECULTUREART DE VIVREMAGAZINE (qui peut faire boguer JavaScript avec certaines regex)


!!!! Normalisation du “t” euphonique                                                              !!

__<i]/tu(tu_t_euphonique_incorrect)__
    ([-–—− ]t(?:[’' ][-–—−]?|[-–—−][’' ]?))(ie?ls?|elles?|on|tu)  @@0,$
        <<- re.search("(?i)^(?:ie?ls|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-
__<i]/tu(tu_t_euphonique_superflu)__
    [td]([- ]t[-’' ])(?:ie?l|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-
__<i]/eleu(eleu_t_euphonique_manquant)__
    [aec](-(ie?l|elle|on))  @@1,2  <<- -1>> -t-\2               && Euphonie. 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-
TEST: A{{ t-}}elle faim ?                               ->> -t-
TEST: a{{ t'}}elle                                      ->> -t-
TEST: a{{-t'}}il                                        ->> -t-
TEST: a{{-t }}il.                                       ->> -t-
TEST: a{{ t’}}il.                                       ->> -t-
TEST: a{{ t-}}on.                                       ->> -t-
TEST: donne{{ t-}}il                                    ->> -t-
TEST: donne{{-t }}il                                    ->> -t-
TEST: vient{{-t-}}il                                    ->> -
TEST: viendras{{-t-}}tu                                 ->> -
TEST: Viendront{{ t-}}ils                               ->> -
TEST: viennent{{ t-}}ils                                ->> -
TEST: mangent{{-t-}}elles                               ->> -
TEST: Ont{{ t’}}ils                                     ->> -
TEST: Ont{{-t’}}ils                                     ->> -
TEST: l’ont{{ t’}}ils vu ?                              ->> -
TEST: exploite{{−t−}}il les ressources numériques       ->> -t-
TEST: vainc{{-il}} ses ennemis aisément                 ->> -t-il
TEST: Assis, gronde{{-t -}}elle                         ->> -t-
TEST: vient-il demain ?
TEST: prend-elle l’avantage ?
TEST: saura-t-on jamais la vérité ?
TEST: arrive-t-elle ce matin ?
TEST: y aura-t-il du poulet au dîner ?



!!
!!
!!
!!
!!
1635
1636
1637
1638
1639
1640
1641
1642
1643
1644
1645
1646
1647
1648
1649
1650
1651
1652
1653
1654
1655
1656
1657
1658
1659
1660
1661
1662
1663
1664
1665
1666
1667
1668
1669
1670
1671
1672
1673
1674
1675
1676
1677
1678
1679
1680
1681
1682
1683
1684
1685
1686
1687
1688
1689
1690
1691
1692
1693
1694
1695
1696
1697
1698
1699
1700
1701
1702
1703
1704
1705
1706
1707
1708
1709
1710
1711
1712
__[s]/num(num_lettre_O_zéro1)__  [\dO]+[O][\dO]+ <<- not option("ocr") ->> =\0.replace("O", "0")    && S’il s’agit d’un nombre, utilisez le chiffre « 0 » plutôt que la lettre « O ».
__[s]/num(num_lettre_O_zéro2)__  [1-9]O <<- not option("ocr") ->> =\0.replace("O", "0")             && S’il s’agit d’un nombre, utilisez le chiffre « 0 » plutôt que la lettre « O ».

TEST: année {{2O11}}                                                        ->> 2011
TEST: {{3O}} (chiffre avec un O).                                           ->> 30



!!!! Purge des références aux notes                                                               !!

# les références aux notes
__<s>(p_exposants)__
    [¹²³⁴⁵⁶⁷⁸⁹⁰]+
        <<- ~>> *

__[i](p_références_aux_notes)__
    ({w_2})(\d+)  @@0,$
        <<- not morph(\0, ":") and morph(\1, ":") ~2>> *

TEST: POLITIQUESOCIÉTÉÉCONOMIEMONDECULTUREART DE VIVREMAGAZINE (qui peut faire boguer JavaScript avec certaines regex)


!!!! Traits d’union                                                                               !!

__[i]/tu(tu_trait_union_douteux)__
    ({w1})(?:--|—|–|−|⁃)({w1})  @@0,$
    <<- spell(\1+"-"+\2) and analyse(\1+"-"+\2, ":") ->> \1-\2                                      && Trait d’union : un tiret simple suffit.

TEST: Nous préparons une {{contre–attaque}}.                    ->> contre-attaque
TEST: Nous préparons une {{contre−attaque}}.                    ->> contre-attaque


__<i]/tu(tu_t_euphonique_incorrect)__
    ([-–—− ]t(?:[’' ][-–—−]?|[-–—−][’' ]?))(ie?ls?|elles?|on|tu)  @@0,$
        <<- re.search("(?i)^(?:ie?ls|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-
__<i]/tu(tu_t_euphonique_superflu)__
    [td]([- ]t[-’' ])(?:ie?l|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-
__<i]/eleu(eleu_t_euphonique_manquant)__
    [aec](-(ie?l|elle|on))  @@1,2  <<- -1>> -t-\2               && Euphonie. 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-
TEST: A{{ t-}}elle faim ?                               ->> -t-
TEST: a{{ t'}}elle                                      ->> -t-
TEST: a{{-t'}}il                                        ->> -t-
TEST: a{{-t }}il.                                       ->> -t-
TEST: a{{ t’}}il.                                       ->> -t-
TEST: a{{ t-}}on.                                       ->> -t-
TEST: donne{{ t-}}il                                    ->> -t-
TEST: donne{{-t }}il                                    ->> -t-
TEST: vient{{-t-}}il                                    ->> -
TEST: viendras{{-t-}}tu                                 ->> -
TEST: Viendront{{ t-}}ils                               ->> -
TEST: viennent{{ t-}}ils                                ->> -
TEST: mangent{{-t-}}elles                               ->> -
TEST: Ont{{ t’}}ils                                     ->> -
TEST: Ont{{-t’}}ils                                     ->> -
TEST: l’ont{{ t’}}ils vu ?                              ->> -
TEST: exploite{{−t−}}il les ressources numériques       ->> -t-
TEST: vainc{{-il}} ses ennemis aisément                 ->> -t-il
TEST: Assis, gronde{{-t -}}elle                         ->> -t-
TEST: vient-il demain ?
TEST: prend-elle l’avantage ?
TEST: saura-t-on jamais la vérité ?
TEST: arrive-t-elle ce matin ?
TEST: y aura-t-il du poulet au dîner ?



@@@@
@@@@
@@@@
@@@@
@@@@GRAPH: graphe0|g0                                                                              _







<
<
<
<
<
<
<
<
<
<
<
<
<
<










<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<







1691
1692
1693
1694
1695
1696
1697














1698
1699
1700
1701
1702
1703
1704
1705
1706
1707








































1708
1709
1710
1711
1712
1713
1714
__[s]/num(num_lettre_O_zéro1)__  [\dO]+[O][\dO]+ <<- not option("ocr") ->> =\0.replace("O", "0")    && S’il s’agit d’un nombre, utilisez le chiffre « 0 » plutôt que la lettre « O ».
__[s]/num(num_lettre_O_zéro2)__  [1-9]O <<- not option("ocr") ->> =\0.replace("O", "0")             && S’il s’agit d’un nombre, utilisez le chiffre « 0 » plutôt que la lettre « O ».

TEST: année {{2O11}}                                                        ->> 2011
TEST: {{3O}} (chiffre avec un O).                                           ->> 30

















!!!! Traits d’union                                                                               !!

__[i]/tu(tu_trait_union_douteux)__
    ({w1})(?:--|—|–|−|⁃)({w1})  @@0,$
    <<- spell(\1+"-"+\2) and analyse(\1+"-"+\2, ":") ->> \1-\2                                      && Trait d’union : un tiret simple suffit.

TEST: Nous préparons une {{contre–attaque}}.                    ->> contre-attaque
TEST: Nous préparons une {{contre−attaque}}.                    ->> contre-attaque











































@@@@
@@@@
@@@@
@@@@
@@@@GRAPH: graphe0|g0                                                                              _
1768
1769
1770
1771
1772
1773
1774

1775
1776
1777
1778
1779
1780
1781


1782

1783
1784
1785
1786

1787
1788
1789
1790
1791
1792
1793
__purge_mot_nombre_parenthèses_crochets_accolades__
    (  [*WORD|*NUM]  )
    {  [*WORD|*NUM]  }
        <<- ~>> *

    ( … )
        <<- ->> […]                 && Pour indiquer une troncature de texte, on utilise usuellement des crochets.

        <<- ~>> *

    (  [re|ré]  )  *WORD
    [  [re|ré]  ]  *WORD
        <<- space(<1, 1) and space(\3, 0, 0) ~1:3>>  *

    [  *NUM  ]


    [  …  ]

        <<- ~>> *

    [  *WORD  ]
        <<- space(<1, 1, 3) and space(\-1, 1, 3) >>>

        <<- morph(\2, ":G") ~>> |\2|
        <<- __else__ ~>> *

    ## chapitres, références
    (  [*WORD|*NUM]  ,  *NUM  )
    [  [*WORD|*NUM]  ,  *NUM  ]
    {  [*WORD|*NUM]  ,  *NUM  }







>







>
>

>




>







1770
1771
1772
1773
1774
1775
1776
1777
1778
1779
1780
1781
1782
1783
1784
1785
1786
1787
1788
1789
1790
1791
1792
1793
1794
1795
1796
1797
1798
1799
1800
__purge_mot_nombre_parenthèses_crochets_accolades__
    (  [*WORD|*NUM]  )
    {  [*WORD|*NUM]  }
        <<- ~>> *

    ( … )
        <<- ->> […]                 && Pour indiquer une troncature de texte, on utilise usuellement des crochets.
        <<- />> _CUT_
        <<- ~>> *

    (  [re|ré]  )  *WORD
    [  [re|ré]  ]  *WORD
        <<- space(<1, 1) and space(\3, 0, 0) ~1:3>>  *

    [  *NUM  ]
        <<- ~>> *

    [  …  ]
        <<- />> _CUT_
        <<- ~>> *

    [  *WORD  ]
        <<- space(<1, 1, 3) and space(\-1, 1, 3) >>>
        <<- />> _CUT_
        <<- morph(\2, ":G") ~>> |\2|
        <<- __else__ ~>> *

    ## chapitres, références
    (  [*WORD|*NUM]  ,  *NUM  )
    [  [*WORD|*NUM]  ,  *NUM  ]
    {  [*WORD|*NUM]  ,  *NUM  }
2948
2949
2950
2951
2952
2953
2954

2955
2956
2957
2958
2959
2960
2961
    contre [moi|toi|soi|lui|elle|iel|nous|vous|eux|elles|iels]
        <<- =>> select(\1, ":R")

    [la|les|des|une|cette|ma|ta|sa|notre|votre|leur+s] >lutte       ?$:A¬:G¿  contre
    [>accusation|>action|>assaut|>attentat|>croisade|>guerre]       ?$:A¬:G¿  contre
    [>guerre|>jihad|offensive+s|>ratonnade|>sortilège|>violence]    ?$:A¬:G¿  contre
    [>antidote|>crime|>philtre|>remède|>sérum|>vaccin|>solution]    ?$:A¬:G¿  contre

        <<- =>> select(\-1, ":R")

    contre
        <<- not morph(<1, ":[DA].*:[me]:[si]") =>> select(\1, "", ":N")

__da_courant__
    courant







>







2955
2956
2957
2958
2959
2960
2961
2962
2963
2964
2965
2966
2967
2968
2969
    contre [moi|toi|soi|lui|elle|iel|nous|vous|eux|elles|iels]
        <<- =>> select(\1, ":R")

    [la|les|des|une|cette|ma|ta|sa|notre|votre|leur+s] >lutte       ?$:A¬:G¿  contre
    [>accusation|>action|>assaut|>attentat|>croisade|>guerre]       ?$:A¬:G¿  contre
    [>guerre|>jihad|offensive+s|>ratonnade|>sortilège|>violence]    ?$:A¬:G¿  contre
    [>antidote|>crime|>philtre|>remède|>sérum|>vaccin|>solution]    ?$:A¬:G¿  contre
    >mandat d’ arrêt                                                ?$:A¬:G¿  contre
        <<- =>> select(\-1, ":R")

    contre
        <<- not morph(<1, ":[DA].*:[me]:[si]") =>> select(\1, "", ":N")

__da_courant__
    courant
3671
3672
3673
3674
3675
3676
3677













































































3678
3679
3680
3681
3682
3683
3684
__tag_ni__
    ni
        <<- />> _ni_



!!!! Fusions et immunités                                                                         !!














































































__fusions_mots_grammaticaux__
    c’ est-à-dire
        <<- ~>> ␣
        <<- =>> setmeta(\1, "WORD")

    le ou la  *WORD







>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>







3679
3680
3681
3682
3683
3684
3685
3686
3687
3688
3689
3690
3691
3692
3693
3694
3695
3696
3697
3698
3699
3700
3701
3702
3703
3704
3705
3706
3707
3708
3709
3710
3711
3712
3713
3714
3715
3716
3717
3718
3719
3720
3721
3722
3723
3724
3725
3726
3727
3728
3729
3730
3731
3732
3733
3734
3735
3736
3737
3738
3739
3740
3741
3742
3743
3744
3745
3746
3747
3748
3749
3750
3751
3752
3753
3754
3755
3756
3757
3758
3759
3760
3761
3762
3763
3764
3765
3766
3767
3768
3769
__tag_ni__
    ni
        <<- />> _ni_



!!!! Fusions et immunités                                                                         !!


__locutions_prépositives__
    à [bord|bout|charge|cause|contre-courant|côté|défaut|destination|disposition|distance|droite]                       [de|du|d’|des]
    à [flanc|gauche|hauteur|mesure|mi-chemin|portée|proximité|propos|raison|rebours]                                    [de|du|d’|des]
    à [compter|partir]                                                                                                  [de|du|d’|des]
    à [base|concurrence|court|titre]                                                                                    [de|d’]
    à [bonne|faible|grande] distance                                                                                    [de|du|d’|des]
    à peu [de|d’] distance                                                                                              [de|du|d’|des]
    à l’ [abri|adresse|affût|affut|approche|arrière|attention|aune|aulne|avant]                                         [de|du|d’|des]
    à l’ [écart|égal|égard|encontre|endroit|entour|épreuve|est|exception|exclusion|exemple|extérieur]                   [de|du|d’|des]
    à l’ [imitation|insu|instar|instigation|intension|intérieur|inverse|issue|occasion|ombre|opposé|orée|ouest|unisson] [de|du|d’|des]
    à l’ autre bout                                                                                                     [de|du|d’|des]
    à la [barbe|différence|faveur|hauteur|lumière|manière|merci|portée|proximité|recherche|suite|tête|vue]              [de|du|d’|des]
    à la grande surprise                                                                                                [de|du|d’|des]
    à [quelques|plusieurs] [mètres|kilomètres|lieues|pas|minutes|heures]                                                [de|du|d’|des]
    à [quelques|plusieurs] [dizaines|centaines|milliers] [de|d’] [mètres|kilomètres]                                    [de|du|d’|des]
    à [base|force|raison]                                                                                               [de|d’]
    à grand renfort                                                                                                     [de|d’]
    à grands coups                                                                                                      [de|d’]
    au [bénéfice|bord|bout|chevet|contraire|courant|côté|cours|début|détour|détriment|fil|fond|gré|hasard|large|lieu]   [de|du|d’|des]
    au [long|mépris|moyen|nord|préjudice|profit|ras|rebours|regard|sein|secours|sommet|sud|sujet|travers|voisinage|vu]  [de|du|d’|des]
    ?d’¿ au ?beau¿ milieu                                                                                               [de|du|d’|des]
    au fin fond                                                                                                         [de|du|d’|des]
    au grand [dam|désespoir]                                                                                            [de|du|d’|des]
    au plus [près|profond]                                                                                              [de|du|d’|des]
    au fur et à mesure                                                                                                  [de|du|d’|des]
    au lieu et place                                                                                                    [de|du|d’|des]
    au vu et ?au¿ su                                                                                                    [de|du|d’|des]
    au nez et à la barbe                                                                                                [de|du|d’|des]
    au secours                                                                                                          [de|du|d’|des]
    au sortir                                                                                                           [de|du|d’|des]
    aux [abords|côtés|dépens]                                                                                           [de|du|d’|des]
    aux [dizaines|douzaines|vingtaines|centaines|milliers|millions|milliards|trillions|trilliards]                      [de|d’]
    aux [dizaines|douzaines|vingtaines|centaines|milliers|millions|milliards|trillions|trilliards]
        || [de|d’] [milliers|millions|milliards|trillions|trilliards]                                                   [de|d’]
    aux bons soins                                                                                                      [de|du|d’|des]
    aux lieu et place                                                                                                   [de|du|d’|des]
    compte tenu                                                                                                         [de|du|d’|des]
    d’ un bout à l’ autre                                                                                               [de|du|d’|des]
    dans l’ [attente|éventualité]                                                                                       [de|du|d’|des]
    dans la ligne de mire                                                                                               [de|du|d’|des]
    dans le sillage                                                                                                     [de|du|d’|des]
    des [quatre|4] coins                                                                                                [de|du|d’|des]
    en [arrière|attente|amont|arrière|aval|avant|bas|cas|conséquence|considération|cours|deçà|dehors|dépit|direction|face|faveur|fonction|haut|marge]  [de|du|d’|des]
    en [mémoire|possession|présence|pproportion|provenance|quête|raison|surplomb|sus|tête|termes|travers|vertu|vue]     [de|du|d’|des]
    en [ma|ta|sa|notre|votre|leur] qualité                                                                              [de|d’]
    en dépit de la proximité                                                                                            [de|du|d’|des]
    en [comparaison|forme|guise|matière|qualité|signe]                                                                  [de|d’]
    en flagrant délit                                                                                                   [de|d’]
    en [hommage|proie|réaction]                                                                                         [à|au|aux]
    en l’ [absence|attente|honneur]                                                                                     [de|du|d’|des]
    en lieu et place                                                                                                    [de|du|d’|des]
    en plein milieu                                                                                                     [de|du|d’|des]
    le long                                                                                                             [de|du|d’|des]
    jusqu’ à concurrence                                                                                                [de|d’]
    par égard                                                                                                           pour
    par le biais                                                                                                        [de|du|d’|des]
    par l’ [entremise|intermédiaire]                                                                                    [de|du|d’|des]
    par [manière|manque]                                                                                                [de|d’]
    par [crainte|habitude|suite]                                                                                        [de|du|d’|des]
    pour le compte                                                                                                      [de|du|d’|des]
    sans distinction                                                                                                    [de|d’]
    sans parler                                                                                                         [de|du|d’|des]
    sous l’ aile                                                                                                        [de|du|d’|des]
    sous l’ œil attentif                                                                                                [de|du|d’|des]
    sous la [coupe|houlette]                                                                                            [de|du|d’|des]
    sous le [coup|couvert|nez]                                                                                          [de|du|d’|des]
    sous [couleur|couvert|forme]                                                                                        [de|d’]
    sur les talons                                                                                                      [de|du|d’|des]
        <<- &>> :LR

    à travers $:D
    à travers >différent
        <<- =>> define(\2, ">travers/:ÉR")
        <<- &1:2>> :LR


__fusions_mots_grammaticaux__
    c’ est-à-dire
        <<- ~>> ␣
        <<- =>> setmeta(\1, "WORD")

    le ou la  *WORD
4899
4900
4901
4902
4903
4904
4905

4906
4907
4908
4909
4910
4911
4912
#    <<- \1 == \2
#        and not value(\2, "|nous|vous|faire|en|la|lui|donnant|œuvre|ah|oh|eh|hé|ho|ha|hou|olé|joli|Bora|couvent|dément|sapiens|très|vroum|")
#        and not (value(\1, "|est|une|") and value(<1, "|l’|d’|"))
#        and not (\2 == "mieux" and value(<1, "|qui|"))
#    ->> \1                                                                                          && Doublon.
#
#TEST: Il y a un {{doublon doublon}}.                                                                ->> doublon



!!
!!
!!!! Élisions & euphonie                                                                          !!
!!
!!







>







4984
4985
4986
4987
4988
4989
4990
4991
4992
4993
4994
4995
4996
4997
4998
#    <<- \1 == \2
#        and not value(\2, "|nous|vous|faire|en|la|lui|donnant|œuvre|ah|oh|eh|hé|ho|ha|hou|olé|joli|Bora|couvent|dément|sapiens|très|vroum|")
#        and not (value(\1, "|est|une|") and value(<1, "|l’|d’|"))
#        and not (\2 == "mieux" and value(<1, "|qui|"))
#    ->> \1                                                                                          && Doublon.
#
#TEST: Il y a un {{doublon doublon}}.                                                                ->> doublon



!!
!!
!!!! Élisions & euphonie                                                                          !!
!!
!!
15770
15771
15772
15773
15774
15775
15776

15777
15778


15779
15780
15781
15782
15783
15784




15785
15786
15787
15788
15789
15790
15791
    [dans|dès|depuis|de|d’] la      fin de l’ année ?[dernière|nouvelle|passée|précédente|prochaine|~^[0-9]{2,5}$]¿
    [dans|dès|depuis|de|d’] la      fin des années [dernières|passées|précédentes|prochaines|~^[0-9]{2,5}$]
    par rapport à la                fin de l’ an ?[dernier|nouveau|passé|précédent|prochain|~^[0-9]{2,5}$]¿
    par rapport à la                fin de l’ année ?[dernière|nouvelle|passée|précédente|prochaine|~^[0-9]{2,5}$]¿
    par rapport à la                fin des années [dernières|passées|précédentes|prochaines|~^[0-9]{2,5}$]
        <<- ~>> *


    en  l’  an  ~^[0-9]{2,5}$
    en  l’  an  de  grâce  ~^[0-9]{2,5}$


        <<- ~>> *

    en  ~^[0-9]{2,5}$  [,|<end>|)]
    en  ~^[0-9]{2,5}$  @:¬:[AN].*:[pi]
    de  ~^[0-9]{2,5}$  @:A.*:s
        <<- ~1:2>> *





    [de|d’] [plusieurs|quelques|$:B|*NUM] [ans|années|mois] [mon|ton|son|notre|votre|leur] [cadet|aîné+e]
    [de|d’] [plusieurs|quelques|$:B|*NUM] [ans|années|mois] [ma|ta|sa|notre|votre|leur] cadette
        <<- ~>> *


__purge_saisons__







>
|
|
>
>
|





>
>
>
>







15856
15857
15858
15859
15860
15861
15862
15863
15864
15865
15866
15867
15868
15869
15870
15871
15872
15873
15874
15875
15876
15877
15878
15879
15880
15881
15882
15883
15884
    [dans|dès|depuis|de|d’] la      fin de l’ année ?[dernière|nouvelle|passée|précédente|prochaine|~^[0-9]{2,5}$]¿
    [dans|dès|depuis|de|d’] la      fin des années [dernières|passées|précédentes|prochaines|~^[0-9]{2,5}$]
    par rapport à la                fin de l’ an ?[dernier|nouveau|passé|précédent|prochain|~^[0-9]{2,5}$]¿
    par rapport à la                fin de l’ année ?[dernière|nouvelle|passée|précédente|prochaine|~^[0-9]{2,5}$]¿
    par rapport à la                fin des années [dernières|passées|précédentes|prochaines|~^[0-9]{2,5}$]
        <<- ~>> *

    à [compter|partir] [de|d’]  ~^[0-9]{2,5}$
    [avant|après]               ~^[0-9]{2,5}$
    [dès|depuis]                ~^[0-9]{2,5}$
    jusqu’ à                    ~^[0-9]{2,5}$
    par rapport à               ~^[0-9]{2,5}$
        <<- not morph(>1, ":N|;S")~>> *

    en  ~^[0-9]{2,5}$  [,|<end>|)]
    en  ~^[0-9]{2,5}$  @:¬:[AN].*:[pi]
    de  ~^[0-9]{2,5}$  @:A.*:s
        <<- ~1:2>> *

    en  l’  an  ~^[0-9]{2,5}$
    en  l’  an  de  grâce  ~^[0-9]{2,5}$
        <<- ~>> *

    [de|d’] [plusieurs|quelques|$:B|*NUM] [ans|années|mois] [mon|ton|son|notre|votre|leur] [cadet|aîné+e]
    [de|d’] [plusieurs|quelques|$:B|*NUM] [ans|années|mois] [ma|ta|sa|notre|votre|leur] cadette
        <<- ~>> *


__purge_saisons__
18085
18086
18087
18088
18089
18090
18091
18092
18093
18094
18095
18096
18097
18098
18099
18100
18101
18102
18103
18104
18105
18106
18107
18108
18109
18110
18111
18112
18113
18114
18115
18116
18117
18118
18119
18120
18121
18122
18123
18124
18125
18126
18127
18128
18129
18130
18131
18132
18133
18134
18135
18136
18137
18138
18139
18140
18141
18142
18143
18144
18145
18146
18147
18148
18149
18150
18151
18152
18153
18154
18155
18156
18157
18158
18159
18160
18161
18162
18163
18164
18165
18166
18167
18168
18169
18170
18171
18172
18173
18174
18175
18176
    village [après|par] village
    voiture [après|par] voiture
    wagon [après|par] wagon
        <<- not morph(<1, ":D") ~>> *


__gestion_mots_grammaticaux__
    à [bord|bout|charge|cause|contre-courant|côté|défaut|destination|disposition|distance|droite]                       [de|du|d’|des]
    à [flanc|gauche|hauteur|mesure|mi-chemin|portée|proximité|propos|raison|rebours]                                    [de|du|d’|des]
    à [compter|partir]                                                                                                  [de|du|d’|des]
    à [base|concurrence|court|titre]                                                                                    [de|d’]
    à [bonne|faible|grande] distance                                                                                    [de|du|d’|des]
    à peu [de|d’] distance                                                                                              [de|du|d’|des]
    à l’ [abri|adresse|affût|affut|approche|arrière|attention|aune|aulne|avant]                                         [de|du|d’|des]
    à l’ [écart|égal|égard|encontre|endroit|entour|épreuve|est|exception|exclusion|exemple|extérieur]                   [de|du|d’|des]
    à l’ [imitation|insu|instar|instigation|intension|intérieur|inverse|issue|occasion|ombre|opposé|orée|ouest|unisson] [de|du|d’|des]
    à l’ autre bout                                                                                                     [de|du|d’|des]
    à la [barbe|différence|faveur|hauteur|lumière|manière|merci|portée|proximité|recherche|suite|tête|vue]              [de|du|d’|des]
    à la grande surprise                                                                                                [de|du|d’|des]
    à [quelques|plusieurs] [mètres|kilomètres|lieues|pas|minutes|heures]                                                [de|du|d’|des]
    à [quelques|plusieurs] [dizaines|centaines|milliers] [de|d’] [mètres|kilomètres]                                    [de|du|d’|des]
    à [base|force|raison]                                                                                               [de|d’]
    à grand renfort                                                                                                     [de|d’]
    à grands coups                                                                                                      [de|d’]
    au [bénéfice|bord|bout|chevet|contraire|courant|côté|cours|début|détour|détriment|fil|fond|gré|hasard|large|lieu]   [de|du|d’|des]
    au [long|mépris|moyen|nord|préjudice|profit|ras|rebours|regard|sein|secours|sommet|sud|sujet|travers|voisinage|vu]  [de|du|d’|des]
    ?d’¿ au ?beau¿ milieu                                                                                               [de|du|d’|des]
    au fin fond                                                                                                         [de|du|d’|des]
    au grand [dam|désespoir]                                                                                            [de|du|d’|des]
    au plus [près|profond]                                                                                              [de|du|d’|des]
    au fur et à mesure                                                                                                  [de|du|d’|des]
    au lieu et place                                                                                                    [de|du|d’|des]
    au vu et ?au¿ su                                                                                                    [de|du|d’|des]
    au nez et à la barbe                                                                                                [de|du|d’|des]
    au secours                                                                                                          [de|du|d’|des]
    au sortir                                                                                                           [de|du|d’|des]
    aux [abords|côtés|dépens]                                                                                           [de|du|d’|des]
    aux [dizaines|douzaines|vingtaines|centaines|milliers|millions|milliards|trillions|trilliards]                      [de|d’]
    aux [dizaines|douzaines|vingtaines|centaines|milliers|millions|milliards|trillions|trilliards]
        || [de|d’] [milliers|millions|milliards|trillions|trilliards]                                                   [de|d’]
    aux bons soins                                                                                                      [de|du|d’|des]
    aux lieu et place                                                                                                   [de|du|d’|des]
    compte tenu                                                                                                         [de|du|d’|des]
    d’ un bout à l’ autre                                                                                               [de|du|d’|des]
    dans l’ [attente|éventualité]                                                                                       [de|du|d’|des]
    dans la ligne de mire                                                                                               [de|du|d’|des]
    dans le sillage                                                                                                     [de|du|d’|des]
    des [quatre|4] coins                                                                                                [de|du|d’|des]
    en [arrière|attente|amont|arrière|aval|avant|bas|cas|conséquence|considération|cours|deçà|dehors|dépit|direction|face|faveur|fonction|haut|marge]  [de|du|d’|des]
    en [mémoire|possession|présence|pproportion|provenance|quête|raison|surplomb|sus|tête|termes|travers|vertu|vue]     [de|du|d’|des]
    en [ma|ta|sa|notre|votre|leur] qualité                                                                              [de|d’]
    en dépit de la proximité                                                                                            [de|du|d’|des]
    en [comparaison|forme|guise|matière|qualité|signe]                                                                  [de|d’]
    en flagrant délit                                                                                                   [de|d’]
    en [hommage|proie|réaction]                                                                                         [à|au|aux]
    en l’ [absence|attente|honneur]                                                                                     [de|du|d’|des]
    en lieu et place                                                                                                    [de|du|d’|des]
    en plein milieu                                                                                                     [de|du|d’|des]
    le long                                                                                                             [de|du|d’|des]
    jusqu’ à concurrence                                                                                                [de|d’]
    par égard                                                                                                           pour
    par le biais                                                                                                        [de|du|d’|des]
    par l’ [entremise|intermédiaire]                                                                                    [de|du|d’|des]
    par [manière|manque]                                                                                                [de|d’]
    par [crainte|habitude|suite]                                                                                        [de|du|d’|des]
    pour le compte                                                                                                      [de|du|d’|des]
    sans distinction                                                                                                    [de|d’]
    sans parler                                                                                                         [de|du|d’|des]
    sous l’ aile                                                                                                        [de|du|d’|des]
    sous l’ œil attentif                                                                                                [de|du|d’|des]
    sous la [coupe|houlette]                                                                                            [de|du|d’|des]
    sous le [coup|couvert|nez]                                                                                          [de|du|d’|des]
    sous [couleur|couvert|forme]                                                                                        [de|d’]
    sur les talons                                                                                                      [de|du|d’|des]
        <<- ~1:-2>> ␣
        <<- =>> define(\1, ":G:R:ÉR")

    tout au [bout|long] [de|du|d’|des]
        <<- not morph(<1, ":R") ~1>> *

    à travers $:D
    à travers >différent
        <<- ~1:2>> ␣
        <<- =>> define(\1, ":G:R:ÉR")

    ainsi
        <<- not value(>1, "|que|qu’|") ~>> *

    alentour
        <<- not morph(<1, ":D") ~>> *

    au moins  @:[DB]







<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<



<
<
<
<
<







18178
18179
18180
18181
18182
18183
18184






































































18185
18186
18187





18188
18189
18190
18191
18192
18193
18194
    village [après|par] village
    voiture [après|par] voiture
    wagon [après|par] wagon
        <<- not morph(<1, ":D") ~>> *


__gestion_mots_grammaticaux__






































































    tout au [bout|long] [de|du|d’|des]
        <<- not morph(<1, ":R") ~1>> *






    ainsi
        <<- not value(>1, "|que|qu’|") ~>> *

    alentour
        <<- not morph(<1, ":D") ~>> *

    au moins  @:[DB]
21066
21067
21068
21069
21070
21071
21072

21073
21074
21075
21076
21077
21078
21079
TEST: Il n’y avait plus qu’à faucher le blé noir, arracher les pommes de terre et gauler noix et châtaignes ; de ceci les gamins se chargeaient le jeudi et le dimanche, pour se distraire.
TEST: Les études révèlent un risque d’écrêtement du trafic ferroviaire dès 2025, lorsque la demande est la plus critique, c’est-à-dire le vendredi soir à l’heure de pointe.
TEST: Beaucoup de conciles émaneront des normes pour que soient nommés des curés idoines, qui soient vraiment au service de la paroisse, qui se distinguent par leur science et qui soient de vrais pasteurs au sein de la communauté de fidèles.
TEST: Les ailes de cet avion viennent de Grande-Bretagne.
TEST: La liberté passait sur le monde, l’internationale était sa voix criant par-dessus les frontières les revendications des déshérités.
TEST: La danse est d’ailleurs en ce sens salvatrice.
TEST: La gauche a fait main basse sur la plupart des villes vaudoises, les Verts bondissent aux législatifs.




@@@@
@@@@
@@@@
@@@@







>







21084
21085
21086
21087
21088
21089
21090
21091
21092
21093
21094
21095
21096
21097
21098
TEST: Il n’y avait plus qu’à faucher le blé noir, arracher les pommes de terre et gauler noix et châtaignes ; de ceci les gamins se chargeaient le jeudi et le dimanche, pour se distraire.
TEST: Les études révèlent un risque d’écrêtement du trafic ferroviaire dès 2025, lorsque la demande est la plus critique, c’est-à-dire le vendredi soir à l’heure de pointe.
TEST: Beaucoup de conciles émaneront des normes pour que soient nommés des curés idoines, qui soient vraiment au service de la paroisse, qui se distinguent par leur science et qui soient de vrais pasteurs au sein de la communauté de fidèles.
TEST: Les ailes de cet avion viennent de Grande-Bretagne.
TEST: La liberté passait sur le monde, l’internationale était sa voix criant par-dessus les frontières les revendications des déshérités.
TEST: La danse est d’ailleurs en ce sens salvatrice.
TEST: La gauche a fait main basse sur la plupart des villes vaudoises, les Verts bondissent aux législatifs.
TEST: cette organisation est à partir de 1945 composée de membres venus de…



@@@@
@@@@
@@@@
@@@@
23205
23206
23207
23208
23209
23210
23211
23212


23213
23214
23215
23216
23217
23218
23219
        <<- /conf/ -3>> nues                                    && Confusion. Écrivez “nues” (ancien terme pour “nuages”).|https://fr.wiktionary.org/wiki/tomber_des_nues

TEST: Elle en est tombée des {{nus}}.                           ->> nues


# numérique / digital
__conf_numérique_digital__
    [>agence|>appareil|>banque|>caméra|>colonie|>colonisation|>communication|>compagnie|>connexion|>économie|>entreprise|>ère|>expérience|>identité|>industrie|>présence|>prise|>service|>solution|>stratégie|>télévision|>transformation|>transition] >digital


        <<- /conf/ -2>> numérique|numériques
        && Confusion : “digital” est un adjectif se rapportant aux doigts (empreinte digitale, arthrose digitale, etc.). Écrivez “numérique”.

    [le|du|au] digital
        <<- /conf/ -2>> numérique
        && Confusion : “digital” est un adjectif se rapportant aux doigts (empreinte digitale, arthrose digitale, etc.). Écrivez “numérique”.








|
>
>







23224
23225
23226
23227
23228
23229
23230
23231
23232
23233
23234
23235
23236
23237
23238
23239
23240
        <<- /conf/ -3>> nues                                    && Confusion. Écrivez “nues” (ancien terme pour “nuages”).|https://fr.wiktionary.org/wiki/tomber_des_nues

TEST: Elle en est tombée des {{nus}}.                           ->> nues


# numérique / digital
__conf_numérique_digital__
    [>agence|>appareil|>banque|>caméra|>colonie|>colonisation|>communication|>compagnie|>connexion]     >digital
    [>document|>économie|>entreprise|>ère|>expérience|>fichier|>identité|>industrie|>présence|>prise]   >digital
    [>service|>solution|>stratégie|>télévision|>transformation|>transition|>révolution]                 >digital
        <<- /conf/ -2>> numérique|numériques
        && Confusion : “digital” est un adjectif se rapportant aux doigts (empreinte digitale, arthrose digitale, etc.). Écrivez “numérique”.

    [le|du|au] digital
        <<- /conf/ -2>> numérique
        && Confusion : “digital” est un adjectif se rapportant aux doigts (empreinte digitale, arthrose digitale, etc.). Écrivez “numérique”.

25838
25839
25840
25841
25842
25843
25844
25845
25846
25847
25848
25849
25850
25851
25852

TEST: deux femmes, {{blessé}} par deux fois                                                 ->> blessées
TEST: trois hommes, {{blessée}} dans leur orgueil                                           ->> blessés


__gn_des_2m__
    des  *WORD  *WORD
        <<- /gn/ (morph(<1, ":(?:[VRBXÉ]|Cs)|>comme/|<start>|>[(,]", "*") or morph(\3, ":N", ":[AQ]")) and not morph(\3, ">(?:seul|minimum|maximum)/") >>>
        <<- /gn/ ( (morph(\2, ":[NA].*:m", ":[fe]") and morph(\3, ":[NA].*:f", "*")) or (morph(\2, ":[NA].*:f", ":[me]") and morph(\3, ":[NA].*:m", "*")) ) and not apposition(\2, \3)
        -3>> =switchGender(\3, True)                                                                && Accord de genre erroné avec « \2 ».
        <<- /gn/ __also__ and hasFemForm(\2) -2>> =switchGender(\2)                                 && Accord de genre erroné avec « \3 ».
        <<- /gn/ morph(\2, ":[NA].*:[pi]", ":G") and morph(\3, ":[NA].*:s", "*") and not apposition(\2, \3) and not (value(>1, "|et|,|") and morph(>2, ":A"))
        -3>> =suggPlur(\3)                                                                          && Accord de nombre erroné avec « \1 \2 » : « \3 » devrait être au pluriel.
        <<- not morph(\3, ":G|>a/") and agreement(\2, \3) =>> =select(\3, "", ":V")








|







25859
25860
25861
25862
25863
25864
25865
25866
25867
25868
25869
25870
25871
25872
25873

TEST: deux femmes, {{blessé}} par deux fois                                                 ->> blessées
TEST: trois hommes, {{blessée}} dans leur orgueil                                           ->> blessés


__gn_des_2m__
    des  *WORD  *WORD
        <<- /gn/ (morphx(\1, ":LR") or morph(<1, ":(?:[VRBXÉ]|Cs)|>comme/|<start>|>[(,]", "*") or morph(\3, ":N", ":[AQ]")) and not morph(\3, ">(?:seul|minimum|maximum)/") >>>
        <<- /gn/ ( (morph(\2, ":[NA].*:m", ":[fe]") and morph(\3, ":[NA].*:f", "*")) or (morph(\2, ":[NA].*:f", ":[me]") and morph(\3, ":[NA].*:m", "*")) ) and not apposition(\2, \3)
        -3>> =switchGender(\3, True)                                                                && Accord de genre erroné avec « \2 ».
        <<- /gn/ __also__ and hasFemForm(\2) -2>> =switchGender(\2)                                 && Accord de genre erroné avec « \3 ».
        <<- /gn/ morph(\2, ":[NA].*:[pi]", ":G") and morph(\3, ":[NA].*:s", "*") and not apposition(\2, \3) and not (value(>1, "|et|,|") and morph(>2, ":A"))
        -3>> =suggPlur(\3)                                                                          && Accord de nombre erroné avec « \1 \2 » : « \3 » devrait être au pluriel.
        <<- not morph(\3, ":G|>a/") and agreement(\2, \3) =>> =select(\3, "", ":V")

26761
26762
26763
26764
26765
26766
26767

26768
26769
26770
26771
26772
26773
26774
26775
26776
26777
26778
26779
26780
TEST: à {{grande}} {{enjambées}}                                ->> grandes|||enjambée
TEST: pour {{grande}} {{appartement}}                           ->> grand|||
TEST: une femme sans a priori


__gn_pfx_de_2m__
    [de|d’]  @:[NA]¬:[YG]  @:[NA]¬*

        <<- /gn/ ( (morph(\2, ":m", ":[fe]") and morph(\3, ":f", "*")) or (morph(\2, ":f", ":[me]") and morph(\3, ":m", "*")) )
            and not apposition(\2, \3) and not morph(<1, ":[NA]|>(?:et|ou)/")
        -3>> =switchGender(\3)                                                                      && Accord de genre erroné avec « \2 ».
        <<- /gn/ __also__ and hasFemForm(\2) -2>> =switchGender(\2)                                 && Accord de genre erroné avec « \3 ».
        <<- /gn/ ( (morph(\2, ":p", ":[si]") and morph(\3, ":s", "*")) or (morph(\2, ":s", ":[pi]") and morph(\3, ":p", "*")) )
            and not apposition(\2, \3) and not morph(<1, ":[NA]|>(?:et|ou)/")
        -3>> =switchPlural(\3)                                                                      && Accord de nombre erroné avec « \2 ».
        <<- /gn/ __also__ -2>> =switchPlural(\2)                                                    && Accord de nombre erroné avec « \3 ».

TEST: Ils ont de {{grandes}} {{oreille}}                        ->> grande|||oreilles
TEST: de {{grands}} {{homme}}                                   ->> grand|||hommes
TEST: de {{petite}} {{homme}}                                   ->> petit|||
TEST: de {{pousse}} {{actif}}                                   ->> poussé|||active







>
|
<


|
<







26782
26783
26784
26785
26786
26787
26788
26789
26790

26791
26792
26793

26794
26795
26796
26797
26798
26799
26800
TEST: à {{grande}} {{enjambées}}                                ->> grandes|||enjambée
TEST: pour {{grande}} {{appartement}}                           ->> grand|||
TEST: une femme sans a priori


__gn_pfx_de_2m__
    [de|d’]  @:[NA]¬:[YG]  @:[NA]¬*
        <<- /gn/ (morphx(\1, ":LR") or not morph(<1, ":[NA]|>(?:et|ou)/")) and not apposition(\2, \3) >>>
        <<- /gn/ (morph(\2, ":m", ":[fe]") and morph(\3, ":f", "*")) or (morph(\2, ":f", ":[me]") and morph(\3, ":m", "*"))

        -3>> =switchGender(\3)                                                                      && Accord de genre erroné avec « \2 ».
        <<- /gn/ __also__ and hasFemForm(\2) -2>> =switchGender(\2)                                 && Accord de genre erroné avec « \3 ».
        <<- /gn/ (morph(\2, ":p", ":[si]") and morph(\3, ":s", "*")) or (morph(\2, ":s", ":[pi]") and morph(\3, ":p", "*"))

        -3>> =switchPlural(\3)                                                                      && Accord de nombre erroné avec « \2 ».
        <<- /gn/ __also__ -2>> =switchPlural(\2)                                                    && Accord de nombre erroné avec « \3 ».

TEST: Ils ont de {{grandes}} {{oreille}}                        ->> grande|||oreilles
TEST: de {{grands}} {{homme}}                                   ->> grand|||hommes
TEST: de {{petite}} {{homme}}                                   ->> petit|||
TEST: de {{pousse}} {{actif}}                                   ->> poussé|||active
28367
28368
28369
28370
28371
28372
28373
28374
28375
28376
28377
28378
28379
28380
28381
28382
28383
28384

    à deux doigts                               [de|d’]  ?[le|l’|la|leur|les|nous|vous|lui]¿  ~..(?:ée?s?|ez)$
        <<- /infi/ morph0(\-1, ":(?:Q|2p)", ":M") --1>> =suggVerbInfi(\-1)                          && Après “à deux doigts de”, le verbe devrait être à l’infinitif.

    à >seul >fin                                [de|d’]  ?[le|l’|la|leur|les|nous|vous|lui]¿  ~..(?:ée?s?|ez)$
        <<- /infi/ morph0(\-1, ":(?:Q|2p)", ":M") --1>> =suggVerbInfi(\-1)                          && Après “à seule fin de”, le verbe devrait être à l’infinitif.

    aulieu                                     [de|d’]  ?[le|les|nous|vous|lui]¿             ~..(?:ée?s?|ez)$
        <<- /infi/ morph0(\-1, ":(?:Q|2p)", ":M") --1>> =suggVerbInfi(\-1)                          && Après “au lieu de”, le verbe devrait être à l’infinitif.

    aulieu                                     [de|d’]  [l’|la|leur]                         ~..(?:ée?s?|ez)$
        <<- /infi/ morph0(\-1, ":(?:Q|2p)", ":[MN]") --1>> =suggVerbInfi(\-1)                       && Après “au lieu de”, le verbe devrait être à l’infinitif.

    >avoir l’ air                               [de|d’]  ?[le|l’|la|leur|les|nous|vous|lui]¿  ~..(?:ée?s?|ez)$
        <<- /infi/ morph0(\-1, ":(?:Q|2p)", ":M") --1>> =suggVerbInfi(\-1)                          && Après “avoir l’air de”, le verbe devrait être à l’infinitif.

    >avoir la chance                            [de|d’]  ?[le|l’|la|leur|les|nous|vous|lui]¿  ~..(?:ée?s?|ez)$
        <<- /infi/ morph0(\-1, ":(?:Q|2p)", ":M") --1>> =suggVerbInfi(\-1)                          && Après “avoir la chance de”, le verbe devrait être à l’infinitif.







|


|







28387
28388
28389
28390
28391
28392
28393
28394
28395
28396
28397
28398
28399
28400
28401
28402
28403
28404

    à deux doigts                               [de|d’]  ?[le|l’|la|leur|les|nous|vous|lui]¿  ~..(?:ée?s?|ez)$
        <<- /infi/ morph0(\-1, ":(?:Q|2p)", ":M") --1>> =suggVerbInfi(\-1)                          && Après “à deux doigts de”, le verbe devrait être à l’infinitif.

    à >seul >fin                                [de|d’]  ?[le|l’|la|leur|les|nous|vous|lui]¿  ~..(?:ée?s?|ez)$
        <<- /infi/ morph0(\-1, ":(?:Q|2p)", ":M") --1>> =suggVerbInfi(\-1)                          && Après “à seule fin de”, le verbe devrait être à l’infinitif.

    au lieu                                     [de|d’]  ?[le|les|nous|vous|lui]¿             ~..(?:ée?s?|ez)$
        <<- /infi/ morph0(\-1, ":(?:Q|2p)", ":M") --1>> =suggVerbInfi(\-1)                          && Après “au lieu de”, le verbe devrait être à l’infinitif.

    au lieu                                     [de|d’]  [l’|la|leur]                         ~..(?:ée?s?|ez)$
        <<- /infi/ morph0(\-1, ":(?:Q|2p)", ":[MN]") --1>> =suggVerbInfi(\-1)                       && Après “au lieu de”, le verbe devrait être à l’infinitif.

    >avoir l’ air                               [de|d’]  ?[le|l’|la|leur|les|nous|vous|lui]¿  ~..(?:ée?s?|ez)$
        <<- /infi/ morph0(\-1, ":(?:Q|2p)", ":M") --1>> =suggVerbInfi(\-1)                          && Après “avoir l’air de”, le verbe devrait être à l’infinitif.

    >avoir la chance                            [de|d’]  ?[le|l’|la|leur|les|nous|vous|lui]¿  ~..(?:ée?s?|ez)$
        <<- /infi/ morph0(\-1, ":(?:Q|2p)", ":M") --1>> =suggVerbInfi(\-1)                          && Après “avoir la chance de”, le verbe devrait être à l’infinitif.
29698
29699
29700
29701
29702
29703
29704
29705

29706
29707


29708
29709
29710
29711
29712
29713
29714


29715

29716
29717
29718
29719
29720
29721
29722
29723
29724
    de  [la|l’|leur]  @:[NA]¬:Y  <end>
    de  [la|l’|leur]  @:[NA]¬:Y  ?,¿ [ne|n’|me|m’|te|t’|se|s’|nous|vous|y|c’|ça|ç’|je|j’|tu|il|elle|on|ils|elles|iel|iels|le|la|l’|les|mon|ma|ta|sa|notre|votre|leur|mes|tes|ses|nos|vos|leurs|ce|cet|cette|ces|quel|quels|quelle|quelles|est]
    de  [la|l’|leur]  @:[NA]¬:Y  @:[VR]¬:[NAP]
    de  [la|l’|leur]  @:[NA]¬:Y  , @¬:[NA]
    de  [la|l’|leur]  @:[NA]¬:Y  ?,¿  [/_VCint_|/_VCimp_]
        <<- ~1:3>> *

    $:ÉR [de|d’]  [la|l’|leur|un|une|ce|cet|cette|ces|mon|ton|son|ma|ta|sa|mes|tes|ses|notre|votre|nos|vos|leurs]  @:[NA]  <end>

    $:ÉR [de|d’]  [la|l’|leur|un|une|ce|cet|cette|ces|mon|ton|son|ma|ta|sa|mes|tes|ses|notre|votre|nos|vos|leurs]  @:[NA]  ?,¿ [ne|n’|me|m’|te|t’|se|s’|nous|vous|y|c’|ça|ç’|je|j’|tu|il|elle|on|ils|elles|iel|iels|le|la|l’|les|mon|ma|ta|sa|notre|votre|leur|mes|tes|ses|nos|vos|leurs|ce|cet|cette|ces|quel|quels|quelle|quelles|est]
    $:ÉR [de|d’]  [la|l’|leur|un|une|ce|cet|cette|ces|mon|ton|son|ma|ta|sa|mes|tes|ses|notre|votre|nos|vos|leurs]  @:[NA]  @:[VR]¬:[NAP]


    $:ÉR [de|d’]  [la|l’|leur|un|une|ce|cet|cette|ces|mon|ton|son|ma|ta|sa|mes|tes|ses|notre|votre|nos|vos|leurs]  @:[NA]  , @¬:[NA]
    $:ÉR [de|d’]  [la|l’|leur|un|une|ce|cet|cette|ces|mon|ton|son|ma|ta|sa|mes|tes|ses|notre|votre|nos|vos|leurs]  @:[NA]  ?,¿  [/_VCint_|/_VCimp_]
        <<- ~1:4>> *

    $:ÉR [des|du|de|d’] @:[NA] <end>
    $:ÉR [des|du|de|d’] @:[NA] ?,¿ [ne|n’|me|m’|te|t’|se|s’|nous|vous|y|c’|ça|ç’|je|j’|tu|il|elle|on|ils|elles|iel|iels|le|la|l’|les|mon|ma|ta|sa|notre|votre|leur|mes|tes|ses|nos|vos|leurs|ce|cet|cette|ces|quel|quels|quelle|quelles|est]
    $:ÉR [des|du|de|d’] @:[NA] @:[VR]¬:[NAP]


    $:ÉR [des|du|de|d’] @:[NA] , @¬:[NA]

    $:ÉR [des|du|de|d’] @:[NA] ?,¿  [/_VCint_|/_VCimp_]
        <<- ~1:3>> *

    en [plein|pleine] @:[NA]  <end>
    en [plein|pleine] @:[NA]  ?,¿ [ne|n’|me|m’|te|t’|se|s’|nous|vous|y|c’|ça|ç’|je|j’|tu|il|elle|on|ils|elles|iel|iels|le|la|l’|les|mon|ma|ta|sa|notre|votre|leur|mes|tes|ses|nos|vos|leurs|ce|cet|cette|ces|quel|quels|quelle|quelles|est]
    en [plein|pleine] @:[NA]  @:[VR]¬:[NAP]
    en [plein|pleine] @:[NA]  , @¬:[NA]
    en [plein|pleine] @:[NA]  ?,¿  [/_VCint_|/_VCimp_]
        <<- ~1:3>> *







|
>
|
|
>
>
|
|
|

|
|
|
>
>
|
>
|
|







29718
29719
29720
29721
29722
29723
29724
29725
29726
29727
29728
29729
29730
29731
29732
29733
29734
29735
29736
29737
29738
29739
29740
29741
29742
29743
29744
29745
29746
29747
29748
29749
29750
    de  [la|l’|leur]  @:[NA]¬:Y  <end>
    de  [la|l’|leur]  @:[NA]¬:Y  ?,¿ [ne|n’|me|m’|te|t’|se|s’|nous|vous|y|c’|ça|ç’|je|j’|tu|il|elle|on|ils|elles|iel|iels|le|la|l’|les|mon|ma|ta|sa|notre|votre|leur|mes|tes|ses|nos|vos|leurs|ce|cet|cette|ces|quel|quels|quelle|quelles|est]
    de  [la|l’|leur]  @:[NA]¬:Y  @:[VR]¬:[NAP]
    de  [la|l’|leur]  @:[NA]¬:Y  , @¬:[NA]
    de  [la|l’|leur]  @:[NA]¬:Y  ?,¿  [/_VCint_|/_VCimp_]
        <<- ~1:3>> *

    &:LR  [la|l’|leur|un|une|ce|cet|cette|ces|mon|ton|son|ma|ta|sa|mes|tes|ses|notre|votre|nos|vos|leurs]  @:[NA]  <end>
    &:LR  [la|l’|leur|un|une|ce|cet|cette|ces|mon|ton|son|ma|ta|sa|mes|tes|ses|notre|votre|nos|vos|leurs]  @:[NA]  [ne|n’|me|m’|te|t’|se|s’|nous|vous|y|c’|ça|ç’|je|j’|tu|il|elle|on|ils|elles|iel|iels|le|la|l’|les|mon|ma|ta|sa|notre|votre|leur|mes|tes|ses|nos|vos|leurs|ce|cet|cette|ces|quel|quels|quelle|quelles|est]
    &:LR  [la|l’|leur|un|une|ce|cet|cette|ces|mon|ton|son|ma|ta|sa|mes|tes|ses|notre|votre|nos|vos|leurs]  @:[NA]  @:[VR]¬:[NAP]
    &:LR  [la|l’|leur|un|une|ce|cet|cette|ces|mon|ton|son|ma|ta|sa|mes|tes|ses|notre|votre|nos|vos|leurs]  @:[NA]  [/_VCint_|/_VCimp_]
        <<- ~1:-2>> *

    &:LR  [la|l’|leur|un|une|ce|cet|cette|ces|mon|ton|son|ma|ta|sa|mes|tes|ses|notre|votre|nos|vos|leurs]  @:[NA]  ,  @:¬:A
    &:LR  [la|l’|leur|un|une|ce|cet|cette|ces|mon|ton|son|ma|ta|sa|mes|tes|ses|notre|votre|nos|vos|leurs]  @:[NA]  ,  [/_VCint_|/_VCimp_]
        <<- ~1:-3>> *

    &:LR @:[NA] <end>
    &:LR @:[NA] [ne|n’|me|m’|te|t’|se|s’|nous|vous|y|c’|ça|ç’|je|j’|tu|il|elle|on|ils|elles|iel|iels|le|la|l’|les|mon|ma|ta|sa|notre|votre|leur|mes|tes|ses|nos|vos|leurs|ce|cet|cette|ces|quel|quels|quelle|quelles|est]
    &:LR @:[NA] @:[VR]¬:[NAP]
    &:LR @:[NA] [/_VCint_|/_VCimp_]
        <<- ~1:-2>> *

    &:LR @:[NA] ,  @:¬:A
    &:LR @:[NA] ,  [/_VCint_|/_VCimp_]
        <<- ~1:-3>> *

    en [plein|pleine] @:[NA]  <end>
    en [plein|pleine] @:[NA]  ?,¿ [ne|n’|me|m’|te|t’|se|s’|nous|vous|y|c’|ça|ç’|je|j’|tu|il|elle|on|ils|elles|iel|iels|le|la|l’|les|mon|ma|ta|sa|notre|votre|leur|mes|tes|ses|nos|vos|leurs|ce|cet|cette|ces|quel|quels|quelle|quelles|est]
    en [plein|pleine] @:[NA]  @:[VR]¬:[NAP]
    en [plein|pleine] @:[NA]  , @¬:[NA]
    en [plein|pleine] @:[NA]  ?,¿  [/_VCint_|/_VCimp_]
        <<- ~1:3>> *
32771
32772
32773
32774
32775
32776
32777
32778
32779
32780
32781
32782
32783
32784
32785
32786
32787
32788
32789
32790
32791
32792
32793
32794
32795
32796
32797
32798
32799
32800
32801
32802
32803
32804
32805
32806
32807
32808
32809
32810
32811
32812
32813
32814
32815
32816
32817
32818
32819
32820
32821
32822
32823
32824
32825
32826
32827
32828
32829
32830
32831


    [<start>|,|(]  [ne|n’]  ?[le|la|l’|les|en|me|m’|te|t’|nous|vous|lui|leur|y]¿  vas
    [<start>|,|(]  [ne|n’]  [me|m’|te|t’|nous|vous]  [le|la|l’|les|en|y]          vas
    [<start>|,|(]  [ne|n’]  [le|la|l’|les]           [lui|leur|en|y]              vas
    [<start>|,|(]  [ne|n’]  [lui|leur]               en                           vas
    [<start>|,|(]  vas
        <<- /imp/ not value(>1, "|tu|") --1>> va                                   && S’il s’agit d’un impératif, pas de “s”.

TEST: {{Vas}} lui mettre une branlée.                                                       ->> Va
TEST: {{Vas}} au diable !                                                                   ->> Va
TEST: N’y {{vas}} pas                                                                       ->> va
TEST: Si vraiment tu es d’attaque et que Martine est d’accord, {{vas}} dans…                ->> va
TEST: — Va-t’en ! [Firefox]


# verbes du 1ᵉʳ et du 3ᵉ groupe en -e
__imp_verbe_groupe1__
    <start>  [ne|n’]  ?[le|la|l’|les|en|me|m’|te|t’|nous|vous|lui|leur|y]¿  ~\w\w+es$
    <start>  [ne|n’]  [me|m’|te|t’|nous|vous]  [le|la|l’|les|en|y]          ~\w\w+es$
    <start>  [ne|n’]  [le|la|l’|les]           [lui|leur|en|y]              ~\w\w+es$
    <start>  [ne|n’]  [lui|leur]               en                           ~\w\w+es$
        <<- /imp/ morph(\-1, ":V[13].*:Ip.*:2s", ":G") and not value(>1, "|tu|")
        --1>> =\-1[:-1]                                                             && S’il s’agit d’un impératif, pas de “s”.

    <start>  ~\w\w+es$
        <<- /imp/ morph(\-1, ":V[13].*:Ip.*:2s", ":[GNAM]") and not value(>1, "|tu|")
        --1>> =\-1[:-1]                                                             && S’il s’agit d’un impératif, pas de “s”.

TEST: {{Apportes}} ton fric à cet homme.                                                    ->> Apporte
TEST: N’{{abandonnes}} pas si vite.                                                         ->> abandonne
TEST: Ne {{demandes}} rien, tais-toi.                                                       ->> demande
TEST: — {{Abandonnes}}.                                                                     ->> Abandonne
TEST: {{Écartes}} de moi cette coupe.                                                       ->> Écarte
TEST: Ne {{donnes}} pas autant.                                                             ->> donne
TEST: N’{{insistes}} pas.                                                                   ->> insiste
TEST: Castres est une belle ville.
TEST: Limoges aussi.


# verbes du 2ᵉ et du 3ᵉ groupe en -t
__imp_verbe_groupe2_groupe3_t__
    <start>  [ne|n’]  ?[le|la|l’|les|en|me|m’|te|t’|nous|vous|lui|leur|y]¿  ~\w\w+t$
    <start>  [ne|n’]  [me|m’|te|t’|nous|vous]  [le|la|l’|les|en|y]          ~\w\w+t$
    <start>  [ne|n’]  [le|la|l’|les]           [lui|leur|en|y]              ~\w\w+t$
    <start>  [ne|n’]  [lui|leur]               en                           ~\w\w+t$
        <<- /imp/ not value(>1, "|il|elle|on|ils|elles|iel|iels|")
            and not value(\-1, "|provient|")
            and not (value(\-1, "|vient|dit|surgit|survient|périt|") and morph(>1, ":(?:[MD]|Oo)|>[A-Z]/"))
            and morph(\-1, ":V[23].*:Ip.*:3s", ":G|>(?:devoir|suffire|para[îi]tre)/") and analyse(\-1[:-1]+"s", ":E:2s")
        --1>> =\-1[:-1]+"s"                                                         && S’il s’agit d’un impératif, la terminaison est “s”, non “t”.

    <start>  ~\w\w+t$
        <<- /imp/ not value(>1, "|il|elle|on|ils|elles|iel|iels|")
            and not value(\-1, "|provient|")
            and not (value(\-1, "|vient|dit|surgit|survient|périt|") and morph(>1, ":(?:[MDR]|Oo)|>[A-Z]/"))
            and morph(\-1, ":V[23].*:Ip.*:3s", ":[GNA]|>(?:devoir|suffire|para[îi]tre)/") and analyse(\-1[:-1]+"s", ":E:2s")
        --1>> =\-1[:-1]+"s"                                                         && S’il s’agit d’un impératif, la terminaison est “s”, non “t”.

TEST: {{Finit}} ton assiette.                                           ->> Finis
TEST: Ne {{pourrit}} pas l’ambiance.                                    ->> pourris







|














|



|



















|






|







32797
32798
32799
32800
32801
32802
32803
32804
32805
32806
32807
32808
32809
32810
32811
32812
32813
32814
32815
32816
32817
32818
32819
32820
32821
32822
32823
32824
32825
32826
32827
32828
32829
32830
32831
32832
32833
32834
32835
32836
32837
32838
32839
32840
32841
32842
32843
32844
32845
32846
32847
32848
32849
32850
32851
32852
32853
32854
32855
32856
32857


    [<start>|,|(]  [ne|n’]  ?[le|la|l’|les|en|me|m’|te|t’|nous|vous|lui|leur|y]¿  vas
    [<start>|,|(]  [ne|n’]  [me|m’|te|t’|nous|vous]  [le|la|l’|les|en|y]          vas
    [<start>|,|(]  [ne|n’]  [le|la|l’|les]           [lui|leur|en|y]              vas
    [<start>|,|(]  [ne|n’]  [lui|leur]               en                           vas
    [<start>|,|(]  vas
        <<- /imp/ not tagbefore(\2, "_CUT_") and not value(>1, "|tu|") --1>> va     && S’il s’agit d’un impératif, pas de “s”.

TEST: {{Vas}} lui mettre une branlée.                                                       ->> Va
TEST: {{Vas}} au diable !                                                                   ->> Va
TEST: N’y {{vas}} pas                                                                       ->> va
TEST: Si vraiment tu es d’attaque et que Martine est d’accord, {{vas}} dans…                ->> va
TEST: — Va-t’en ! [Firefox]


# verbes du 1ᵉʳ et du 3ᵉ groupe en -e
__imp_verbe_groupe1__
    <start>  [ne|n’]  ?[le|la|l’|les|en|me|m’|te|t’|nous|vous|lui|leur|y]¿  ~\w\w+es$
    <start>  [ne|n’]  [me|m’|te|t’|nous|vous]  [le|la|l’|les|en|y]          ~\w\w+es$
    <start>  [ne|n’]  [le|la|l’|les]           [lui|leur|en|y]              ~\w\w+es$
    <start>  [ne|n’]  [lui|leur]               en                           ~\w\w+es$
        <<- /imp/ not tagbefore(\2, "_CUT_") and morph(\-1, ":V[13].*:Ip.*:2s", ":G") and not value(>1, "|tu|")
        --1>> =\-1[:-1]                                                             && S’il s’agit d’un impératif, pas de “s”.

    <start>  ~\w\w+es$
        <<- /imp/ not tagbefore(\2, "_CUT_") and morph(\-1, ":V[13].*:Ip.*:2s", ":[GNAM]") and not value(>1, "|tu|")
        --1>> =\-1[:-1]                                                             && S’il s’agit d’un impératif, pas de “s”.

TEST: {{Apportes}} ton fric à cet homme.                                                    ->> Apporte
TEST: N’{{abandonnes}} pas si vite.                                                         ->> abandonne
TEST: Ne {{demandes}} rien, tais-toi.                                                       ->> demande
TEST: — {{Abandonnes}}.                                                                     ->> Abandonne
TEST: {{Écartes}} de moi cette coupe.                                                       ->> Écarte
TEST: Ne {{donnes}} pas autant.                                                             ->> donne
TEST: N’{{insistes}} pas.                                                                   ->> insiste
TEST: Castres est une belle ville.
TEST: Limoges aussi.


# verbes du 2ᵉ et du 3ᵉ groupe en -t
__imp_verbe_groupe2_groupe3_t__
    <start>  [ne|n’]  ?[le|la|l’|les|en|me|m’|te|t’|nous|vous|lui|leur|y]¿  ~\w\w+t$
    <start>  [ne|n’]  [me|m’|te|t’|nous|vous]  [le|la|l’|les|en|y]          ~\w\w+t$
    <start>  [ne|n’]  [le|la|l’|les]           [lui|leur|en|y]              ~\w\w+t$
    <start>  [ne|n’]  [lui|leur]               en                           ~\w\w+t$
        <<- /imp/ not tagbefore(\2, "_CUT_") and not value(>1, "|il|elle|on|ils|elles|iel|iels|")
            and not value(\-1, "|provient|")
            and not (value(\-1, "|vient|dit|surgit|survient|périt|") and morph(>1, ":(?:[MD]|Oo)|>[A-Z]/"))
            and morph(\-1, ":V[23].*:Ip.*:3s", ":G|>(?:devoir|suffire|para[îi]tre)/") and analyse(\-1[:-1]+"s", ":E:2s")
        --1>> =\-1[:-1]+"s"                                                         && S’il s’agit d’un impératif, la terminaison est “s”, non “t”.

    <start>  ~\w\w+t$
        <<- /imp/ not tagbefore(\2, "_CUT_") and not value(>1, "|il|elle|on|ils|elles|iel|iels|")
            and not value(\-1, "|provient|")
            and not (value(\-1, "|vient|dit|surgit|survient|périt|") and morph(>1, ":(?:[MDR]|Oo)|>[A-Z]/"))
            and morph(\-1, ":V[23].*:Ip.*:3s", ":[GNA]|>(?:devoir|suffire|para[îi]tre)/") and analyse(\-1[:-1]+"s", ":E:2s")
        --1>> =\-1[:-1]+"s"                                                         && S’il s’agit d’un impératif, la terminaison est “s”, non “t”.

TEST: {{Finit}} ton assiette.                                           ->> Finis
TEST: Ne {{pourrit}} pas l’ambiance.                                    ->> pourris
32854
32855
32856
32857
32858
32859
32860
32861
32862
32863
32864
32865
32866
32867
32868
32869
32870
32871
32872
32873
32874
32875
32876
32877
32878
32879

32880
32881
32882
32883
32884
32885
32886

# verbes du 3ᵉ groupe en -d
__imp_verbe_groupe3_d__
    <start>  [ne|n’]  ?[le|la|l’|les|en|me|m’|te|t’|nous|vous|lui|leur|y]¿  ~\w\w+d$
    <start>  [ne|n’]  [me|m’|te|t’|nous|vous]  [le|la|l’|les|en|y]          ~\w\w+d$
    <start>  [ne|n’]  [le|la|l’|les]           [lui|leur|en|y]              ~\w\w+d$
    <start>  [ne|n’]  [lui|leur]               en                           ~\w\w+d$
        <<- /imp/ not value(>1, "|il|elle|on|")
            and not ( value(\-1, "|répond|") and (morph(>1, ":[MD]|>[A-Z]/") or value(>1, "|l’|d’|")) )
            and morph(\-1, ":V3.*:Ip.*:3s", ":G")
        --1>> \-1s                                                                  && S’il s’agit d’un impératif, ajoutez un “s”.

    <start>  ~\w\w+d$
        <<- /imp/ not value(>1, "|il|elle|on|")
            and not ( value(\-1, "|répond|") and (morph(>1, ":[MD]|>[A-Z]/") or value(>1, "|l’|d’|")) )
            and morph(\-1, ":V3.*:Ip.*:3s", ":[GNA]")
        --1>> \-1s                                                                  && S’il s’agit d’un impératif, ajoutez un “s”.

TEST: {{Prend}} le chemin de droite.                                    ->> Prends
TEST: N’{{attend}} pas qu’elle vienne.                                  ->> attends
TEST: Ne me {{prend}} pas pour un idiot                                 ->> prends
TEST: Ne l’{{attend}} pas                                               ->> attends
TEST: Ne m’y {{attend}} pas                                             ->> attends
TEST: {{Attend}} la correction.                                         ->> Attends
TEST: {{Répond}}.                                                       ->> Réponds
TEST: — Pour ma part, répond P., je crois que tout va bien.



!!
!!
!!!! Impératif : traits d’union manquants                                                         !!
!!
!!







|





|












>







32880
32881
32882
32883
32884
32885
32886
32887
32888
32889
32890
32891
32892
32893
32894
32895
32896
32897
32898
32899
32900
32901
32902
32903
32904
32905
32906
32907
32908
32909
32910
32911
32912
32913

# verbes du 3ᵉ groupe en -d
__imp_verbe_groupe3_d__
    <start>  [ne|n’]  ?[le|la|l’|les|en|me|m’|te|t’|nous|vous|lui|leur|y]¿  ~\w\w+d$
    <start>  [ne|n’]  [me|m’|te|t’|nous|vous]  [le|la|l’|les|en|y]          ~\w\w+d$
    <start>  [ne|n’]  [le|la|l’|les]           [lui|leur|en|y]              ~\w\w+d$
    <start>  [ne|n’]  [lui|leur]               en                           ~\w\w+d$
        <<- /imp/ not tagbefore(\2, "_CUT_") and not value(>1, "|il|elle|on|")
            and not ( value(\-1, "|répond|") and (morph(>1, ":[MD]|>[A-Z]/") or value(>1, "|l’|d’|")) )
            and morph(\-1, ":V3.*:Ip.*:3s", ":G")
        --1>> \-1s                                                                  && S’il s’agit d’un impératif, ajoutez un “s”.

    <start>  ~\w\w+d$
        <<- /imp/ not tagbefore(\2, "_CUT_") and not value(>1, "|il|elle|on|")
            and not ( value(\-1, "|répond|") and (morph(>1, ":[MD]|>[A-Z]/") or value(>1, "|l’|d’|")) )
            and morph(\-1, ":V3.*:Ip.*:3s", ":[GNA]")
        --1>> \-1s                                                                  && S’il s’agit d’un impératif, ajoutez un “s”.

TEST: {{Prend}} le chemin de droite.                                    ->> Prends
TEST: N’{{attend}} pas qu’elle vienne.                                  ->> attends
TEST: Ne me {{prend}} pas pour un idiot                                 ->> prends
TEST: Ne l’{{attend}} pas                                               ->> attends
TEST: Ne m’y {{attend}} pas                                             ->> attends
TEST: {{Attend}} la correction.                                         ->> Attends
TEST: {{Répond}}.                                                       ->> Réponds
TEST: — Pour ma part, répond P., je crois que tout va bien.
TEST: […] vient nous faire la morale


!!
!!
!!!! Impératif : traits d’union manquants                                                         !!
!!
!!
33792
33793
33794
33795
33796
33797
33798



33799
33800
33801
33802
33803
33804
33805

    ## _VCOD_
    [ne|n’] [le|l’|la|les] ?[lui|leur]¿                 @:V¬:[GQ]
    [ne|n’] [nous|vous] [le|l’|la|les]                  @:V¬:[GQ]
    [je|tu|il|ils|me|m’|te|t’|se|s’] [le|l’|la|les]     @:V¬:[GQ]
    [le|l’|la|les]                                      @:V¬:[GNAWQTML]
        <<- /-1>> _VCOD_




    [elle|elles|iel|iels|nous|vous]  [le|l’|la|les]    @:V¬:[GQ]
        <<- not morph(<1, ":R") /-1>> _VCOD_


__purge_préverbes2__
    nous              [le|la|l’|les|en|y]  @:(?:[123][sp]|P|Y)¬:(?:X|1p)







>
>
>







33819
33820
33821
33822
33823
33824
33825
33826
33827
33828
33829
33830
33831
33832
33833
33834
33835

    ## _VCOD_
    [ne|n’] [le|l’|la|les] ?[lui|leur]¿                 @:V¬:[GQ]
    [ne|n’] [nous|vous] [le|l’|la|les]                  @:V¬:[GQ]
    [je|tu|il|ils|me|m’|te|t’|se|s’] [le|l’|la|les]     @:V¬:[GQ]
    [le|l’|la|les]                                      @:V¬:[GNAWQTML]
        <<- /-1>> _VCOD_

    [me|m’|te|t’] @:V...t¬:[GQ]
        <<- /-1>> _VCOD_

    [elle|elles|iel|iels|nous|vous]  [le|l’|la|les]    @:V¬:[GQ]
        <<- not morph(<1, ":R") /-1>> _VCOD_


__purge_préverbes2__
    nous              [le|la|l’|les|en|y]  @:(?:[123][sp]|P|Y)¬:(?:X|1p)
35730
35731
35732
35733
35734
35735
35736

35737
35738
35739
35740
35741
35742
35743

    >attendre  [que|qu’]  [les|ces|mes|tes|ses|nos|vos|leurs|plusieurs|quelques|certains|certaines|maints|maintes]   @:N.*:[pi]  @:I¬:[GYSA]
        <<- /vmode/ not morph(\1, ":Q") --1>> =suggVerbMode(\-1, ":S", ":3p")                       && Après “attendre que”, ce verbe devrait être au subjonctif.

TEST: j’attends que tu {{viens}}.                                                   ->> viennes|vinsses
TEST: Attends que Céline {{dort}}                                                   ->> dorme|dormît
TEST: nous attendions que les petits {{étaient}} partis                             ->> soient|fussent|étayent|étayassent



__vmode_il_importe_que_subjonctif__
    il  >importer  [que|qu’]  [je|j’|tu|il|elle|iel|nous|vous|ils|elles|iels]  @:I¬:[GYS]
        <<- /vmode/ --1>> =suggVerbMode(\-1, ":S", \-2)                                             && Après le syntagme “il importe que”, ce verbe devrait être au subjonctif.

    il  >importer  [que|qu’]  $:M  @:I¬:[GYS]







>







35760
35761
35762
35763
35764
35765
35766
35767
35768
35769
35770
35771
35772
35773
35774

    >attendre  [que|qu’]  [les|ces|mes|tes|ses|nos|vos|leurs|plusieurs|quelques|certains|certaines|maints|maintes]   @:N.*:[pi]  @:I¬:[GYSA]
        <<- /vmode/ not morph(\1, ":Q") --1>> =suggVerbMode(\-1, ":S", ":3p")                       && Après “attendre que”, ce verbe devrait être au subjonctif.

TEST: j’attends que tu {{viens}}.                                                   ->> viennes|vinsses
TEST: Attends que Céline {{dort}}                                                   ->> dorme|dormît
TEST: nous attendions que les petits {{étaient}} partis                             ->> soient|fussent|étayent|étayassent
TEST: c’était si attendu qu’il avait renoncé avant même d’y aller


__vmode_il_importe_que_subjonctif__
    il  >importer  [que|qu’]  [je|j’|tu|il|elle|iel|nous|vous|ils|elles|iels]  @:I¬:[GYS]
        <<- /vmode/ --1>> =suggVerbMode(\-1, ":S", \-2)                                             && Après le syntagme “il importe que”, ce verbe devrait être au subjonctif.

    il  >importer  [que|qu’]  $:M  @:I¬:[GYS]

Modified misc/grammalecte.sublime-color-scheme from [c24fa9f267] to [a8384ba127].

64
65
66
67
68
69
70
71
72
73
74
75

76
77
78
79
80
81
82

        {   "name": "Entity Valid",         "scope": "entity.valid",        "foreground": "hsl(150, 100%, 80%)",    "background": "hsl(150, 100%, 20%)",    "font_style": "bold",   },
        {   "name": "Entity Invalid",       "scope": "entity.invalid",      "foreground": "hsl(0, 100%, 80%)",      "background": "hsl(0, 100%, 20%)",      "font_style": "bold",   },
        {   "name": "Token meta",           "scope": "string.meta",         "foreground": "hsl(270, 100%, 90%)",    "background": "hsl(270, 100%, 40%)",  },
        {   "name": "Token token",          "scope": "string.token",        "foreground": "hsl(240, 50%, 90%)",     "background": "hsl(240, 50%, 40%)",  },
        {   "name": "Token Jumptoken",      "scope": "string.jumptoken",    "foreground": "hsl(0, 50%, 90%)",       "background": "hsl(10, 50%, 40%)",  },
        {   "name": "Token lemma",          "scope": "string.lemma",        "foreground": "hsl(210, 100%, 80%)",    "background": "hsl(210, 100%, 15%)",  },
        {   "name": "Token phonet",         "scope": "string.phonet",       "foreground": "hsl(90, 100%, 80%)",    "background": "hsl(90, 100%, 10%)",  },
        {   "name": "Token tag",            "scope": "string.tag",          "foreground": "hsl(30, 100%, 90%)",     "background": "hsl(30, 100%, 20%)",  },
        {   "name": "Token regex",          "scope": "string.regex",        "foreground": "hsl(60, 100%, 80%)",     "background": "hsl(60, 100%, 10%)",  },
        {   "name": "Token morph regex",    "scope": "string.morph.regex",  "foreground": "hsl(150, 80%, 90%)",     "background": "hsl(150, 80%, 10%)",  },
        {   "name": "Token morph negregex", "scope": "string.morph.negregex","foreground": "hsl(0, 80%, 90%)",      "background": "hsl(0, 80%, 10%)",  },



        {   "name": "Keyword Python",       "scope": "keyword.python",      "foreground": "#A0A0A0",  },

        {   "name": "Keyword",              "scope": "keyword - (source.c keyword.operator | source.c++ keyword.operator | source.objc keyword.operator | source.objc++ keyword.operator), keyword.operator.word",  "foreground": "#F06070", },
        {   "name": "String",               "scope": "string",              "foreground": "hsl(40, 100%, 80%)",  },
        {   "name": "Number",               "scope": "constant.numeric",    "foreground": "hsl(270, 100%, 70%)",                                            "font_style": "bold",  },







|



|
>







64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83

        {   "name": "Entity Valid",         "scope": "entity.valid",        "foreground": "hsl(150, 100%, 80%)",    "background": "hsl(150, 100%, 20%)",    "font_style": "bold",   },
        {   "name": "Entity Invalid",       "scope": "entity.invalid",      "foreground": "hsl(0, 100%, 80%)",      "background": "hsl(0, 100%, 20%)",      "font_style": "bold",   },
        {   "name": "Token meta",           "scope": "string.meta",         "foreground": "hsl(270, 100%, 90%)",    "background": "hsl(270, 100%, 40%)",  },
        {   "name": "Token token",          "scope": "string.token",        "foreground": "hsl(240, 50%, 90%)",     "background": "hsl(240, 50%, 40%)",  },
        {   "name": "Token Jumptoken",      "scope": "string.jumptoken",    "foreground": "hsl(0, 50%, 90%)",       "background": "hsl(10, 50%, 40%)",  },
        {   "name": "Token lemma",          "scope": "string.lemma",        "foreground": "hsl(210, 100%, 80%)",    "background": "hsl(210, 100%, 15%)",  },
        {   "name": "Token phonet",         "scope": "string.phonet",       "foreground": "hsl(90, 100%, 80%)",     "background": "hsl(90, 100%, 10%)",  },
        {   "name": "Token tag",            "scope": "string.tag",          "foreground": "hsl(30, 100%, 90%)",     "background": "hsl(30, 100%, 20%)",  },
        {   "name": "Token regex",          "scope": "string.regex",        "foreground": "hsl(60, 100%, 80%)",     "background": "hsl(60, 100%, 10%)",  },
        {   "name": "Token morph regex",    "scope": "string.morph.regex",  "foreground": "hsl(150, 80%, 90%)",     "background": "hsl(150, 80%, 10%)",  },
        {   "name": "Token morph negregex", "scope": "string.morph.negregex", "foreground": "hsl(0, 80%, 90%)",     "background": "hsl(0, 80%, 10%)",  },
        {   "name": "MulToken morph regex", "scope": "string.mt.morph.regex", "foreground": "hsl(180, 80%, 90%)",   "background": "hsl(180, 80%, 10%)",  },


        {   "name": "Keyword Python",       "scope": "keyword.python",      "foreground": "#A0A0A0",  },

        {   "name": "Keyword",              "scope": "keyword - (source.c keyword.operator | source.c++ keyword.operator | source.objc keyword.operator | source.objc++ keyword.operator), keyword.operator.word",  "foreground": "#F06070", },
        {   "name": "String",               "scope": "string",              "foreground": "hsl(40, 100%, 80%)",  },
        {   "name": "Number",               "scope": "constant.numeric",    "foreground": "hsl(270, 100%, 70%)",                                            "font_style": "bold",  },

Modified misc/grammalecte.sublime-syntax from [9e26acd942] to [d99bbc135a].

166
167
168
169
170
171
172
173
174
175
176
177
178






179
180
181
182
183
184
185

    - match: '(@)([^@\s¬]*)'
      scope: string.morph
      captures:
        1: entity.valid
        2: string.morph.regex

    - match: '(\$)([^@\s¬]*)'
      scope: string.morph
      captures:
        1: entity.valid
        2: string.morph.regex







    - match: '(/)[\w-]+'
      scope: string.tag
      captures:
        1: entity.valid

    - match: '(?<=[^\w])([*][a-zA-Z0-9_]+)'
      scope: string.morph







|





>
>
>
>
>
>







166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191

    - match: '(@)([^@\s¬]*)'
      scope: string.morph
      captures:
        1: entity.valid
        2: string.morph.regex

    - match: '(\$)([^\s¬]*)'
      scope: string.morph
      captures:
        1: entity.valid
        2: string.morph.regex

    - match: '(&)([^\s¬]*)'
      scope: string.morph
      captures:
        1: entity.valid
        2: string.mt.morph.regex

    - match: '(/)[\w-]+'
      scope: string.tag
      captures:
        1: entity.valid

    - match: '(?<=[^\w])([*][a-zA-Z0-9_]+)'
      scope: string.morph