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 @@ -195,10 +195,12 @@ this.parseText(this.sText, this.sText0, true, 0, sCountry, dOpt, bShowRuleId, bDebug, bContext); } catch (e) { console.error(e); } + this.lTokens = null; + this.lTokens0 = null; let lParagraphErrors = null; if (bFullInfo) { lParagraphErrors = Array.from(this.dError.values()); this.dSentenceError.clear(); } @@ -209,30 +211,39 @@ for (let [iStart, iEnd] of text.getSentenceBoundaries(sText)) { try { this.sSentence = sText.slice(iStart, iEnd); this.sSentence0 = this.sText0.slice(iStart, iEnd); this.nOffsetWithinParagraph = iStart; - this.lToken = Array.from(gc_engine.oTokenizer.genTokens(this.sSentence, true)); + this.lTokens = Array.from(gc_engine.oTokenizer.genTokens(this.sSentence, true)); this.dTokenPos.clear(); - for (let dToken of this.lToken) { + for (let dToken of this.lTokens) { if (dToken["sType"] != "INFO") { this.dTokenPos.set(dToken["nStart"], dToken); } } if (bFullInfo) { - oSentence = { "nStart": iStart, "nEnd": iEnd, "sSentence": this.sSentence, "lToken": Array.from(this.lToken) }; - for (let oToken of oSentence["lToken"]) { + this.lTokens0 = Array.from(this.lTokens); + // the list of tokens is duplicated, to keep tokens from being deleted when analysis + } + this.parseText(this.sSentence, this.sSentence0, false, iStart, sCountry, dOpt, bShowRuleId, bDebug, bContext); + if (bFullInfo) { + for (let oToken of this.lTokens0) { if (oToken["sType"] == "WORD") { oToken["bValidToken"] = gc_engine.oSpellChecker.isValidToken(oToken["sValue"]); } + if (!oToken.hasOwnProperty("lMorph")) { + oToken["lMorph"] = gc_engine.oSpellChecker.getMorph(oToken["sValue"]); + } + gc_engine.oSpellChecker.setLabelsOnToken(oToken); } - // the list of tokens is duplicated, to keep all tokens from being deleted when analysis - } - this.parseText(this.sSentence, this.sSentence0, false, iStart, sCountry, dOpt, bShowRuleId, bDebug, bContext); - if (bFullInfo) { - oSentence["lGrammarErrors"] = Array.from(this.dSentenceError.values()); - lSentences.push(oSentence); + lSentences.push({ + "nStart": iStart, + "nEnd": iEnd, + "sSentence": this.sSentence0, + "lTokens": this.lTokens0, + "lGrammarErrors": Array.from(this.dSentenceError.values()) + }); this.dSentenceError.clear(); } } catch (e) { console.error(e); @@ -372,13 +383,13 @@ } if (this.dTokenPos.gl_get(oToken["nStart"], {}).hasOwnProperty("aTags")) { oToken["aTags"] = this.dTokenPos.get(oToken["nStart"])["aTags"]; } } - this.lToken = lNewToken; + this.lTokens = lNewToken; this.dTokenPos.clear(); - for (let oToken of this.lToken) { + for (let oToken of this.lTokens) { if (oToken["sType"] != "INFO") { this.dTokenPos.set(oToken["nStart"], oToken); } } if (bDebug) { @@ -614,11 +625,11 @@ 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.lToken.entries()) { + for (let [iToken, oToken] of this.lTokens.entries()) { if (bDebug) { console.log("TOKEN: " + oToken["sValue"]); } // check arcs for each existing pointer let lNextPointer = []; @@ -666,21 +677,21 @@ // 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.lToken, nTokenOffset, nLastToken, sCountry, bCondMemo, this.dTags, this.sSentence, this.sSentence0); - //bCondMemo = !sFuncCond || oEvalFunc[sFuncCond](this.lToken, nTokenOffset, nLastToken, sCountry, bCondMemo, this.dTags, this.sSentence, this.sSentence0); + bCondMemo = !sFuncCond || gc_functions[sFuncCond](this.lTokens, nTokenOffset, nLastToken, sCountry, bCondMemo, this.dTags, this.sSentence, this.sSentence0); + //bCondMemo = !sFuncCond || oEvalFunc[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.lToken[nTokenErrorStart].hasOwnProperty("bImmune")) { + if (!this.lTokens[nTokenErrorStart].hasOwnProperty("bImmune")) { let nTokenErrorEnd = (iTokenEnd > 0) ? nTokenOffset + iTokenEnd : nLastToken + iTokenEnd; - let nErrorStart = this.nOffsetWithinParagraph + ((cStartLimit == "<") ? this.lToken[nTokenErrorStart]["nStart"] : this.lToken[nTokenErrorStart]["nEnd"]); - let nErrorEnd = this.nOffsetWithinParagraph + ((cEndLimit == ">") ? this.lToken[nTokenErrorEnd]["nEnd"] : this.lToken[nTokenErrorEnd]["nStart"]); + 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)); @@ -695,19 +706,19 @@ 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.lToken[nTokenStart]["sValue"]}:${this.lToken[nTokenEnd]["sValue"]}] > ${sWhat}`); + console.log(` TEXT_PROCESSOR: [${this.lTokens[nTokenStart]["sValue"]}:${this.lTokens[nTokenEnd]["sValue"]}] > ${sWhat}`); } } else if (cActionType == "=") { // disambiguation - gc_functions[sWhat](this.lToken, nTokenOffset, nLastToken); - //oEvalFunc[sWhat](this.lToken, nTokenOffset, nLastToken); + gc_functions[sWhat](this.lTokens, nTokenOffset, nLastToken); + //oEvalFunc[sWhat](this.lTokens, nTokenOffset, nLastToken); if (bDebug) { - console.log(` DISAMBIGUATOR: (${sWhat}) [${this.lToken[nTokenOffset+1]["sValue"]}:${this.lToken[nLastToken]["sValue"]}]`); + 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) { @@ -717,18 +728,18 @@ 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.lToken[i].hasOwnProperty("aTags")) { - this.lToken[i]["aTags"].add(...sWhat.split("|")) + if (this.lTokens[i].hasOwnProperty("aTags")) { + this.lTokens[i]["aTags"].add(...sWhat.split("|")) } else { - this.lToken[i]["aTags"] = new Set(sWhat.split("|")); + this.lTokens[i]["aTags"] = new Set(sWhat.split("|")); } } if (bDebug) { - console.log(` TAG: ${sWhat} > [${this.lToken[nTokenStart]["sValue"]}:${this.lToken[nTokenEnd]["sValue"]}]`); + 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 { @@ -742,19 +753,19 @@ 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]; if (nTokenEnd - nTokenStart == 0) { - this.lToken[nTokenStart]["bImmune"] = true; - let nErrorStart = this.nOffsetWithinParagraph + this.lToken[nTokenStart]["nStart"]; + this.lTokens[nTokenStart]["bImmune"] = true; + 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.lToken[i]["bImmune"] = true; - let nErrorStart = this.nOffsetWithinParagraph + this.lToken[i]["nStart"]; + this.lTokens[i]["bImmune"] = true; + let nErrorStart = this.nOffsetWithinParagraph + this.lTokens[i]["nStart"]; if (this.dError.has(nErrorStart)) { this.dError.delete(nErrorStart); } } } @@ -808,24 +819,24 @@ _createErrorFromTokens (sSugg, nTokenOffset, nLastToken, iFirstToken, nStart, nEnd, sLineId, sRuleId, bCaseSvty, sMsg, sURL, bShowRuleId, sOption, bContext) { // suggestions let lSugg = []; if (sSugg.startsWith("=")) { - sSugg = gc_functions[sSugg.slice(1)](this.lToken, nTokenOffset, nLastToken); - //sSugg = oEvalFunc[sSugg.slice(1)](this.lToken, nTokenOffset, nLastToken); + sSugg = gc_functions[sSugg.slice(1)](this.lTokens, nTokenOffset, nLastToken); + //sSugg = oEvalFunc[sSugg.slice(1)](this.lTokens, nTokenOffset, nLastToken); lSugg = (sSugg) ? sSugg.split("|") : []; } else if (sSugg == "_") { lSugg = []; } else { lSugg = this._expand(sSugg, nTokenOffset, nLastToken).split("|"); } - if (bCaseSvty && lSugg.length > 0 && this.lToken[iFirstToken]["sValue"].slice(0,1).gl_isUpperCase()) { + if (bCaseSvty && lSugg.length > 0 && this.lTokens[iFirstToken]["sValue"].slice(0,1).gl_isUpperCase()) { lSugg = (this.sSentence.slice(nStart, nEnd).gl_isUpperCase()) ? lSugg.map((s) => s.toUpperCase()) : capitalizeArray(lSugg); } // Message - let sMessage = (sMsg.startsWith("=")) ? gc_functions[sMsg.slice(1)](this.lToken, nTokenOffset, nLastToken) : this._expand(sMsg, nTokenOffset, nLastToken); - //let sMessage = (sMsg.startsWith("=")) ? oEvalFunc[sMsg.slice(1)](this.lToken, nTokenOffset, nLastToken) : this._expand(sMsg, nTokenOffset, nLastToken); + let sMessage = (sMsg.startsWith("=")) ? gc_functions[sMsg.slice(1)](this.lTokens, nTokenOffset, nLastToken) : this._expand(sMsg, nTokenOffset, nLastToken); + //let sMessage = (sMsg.startsWith("=")) ? oEvalFunc[sMsg.slice(1)](this.lTokens, nTokenOffset, nLastToken) : this._expand(sMsg, nTokenOffset, nLastToken); if (bShowRuleId) { sMessage += " #" + sLineId + " / " + sRuleId; } // return this._createError(nStart, nEnd, sLineId, sRuleId, sOption, sMessage, lSugg, sURL, bContext); @@ -853,13 +864,13 @@ _expand (sText, nTokenOffset, nLastToken) { let m; while ((m = /\\(-?[0-9]+)/.exec(sText)) !== null) { if (m[1].slice(0,1) == "-") { - sText = sText.replace(m[0], this.lToken[nLastToken+parseInt(m[1],10)+1]["sValue"]); + sText = sText.replace(m[0], this.lTokens[nLastToken+parseInt(m[1],10)+1]["sValue"]); } else { - sText = sText.replace(m[0], this.lToken[nTokenOffset+parseInt(m[1],10)]["sValue"]); + sText = sText.replace(m[0], this.lTokens[nTokenOffset+parseInt(m[1],10)]["sValue"]); } } return sText; } @@ -894,45 +905,45 @@ _tagAndPrepareTokenForRewriting (sWhat, nTokenRewriteStart, nTokenRewriteEnd, nTokenOffset, nLastToken, bCaseSvty, bDebug) { // text processor: rewrite tokens between and position if (sWhat === "*") { // purge text if (nTokenRewriteEnd - nTokenRewriteStart == 0) { - this.lToken[nTokenRewriteStart]["bToRemove"] = true; + this.lTokens[nTokenRewriteStart]["bToRemove"] = true; } else { for (let i = nTokenRewriteStart; i <= nTokenRewriteEnd; i++) { - this.lToken[i]["bToRemove"] = true; + this.lTokens[i]["bToRemove"] = true; } } } else if (sWhat === "␣") { // merge tokens - this.lToken[nTokenRewriteStart]["nMergeUntil"] = nTokenRewriteEnd; + this.lTokens[nTokenRewriteStart]["nMergeUntil"] = nTokenRewriteEnd; } else if (sWhat === "_") { // neutralized token if (nTokenRewriteEnd - nTokenRewriteStart == 0) { - this.lToken[nTokenRewriteStart]["sNewValue"] = "_"; + this.lTokens[nTokenRewriteStart]["sNewValue"] = "_"; } else { for (let i = nTokenRewriteStart; i <= nTokenRewriteEnd; i++) { - this.lToken[i]["sNewValue"] = "_"; + this.lTokens[i]["sNewValue"] = "_"; } } } else { if (sWhat.startsWith("=")) { - sWhat = gc_functions[sWhat.slice(1)](this.lToken, nTokenOffset, nLastToken); - //sWhat = oEvalFunc[sWhat.slice(1)](this.lToken, nTokenOffset, nLastToken); + sWhat = gc_functions[sWhat.slice(1)](this.lTokens, nTokenOffset, nLastToken); + //sWhat = oEvalFunc[sWhat.slice(1)](this.lTokens, nTokenOffset, nLastToken); } else { sWhat = this._expand(sWhat, nTokenOffset, nLastToken); } - let bUppercase = bCaseSvty && this.lToken[nTokenRewriteStart]["sValue"].slice(0,1).gl_isUpperCase(); + 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.lToken[nTokenRewriteStart]["sNewValue"] = sWhat; + this.lTokens[nTokenRewriteStart]["sNewValue"] = sWhat; } else { // several tokens let lTokenValue = sWhat.split("|"); if (lTokenValue.length != (nTokenRewriteEnd - nTokenRewriteStart + 1)) { @@ -943,16 +954,16 @@ } let j = 0; for (let i = nTokenRewriteStart; i <= nTokenRewriteEnd; i++) { let sValue = lTokenValue[j]; if (!sValue || sValue === "*") { - this.lToken[i]["bToRemove"] = true; + this.lTokens[i]["bToRemove"] = true; } else { if (bUppercase) { sValue = sValue.gl_toCapitalize(); } - this.lToken[i]["sNewValue"] = sValue; + this.lTokens[i]["sNewValue"] = sValue; } j++; } } } @@ -964,19 +975,20 @@ console.log("REWRITE"); } let lNewToken = []; let nMergeUntil = 0; let oMergingToken = null; - for (let [iToken, oToken] of this.lToken.entries()) { + for (let [iToken, oToken] of this.lTokens.entries()) { let bKeepToken = true; if (oToken["sType"] != "INFO") { if (nMergeUntil && iToken <= nMergeUntil) { oMergingToken["sValue"] += " ".repeat(oToken["nStart"] - oMergingToken["nEnd"]) + oToken["sValue"]; oMergingToken["nEnd"] = oToken["nEnd"]; if (bDebug) { console.log(" MERGED TOKEN: " + oMergingToken["sValue"]); } + oToken["bMerged"] = true; bKeepToken = false; } if (oToken.hasOwnProperty("nMergeUntil")) { if (iToken > nMergeUntil) { // this token is not already merged with a previous token oMergingToken = oToken; @@ -1021,12 +1033,12 @@ } } if (bDebug) { console.log(" TEXT REWRITED: " + this.sSentence); } - this.lToken.length = 0; - this.lToken = lNewToken; + this.lTokens.length = 0; + this.lTokens = lNewToken; } }; if (typeof(exports) !== 'undefined') { 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 @@ -233,11 +233,11 @@ self.sText = sText self.sText0 = sText self.sSentence = "" self.sSentence0 = "" self.nOffsetWithinParagraph = 0 - self.lToken = [] + self.lTokens = [] self.dTokenPos = {} # {position: token} self.dTags = {} # {position: tags} self.dError = {} # {position: error} self.dSentenceError = {} # {position: error} (for the current sentence only) self.dErrorPriority = {} # {position: priority of the current error} @@ -244,11 +244,11 @@ def __str__ (self): s = "===== TEXT =====\n" s += "sentence: " + self.sSentence0 + "\n" s += "now: " + self.sSentence + "\n" - for dToken in self.lToken: + 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"]) @@ -265,10 +265,12 @@ # parse paragraph try: self.parseText(self.sText, self.sText0, True, 0, sCountry, dOpt, bShowRuleId, bDebug, bContext) except: raise + self.lTokens = None + self.lTokens0 = None if bFullInfo: lParagraphErrors = list(self.dError.values()) lSentences = [] self.dSentenceError.clear() # parse sentences @@ -277,22 +279,29 @@ if 4 < (iEnd - iStart) < 2000: try: self.sSentence = sText[iStart:iEnd] self.sSentence0 = self.sText0[iStart:iEnd] self.nOffsetWithinParagraph = iStart - self.lToken = list(_oTokenizer.genTokens(self.sSentence, True)) - self.dTokenPos = { dToken["nStart"]: dToken for dToken in self.lToken if dToken["sType"] != "INFO" } + self.lTokens = list(_oTokenizer.genTokens(self.sSentence, True)) + self.dTokenPos = { dToken["nStart"]: dToken for dToken in self.lTokens if dToken["sType"] != "INFO" } + if bFullInfo: + self.lTokens0 = list(self.lTokens) # the list of tokens is duplicated, to keep tokens from being deleted when analysis + self.parseText(self.sSentence, self.sSentence0, False, iStart, sCountry, dOpt, bShowRuleId, bDebug, bContext) if bFullInfo: - dSentence = { "nStart": iStart, "nEnd": iEnd, "sSentence": self.sSentence, "lToken": list(self.lToken) } - for dToken in dSentence["lToken"]: + for dToken in self.lTokens0: if dToken["sType"] == "WORD": dToken["bValidToken"] = _oSpellChecker.isValidToken(dToken["sValue"]) - # the list of tokens is duplicated, to keep all tokens from being deleted when analysis - self.parseText(self.sSentence, self.sSentence0, False, iStart, sCountry, dOpt, bShowRuleId, bDebug, bContext) - if bFullInfo: - dSentence["lGrammarErrors"] = list(self.dSentenceError.values()) - lSentences.append(dSentence) + if "lMorph" not in dToken: + dToken["lMorph"] = _oSpellChecker.getMorph(dToken["sValue"]) + _oSpellChecker.setLabelsOnToken(dToken) + lSentences.append({ + "nStart": iStart, + "nEnd": iEnd, + "sSentence": self.sSentence0, + "lTokens": self.lTokens0, + "lGrammarErrors": list(self.dSentenceError.values()) + }) self.dSentenceError.clear() except: raise if bFullInfo: # Grammar checking and sentence analysis @@ -379,18 +388,18 @@ self.sSentence = sText def update (self, sSentence, bDebug=False): "update and retokenize" self.sSentence = sSentence - lNewToken = list(_oTokenizer.genTokens(sSentence, True)) - for dToken in lNewToken: + lNewTokens = list(_oTokenizer.genTokens(sSentence, True)) + for dToken in lNewTokens: if "lMorph" in self.dTokenPos.get(dToken["nStart"], {}): dToken["lMorph"] = self.dTokenPos[dToken["nStart"]]["lMorph"] if "aTags" in self.dTokenPos.get(dToken["nStart"], {}): dToken["aTags"] = self.dTokenPos[dToken["nStart"]]["aTags"] - self.lToken = lNewToken - self.dTokenPos = { dToken["nStart"]: dToken for dToken in self.lToken if dToken["sType"] != "INFO" } + self.lTokens = lNewTokens + self.dTokenPos = { dToken["nStart"]: dToken for dToken in self.lTokens if dToken["sType"] != "INFO" } if bDebug: echo("UPDATE:") echo(self) def _getNextPointers (self, dToken, dGraph, dPointer, bDebug=False): @@ -550,11 +559,11 @@ 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.lToken): + for iToken, dToken in enumerate(self.lTokens): if bDebug: echo("TOKEN: " + dToken["sValue"]) # check arcs for each existing pointer lNextPointer = [] for dPointer in lPointer: @@ -591,20 +600,20 @@ # Disambiguator [ option, condition, "=", replacement/suggestion/action ] # Tag [ option, condition, "/", replacement/suggestion/action, iTokenStart, iTokenEnd ] # Immunity [ option, condition, "!", "", iTokenStart, iTokenEnd ] # Test [ option, condition, ">", "" ] if not sOption or dOptions.get(sOption, False): - bCondMemo = not sFuncCond or getattr(gc_functions, sFuncCond)(self.lToken, nTokenOffset, nLastToken, sCountry, bCondMemo, self.dTags, self.sSentence, self.sSentence0) + 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 "bImmune" not in self.lToken[nTokenErrorStart]: + if "bImmune" not in self.lTokens[nTokenErrorStart]: nTokenErrorEnd = nTokenOffset + iTokenEnd if iTokenEnd > 0 else nLastToken + iTokenEnd - nErrorStart = self.nOffsetWithinParagraph + (self.lToken[nTokenErrorStart]["nStart"] if cStartLimit == "<" else self.lToken[nTokenErrorStart]["nEnd"]) - nErrorEnd = self.nOffsetWithinParagraph + (self.lToken[nTokenErrorEnd]["nEnd"] if cEndLimit == ">" else self.lToken[nTokenErrorEnd]["nStart"]) + 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] @@ -615,31 +624,31 @@ 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.lToken[nTokenStart]["sValue"], self.lToken[nTokenEnd]["sValue"], sWhat)) + echo(" TEXT_PROCESSOR: [{}:{}] > {}".format(self.lTokens[nTokenStart]["sValue"], self.lTokens[nTokenEnd]["sValue"], sWhat)) elif cActionType == "=": # disambiguation - getattr(gc_functions, sWhat)(self.lToken, nTokenOffset, nLastToken) + getattr(gc_functions, sWhat)(self.lTokens, nTokenOffset, nLastToken) if bDebug: - echo(" DISAMBIGUATOR: ({}) [{}:{}]".format(sWhat, self.lToken[nTokenOffset+1]["sValue"], self.lToken[nLastToken]["sValue"])) + 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.lToken[i]: - self.lToken[i]["aTags"].update(sWhat.split("|")) + if "aTags" in self.lTokens[i]: + self.lTokens[i]["aTags"].update(sWhat.split("|")) else: - self.lToken[i]["aTags"] = set(sWhat.split("|")) + self.lTokens[i]["aTags"] = set(sWhat.split("|")) if bDebug: - echo(" TAG: {} > [{}:{}]".format(sWhat, self.lToken[nTokenStart]["sValue"], self.lToken[nTokenEnd]["sValue"])) + 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]) @@ -649,18 +658,18 @@ 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] if nTokenEnd - nTokenStart == 0: - self.lToken[nTokenStart]["bImmune"] = True - nErrorStart = self.nOffsetWithinParagraph + self.lToken[nTokenStart]["nStart"] + self.lTokens[nTokenStart]["bImmune"] = True + 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.lToken[i]["bImmune"] = True - nErrorStart = self.nOffsetWithinParagraph + self.lToken[i]["nStart"] + self.lTokens[i]["bImmune"] = True + 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 == ">": @@ -694,20 +703,20 @@ return self._createErrorAsDict(nStart, nEnd, sLineId, sRuleId, sOption, sMessage, lSugg, sURL, bContext) def _createErrorFromTokens (self, sSugg, nTokenOffset, nLastToken, iFirstToken, nStart, nEnd, sLineId, sRuleId, bCaseSvty, sMsg, sURL, bShowRuleId, sOption, bContext): # suggestions if sSugg[0:1] == "=": - sSugg = getattr(gc_functions, sSugg[1:])(self.lToken, nTokenOffset, nLastToken) + sSugg = getattr(gc_functions, sSugg[1:])(self.lTokens, nTokenOffset, nLastToken) lSugg = sSugg.split("|") if sSugg else [] elif sSugg == "_": lSugg = [] else: lSugg = self._expand(sSugg, nTokenOffset, nLastToken).split("|") - if bCaseSvty and lSugg and self.lToken[iFirstToken]["sValue"][0:1].isupper(): + if bCaseSvty and lSugg and self.lTokens[iFirstToken]["sValue"][0:1].isupper(): lSugg = list(map(lambda s: s.upper(), lSugg)) if self.sSentence[nStart:nEnd].isupper() else list(map(lambda s: s[0:1].upper()+s[1:], lSugg)) # Message - sMessage = getattr(gc_functions, sMsg[1:])(self.lToken, nTokenOffset, nLastToken) if sMsg[0:1] == "=" else self._expand(sMsg, nTokenOffset, nLastToken) + sMessage = getattr(gc_functions, sMsg[1:])(self.lTokens, nTokenOffset, nLastToken) if sMsg[0:1] == "=" else self._expand(sMsg, nTokenOffset, nLastToken) if bShowRuleId: sMessage += " #" + sLineId + " / " + sRuleId # if _bWriterError: return self._createErrorForWriter(nStart, nEnd - nStart, sRuleId, sOption, sMessage, lSugg, sURL) @@ -752,13 +761,13 @@ return dErr def _expand (self, sText, nTokenOffset, nLastToken): for m in re.finditer(r"\\(-?[0-9]+)", sText): if m.group(1)[0:1] == "-": - sText = sText.replace(m.group(0), self.lToken[nLastToken+int(m.group(1))+1]["sValue"]) + sText = sText.replace(m.group(0), self.lTokens[nLastToken+int(m.group(1))+1]["sValue"]) else: - sText = sText.replace(m.group(0), self.lToken[nTokenOffset+int(m.group(1))]["sValue"]) + sText = sText.replace(m.group(0), self.lTokens[nTokenOffset+int(m.group(1))]["sValue"]) return sText def rewriteText (self, sText, sRepl, iGroup, m, bUppercase): "text processor: write in at position" nLen = m.end(iGroup) - m.start(iGroup) @@ -781,80 +790,85 @@ def _tagAndPrepareTokenForRewriting (self, sWhat, nTokenRewriteStart, nTokenRewriteEnd, nTokenOffset, nLastToken, bCaseSvty, bDebug): "text processor: rewrite tokens between and position" if sWhat == "*": # purge text if nTokenRewriteEnd - nTokenRewriteStart == 0: - self.lToken[nTokenRewriteStart]["bToRemove"] = True + self.lTokens[nTokenRewriteStart]["bToRemove"] = True else: for i in range(nTokenRewriteStart, nTokenRewriteEnd+1): - self.lToken[i]["bToRemove"] = True + self.lTokens[i]["bToRemove"] = True elif sWhat == "␣": # merge tokens - self.lToken[nTokenRewriteStart]["nMergeUntil"] = nTokenRewriteEnd + self.lTokens[nTokenRewriteStart]["nMergeUntil"] = nTokenRewriteEnd elif sWhat == "_": # neutralized token if nTokenRewriteEnd - nTokenRewriteStart == 0: - self.lToken[nTokenRewriteStart]["sNewValue"] = "_" + self.lTokens[nTokenRewriteStart]["sNewValue"] = "_" else: for i in range(nTokenRewriteStart, nTokenRewriteEnd+1): - self.lToken[i]["sNewValue"] = "_" + self.lTokens[i]["sNewValue"] = "_" else: if sWhat.startswith("="): - sWhat = getattr(gc_functions, sWhat[1:])(self.lToken, nTokenOffset, nLastToken) + sWhat = getattr(gc_functions, sWhat[1:])(self.lTokens, nTokenOffset, nLastToken) else: sWhat = self._expand(sWhat, nTokenOffset, nLastToken) - bUppercase = bCaseSvty and self.lToken[nTokenRewriteStart]["sValue"][0:1].isupper() + 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.lToken[nTokenRewriteStart]["sNewValue"] = sWhat + 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.lToken[i]["bToRemove"] = True + self.lTokens[i]["bToRemove"] = True else: if bUppercase: sValue = sValue[0:1].upper() + sValue[1:] - self.lToken[i]["sNewValue"] = sValue + self.lTokens[i]["sNewValue"] = sValue def rewriteFromTags (self, bDebug=False): "rewrite the sentence, modify tokens, purge the token list" if bDebug: echo("REWRITE") - lNewToken = [] + lNewTokens = [] + lNewTokens0 = [] nMergeUntil = 0 dTokenMerger = {} - for iToken, dToken in enumerate(self.lToken): + for iToken, dToken in enumerate(self.lTokens): bKeepToken = True if dToken["sType"] != "INFO": if nMergeUntil and iToken <= nMergeUntil: + # token to merge dTokenMerger["sValue"] += " " * (dToken["nStart"] - dTokenMerger["nEnd"]) + dToken["sValue"] dTokenMerger["nEnd"] = dToken["nEnd"] if bDebug: echo(" MERGED TOKEN: " + dTokenMerger["sValue"]) + dToken["bMerged"] = True bKeepToken = False if "nMergeUntil" in dToken: - if iToken > nMergeUntil: # this token is not already merged with a previous token + # first token to be merge with + if iToken > nMergeUntil: # this token is not to be merged with a previous token dTokenMerger = dToken if dToken["nMergeUntil"] > nMergeUntil: nMergeUntil = dToken["nMergeUntil"] del dToken["nMergeUntil"] elif "bToRemove" in dToken: + # deletion required if bDebug: echo(" REMOVED: " + dToken["sValue"]) self.sSentence = self.sSentence[:dToken["nStart"]] + " " * (dToken["nEnd"] - dToken["nStart"]) + self.sSentence[dToken["nEnd"]:] bKeepToken = False # if bKeepToken: - lNewToken.append(dToken) + lNewTokens.append(dToken) if "sNewValue" in dToken: # rewrite token and sentence if bDebug: echo(dToken["sValue"] + " -> " + dToken["sNewValue"]) dToken["sRealValue"] = dToken["sValue"] @@ -869,7 +883,7 @@ except KeyError: echo(self) echo(dToken) if bDebug: echo(" TEXT REWRITED: " + self.sSentence) - self.lToken.clear() - self.lToken = lNewToken + self.lTokens.clear() + self.lTokens = lNewTokens Index: gc_lang/fr/rules.grx ================================================================== --- gc_lang/fr/rules.grx +++ gc_lang/fr/rules.grx @@ -3656,11 +3656,11 @@ !! # élisions __eleu_élisions_manquantes__ [le|la|de] ~^[aâeéèêiîoôuûyœæ]. - <<- /eleu/ space_after(\1, 1, 1) and not re.search("(?i)^(?:onz[ei]|énième|iourte|ouistiti|ouate|one-?step|ouf|Ouagadougou|I(?:I|V|X|er|ᵉʳ|ʳᵉ|è?re))", \2) and not morph(\2, ":G") + <<- /eleu/ space_after(\1, 1, 1) and not re.search("(?i)^(?:onz[ei]|énième|iourte|oui|ouï-dire|ouistiti|ouate|one-?step|ouf|Ouagadougou|I(?:I|V|X|er|ᵉʳ|ʳᵉ|è?re))", \2) and not morph(\2, ":G") -1:.2>> =\1[0:1]+"’" && Élision de l’article devant un mot commençant par une voyelle.|http://fr.wikipedia.org/wiki/Élision si [il|ils] <<- /eleu/ space_after(\1, 1, 1) -1:.2>> s’ && Il faut élider “si” et l’accoler au pronom.|http://fr.wikipedia.org/wiki/Élision @@ -8122,10 +8122,14 @@ [mon|son|ton|son|notre|votre|leur] [elle+s] [droite|gauche] une [elle+s] [de|d’|du] [poulet|poule|perdreau|canard|perdrix|pigeon|raie|papillon|voiture|bâtiment|château|manoir|palais] <<- /conf/ -2>> aile && Confusion probable : “elle” est un pronom personnel féminin. Pour les oiseaux, les avions ou les parties d’un bâtiment ou d’une armée, écrivez “aile”.|https://fr.wiktionary.org/wiki/aile + [sous|sur] [mon|ton|son|notre|votre|leur] [elle+s] + <<- /conf/ --1>> aile + && Confusion probable : “elle” est un pronom personnel féminin. Pour les oiseaux, les avions ou les parties d’un bâtiment ou d’une armée, écrivez “aile”.|https://fr.wiktionary.org/wiki/aile + [des|mes|tes|ses|ces|nos|vos] ([elle+s]) les ([elle+s]) [de|des|du|d’] [sous|sur] leurs ([elle+s]) [|,|$:R] <<- /conf/ -1>> ailes && Confusion probable : “elle” est un pronom personnel féminin. Pour les oiseaux, les avions ou les parties d’un bâtiment ou d’une armée, écrivez “aile”.|https://fr.wiktionary.org/wiki/aile @@ -8136,10 +8140,11 @@ && Confusion probable : “elle” est un pronom personnel féminin. Pour les oiseaux, les avions ou les parties d’un bâtiment ou d’une armée, écrivez “aile”.|https://fr.wiktionary.org/wiki/aile TEST: l’{{elle}} est en feu. TEST: sous l’{{elle}} de sa mère, il ne craint rien TEST: sur son {{elle}} droite +TEST: le prendre sous son {{elle}}, c’était au-dessus de ses forces TEST: des {{elles}} enduites de pétrole TEST: De l’autre côté du mur, dans l’{{elle}} réservée aux femmes, il y a des jeunes filles dont nul n’a parlé TEST: vous réfugier sous leurs {{elles}}. TEST: les {{elles}} du désir TEST: {{elle}} droite du palais du roi @@ -8811,10 +8816,14 @@ >avoir ?@:[WX]¿ [cour|court+s|courre+s] <<- /conf/ --1>> cours && Locution « avoir cours ».|https://fr.wiktionary.org/wiki/avoir_cours [>avoir|>donner|>laisser] ?@:[WX]¿ libre [cour|court+s|courre+s] <<- /conf/ --1>> cours && Confusion probable. Ce qui a « libre cours ».|https://fr.wiktionary.org/wiki/donner_libre_cours + + >changer le [cour|court+s|courre+s] [de|des|du|d’] + <<- /conf/ --2>> cours && Confusion. Changer le cours de quelque chose. + TEST: au {{court}} de cette journée TEST: les exercices {{en cour}} se déroulent bien. TEST: je vais couper {{cours}} à ces conneries. TEST: il faut donner libre {{cour}} à ses envies. @@ -8825,10 +8834,11 @@ TEST: à {{cours}} d’argent TEST: le portage a encore {{cour}} TEST: cette expérience ne va pas tarder à tourner {{cours}}. TEST: un {{court}} d’eau TEST: desdits {{courts}} d’eau +TEST: elle décida alors de changer le {{court}} de sa vie TEST: porter l’affaire en Cour de justice TEST: jusqu’en cour de cassation TEST: le jugement en cour d’assises TEST: ils vont passer prochainement en cour martiale. TEST: je ne veux pas la prendre de court. @@ -10111,11 +10121,13 @@ [à|a] [mon|ton|son|notre|votre|leur] né au [né+ses] [dans|sur|sous] le [né+ses] >crotte [de|d’] [né+ses] + >saignement [de|d’] [né+ses] [>mettre|>fourrer] ?@:[WX]¿ [le|mon|ton|son|notre|votre|leur] [né+ses] + >pendre ?@:[WX]¿ [au|aux] [né+s] >voir ?@:[WX]¿ plus loin [que|qu’] [le|mon|ton|son|notre|votre|leur] [né+ses] >voir ?@:[WX]¿ plus loin [que|qu’] le bout [de|d’] [mon|ton|son|notre|votre|leur] [né+ses] >avoir du [né+ses] <<- /conf/ --1>> nez && Confusion. Pour l’organe olfactif, écrivez “nez”.|https://fr.wiktionary.org/wiki/nez @@ -10299,10 +10311,13 @@ <<- /conf/ --1>> où && Confusion probable. La conjonction “ou” signale une alternative. Pour évoquer un lieu, un temps ou une situation, écrivez “où”. [là|>aller] ou le vent <<- /conf/ -2>> où && Confusion. La conjonction “ou” signale une alternative. Pour évoquer un lieu, un temps ou une situation, écrivez “où”. + ou /_VCint_ + <<- /conf/ -2>> où && Confusion probable. La conjonction “ou” signale une alternative. Pour évoquer un lieu, un temps ou une situation, écrivez “où”. + TEST: {{Ou}} sont tes affaires ? ->> Où TEST: au moment {{ou}} elle allait enfin réussir ->> où TEST: je ne sais même pas par {{ou}} commencer ->> où TEST: {{ou}} et comment s’y prendre ->> où TEST: vers {{ou}} se tourner quand tout va mal ? ->> où @@ -10316,10 +10331,13 @@ TEST: depuis le jour {{ou}} il a été blessé. ->> où TEST: nous sommes dans une situation {{ou}} il faut avancer ->> où TEST: j’irai là {{ou}} le vent me portera ->> où TEST: il fait ça n’importe {{ou}} ->> où TEST: toutes les fois {{ou}} nous avons couvert votre cul ->> où +TEST: {{ou}} va-t-il ->> où +TEST: {{ou}} irons-nous ->> où +TEST: {{ou}} allait-elle ->> où TEST: cela peut vouloir dire plusieurs choses : qu’il y a anguille sous roche, ou qu’elles se trouvent dans de bonnes dispositions à notre égard. TEST: c’en est où ? # pale / pâle @@ -10891,10 +10909,13 @@ <<- /conf/ --1>> prix && Confusion : pour évoquer la valeur d’une chose, écrivez “prix”.|https://fr.wiktionary.org/wiki/prix [à|a] pris [coûtant|coutant] <<- /conf/ ->> à prix coûtant && Confusion : pour évoquer la valeur d’une chose, écrivez “prix”.|https://fr.wiktionary.org/wiki/prix + [au|aux] pris [fort+s] + <<- /conf/ ->> au prix fort && Confusion : pour évoquer la valeur d’une chose, écrivez “prix”.|https://fr.wiktionary.org/wiki/prix + prix à la gorge prix dans [la|une|cette] [tourmente|tempête|tornade] prix dans [la|une|cette] coulée de [boue|lave] prix dans l’ [orage|ouragan] prix dans les [flots|vagues|tempêtes|tornades|orages|ouragans] @@ -10906,10 +10927,11 @@ TEST: réussir à n’importe quel {{pris}}. TEST: quel est leur {{pris}} ? TEST: {{prix}} par surprise, TEST: vendre {{à pris coûtant}} +TEST: encore un jeu vendu en kit {{au pris fort}} # puits / puis __conf_puits_puis__ [des|ces|mes|tes|ses|nos|vos|leurs|quelques|ce|mon|du] puis @@ -11831,10 +11853,11 @@ # confusion toit / toi __conf_toi_toit__ [chez|pour|contre|avec] toit >parler ?@:[WX]¿ de toit + c’ >être toit <<- /conf/ --1>> toi && Confusion. Le toit est constitué d’une toiture. Pour le pronom personnel à la 2ᵉ personne, écrivez “toi”.|https://fr.wiktionary.org/wiki/toi [sous|sur] [mon|ton|son|notre|votre|leur] toi [sous|sur] [le|ce|un] toi <<- /conf/ -3>> toit && Confusion : “toi” est le pronom personnel à la 2ᵉ personne. Pour évoquer le sommet d’un bâtiment, écrivez “toit”.|https://fr.wiktionary.org/wiki/toit @@ -13497,16 +13520,16 @@ dès la première heure l’ heure venue jusqu’ à pas d’ heure sur l’ heure tout à l’ heure - trois quarts d’ heure plus [tôt|tard] - trois quarts d’ heure auparavant - un quart d’ heure plus [tôt|tard] - un quart d’ heure auparavant - une fraction [de|d’] seconde auparavant - une fraction [de|d’] seconde plus [tard|tôt] + ?[de|d’]¿ trois quarts d’ heure plus [tôt|tard] + ?[de|d’]¿ trois quarts d’ heure auparavant + ?d’¿ un quart d’ heure plus [tôt|tard] + ?d’¿ un quart d’ heure auparavant + ?d’¿ une fraction [de|d’] seconde auparavant + ?d’¿ une fraction [de|d’] seconde plus [tard|tôt] vers [midi|minuit] <<- ~>> * DEF: unit_mesure_sing_mas [jour|mois|trimestre|semestre|an|siècle|millénaire] DEF: unit_mesure_sing_fem [nanoseconde|milliseconde|seconde|minute|heure|journée|semaine|année|décennie] @@ -13845,10 +13868,12 @@ à cet [instant|instant-là] ?[exact|particulier|précis]¿ à chaque [instant|moment] ?passé¿ à un moment donné à un moment ou à un autre au [dernier|même|bon|mauvais] [moment|instant] + au bon endroit ?,¿ au bon moment + au mauvais endroit ?,¿ au mauvais moment au bout d’ un [instant|moment] dans ces moments-là d’ instant en instant d’ un [instant|moment] à l’ autre en ce [moment|moment-là] ?[particulier]¿ @@ -15826,17 +15851,19 @@ >arme [de|d’] [poing|guerre] >arme [de|d’] destruction massive >armoire à [>glace|>pharmacie] >argent [de|d’] poche >arnaque à l’ assurance + >arrêt [de|d’] bus >article [de|d’] presse >article à >sensation >assignation à [comparaître|comparaitre|résidence] >attaché [de|d’] presse >attaque à main armée >attestation sur l’ honneur >atteinte aux bonnes mœurs + >auberge [de|d’] jeunesse >avion à [>hélice|réaction] ayants droit >bac à légumes >bain [de|d’] sang >banc [de|d’] touche @@ -15924,10 +15951,11 @@ >classement sans suite [>clé|>clef] à molette >clin d’ œil >clause [de|d’] [confidentialité|non-concurrence] >clause [de|d’] non concurrence + >clou du spectacle >clown [de|d’] service >code d’ accès >code [de|d’] ?bonne¿ conduite >code [de|d’] la route >collègue [de|d’] bureau @@ -16016,10 +16044,11 @@ >épreuve [de|d’] force >erreur [de|d’] [calcul|jeunesse|jugement] >erreur d’ appréciation >escroquerie à l’ assurance >espérance [de|d’] vie + >espérance [de|d’] vie en bonne santé >état [de|d’] [âme|esprit|urgence|conservation|fait] >état d’ extrême urgence >état [de|d’] l’ art >étui à cigarettes >examen d’ entrée @@ -16200,11 +16229,11 @@ >onde [de|d’] choc >opération [de|d’] nuit >ordre du jour orge [perlé|mondé|carré] >nuit [de|d’] noces - >pacte [de|d’] non-agression + >pacte [de|d’] [non-agression|sang] >pain au levain ?liquide¿ >pain [de|d’] mie >palais d’ [été|hiver] >panier à linge >papier à [>lettre|musique] @@ -16310,10 +16339,11 @@ >roulement à billes [>ru|>ruisseau] à sec >ruée vers l’ or >sac à [bandoulière|dos|main|langer|merde|foutre] >sac [de|d’] [couchage|sport|voyage] + >saignement [de|d’] nez >salaire à vie >salle à manger >salle [de|d’] [attente|>bain|bal|conférence|lecture|séjour|vente] >salon [de|d’] coiffure sas [de|d’] [confinement|décompression|décontamination|désinfection|livraison|protection|secours|sécurité] @@ -16325,10 +16355,11 @@ >service clientèle >service d’ ordre >service [de|d’] renseignement >seuil [de|d’] tolérance >seuil [de|d’] tolérance à la douleur + >signal d’ alarme >silo à [>grains|blé] >site [de|d’] lancement >soldat d’ élite >solution [de|d’] [rechange|repli] >sonnette d’ alarme @@ -16397,11 +16428,11 @@ vacances d’ [été|hiver] >vache à lait >vague à l’ âme >vecteur [>accélération|>position|>rotation|>vitesse] >véhicule [de|d’] location - >vente à [domicile|emporter] + >vente à [découvert|domicile|emporter] >vente aux enchères >vérification [de|d’] routine vernis à ongles >ver [de|d’] terre >verre à pied @@ -16415,10 +16446,11 @@ >vol à la [sauvette|tire] >vol à main armée >volée [de|d’] bois vert >voiture [de|d’] location >vote par correspondance ?électronique¿ + [vue+s] d’ ensemble >wagon à bestiaux >zone [de|d’] [confort|non-droit] >zone [de|d’] transit ?principal¿ <<- morph(\1, ":N") and not morph(<1, ":Os") ~2:0>> * <<- __also__ =>> =select(\1, ":[NA]") @@ -16625,11 +16657,11 @@ [le|ce|du] [baron|docteur|député|duc|frère|ministre|prince|professeur|président|roi|sénateur|mir] @:M[12] [la|cette] [baronne|docteur|docteure|députée|duchesse|ministre|sœur|princesse|présidente|professeure|reine|sénatrice] @:M[12] <<- ~3>> * - [Mr|Mlle|Mme|Mgr|miss] /_Maj_ + [Mr|Mʳ|Mlle|Mˡˡᵉ|Mme|Mᵐᵉ|Mgr|Mᵍʳ|miss] /_Maj_ <<- ~2>> * __purge_pronom_aussi__ [j’|je] *WORD moi aussi @@ -16638,10 +16670,11 @@ tu *WORD toi aussi <<- morph(\2, ":2s") ~3:0>> * il *WORD lui aussi elle *WORD elle aussi + iel *WORD iel aussi on *WORD nous aussi <<- morph(\2, ":3s") ~3:0>> * nous *WORD nous aussi <<- morph(\2, ":1p") ~3:0>> * @@ -16649,10 +16682,11 @@ vous *WORD vous aussi <<- morph(\2, ":2p") ~3:0>> * ils *WORD eux aussi elles *WORD elles aussi + iels *WORD iels aussi <<- morph(\2, ":3p") ~3:0>> * __purge_après_être__ [>être|>devenir|>rester] [bon|meilleur] marché @@ -17843,10 +17877,11 @@ TEST: Il faut donc examiner ensemble le panneau et la paroi latéraux. TEST: Il faut donc examiner ensemble les panneaux et la paroi latéraux. TEST: Il faut donc examiner ensemble les panneaux et la paroi latérale. TEST: Il faut donc examiner ensemble les panneaux et les parois latéraux. TEST: Il faut donc examiner ensemble les panneaux et les parois latérales. +TEST: ce jour verrait la violence se déchaîner, le sang couler, le chaos tout emporter @@@@ @@@@ @@@@ Index: gc_lang/fr/webext/content_scripts/init.js ================================================================== --- gc_lang/fr/webext/content_scripts/init.js +++ gc_lang/fr/webext/content_scripts/init.js @@ -342,16 +342,16 @@ parseAndSpellcheck1: function (sText, sDestination, sParagraphId) { this.send("parseAndSpellcheck1", { sText: sText, sCountry: "FR", bDebug: false, bContext: false }, { sDestination: sDestination, sParagraphId: sParagraphId }); }, - getListOfTokens: function (sText) { - this.send("getListOfTokens", { sText: sText }, {}); + parseFull: function (sText, sDestination, sParagraphId) { + this.send("parseFull", { sText: sText, sCountry: "FR", bDebug: false, bContext: false }, { sDestination: sDestination }); }, - parseFull: function (sText) { - this.send("parseFull", { sText: sText, sCountry: "FR", bDebug: false, bContext: false }, {}); + getListOfTokens: function (sText, sDestination) { + this.send("getListOfTokens", { sText: sText }, { sDestination: sDestination }); }, getVerb: function (sVerb, bStart=true, bPro=false, bNeg=false, bTpsCo=false, bInt=false, bFem=false) { this.send("getVerb", { sVerb: sVerb, bPro: bPro, bNeg: bNeg, bTpsCo: bTpsCo, bInt: bInt, bFem: bFem }, { bStart: bStart }); }, @@ -420,18 +420,22 @@ if (oInfo.sDestination == "__GrammalectePanel__") { oGrammalecte.oGCPanel.refreshParagraph(oInfo.sParagraphId, result); } break; case "parseFull": - // TODO + if (oInfo.sDestination == "__GrammalectePanel__") { + oGrammalecte.oGCPanel.showParagraphAnalysis(result); + } break; case "getListOfTokens": - if (!bEnd) { - oGrammalecte.oGCPanel.addListOfTokens(result); - } else { - oGrammalecte.oGCPanel.stopWaitIcon(); - oGrammalecte.oGCPanel.endTimer(); + if (oInfo.sDestination == "__GrammalectePanel__") { + if (!bEnd) { + oGrammalecte.oGCPanel.addListOfTokens(result); + } else { + oGrammalecte.oGCPanel.stopWaitIcon(); + oGrammalecte.oGCPanel.endTimer(); + } } break; case "getSpellSuggestions": if (oInfo.sDestination == "__GrammalectePanel__") { oGrammalecte.oGCPanel.oTooltip.setSpellSuggestionsFor(result.sWord, result.aSugg, result.iSuggBlock, oInfo.sErrorId); Index: gc_lang/fr/webext/content_scripts/panel_gc.css ================================================================== --- gc_lang/fr/webext/content_scripts/panel_gc.css +++ gc_lang/fr/webext/content_scripts/panel_gc.css @@ -9,25 +9,23 @@ overflow: auto; } div.grammalecte_paragraph_block { margin: 5px 5px 0 5px; + background-color: hsl(0, 0%, 96%); + border-radius: 2px; } - p.grammalecte_paragraph { margin: 0; padding: 12px; - background-color: hsl(0, 0%, 96%); - border-radius: 2px; line-height: 1.3; text-align: left; font-size: 14px; font-family: "Courier New", Courier, "Lucida Sans Typewriter", "Lucida Typewriter", monospace; color: hsl(0, 0%, 0%); hyphens: none; } - /* Action buttons */ div.grammalecte_paragraph_actions { @@ -45,10 +43,17 @@ font-size: 14px; color: hsl(0, 0%, 96%); border-radius: 2px; } +div.grammalecte_paragraph_actions .grammalecte_blue { + color: hsl(0, 0%, 80%); +} +div.grammalecte_paragraph_actions .grammalecte_blue:hover { + background-color: hsl(210, 50%, 40%); + color: hsl(0, 0%, 100%); +} div.grammalecte_paragraph_actions .grammalecte_green { color: hsl(0, 0%, 80%); } div.grammalecte_paragraph_actions .grammalecte_green:hover { background-color: hsl(120, 50%, 40%); Index: gc_lang/fr/webext/content_scripts/panel_gc.js ================================================================== --- gc_lang/fr/webext/content_scripts/panel_gc.js +++ gc_lang/fr/webext/content_scripts/panel_gc.js @@ -3,19 +3,22 @@ /* jshint esversion:6, -W097 */ /* jslint esversion:6 */ /* global GrammalectePanel, oGrammalecte, oGrammalecteBackgroundPort, showError, window, document, console */ "use strict"; + function onGrammalecteGCPanelClick (xEvent) { try { let xElem = xEvent.target; if (xElem.id) { if (xElem.id.startsWith("grammalecte_sugg")) { oGrammalecte.oGCPanel.applySuggestion(xElem.id); } else if (xElem.id === "grammalecte_tooltip_ignore") { oGrammalecte.oGCPanel.ignoreError(xElem.id); + } else if (xElem.id.startsWith("grammalecte_analysis")) { + oGrammalecte.oGCPanel.sendParagraphToGrammaticalAnalysis(parseInt(xElem.dataset.para_num, 10)); } else if (xElem.id.startsWith("grammalecte_check")) { oGrammalecte.oGCPanel.recheckParagraph(parseInt(xElem.dataset.para_num, 10)); } else if (xElem.id.startsWith("grammalecte_hide")) { xElem.parentNode.parentNode.style.display = "none"; } else if (xElem.id.startsWith("grammalecte_err") @@ -64,10 +67,22 @@ this.iLastEditedParagraph = -1; this.nParagraph = 0; // Lexicographer this.nLxgCount = 0; this.xLxgPanelContent = oGrammalecte.createNode("div", {id: "grammalecte_lxg_panel_content"}); + this.xLxgInputBlock = oGrammalecte.createNode("div", {id: "grammalecte_lxg_input_block"}); + this.xLxgInput = oGrammalecte.createNode("div", {id: "grammalecte_lxg_input", lang: "fr", contentEditable: "true"}); + this.xLxgInputButton = oGrammalecte.createNode("div", {id: "grammalecte_lxg_input_button", textContent: "Analyse grammaticale"}); + this.xLxgInputButton.addEventListener("click", () => { this.grammaticalAnalysis(); }, false); + this.xLxgInputButton2 = oGrammalecte.createNode("div", {id: "grammalecte_lxg_input_button", textContent: "Analyse lexicale"}); + this.xLxgInputButton2.addEventListener("click", () => { this.getListOfTokens(); }, false); + this.xLxgInputBlock.appendChild(this.xLxgInput); + this.xLxgInputBlock.appendChild(this.xLxgInputButton); + this.xLxgInputBlock.appendChild(this.xLxgInputButton2); + this.xLxgPanelContent.appendChild(this.xLxgInputBlock); + this.xLxgResultZone = oGrammalecte.createNode("div", {id: "grammalecte_lxg_result_zone"}); + this.xLxgPanelContent.appendChild(this.xLxgResultZone); this.xPanelContent.appendChild(this.xLxgPanelContent); // Conjugueur this.xConjPanelContent = oGrammalecte.createNode("div", {id: "grammalecte_conj_panel_content"}); this.xConjPanelContent.innerHTML = sGrammalecteConjugueurHTML; // @Reviewers: sGrammalecteConjugueurHTML is a const value defined in this.xPanelContent.appendChild(this.xConjPanelContent); @@ -112,14 +127,10 @@ this.setAutoRefreshButton(); } this.xLxgButton.onclick = () => { if (!this.bWorking) { this.showLexicographer(); - this.clearLexicographer(); - this.startWaitIcon(); - oGrammalecteBackgroundPort.getListOfTokens(this.oTextControl.getText()); - //oGrammalecteBackgroundPort.parseFull(this.oTextControl.getText()) } }; this.xConjButton.onclick = () => { if (!this.bWorking) { this.showConjugueur(); @@ -238,14 +249,14 @@ addParagraphResult (oResult) { try { this.resetTimer(); if (oResult && (oResult.sParagraph.trim() !== "" || oResult.aGrammErr.length > 0 || oResult.aSpellErr.length > 0)) { - let xNodeDiv = oGrammalecte.createNode("div", {className: "grammalecte_paragraph_block"}); // actions let xActionsBar = oGrammalecte.createNode("div", {className: "grammalecte_paragraph_actions"}); xActionsBar.appendChild(oGrammalecte.createNode("div", {id: "grammalecte_check" + oResult.iParaNum, className: "grammalecte_paragraph_button grammalecte_green", textContent: "↻", title: "Réanalyser…"}, {para_num: oResult.iParaNum})); + xActionsBar.appendChild(oGrammalecte.createNode("div", {id: "grammalecte_analysis" + oResult.iParaNum, className: "grammalecte_paragraph_button grammalecte_blue", textContent: "»", title: "Analyse grammaticale…"}, {para_num: oResult.iParaNum})); xActionsBar.appendChild(oGrammalecte.createNode("div", {id: "grammalecte_hide" + oResult.iParaNum, className: "grammalecte_paragraph_button grammalecte_red", textContent: "×", title: "Cacher", style: "font-weight: bold;"})); // paragraph let xParagraph = oGrammalecte.createNode("p", {id: "grammalecte_paragraph"+oResult.iParaNum, className: "grammalecte_paragraph", lang: "fr", contentEditable: "true"}, {para_num: oResult.iParaNum}); xParagraph.setAttribute("spellcheck", "false"); // doesn’t seem possible to use “spellcheck” as a common attribute. xParagraph.dataset.timer_id = "0"; @@ -260,13 +271,14 @@ this.oTextControl.setParagraph(parseInt(xEvent.target.dataset.para_num, 10), xEvent.target.textContent); }.bind(this) , true); this._tagParagraph(xParagraph, oResult.sParagraph, oResult.iParaNum, oResult.aGrammErr, oResult.aSpellErr); // creation - xNodeDiv.appendChild(xActionsBar); - xNodeDiv.appendChild(xParagraph); - this.xParagraphList.appendChild(xNodeDiv); + let xParagraphBlock = oGrammalecte.createNode("div", {className: "grammalecte_paragraph_block"}); + xParagraphBlock.appendChild(xActionsBar); + xParagraphBlock.appendChild(xParagraph); + this.xParagraphList.appendChild(xParagraphBlock); this.nParagraph += 1; } } catch (e) { showError(e); @@ -530,36 +542,134 @@ // Lexicographer clearLexicographer () { this.nLxgCount = 0; - while (this.xLxgPanelContent.firstChild) { - this.xLxgPanelContent.removeChild(this.xLxgPanelContent.firstChild); - } - } - - addLxgSeparator (sText) { - if (this.xLxgPanelContent.textContent !== "") { - this.xLxgPanelContent.appendChild(oGrammalecte.createNode("div", {className: "grammalecte_lxg_separator", textContent: sText})); - } - } - - addMessageToLxgPanel (sMessage) { - let xNode = oGrammalecte.createNode("div", {className: "grammalecte_panel_flow_message", textContent: sMessage}); - this.xLxgPanelContent.appendChild(xNode); - } - - addListOfTokens (lToken) { + while (this.xLxgResultZone.firstChild) { + this.xLxgResultZone.removeChild(this.xLxgResultZone.firstChild); + } + } + + // Grammatical analysis + + sendParagraphToGrammaticalAnalysis (iParaNum) { + let xParagraph = this.xParent.getElementById("grammalecte_paragraph" + iParaNum); + this.xLxgInput.textContent = xParagraph.textContent; + this.grammaticalAnalysis(); + this.showLexicographer(); + } + + grammaticalAnalysis (iParaNum) { + if (!this.bOpened || this.bWorking) { + return; + } + this.startWaitIcon(); + this.clearLexicographer(); + let sText = this.xLxgInput.innerText.replace(/\n/g, " "); + console.log(sText); + oGrammalecteBackgroundPort.parseFull(sText, "__GrammalectePanel__"); + } + + showParagraphAnalysis (oResult) { + if (!this.bOpened || oResult === null) { + return; + } + try { + for (let oSentence of oResult.lSentences) { + this.nLxgCount += 1; + if (oSentence.sSentence.trim() !== "") { + let xSentenceBlock = oGrammalecte.createNode("div", {className: "grammalecte_lxg_paragraph_sentence_block"}); + xSentenceBlock.appendChild(oGrammalecte.createNode("div", {className: "grammalecte_lxg_list_num", textContent: this.nLxgCount})); + xSentenceBlock.appendChild(oGrammalecte.createNode("p", {className: "grammalecte_lxg_paragraph_sentence", textContent: oSentence.sSentence})); + let xTokenList = oGrammalecte.createNode("div", {className: "grammalecte_lxg_list_of_tokens"}); + for (let oToken of oSentence.lTokens) { + if (oToken["sType"] != "INFO" && !oToken.hasOwnProperty("bMerged")) { + if (oToken["sType"] == "WORD" && !oToken["bValidToken"]) { + oToken["sType"] = "UNKNOWN_WORD"; + } + xTokenList.appendChild(this._createTokenBlock2(oToken)); + } + } + xSentenceBlock.appendChild(xTokenList); + this.xLxgResultZone.appendChild(xSentenceBlock); + } + } + } + catch (e) { + showError(e); + } + this.stopWaitIcon(); + } + + _createTokenBlock2 (oToken) { + let xTokenBlock = oGrammalecte.createNode("div", {className: "grammalecte_lxg_token_block"}); + // token description + xTokenBlock.appendChild(this._createTokenDescr2(oToken)); + return xTokenBlock; + } + + _createTokenDescr2 (oToken) { + try { + let xTokenDescr = oGrammalecte.createNode("div", {className: "grammalecte_lxg_token_descr"}); + xTokenDescr.appendChild(oGrammalecte.createNode("div", {className: "grammalecte_lxg_token grammalecte_lxg_token_" + oToken.sType, textContent: oToken.sValue})); + xTokenDescr.appendChild(oGrammalecte.createNode("div", {className: "grammalecte_lxg_token_colon", textContent: ":"})); + if (oToken.aLabels) { + if (oToken.aLabels.length < 2) { + // one morphology only + xTokenDescr.appendChild(oGrammalecte.createNode("div", {className: "grammalecte_lxg_morph_elem_inline", textContent: oToken.aLabels[0]})); + } else { + // several morphology + let xMorphList = oGrammalecte.createNode("div", {className: "grammalecte_lxg_morph_list"}); + for (let sLabel of oToken.aLabels) { + xMorphList.appendChild(oGrammalecte.createNode("div", {className: "grammalecte_lxg_morph_elem", textContent: "• " + sLabel})); + } + xTokenDescr.appendChild(xMorphList); + } + } else { + xTokenDescr.appendChild(oGrammalecte.createNode("div", {className: "grammalecte_lxg_morph_elem_inline", textContent: "étiquettes non décrites : [" + oToken.lMorph + "]" })); + } + // other labels description + if (oToken.aOtherLabels) { + let xSubBlock = oGrammalecte.createNode("div", {className: "grammalecte_lxg_token_subblock"}); + for (let sLabel of oToken.aOtherLabels) { + xSubBlock.appendChild(oGrammalecte.createNode("div", {className: "grammalecte_lxg_other_tags", textContent: "• " + sLabel})); + } + xTokenDescr.appendChild(xSubBlock); + } + return xTokenDescr; + } + catch (e) { + showError(e); + } + } + + // Lexical analysis + + getListOfTokens () { + if (!this.bOpened || this.bWorking) { + return; + } + this.startWaitIcon(); + this.clearLexicographer(); + let sText = this.xLxgInput.innerText; // to get carriage return (\n) + console.log(sText); + oGrammalecteBackgroundPort.getListOfTokens(sText, "__GrammalectePanel__"); + } + + addListOfTokens (oResult) { try { - if (lToken) { + if (oResult && oResult.sParagraph != "") { this.nLxgCount += 1; + let xSentenceBlock = oGrammalecte.createNode("div", {className: "grammalecte_lxg_paragraph_sentence_block"}); + xSentenceBlock.appendChild(oGrammalecte.createNode("div", {className: "grammalecte_lxg_list_num", textContent: this.nLxgCount})); + xSentenceBlock.appendChild(oGrammalecte.createNode("p", {className: "grammalecte_lxg_paragraph_sentence", textContent: oResult.sParagraph})); let xTokenList = oGrammalecte.createNode("div", {className: "grammalecte_lxg_list_of_tokens"}); - xTokenList.appendChild(oGrammalecte.createNode("div", {className: "grammalecte_lxg_list_num", textContent: this.nLxgCount})); - for (let oToken of lToken) { + for (let oToken of oResult.lTokens) { xTokenList.appendChild(this._createTokenBlock(oToken)); } - this.xLxgPanelContent.appendChild(xTokenList); + xSentenceBlock.appendChild(xTokenList); + this.xLxgResultZone.appendChild(xSentenceBlock); } } catch (e) { showError(e); } @@ -600,16 +710,10 @@ catch (e) { showError(e); } } - setHidden (sClass, bHidden) { - let xPanelContent = this.xParent.getElementById('grammalecte_panel_content'); - for (let xNode of xPanelContent.getElementsByClassName(sClass)) { - xNode.hidden = bHidden; - } - } // Conjugueur listenConj () { if (!this.bListenConj) { Index: gc_lang/fr/webext/content_scripts/panel_lxg.css ================================================================== --- gc_lang/fr/webext/content_scripts/panel_lxg.css +++ gc_lang/fr/webext/content_scripts/panel_lxg.css @@ -6,21 +6,66 @@ position: absolute; height: 100%; width: 100%; font-size: 13px; } + +div#grammalecte_lxg_input_block { + padding: 10px; + /*background-color: hsl(210, 50%, 95%);*/ + /*border-bottom: solid 1px hsl(210, 0%, 90%);*/ + text-align: right; +} + +div#grammalecte_lxg_result_zone { + +} + +div#grammalecte_lxg_input { + min-height: 100px; + padding: 10px; + background-color: hsl(210, 0%, 100%); + border: solid 1px hsl(210, 20%, 80%); + border-radius: 3px; + font-family: "Courier New", Courier, "Lucida Sans Typewriter", "Lucida Typewriter", monospace; + text-align: left; +} + +div#grammalecte_lxg_input_button { + display: inline-block; + margin: 0 10px 0 0; + padding: 3px 10px; + background-color: hsl(210, 50%, 50%); + color: hsl(210, 50%, 98%); + text-align: center; + cursor: pointer; + border-radius: 0 0 3px 3px; +} + +div.grammalecte_lxg_paragraph_sentence_block { + margin: 5px 0px 20px 0px; + background-color: hsl(210, 50%, 95%); + border-radius: 3px; + border-top: solid 1px hsl(210, 50%, 90%); + border-bottom: solid 1px hsl(210, 50%, 90%); + hyphens: none; +} +p.grammalecte_lxg_paragraph_sentence { + padding: 3px 10px; + font-weight: bold; + color: hsl(210, 50%, 40%); +} div.grammalecte_lxg_list_of_tokens { - margin: 10px 5px 0 5px; padding: 10px; - background-color: hsla(0, 0%, 95%, 1); + background-color: hsl(210, 50%, 99%); border-radius: 5px; } div.grammalecte_lxg_list_num { float: right; - margin: -12px 0 5px 10px; + margin: -2px 5px 5px 10px; padding: 5px 10px; font-family: "Trebuchet MS", "Fira Sans", "Ubuntu Condensed", "Liberation Sans", sans-serif; font-size: 14px; font-weight: bold; border-radius: 0 0 4px 4px; @@ -85,10 +130,16 @@ } div.grammalecte_lxg_morph_elem { font-family: "Trebuchet MS", "Fira Sans", "Ubuntu Condensed", "Liberation Sans", sans-serif; color: hsl(0, 0%, 0%); } +div.grammalecte_lxg_other_tags { + padding-left: 20px; + font-family: "Trebuchet MS", "Fira Sans", "Ubuntu Condensed", "Liberation Sans", sans-serif; + color: hsl(0, 0%, 50%); +} + div.grammalecte_lxg_token_LOC { background-color: hsla(150, 50%, 30%, 1); } div.grammalecte_lxg_token_WORD { background-color: hsla(150, 50%, 50%, 1); Index: gc_lang/fr/webext/gce_worker.js ================================================================== --- gc_lang/fr/webext/gce_worker.js +++ gc_lang/fr/webext/gce_worker.js @@ -229,29 +229,24 @@ let aGrammErr = gc_engine.parse(sParagraph, sCountry, bDebug, null, bContext); let aSpellErr = oSpellChecker.parseParagraph(sParagraph); postMessage(createResponse("parseAndSpellcheck1", {sParagraph: sParagraph, aGrammErr: aGrammErr, aSpellErr: aSpellErr}, oInfo, true)); } -function parseFull (sText, sCountry, bDebug, bContext, oInfo={}) { - let i = 0; - sText = sText.replace(/­/g, "").normalize("NFC"); - for (let sParagraph of text.getParagraph(sText)) { - let lSentence = gc_engine.parse(sParagraph, sCountry, bDebug, null, bContext, true); - console.log("*", lSentence); - postMessage(createResponse("parseFull", {sParagraph: sParagraph, iParaNum: i, lSentence: lSentence}, oInfo, false)); - i += 1; - } - postMessage(createResponse("parseFull", null, oInfo, true)); +function parseFull (sParagraph, sCountry, bDebug, bContext, oInfo={}) { + sParagraph = sParagraph.replace(/­/g, "").normalize("NFC"); + let [lParagraphErrors, lSentences] = gc_engine.parse(sParagraph, sCountry, bDebug, null, bContext, true); + //console.log(lSentences); + postMessage(createResponse("parseFull", { lParagraphErrors: lParagraphErrors, lSentences: lSentences }, oInfo, true)); } function getListOfTokens (sText, oInfo={}) { // lexicographer try { sText = sText.replace(/­/g, "").normalize("NFC"); for (let sParagraph of text.getParagraph(sText)) { if (sParagraph.trim() !== "") { - postMessage(createResponse("getListOfTokens", lexgraph_fr.getListOfTokensReduc(sParagraph, true), oInfo, false)); + postMessage(createResponse("getListOfTokens", { sParagraph: sParagraph, lTokens: lexgraph_fr.getListOfTokens(sParagraph, true) }, oInfo, false)); } } postMessage(createResponse("getListOfTokens", null, oInfo, true)); } catch (e) { Index: grammalecte-cli.py ================================================================== --- grammalecte-cli.py +++ grammalecte-cli.py @@ -130,10 +130,14 @@ return (nError, cAction, vSugg) def main (): "launch the CLI (command line interface)" + if sys.version < "3.7": + print("Python 3.7+ required") + return + xParser = argparse.ArgumentParser() xParser.add_argument("-f", "--file", help="parse file (UTF-8 required!) [on Windows, -f is similar to -ff]", type=str) xParser.add_argument("-ff", "--file_to_file", help="parse file (UTF-8 required!) and create a result file (*.res.txt)", type=str) xParser.add_argument("-iff", "--interactive_file_to_file", help="parse file (UTF-8 required!) and create a result file (*.res.txt)", type=str) xParser.add_argument("-owe", "--only_when_errors", help="display results only when there are errors", action="store_true") @@ -336,21 +340,24 @@ elif sText.startswith("$"): for sParagraph in txt.getParagraph(sText[1:]): if xArgs.textformatter: sParagraph = oTextFormatter.formatText(sParagraph) lParagraphErrors, lSentences = oGrammarChecker.gce.parse(sParagraph, bDebug=xArgs.debug, bFullInfo=True) - echo(txt.getReadableErrors(lParagraphErrors, xArgs.width)) + #echo(txt.getReadableErrors(lParagraphErrors, xArgs.width)) for dSentence in lSentences: - echo("{nStart}:{nEnd}".format(**dSentence)) - echo(" <" + dSentence["sSentence"]+">") - for dToken in dSentence["lToken"]: - echo(" {0[nStart]:>3}:{0[nEnd]:<3} {1} {0[sType]:<14} {2} {0[sValue]:<16} {3:<10} {4}".format(dToken, \ - "×" if dToken.get("bToRemove", False) else " ", - "!" if dToken["sType"] == "WORD" and not dToken.get("bValidToken", False) else " ", - " ".join(dToken.get("lMorph", "")), \ - "·".join(dToken.get("aTags", "")) ) ) - echo(txt.getReadableErrors(dSentence["lGrammarErrors"], xArgs.width)) + echo("{nStart}:{nEnd} <{sSentence}>".format(**dSentence)) + for dToken in dSentence["lTokens"]: + if dToken["sType"] == "INFO" or "bMerged" in dToken: + continue + echo(" {0[nStart]:>3}:{0[nEnd]:<3} {1} {0[sType]:<14} {2} {0[sValue]:<16} {3}".format(dToken, \ + "×" if dToken.get("bToRemove", False) else " ", + "!" if dToken["sType"] == "WORD" and not dToken.get("bValidToken", False) else " ", + " ".join(dToken.get("aTags", "")) ) ) + if "lMorph" in dToken: + for sMorph, sLabel in zip(dToken["lMorph"], dToken["aLabels"]): + echo(" {0:40} {1}".format(sMorph, sLabel)) + #echo(txt.getReadableErrors(dSentence["lGrammarErrors"], xArgs.width)) else: for sParagraph in txt.getParagraph(sText): if xArgs.textformatter: sParagraph = oTextFormatter.formatText(sParagraph) sRes, _ = oGrammarChecker.getParagraphWithErrors(sParagraph, bEmptyIfNoErrors=xArgs.only_when_errors, nWidth=xArgs.width, bDebug=xArgs.debug) Index: graphspell-js/lexgraph_fr.js ================================================================== --- graphspell-js/lexgraph_fr.js +++ graphspell-js/lexgraph_fr.js @@ -164,13 +164,16 @@ [':f', [" féminin", "féminin"]], [':s', [" singulier", "singulier"]], [':p', [" pluriel", "pluriel"]], [':i', [" invariable", "invariable"]], - [':V1', [" verbe (1ᵉʳ gr.),", "Verbe du 1ᵉʳ groupe"]], - [':V2', [" verbe (2ᵉ gr.),", "Verbe du 2ᵉ groupe"]], - [':V3', [" verbe (3ᵉ gr.),", "Verbe du 3ᵉ groupe"]], + [':V1_', [" verbe (1ᵉʳ gr.),", "Verbe du 1ᵉʳ groupe"]], + [':V2_', [" verbe (2ᵉ gr.),", "Verbe du 2ᵉ groupe"]], + [':V3_', [" verbe (3ᵉ gr.),", "Verbe du 3ᵉ groupe"]], + [':V1e', [" verbe (1ᵉʳ gr.),", "Verbe du 1ᵉʳ groupe"]], + [':V2e', [" verbe (2ᵉ gr.),", "Verbe du 2ᵉ groupe"]], + [':V3e', [" verbe (3ᵉ gr.),", "Verbe du 3ᵉ groupe"]], [':V0e', [" verbe,", "Verbe auxiliaire être"]], [':V0a', [" verbe,", "Verbe auxiliaire avoir"]], [':Y', [" infinitif,", "infinitif"]], [':P', [" participe présent,", "participe présent"]], @@ -211,26 +214,26 @@ [':Ot', [" pronom interrogatif,", "Pronom interrogatif"]], [':Or', [" pronom relatif,", "Pronom relatif"]], [':Ow', [" pronom adverbial,", "Pronom adverbial"]], [':Os', [" pronom personnel sujet,", "Pronom personnel sujet"]], [':Oo', [" pronom personnel objet,", "Pronom personnel objet"]], - [':Ov', [" préverbe,", "Préverbe (pronom personnel objet, +ne)"]], + [':Ov', [" préverbe,", "Préverbe"]], [':O1', [" 1ʳᵉ pers.,", "Pronom : 1ʳᵉ personne"]], [':O2', [" 2ᵉ pers.,", "Pronom : 2ᵉ personne"]], [':O3', [" 3ᵉ pers.,", "Pronom : 3ᵉ personne"]], [':C', [" conjonction,", "Conjonction"]], - [':Ĉ', [" conjonction (él.),", "Conjonction (élément)"]], [':Cc', [" conjonction de coordination,", "Conjonction de coordination"]], [':Cs', [" conjonction de subordination,", "Conjonction de subordination"]], - [':Ĉs', [" conjonction de subordination (él.),", "Conjonction de subordination (élément)"]], - - [':Ñ', [" locution nominale (él.),", "Locution nominale (élément)"]], - [':Â', [" locution adjectivale (él.),", "Locution adjectivale (élément)"]], - [':Ṽ', [" locution verbale (él.),", "Locution verbale (élément)"]], - [':Ŵ', [" locution adverbiale (él.),", "Locution adverbiale (élément)"]], - [':Ŕ', [" locution prépositive (él.),", "Locution prépositive (élément)"]], - [':Ĵ', [" locution interjective (él.),", "Locution interjective (élément)"]], + + [':ÉC', [" élément de conjonction,", "Élément de conjonction"]], + [':ÉCs', [" élément de conjonction de subordination,", "Élément de conjonction de subordination"]], + [':ÉN', [" élément de locution nominale,", "Élément de locution nominale"]], + [':ÉA', [" élément de locution adjectivale,", "Élément de locution adjectivale"]], + [':ÉV', [" élément de locution verbale,", "Élément de locution verbale"]], + [':ÉW', [" élément de locution adverbiale,", "Élément de locution adverbiale"]], + [':ÉR', [" élément de locution prépositive,", "Élément de locution prépositive"]], + [':ÉJ', [" élément de locution interjective,", "Élément de locution interjective"]], [':Zp', [" préfixe,", "Préfixe"]], [':Zs', [" suffixe,", "Suffixe"]], [':H', ["", ""]], @@ -277,64 +280,87 @@ ['t', " transitive directe"], ['p', " pronominale"], ['m', " impersonnelle"], ]), - dElidedPrefix: new Map([ - ['d', "(de), déterminant épicène invariable"], - ['l', "(le/la), déterminant masculin/féminin singulier"], - ['j', "(je), pronom personnel sujet, 1ʳᵉ pers., épicène singulier"], - ['m', "(me), pronom personnel objet, 1ʳᵉ pers., épicène singulier"], - ['t', "(te), pronom personnel objet, 2ᵉ pers., épicène singulier"], - ['s', "(se), pronom personnel objet, 3ᵉ pers., épicène singulier/pluriel"], - ['n', "(ne), adverbe de négation"], - ['c', "(ce), pronom démonstratif, masculin singulier/pluriel"], - ['ç', "(ça), pronom démonstratif, masculin singulier"], + dValues: new Map([ + ['d’', "(de), préposition ou déterminant épicène invariable"], + ['l’', "(le/la), déterminant ou pronom personnel objet, masculin/féminin singulier"], + ['j’', "(je), pronom personnel sujet, 1ʳᵉ pers., épicène singulier"], + ['m’', "(me), pronom personnel objet, 1ʳᵉ pers., épicène singulier"], + ['t’', "(te), pronom personnel objet, 2ᵉ pers., épicène singulier"], + ['s’', "(se), pronom personnel objet, 3ᵉ pers., épicène singulier/pluriel"], + ['n’', "(ne), adverbe de négation"], + ['c’', "(ce), pronom démonstratif, masculin singulier/pluriel"], + ['ç’', "(ça), pronom démonstratif, masculin singulier"], ['qu', "(que), conjonction de subordination"], - ['lorsqu', "(lorsque), conjonction de subordination"], - ['puisqu', "(lorsque), conjonction de subordination"], - ['quoiqu', "(quoique), conjonction de subordination"], - ['jusqu', "(jusque), préposition"] - ]), - - dPronoms: new Map([ - ['je', " pronom personnel sujet, 1ʳᵉ pers. sing."], - ['tu', " pronom personnel sujet, 2ᵉ pers. sing."], - ['il', " pronom personnel sujet, 3ᵉ pers. masc. sing."], - ['on', " pronom personnel sujet, 3ᵉ pers. sing. ou plur."], - ['elle', " pronom personnel sujet, 3ᵉ pers. fém. sing."], - ['nous', " pronom personnel sujet/objet, 1ʳᵉ pers. plur."], - ['vous', " pronom personnel sujet/objet, 2ᵉ pers. plur."], - ['ils', " pronom personnel sujet, 3ᵉ pers. masc. plur."], - ['elles', " pronom personnel sujet, 3ᵉ pers. masc. plur."], - - ["là", " particule démonstrative"], - ["ci", " particule démonstrative"], - - ['le', " COD, masc. sing."], - ['la', " COD, fém. sing."], - ['les', " COD, plur."], - - ['moi', " COI (à moi), sing."], - ['toi', " COI (à toi), sing."], - ['lui', " COI (à lui ou à elle), sing."], - ['nous2', " COI (à nous), plur."], - ['vous2', " COI (à vous), plur."], - ['leur', " COI (à eux ou à elles), plur."], - - ['y', " pronom adverbial"], - ["m'y", " (me) pronom personnel objet + (y) pronom adverbial"], - ["t'y", " (te) pronom personnel objet + (y) pronom adverbial"], - ["s'y", " (se) pronom personnel objet + (y) pronom adverbial"], - - ['en', " pronom adverbial"], - ["m'en", " (me) pronom personnel objet + (en) pronom adverbial"], - ["t'en", " (te) pronom personnel objet + (en) pronom adverbial"], - ["s'en", " (se) pronom personnel objet + (en) pronom adverbial"] - ]), - - dChar: new Map([ + ['lorsqu’', "(lorsque), conjonction de subordination"], + ['puisqu’', "(lorsque), conjonction de subordination"], + ['quoiqu’', "(quoique), conjonction de subordination"], + ['jusqu’', "(jusque), préposition"], + + ['-je', " pronom personnel sujet, 1ʳᵉ pers. sing."], + ['-tu', " pronom personnel sujet, 2ᵉ pers. sing."], + ['-il', " pronom personnel sujet, 3ᵉ pers. masc. sing."], + ['-iel', " pronom personnel sujet, 3ᵉ pers. sing."], + ['-on', " pronom personnel sujet, 3ᵉ pers. sing. ou plur."], + ['-elle', " pronom personnel sujet, 3ᵉ pers. fém. sing."], + ['-t-il', " “t” euphonique + pronom personnel sujet, 3ᵉ pers. masc. sing."], + ['-t-on', " “t” euphonique + pronom personnel sujet, 3ᵉ pers. sing. ou plur."], + ['-t-elle', " “t” euphonique + pronom personnel sujet, 3ᵉ pers. fém. sing."], + ['-t-iel', " “t” euphonique + pronom personnel sujet, 3ᵉ pers. sing."], + ['-nous', " pronom personnel sujet/objet, 1ʳᵉ pers. plur. ou COI (à nous), plur."], + ['-vous', " pronom personnel sujet/objet, 2ᵉ pers. plur. ou COI (à vous), plur."], + ['-ils', " pronom personnel sujet, 3ᵉ pers. masc. plur."], + ['-elles', " pronom personnel sujet, 3ᵉ pers. masc. plur."], + ['-iels', " pronom personnel sujet, 3ᵉ pers. plur."], + + ["-là", " particule démonstrative (là)"], + ["-ci", " particule démonstrative (ci)"], + + ['-le', " COD, masc. sing."], + ['-la', " COD, fém. sing."], + ['-les', " COD, plur."], + + ['-moi', " COI (à moi), sing."], + ['-toi', " COI (à toi), sing."], + ['-lui', " COI (à lui ou à elle), sing."], + ['-nous2', " COI (à nous), plur."], + ['-vous2', " COI (à vous), plur."], + ['-leur', " COI (à eux ou à elles), plur."], + + ['-le-moi', " COD, masc. sing. + COI (à moi), sing."], + ['-le-toi', " COD, masc. sing. + COI (à toi), sing."], + ['-le-lui', " COD, masc. sing. + COI (à lui ou à elle), sing."], + ['-le-nous', " COD, masc. sing. + COI (à nous), plur."], + ['-le-vous', " COD, masc. sing. + COI (à vous), plur."], + ['-le-leur', " COD, masc. sing. + COI (à eux ou à elles), plur."], + + ['-la-moi', " COD, fém. sing. + COI (à moi), sing."], + ['-la-toi', " COD, fém. sing. + COI (à toi), sing."], + ['-la-lui', " COD, fém. sing. + COI (à lui ou à elle), sing."], + ['-la-nous', " COD, fém. sing. + COI (à nous), plur."], + ['-la-vous', " COD, fém. sing. + COI (à vous), plur."], + ['-la-leur', " COD, fém. sing. + COI (à eux ou à elles), plur."], + + ['-les-moi', " COD, plur. + COI (à moi), sing."], + ['-les-toi', " COD, plur. + COI (à toi), sing."], + ['-les-lui', " COD, plur. + COI (à lui ou à elle), sing."], + ['-les-nous', " COD, plur. + COI (à nous), plur."], + ['-les-vous', " COD, plur. + COI (à vous), plur."], + ['-les-leur', " COD, plur. + COI (à eux ou à elles), plur."], + + ['-y', " pronom adverbial"], + ["-m’y", " (me) pronom personnel objet + (y) pronom adverbial"], + ["-t’y", " (te) pronom personnel objet + (y) pronom adverbial"], + ["-s’y", " (se) pronom personnel objet + (y) pronom adverbial"], + + ['-en', " pronom adverbial"], + ["-m’en", " (me) pronom personnel objet + (en) pronom adverbial"], + ["-t’en", " (te) pronom personnel objet + (en) pronom adverbial"], + ["-s’en", " (se) pronom personnel objet + (en) pronom adverbial"], + ['.', "point"], ['·', "point médian"], ['…', "points de suspension"], [':', "deux-points"], [';', "point-virgule"], @@ -373,12 +399,12 @@ oTokenizer: null, oLocGraph: null, _zPartDemForm: new RegExp("([a-zA-Zà-ö0-9À-Öø-ÿØ-ßĀ-ʯ]+)-(là|ci)$", "i"), _aPartDemExceptList: new Set(["celui", "celle", "ceux", "celles", "de", "jusque", "par", "marie-couche-toi"]), - _zInterroVerb: new RegExp("([a-zA-Zà-ö0-9À-Öø-ÿØ-ßĀ-ʯ]+)-(t-(?:il|elle|on)|je|tu|ils?|elles?|on|[nv]ous)$", "i"), - _zImperatifVerb: new RegExp("([a-zA-Zà-ö0-9À-Öø-ÿØ-ßĀ-ʯ]+)-((?:les?|la)-(?:moi|toi|lui|[nv]ous|leur)|y|en|[mts][’'](?:y|en)|les?|la|[mt]oi|leur|lui)$", "i"), + _zInterroVerb: new RegExp("([a-zA-Zà-ö0-9À-Öø-ÿØ-ßĀ-ʯ]+)(-(?:t-(?:ie?l|elle|on)|je|tu|ie?ls?|elles?|on|[nv]ous))$", "i"), + _zImperatifVerb: new RegExp("([a-zA-Zà-ö0-9À-Öø-ÿØ-ßĀ-ʯ]+)(-(?:l(?:es?|a)-(?:moi|toi|lui|[nv]ous|leur)|y|en|[mts][’'](?:y|en)|les?|la|[mt]oi|leur|lui))$", "i"), _zTag: new RegExp("[:;/][a-zA-Z0-9ÑÂĴĈŔÔṼŴ!][^:;/]*", "g"), load: function (oSpellChecker, oTokenizer, oLocGraph) { this.oSpellChecker = oSpellChecker; @@ -403,10 +429,132 @@ sSuffix = m[2]; } // split word in 3 parts: prefix, root, suffix return [sPrefix, sWord, sSuffix]; }, + + analyze: function (sWord) { + // return meaning of if found else an empty string + sWord = sWord.toLowerCase(); + if (this.dValues.has(sWord)) { + return this.dValues.get(sWord); + } + return ""; + }, + + readableMorph: function (sMorph) { + let sRes = ""; + sMorph = sMorph.replace(/:V([0-3][ea_])[itpqnmr_eaxz]+/, ":V$1"); + let m; + while ((m = this._zTag.exec(sMorph)) !== null) { + if (this.dTag.has(m[0])) { + sRes += this.dTag.get(m[0])[0]; + } else { + sRes += " [" + m[0] + "]?"; + } + } + if (sRes.startsWith(" verbe") && !sRes.includes("infinitif")) { + sRes += " [" + sMorph.slice(1, sMorph.indexOf("/")) + "]"; + } + if (!sRes) { + return " [" + sMorph + "]: étiquettes inconnues"; + } + return sRes.gl_trimRight(","); + }, + + setLabelsOnToken (oToken) { + // Token: .sType, .sValue, .nStart, .nEnd, .lMorph + let m = null; + try { + switch (oToken.sType) { + case 'PUNC': + case 'SIGN': + oToken["aLabels"] = [this.dValues.gl_get(oToken["sValue"], "signe de ponctuation divers")]; + break; + case 'NUM': + oToken["aLabels"] = ["nombre"]; + break; + case 'LINK': + oToken["aLabels"] = ["hyperlien"]; + break; + case 'TAG': + oToken["aLabels"] = ["étiquette (hashtag)"]; + break; + case 'HTML': + oToken["aLabels"] = ["balise HTML"]; + break; + case 'PSEUDOHTML': + oToken["aLabels"] = ["balise pseudo-HTML"]; + break; + case 'HTMLENTITY': + oToken["aLabels"] = ["entité caractère XML/HTML"]; + break; + case 'HOUR': + oToken["aLabels"] = ["heure"]; + break; + case 'WORD_ELIDED': + oToken["aLabels"] = [this.dValues.gl_get(oToken["sValue"].toLowerCase(), "préfixe élidé inconnu")]; + break; + case 'WORD_ORDINAL': + oToken["aLabels"] = ["nombre ordinal"]; + break; + case 'FOLDERUNIX': + oToken["aLabels"] = ["dossier UNIX (et dérivés)"]; + break; + case 'FOLDERWIN': + oToken["aLabels"] = ["dossier Windows"]; + break; + case 'WORD_ACRONYM': + oToken["aLabels"] = ["sigle ou acronyme"]; + break; + case 'WORD': + if (oToken.hasOwnProperty("lMorph") && oToken["lMorph"].length > 0) { + // with morphology + oToken["aLabels"] = []; + for (let sMorph of oToken["lMorph"]) { + oToken["aLabels"].push(this.readableMorph(sMorph)); + } + if (oToken.hasOwnProperty("sTags")) { + let aTags = []; + for (let sTag of oToken["sTags"]) { + if (this.dValues.has(sTag)) { + aTags.push(this.dValues.get(sTag)) + } + } + if (aTags.length > 0) { + oToken["aOtherLabels"] = aTags; + } + } + } else { + // no morphology, guessing + if (oToken["sValue"].gl_count("-") > 4) { + oToken["aLabels"] = ["élément complexe indéterminé"]; + } + else if (m = this._zPartDemForm.exec(oToken["sValue"])) { + // mots avec particules démonstratives + oToken["aLabels"] = ["mot avec particule démonstrative"]; + } + else if (m = this._zImperatifVerb.exec(oToken["sValue"])) { + // formes interrogatives + oToken["aLabels"] = ["forme verbale impérative"]; + } + else if (m = this._zInterroVerb.exec(oToken["sValue"])) { + // formes interrogatives + oToken["aLabels"] = ["forme verbale interrogative"]; + } + else { + oToken["aLabels"] = ["mot inconnu du dictionnaire"]; + } + } + break; + default: + oToken["aLabels"] = ["token de nature inconnue"]; + } + } catch (e) { + console.error(e); + } + }, getInfoForToken: function (oToken) { // Token: .sType, .sValue, .nStart, .nEnd // return a object {sType, sValue, aLabel} let m = null; @@ -415,11 +563,11 @@ case 'PUNC': case 'SIGN': return { sType: oToken.sType, sValue: oToken.sValue, - aLabel: [this.dChar.gl_get(oToken.sValue, "caractère indéterminé")] + aLabel: [this.dValues.gl_get(oToken.sValue, "caractère indéterminé")] }; break; case 'NUM': return { sType: oToken.sType, @@ -468,15 +616,14 @@ sValue: oToken.sValue, aLabel: ["heure"] }; break; case 'WORD_ELIDED': - let sTemp = oToken.sValue.replace("’", "").replace("'", "").replace("`", "").toLowerCase(); return { sType: oToken.sType, sValue: oToken.sValue, - aLabel: [this.dElidedPrefix.gl_get(sTemp, "préfixe élidé inconnu")] + aLabel: [this.dValues.gl_get(oToken.sValue.toLowerCase(), "préfixe élidé inconnu")] }; break; case 'WORD_ORDINAL': return { sType: oToken.sType, @@ -524,34 +671,34 @@ return { sType: oToken.sType, sValue: oToken.sValue, aLabel: ["mot avec particule démonstrative"], aSubElem: [ - { sType: oToken.sType, sValue: m[1], aLabel: this._getMorph(m[1]) }, - { sType: oToken.sType, sValue: "-" + m[2], aLabel: [this._formatSuffix(m[2].toLowerCase())] } + { sType: oToken.sType, sValue: m[1], aLabel: this._getMorph(m[1]) }, + { sType: oToken.sType, sValue: m[2], aLabel: [ this._formatSuffix(m[2]) ] } ] }; } else if (m = this._zImperatifVerb.exec(oToken.sValue)) { // formes interrogatives return { sType: oToken.sType, sValue: oToken.sValue, aLabel: ["forme verbale impérative"], aSubElem: [ - { sType: oToken.sType, sValue: m[1], aLabel: this._getMorph(m[1]) }, - { sType: oToken.sType, sValue: "-" + m[2], aLabel: [this._formatSuffix(m[2].toLowerCase())] } + { sType: oToken.sType, sValue: m[1], aLabel: this._getMorph(m[1]) }, + { sType: oToken.sType, sValue: m[2], aLabel: [ this._formatSuffix(m[2]) ] } ] }; } else if (m = this._zInterroVerb.exec(oToken.sValue)) { // formes interrogatives return { sType: oToken.sType, sValue: oToken.sValue, aLabel: ["forme verbale interrogative"], aSubElem: [ - { sType: oToken.sType, sValue: m[1], aLabel: this._getMorph(m[1]) }, - { sType: oToken.sType, sValue: "-" + m[2], aLabel: [this._formatSuffix(m[2].toLowerCase())] } + { sType: oToken.sType, sValue: m[1], aLabel: this._getMorph(m[1]) }, + { sType: oToken.sType, sValue: m[2], aLabel: [ this._formatSuffix(m[2]) ] } ] }; } else if (this.oSpellChecker.isValidToken(oToken.sValue)) { return { sType: oToken.sType, @@ -580,65 +727,24 @@ }, _getMorph (sWord) { let aElem = []; for (let s of this.oSpellChecker.getMorph(sWord)) { - if (s.includes(":")) aElem.push(this._formatTags(s)); + if (s.includes(":")) aElem.push(this.readableMorph(s)); } if (aElem.length == 0) { aElem.push("mot inconnu du dictionnaire"); } return aElem; }, - _formatTags (sTags) { - let sRes = ""; - sTags = sTags.replace(/V([0-3][ea]?)[itpqnmr_eaxz]+/, "V$1"); - let m; - while ((m = this._zTag.exec(sTags)) !== null) { - sRes += this.dTag.get(m[0])[0]; - } - if (sRes.startsWith(" verbe") && !sRes.includes("infinitif")) { - sRes += " [" + sTags.slice(1, sTags.indexOf("/")) + "]"; - } - if (!sRes) { - return "#Erreur. Étiquette inconnue : [" + sTags + "]"; - } - return sRes.gl_trimRight(","); - }, - - _formatTagsLoc (sTags) { - let sRes = ""; - let m; - while ((m = this._zTag.exec(sTags)) !== null) { - if (m[0].startsWith(":LV")) { - sRes += this.dLocTag.get(":LV"); - for (let c of m[0].slice(3)) { - sRes += this.dLocVerb.get(c); - } - } else { - sRes += this.dLocTag.get(m[0]); - } - } - if (!sRes) { - return "#Erreur. Étiquette inconnue : [" + sTags + "]"; - } - return sRes.gl_trimRight(","); - }, - - _formatSuffix (s) { - if (s.startsWith("t-")) { - return "“t” euphonique +" + this.dPronoms.get(s.slice(2)); - } - if (!s.includes("-")) { - return this.dPronoms.get(s.replace("’", "'")); - } - if (s.endsWith("ous")) { - s += '2'; - } - let nPos = s.indexOf("-"); - return this.dPronoms.get(s.slice(0, nPos)) + " +" + this.dPronoms.get(s.slice(nPos + 1)); + _formatSuffix (sSuffix) { + sSuffix = sSuffix.replace(/['’ʼ‘‛´`′‵՚ꞌꞋ]/g, "’").toLowerCase(); + if (this.dValues.has(sSuffix)) { + return this.dValues.get(sSuffix); + } + return " suffixe inconnu"; }, getListOfTokens (sText, bInfo=true) { let aElem = []; if (sText !== "") { @@ -654,106 +760,13 @@ } } return aElem; }, - * generateInfoForTokenList (lToken) { - for (let oToken of lToken) { - let aRes = this.getInfoForToken(oToken); - if (aRes) { - yield aRes; - } - } - }, - - getListOfTokensReduc (sText, bInfo=true) { - let lToken = this.getListOfTokens(sText.replace("'", "’").trim(), false); - let iToken = 0; - let aElem = []; - if (lToken.length == 0) { - return aElem; - } - do { - let oToken = lToken[iToken]; - let sMorphLoc = ''; - let aTokenTempList = [oToken]; - if (oToken.sType == "WORD" || oToken.sType == "WORD_ELIDED"){ - let iLocEnd = iToken + 1; - let oLocNode = this.oLocGraph[oToken.sValue.toLowerCase()]; - while (oLocNode) { - let oTokenNext = lToken[iLocEnd]; - iLocEnd++; - if (oTokenNext) { - oLocNode = oLocNode[oTokenNext.sValue.toLowerCase()]; - } - if (oLocNode && iLocEnd <= lToken.length) { - sMorphLoc = oLocNode["_:_"]; - aTokenTempList.push(oTokenNext); - } else { - break; - } - } - } - - if (sMorphLoc) { - // we have a locution - let sValue = ''; - for (let oTokenWord of aTokenTempList) { - sValue += oTokenWord.sValue+' '; - } - let oTokenLocution = { - 'nStart': aTokenTempList[0].nStart, - 'nEnd': aTokenTempList[aTokenTempList.length-1].nEnd, - 'sType': "LOC", - 'sValue': sValue.replace('’ ','’').trim(), - 'aSubToken': aTokenTempList - }; - if (bInfo) { - let aSubElem = null; - if (sMorphLoc.startsWith("*|")) { - // cette suite de tokens n’est une locution que dans certains cas minoritaires - oTokenLocution.sType = "LOCP"; - for (let oElem of this.generateInfoForTokenList(aTokenTempList)) { - aElem.push(oElem); - } - sMorphLoc = sMorphLoc.slice(2); - } else { - aSubElem = [...this.generateInfoForTokenList(aTokenTempList)]; - } - // cette suite de tokens est la plupart du temps une locution - let aFormatedTag = []; - for (let sTagLoc of sMorphLoc.split('|') ){ - aFormatedTag.push(this._formatTagsLoc(sTagLoc)); - } - aElem.push({ - sType: oTokenLocution.sType, - sValue: oTokenLocution.sValue, - aLabel: aFormatedTag, - aSubElem: aSubElem - }); - } else { - aElem.push(oTokenLocution); - } - iToken = iToken + aTokenTempList.length; - } - else { - // No locution, we just add information - if (bInfo) { - let aRes = this.getInfoForToken(oToken); - if (aRes) { - aElem.push(aRes); - } - } else { - aElem.push(oToken); - } - iToken++; - } - } while (iToken < lToken.length); - return aElem; - }, // Other functions + filterSugg: function (aSugg) { return aSugg.filter((sSugg) => { return !sSugg.endsWith("è") && !sSugg.endsWith("È"); }); } } Index: graphspell-js/spellchecker.js ================================================================== --- graphspell-js/spellchecker.js +++ graphspell-js/spellchecker.js @@ -132,13 +132,53 @@ loadLexicographer (sLangCode) { // load default suggestion module for if (typeof(process) !== 'undefined') { this.lexicographer = require(`./lexgraph_${sLangCode}.js`); } - else if (typeof(require) !== 'undefined') { - this.lexicographer = require(`resource://grammalecte/graphspell/lexgraph_${sLangCode}.js`); + else if (self && self.hasOwnProperty("lexgraph_"+sLangCode)) { // self is the Worker + this.lexicographer = self["lexgraph_"+sLangCode]; + } + } + + analyze (sWord) { + // returns a list of words and their morphologies + if (!this.lexicographer) { + return []; + } + let lWordAndMorph = []; + for (let sElem of this.lexicographer.split(sWord)) { + if (sElem) { + let lMorph = this.getMorph(sElem); + let sLex = this.lexicographer.analyze(sElem) + let aRes = []; + if (sLex) { + aRes = [ [lMorph.join(" | "), sLex] ]; + } else { + for (let sMorph of lMorph) { + aRes.push([sMorph, this.lexicographer.formatTags(sMorph)]); + } + } + if (aRes.length > 0) { + lWordAndMorph.push([sElem, aRes]); + } + } + } + return lWordAndMorph; + } + + readableMorph (sMorph) { + if (!this.lexicographer) { + return []; + } + return this.lexicographer.formatTags(sMorph); + } + + setLabelsOnToken (oToken) { + if (!this.lexicographer) { + return; } + this.lexicographer.setLabelsOnToken(oToken); } // Storage Index: graphspell/ibdawg.py ================================================================== --- graphspell/ibdawg.py +++ graphspell/ibdawg.py @@ -155,11 +155,11 @@ self.bNumAtLastValid = False # lexicographer module ? self.lexicographer = None try: - self.lexicographer = importlib.import_module("graphspell.lexgraph_"+self.sLangCode) + self.lexicographer = importlib.import_module(".lexgraph_"+self.sLangCode, "grammalecte.graphspell") except ImportError: print("# No module ") def _initBinary (self): Index: graphspell/lexgraph_fr.py ================================================================== --- graphspell/lexgraph_fr.py +++ graphspell/lexgraph_fr.py @@ -7,11 +7,11 @@ # : a dictionary for default suggestions. # : a boolean False # if the boolean is True, 4 functions are required: # split(sWord) -> returns a list of string (that will be analyzed) # analyze(sWord) -> returns a string with the meaning of word -# formatTags(sTags) -> returns a string with the meaning of tags +# readableMorph(sMorph) -> returns a string with the meaning of tags # filterSugg(aWord) -> returns a filtered list of suggestions import re @@ -170,13 +170,16 @@ ':f': (" féminin", "féminin"), ':s': (" singulier", "singulier"), ':p': (" pluriel", "pluriel"), ':i': (" invariable", "invariable"), - ':V1': (" verbe (1ᵉʳ gr.),", "Verbe du 1ᵉʳ groupe"), - ':V2': (" verbe (2ᵉ gr.),", "Verbe du 2ᵉ groupe"), - ':V3': (" verbe (3ᵉ gr.),", "Verbe du 3ᵉ groupe"), + ':V1_': (" verbe (1ᵉʳ gr.),", "Verbe du 1ᵉʳ groupe"), + ':V2_': (" verbe (2ᵉ gr.),", "Verbe du 2ᵉ groupe"), + ':V3_': (" verbe (3ᵉ gr.),", "Verbe du 3ᵉ groupe"), + ':V1e': (" verbe (1ᵉʳ gr.),", "Verbe du 1ᵉʳ groupe"), + ':V2e': (" verbe (2ᵉ gr.),", "Verbe du 2ᵉ groupe"), + ':V3e': (" verbe (3ᵉ gr.),", "Verbe du 3ᵉ groupe"), ':V0e': (" verbe,", "Verbe auxiliaire être"), ':V0a': (" verbe,", "Verbe auxiliaire avoir"), ':Y': (" infinitif,", "infinitif"), ':P': (" participe présent,", "participe présent"), @@ -217,26 +220,26 @@ ':Ot': (" pronom interrogatif,", "Pronom interrogatif"), ':Or': (" pronom relatif,", "Pronom relatif"), ':Ow': (" pronom adverbial,", "Pronom adverbial"), ':Os': (" pronom personnel sujet,", "Pronom personnel sujet"), ':Oo': (" pronom personnel objet,", "Pronom personnel objet"), - ':Ov': (" préverbe,", "Préverbe (pronom personnel objet, +ne)"), + ':Ov': (" préverbe,", "Préverbe"), ':O1': (" 1ʳᵉ pers.,", "Pronom : 1ʳᵉ personne"), ':O2': (" 2ᵉ pers.,", "Pronom : 2ᵉ personne"), ':O3': (" 3ᵉ pers.,", "Pronom : 3ᵉ personne"), ':C': (" conjonction,", "Conjonction"), - ':Ĉ': (" conjonction (él.),", "Conjonction (élément)"), ':Cc': (" conjonction de coordination,", "Conjonction de coordination"), ':Cs': (" conjonction de subordination,", "Conjonction de subordination"), - ':Ĉs': (" conjonction de subordination (él.),", "Conjonction de subordination (élément)"), - - ':ÉN': (" locution nominale (él.),", "Locution nominale (élément)"), - ':ÉA': (" locution adjectivale (él.),", "Locution adjectivale (élément)"), - ':ÉV': (" locution verbale (él.),", "Locution verbale (élément)"), - ':ÉW': (" locution adverbiale (él.),", "Locution adverbiale (élément)"), - ':ÉR': (" locution prépositive (él.),", "Locution prépositive (élément)"), - ':ÉJ': (" locution interjective (él.),", "Locution interjective (élément)"), + + ':ÉC': (" élément de conjonction,", "Élément de conjonction"), + ':ÉCs': (" élément de conjonction de subordination,", "Élément de conjonction de subordination"), + ':ÉN': (" élément de locution nominale,", "Élément de locution nominale"), + ':ÉA': (" élément de locution adjectivale,", "Élément de locution adjectivale"), + ':ÉV': (" élément de locution verbale,", "Élément de locution verbale"), + ':ÉW': (" élément de locution adverbiale,", "Élément de locution adverbiale"), + ':ÉR': (" élément de locution prépositive,", "Élément de locution prépositive"), + ':ÉJ': (" élément de locution interjective,", "Élément de locution interjective"), ':Zp': (" préfixe,", "Préfixe"), ':Zs': (" suffixe,", "Suffixe"), ':H': ("", ""), @@ -272,22 +275,25 @@ 'jusqu’': "(jusque), préposition", '-je': " pronom personnel sujet, 1ʳᵉ pers. sing.", '-tu': " pronom personnel sujet, 2ᵉ pers. sing.", '-il': " pronom personnel sujet, 3ᵉ pers. masc. sing.", + '-iel': " pronom personnel sujet, 3ᵉ pers. sing.", '-on': " pronom personnel sujet, 3ᵉ pers. sing. ou plur.", '-elle': " pronom personnel sujet, 3ᵉ pers. fém. sing.", '-t-il': " “t” euphonique + pronom personnel sujet, 3ᵉ pers. masc. sing.", '-t-on': " “t” euphonique + pronom personnel sujet, 3ᵉ pers. sing. ou plur.", '-t-elle': " “t” euphonique + pronom personnel sujet, 3ᵉ pers. fém. sing.", + '-t-iel': " “t” euphonique + pronom personnel sujet, 3ᵉ pers. sing.", '-nous': " pronom personnel sujet/objet, 1ʳᵉ pers. plur. ou COI (à nous), plur.", '-vous': " pronom personnel sujet/objet, 2ᵉ pers. plur. ou COI (à vous), plur.", '-ils': " pronom personnel sujet, 3ᵉ pers. masc. plur.", '-elles': " pronom personnel sujet, 3ᵉ pers. masc. plur.", + '-iels': " pronom personnel sujet, 3ᵉ pers. plur.", - "-là": " particule démonstrative", - "-ci": " particule démonstrative", + "-là": " particule démonstrative (là)", + "-ci": " particule démonstrative (ci)", '-le': " COD, masc. sing.", '-la': " COD, fém. sing.", '-les': " COD, plur.", @@ -324,10 +330,45 @@ '-en': " pronom adverbial", "-m’en": " (me) pronom personnel objet + (en) pronom adverbial", "-t’en": " (te) pronom personnel objet + (en) pronom adverbial", "-s’en": " (se) pronom personnel objet + (en) pronom adverbial", + + '.': "point", + '·': "point médian", + '…': "points de suspension", + ':': "deux-points", + ';': "point-virgule", + ',': "virgule", + '?': "point d’interrogation", + '!': "point d’exclamation", + '(': "parenthèse ouvrante", + ')': "parenthèse fermante", + '[': "crochet ouvrant", + ']': "crochet fermant", + '{': "accolade ouvrante", + '}': "accolade fermante", + '-': "tiret", + '—': "tiret cadratin", + '–': "tiret demi-cadratin", + '«': "guillemet ouvrant (chevrons)", + '»': "guillemet fermant (chevrons)", + '“': "guillemet ouvrant double", + '”': "guillemet fermant double", + '‘': "guillemet ouvrant", + '’': "guillemet fermant", + '"': "guillemets droits (déconseillé en typographie)", + '/': "signe de la division", + '+': "signe de l’addition", + '*': "signe de la multiplication", + '=': "signe de l’égalité", + '<': "inférieur à", + '>': "supérieur à", + '⩽': "inférieur ou égal à", + '⩾': "supérieur ou égal à", + '%': "signe de pourcentage", + '‰': "signe pour mille" } _zElidedPrefix = re.compile("(?i)^([ldmtsnjcç]|lorsqu|presqu|jusqu|puisqu|quoiqu|quelqu|qu)[’'‘`ʼ]([\\w-]+)") _zCompoundWord = re.compile("(?i)(\\w+)(-(?:(?:les?|la)-(?:moi|toi|lui|[nv]ous|leur)|t-(?:il|elle|on)|y|en|[mts]’(?:y|en)|les?|l[aà]|[mt]oi|leur|lui|je|tu|ils?|elles?|on|[nv]ous|ce))$") @@ -356,22 +397,91 @@ if sWord in _dValues: return _dValues[sWord] return "" -def formatTags (sTags): +def readableMorph (sMorph): "returns string: readable tags" sRes = "" - sTags = re.sub("(?<=V[1-3])[itpqnmr_eaxz]+", "", sTags) - sTags = re.sub("(?<=V0[ea])[itpqnmr_eaxz]+", "", sTags) - for m in _zTag.finditer(sTags): - sRes += _dTAGS.get(m.group(0), " [{}]".format(m.group(0)))[0] + sMorph = re.sub("(?<=V[0123][ea_])[itpqnmr_eaxz]+", "", sMorph) + for m in _zTag.finditer(sMorph): + if m.group(0) in _dTAGS: + sRes += _dTAGS[m.group(0)][0] + else: + sRes += " [" + m.group(0) + "]?" if sRes.startswith(" verbe") and not sRes.endswith("infinitif"): - sRes += " [{}]".format(sTags[1:sTags.find("/")]) + sRes += " [" + sMorph[1:sMorph.find("/")] +"]" + if not sRes: + return " [" + sMorph + "]: étiquettes inconnues" return sRes.rstrip(",") + +_zPartDemForm = re.compile("([\\w]+)-(là|ci)$") +_zInterroVerb = re.compile("([\\w]+)(-(?:t-(?:ie?l|elle|on)|je|tu|ie?ls?|elles?|on|[nv]ous))$") +_zImperatifVerb = re.compile("([\\w]+)(-(?:l(?:es?|a)-(?:moi|toi|lui|[nv]ous|leur)|y|en|[mts][’'](?:y|en)|les?|la|[mt]oi|leur|lui))$") + +def setLabelsOnToken (dToken): + # Token: .sType, .sValue, .nStart, .nEnd, .lMorph + try: + if dToken["sType"] == "PUNC" or dToken["sType"] == "SIGN": + dToken["aLabels"] = [_dValues.get(dToken["sValue"], "signe de ponctuation divers")] + elif dToken["sType"] == 'NUM': + dToken["aLabels"] = ["nombre"] + elif dToken["sType"] == 'LINK': + dToken["aLabels"] = ["hyperlien"] + elif dToken["sType"] == 'TAG': + dToken["aLabels"] = ["étiquette (hashtag)"] + elif dToken["sType"] == 'HTML': + dToken["aLabels"] = ["balise HTML"] + elif dToken["sType"] == 'PSEUDOHTML': + dToken["aLabels"] = ["balise pseudo-HTML"] + elif dToken["sType"] == 'HTMLENTITY': + dToken["aLabels"] = ["entité caractère XML/HTML"] + elif dToken["sType"] == 'HOUR': + dToken["aLabels"] = ["heure"] + elif dToken["sType"] == 'WORD_ELIDED': + dToken["aLabels"] = [_dValues.get(dToken["sValue"], "préfixe élidé inconnu")] + elif dToken["sType"] == 'WORD_ORDINAL': + dToken["aLabels"] = ["nombre ordinal"] + elif dToken["sType"] == 'FOLDERUNIX': + dToken["aLabels"] = ["dossier UNIX (et dérivés)"] + elif dToken["sType"] == 'FOLDERWIN': + dToken["aLabels"] = ["dossier Windows"] + elif dToken["sType"] == 'WORD_ACRONYM': + dToken["aLabels"] = ["sigle ou acronyme"] + elif dToken["sType"] == 'WORD': + if "lMorph" in dToken and dToken["lMorph"]: + # with morphology + dToken["aLabels"] = [] + for sMorph in dToken["lMorph"]: + dToken["aLabels"].append(readableMorph(sMorph)) + if "sTags" in dToken: + aTags = [] + for sTag in dToken["sTags"]: + if sTag in _dValues: + aTags.append(_dValues[sTag]) + if aTags: + dToken["aOtherLabels"] = aTags + else: + # no morphology, guessing + if dToken["sValue"].count("-") > 4: + dToken["aLabels"] = ["élément complexe indéterminé"] + elif _zPartDemForm.search(dToken["sValue"]): + # mots avec particules démonstratives + dToken["aLabels"] = ["mot avec particule démonstrative"] + elif _zImperatifVerb.search(dToken["sValue"]): + # formes interrogatives + dToken["aLabels"] = ["forme verbale impérative"] + elif _zInterroVerb.search(dToken["sValue"]): + # formes interrogatives + dToken["aLabels"] = ["forme verbale interrogative"] + else: + dToken["aLabels"] = ["token de nature inconnue"] + except: + return + # Other functions def filterSugg (aSugg): "exclude suggestions" return filter(lambda sSugg: not sSugg.endswith(("è", "È")), aSugg) Index: graphspell/spellchecker.py ================================================================== --- graphspell/spellchecker.py +++ graphspell/spellchecker.py @@ -98,11 +98,11 @@ def deactivatePersonalDictionary (self): "deactivate personal dictionary" self.bPersonalDic = False - # Default suggestions + # Lexicographer def loadLexicographer (self, sLangCode): "load default suggestion module for " try: self.lexicographer = importlib.import_module(".lexgraph_"+sLangCode, "grammalecte.graphspell") @@ -120,15 +120,25 @@ lMorph = self.getMorph(sElem) sLex = self.lexicographer.analyze(sElem) if sLex: aRes = [ (" | ".join(lMorph), sLex) ] else: - aRes = [ (sMorph, self.lexicographer.formatTags(sMorph)) for sMorph in lMorph ] + aRes = [ (sMorph, self.lexicographer.readableMorph(sMorph)) for sMorph in lMorph ] if aRes: lWordAndMorph.append((sElem, aRes)) return lWordAndMorph + def readableMorph (self, sMorph): + if not self.lexicographer: + return "" + return self.lexicographer.readableMorph(sMorph) + + def setLabelsOnToken (self, dToken): + if not self.lexicographer: + return + self.lexicographer.setLabelsOnToken(dToken) + # Storage def activateStorage (self): "store all lemmas and morphologies retrieved from the word graph" @@ -233,11 +243,11 @@ return self._dLemmas[sWord] return { s[1:s.find("/")] for s in self.getMorph(sWord) } def suggest (self, sWord, nSuggLimit=10): "generator: returns 1, 2 or 3 lists of suggestions" - if self.lexicographer.dSugg: + if self.lexicographer: if sWord in self.lexicographer.dSugg: yield self.lexicographer.dSugg[sWord].split("|") elif sWord.istitle() and sWord.lower() in self.lexicographer.dSugg: lRes = self.lexicographer.dSugg[sWord.lower()].split("|") yield list(map(lambda sSugg: sSugg[0:1].upper()+sSugg[1:], lRes))