Index: compile_rules_graph.py ================================================================== --- compile_rules_graph.py +++ compile_rules_graph.py @@ -285,11 +285,11 @@ sAction = sAction[m.end():].strip() if nPriority == -1: nPriority = self.dOptPriority.get(sOption, 4) # valid action? - m = re.search(r"(?P[-=~/!>])(?P-?\d+\.?|)(?P:\.?-?\d+|)(?P:|)>>", sAction) + m = re.search(r"(?P[-=~/!>&])(?P-?\d+\.?|)(?P:\.?-?\d+|)(?P:|)>>", sAction) if not m: print("\n# Error. No action found at: ", sLineId, sActionId) exit() # Condition @@ -373,11 +373,11 @@ if cAction == ">": ## no action, break loop if condition is False return [sLineId, sOption, sCondition, cAction, ""] - if not sAction and 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) @@ -404,11 +404,11 @@ 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 "!/": + if cAction in "!/&": ## tags return [sLineId, sOption, sCondition, cAction, sAction, iStartAction, iEndAction] if cAction == "=": ## disambiguator sAction = self.createFunction("da", sAction) @@ -543,21 +543,21 @@ 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): + 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): + if re.search(r"[-=~/!>&](?:-?\d\.?(?::\.?-?\d+|)|):?>>", sLine): bActionBlock = False elif re.match("[  ]*$", sLine): # empty line to end merging if not lTokenLine: continue Index: darg.py ================================================================== --- darg.py +++ darg.py @@ -214,13 +214,14 @@ 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 + 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 = {} @@ -227,10 +228,12 @@ 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: @@ -245,10 +248,12 @@ dNode[sArc] = oNode.__hash__() if dReValue: dNode[""] = dReValue if dReMorph: dNode[""] = dReMorph + if dReMultiMorph: + dNode[""] = dReMultiMorph if dMorph: dNode[""] = dMorph if dLemma: dNode[""] = dLemma if dPhonet: Index: gc_core/js/lang_core/gc_engine.js ================================================================== --- gc_core/js/lang_core/gc_engine.js +++ gc_core/js/lang_core/gc_engine.js @@ -597,19 +597,28 @@ console.log("TOKEN: " + oToken["sValue"]); } // check arcs for each existing pointer let lNextPointer = []; for (let oPointer of lPointer) { + if (oPointer["nMultiEnd"] != -1) { + if (oToken["i"] <= oPointer["nMultiEnd"]) { + lNextPointer.push(oPointer); + } + if (oToken["i"] != oPointer["nMultiEnd"]) { + continue; + } + } 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 }); + let nMultiEnd = (cActionType != "&") ? -1 : dToken["nMultiStartTo"]; + lNextPointer.push({ "iToken1": oPointer["iToken1"], "iNode": iNode, "nMultiEnd": nMultiEnd }); } } lPointer = lNextPointer; // check arcs of first nodes for (let [cActionType, sMatch, iNode] of this._getMatches(oGraph, oToken, oGraph[0])) { @@ -617,14 +626,23 @@ continue; } if (bDebug) { console.log(" MATCH: " + cActionType + sMatch); } - lPointer.push({ "iToken1": iToken, "iNode": iNode }); + let nMultiEnd = (cActionType != "&") ? -1 : dToken["nMultiStartTo"]; + lPointer.push({ "iToken1": iToken, "iNode": iNode, "nMultiEnd": nMultiEnd }); } // check if there is rules to check for each pointer for (let oPointer of lPointer) { + if (oPointer["nMultiEnd"] != -1) { + if (oToken["i"] < oPointer["nMultiEnd"]) { + continue; + } + if (oToken["i"] == oPointer["nMultiEnd"]) { + oPointer["nMultiEnd"] = -1; + } + } if (oGraph[oPointer["iNode"]].hasOwnProperty("")) { let bChange = this._executeActions(oGraph, oGraph[oPointer["iNode"]][""], oPointer["iToken1"]-1, iToken, dOptions, sCountry, bShowRuleId, bDebug, bContext); if (bChange) { bTagAndRewrite = true; } @@ -657,10 +675,11 @@ // 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 == "-") { @@ -748,11 +767,27 @@ if (this.dError.has(nErrorStart)) { this.dError.delete(nErrorStart); } } } - } else { + } + 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": (sWhat) ? sWhat.split("|") : [":HM"] + } + this.lTokens[nTokenStart]["nMultiStartTo"] = nTokenEnd + this.lTokens[nTokenEnd]["nMultiEndFrom"] = nTokenStart + this.lTokens[nTokenStart]["dMultiToken"] = dMultiToken + this.lTokens[nTokenEnd]["dMultiToken"] = dMultiToken + } + else { console.log("# error: unknown action at " + sLineId); } } else if (cActionType == ">") { if (bDebug) { Index: gc_core/py/lang_core/gc_engine.py ================================================================== --- gc_core/py/lang_core/gc_engine.py +++ gc_core/py/lang_core/gc_engine.py @@ -507,10 +507,35 @@ 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[""][sRegex]) bTokenFound = True + # regex multi morph arcs + if "" in dNode: + if "nMultiStartTo" in dToken: + lMorph = dToken["dMultiToken"]["lMorph"] + for sRegex in dNode[""]: + if "¬" not in sRegex: + # no anti-pattern + if any(re.search(sRegex, sMorph) for sMorph in lMorph): + yield ("&", sRegex, dNode[""][sRegex]) + bTokenFound = True + else: + # there is an anti-pattern + sPattern, sNegPattern = sRegex.split("¬", 1) + if sNegPattern == "*": + # all morphologies must match with + if sPattern: + if all(re.search(sPattern, sMorph) for sMorph in lMorph): + yield ("&", sRegex, dNode[""][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[""][sRegex]) + bTokenFound = True # token tags if "aTags" in dToken and "" in dNode: for sTag in dToken["aTags"]: if sTag in dNode[""]: yield ("/", sTag, dNode[""][sTag]) @@ -541,29 +566,39 @@ if bDebug: echo("TOKEN: " + dToken["sValue"]) # check arcs for each existing pointer lNextPointer = [] for dPointer in lPointer: + if dPointer["nMultiEnd"] != -1: + if dToken["i"] <= dPointer["nMultiEnd"]: + lNextPointer.append(dPointer) + if dToken["i"] != dPointer["nMultiEnd"]: + continue 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 }) + nMultiEnd = -1 if cActionType != "&" else dToken["nMultiStartTo"] + lNextPointer.append({ "iToken1": dPointer["iToken1"], "iNode": iNode, "nMultiEnd": nMultiEnd }) 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 }) + nMultiEnd = -1 if cActionType != "&" else dToken["nMultiStartTo"] + lPointer.append({ "iToken1": iToken, "iNode": iNode, "nMultiEnd": nMultiEnd }) # check if there is rules to check for each pointer for dPointer in lPointer: - #if bDebug: - # echo("+", dPointer) + if dPointer["nMultiEnd"] != -1: + if dToken["i"] < dPointer["nMultiEnd"]: + continue + if dToken["i"] == dPointer["nMultiEnd"]: + dPointer["nMultiEnd"] = -1 if "" in dGraph[dPointer["iNode"]]: bChange = self._executeActions(dGraph, dGraph[dPointer["iNode"]][""], dPointer["iToken1"]-1, iToken, dOptions, sCountry, bShowRuleId, bDebug, bContext) if bChange: bTagAndRewrite = True if bTagAndRewrite: @@ -585,10 +620,11 @@ # 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 == "-": @@ -656,10 +692,25 @@ 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": sWhat.split("|") if sWhat else [":HM"] + } + self.lTokens[nTokenStart]["nMultiStartTo"] = nTokenEnd + self.lTokens[nTokenEnd]["nMultiEndFrom"] = nTokenStart + self.lTokens[nTokenStart]["dMultiToken"] = dMultiToken + self.lTokens[nTokenEnd]["dMultiToken"] = dMultiToken + print(dMultiToken) else: echo("# error: unknown action at " + sLineId) elif cActionType == ">": if bDebug: echo(" COND_BREAK") Index: gc_lang/fr/rules.grx ================================================================== --- gc_lang/fr/rules.grx +++ gc_lang/fr/rules.grx @@ -430,10 +430,11 @@ __/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 @@ -1560,10 +1561,65 @@ 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 +__(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 !! + +__> - && 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- +__> - && 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- +__> -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 ? + !! !! !! @@ -1637,24 +1693,10 @@ 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 -__(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,$ @@ -1661,50 +1703,10 @@ <<- 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 - -__> - && 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- -__> - && 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- -__> -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 ? - @@@@ @@@@ @@@@ @@ -3826,10 +3828,15 @@ ~^[A-ZÀÂÉÈÊÎÔ]. Airways <<- ~>> ␣ <<- =>> define(\2, ":MP:e:i") + +__merge__ + à la pp + <<- &>> :LW + __immunités__ il y a il n’ y a <<- !-1>> @@ -4901,10 +4908,23 @@ # 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 + +__test_merge__ + &:LW + <<- echo("DETECTED") ~>> * + + je vais &:LW + <<- --1>> X && TEST0. + + je vais &:LW de + <<- --1>> X && TEST2. + <<- -1>> Z && TEST1. + + !! !! !!!! Élisions & euphonie !! @@ -23207,11 +23227,13 @@ 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 + [>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 Index: misc/grammalecte.sublime-color-scheme ================================================================== --- misc/grammalecte.sublime-color-scheme +++ misc/grammalecte.sublime-color-scheme @@ -66,15 +66,16 @@ { "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 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": "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", }, Index: misc/grammalecte.sublime-syntax ================================================================== --- misc/grammalecte.sublime-syntax +++ misc/grammalecte.sublime-syntax @@ -168,16 +168,22 @@ scope: string.morph captures: 1: entity.valid 2: string.morph.regex - - match: '(\$)([^@\s¬]*)' + - 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