Grammalecte  Hex Artifact Content

Artifact f2f8ff9a94f0295822f5bbd299abb483111aab9962b32e1151d4ffa9ee3b4579:


0000: 23 21 20 70 79 74 68 6f 6e 33 0a 0a 22 22 22 0a  #! python3..""".
0010: 47 72 61 6d 6d 61 72 20 63 68 65 63 6b 65 72 20  Grammar checker 
0020: 74 65 73 74 73 20 66 6f 72 20 46 72 65 6e 63 68  tests for French
0030: 20 6c 61 6e 67 75 61 67 65 0a 22 22 22 0a 0a 69   language."""..i
0040: 6d 70 6f 72 74 20 75 6e 69 74 74 65 73 74 0a 69  mport unittest.i
0050: 6d 70 6f 72 74 20 6f 73 0a 69 6d 70 6f 72 74 20  mport os.import 
0060: 72 65 0a 69 6d 70 6f 72 74 20 74 69 6d 65 0a 66  re.import time.f
0070: 72 6f 6d 20 63 6f 6e 74 65 78 74 6c 69 62 20 69  rom contextlib i
0080: 6d 70 6f 72 74 20 63 6f 6e 74 65 78 74 6d 61 6e  mport contextman
0090: 61 67 65 72 0a 0a 0a 66 72 6f 6d 20 2e 2e 67 72  ager...from ..gr
00a0: 61 70 68 73 70 65 6c 6c 2e 65 63 68 6f 20 69 6d  aphspell.echo im
00b0: 70 6f 72 74 20 65 63 68 6f 0a 66 72 6f 6d 20 2e  port echo.from .
00c0: 20 69 6d 70 6f 72 74 20 67 63 5f 65 6e 67 69 6e   import gc_engin
00d0: 65 0a 0a 0a 40 63 6f 6e 74 65 78 74 6d 61 6e 61  e...@contextmana
00e0: 67 65 72 0a 64 65 66 20 74 69 6d 65 62 6c 6f 63  ger.def timebloc
00f0: 6b 20 28 6c 61 62 65 6c 2c 20 68 44 73 74 29 3a  k (label, hDst):
0100: 0a 20 20 20 20 22 70 65 72 66 6f 72 6d 61 6e 63  .    "performanc
0110: 65 20 63 6f 75 6e 74 65 72 20 28 63 6f 6e 74 65  e counter (conte
0120: 78 74 6d 61 6e 61 67 65 72 29 22 0a 20 20 20 20  xtmanager)".    
0130: 73 74 61 72 74 20 3d 20 74 69 6d 65 2e 70 65 72  start = time.per
0140: 66 5f 63 6f 75 6e 74 65 72 28 29 0a 20 20 20 20  f_counter().    
0150: 74 72 79 3a 0a 20 20 20 20 20 20 20 20 79 69 65  try:.        yie
0160: 6c 64 0a 20 20 20 20 66 69 6e 61 6c 6c 79 3a 0a  ld.    finally:.
0170: 20 20 20 20 20 20 20 20 65 6e 64 20 3d 20 74 69          end = ti
0180: 6d 65 2e 70 65 72 66 5f 63 6f 75 6e 74 65 72 28  me.perf_counter(
0190: 29 0a 20 20 20 20 20 20 20 20 70 72 69 6e 74 28  ).        print(
01a0: 27 7b 7d 20 3a 20 7b 7d 27 2e 66 6f 72 6d 61 74  '{} : {}'.format
01b0: 28 6c 61 62 65 6c 2c 20 65 6e 64 20 2d 20 73 74  (label, end - st
01c0: 61 72 74 29 29 0a 20 20 20 20 20 20 20 20 69 66  art)).        if
01d0: 20 68 44 73 74 3a 0a 20 20 20 20 20 20 20 20 20   hDst:.         
01e0: 20 20 20 68 44 73 74 2e 77 72 69 74 65 28 22 7b     hDst.write("{
01f0: 3a 3c 31 32 2e 36 7d 22 2e 66 6f 72 6d 61 74 28  :<12.6}".format(
0200: 65 6e 64 2d 73 74 61 72 74 29 29 0a 0a 0a 64 65  end-start))...de
0210: 66 20 70 65 72 66 20 28 73 56 65 72 73 69 6f 6e  f perf (sVersion
0220: 2c 20 73 52 65 73 75 6c 74 46 69 6c 65 3d 22 22  , sResultFile=""
0230: 29 3a 0a 20 20 20 20 22 70 65 72 66 6f 72 6d 61  ):.    "performa
0240: 6e 63 65 20 74 65 73 74 73 22 0a 20 20 20 20 70  nce tests".    p
0250: 72 69 6e 74 28 22 50 65 72 66 6f 72 6d 61 6e 63  rint("Performanc
0260: 65 20 74 65 73 74 73 22 29 0a 20 20 20 20 67 63  e tests").    gc
0270: 5f 65 6e 67 69 6e 65 2e 6c 6f 61 64 28 29 0a 20  _engine.load(). 
0280: 20 20 20 67 63 5f 65 6e 67 69 6e 65 2e 70 61 72     gc_engine.par
0290: 73 65 28 22 54 65 78 74 20 74 6f 20 63 6f 6d 70  se("Text to comp
02a0: 69 6c 65 20 72 75 6c 65 73 20 62 65 66 6f 72 65  ile rules before
02b0: 20 6c 61 75 6e 63 68 69 6e 67 20 72 65 61 6c 20   launching real 
02c0: 74 65 73 74 73 2e 22 29 0a 0a 20 20 20 20 73 70  tests.")..    sp
02d0: 48 65 72 65 2c 20 5f 20 3d 20 6f 73 2e 70 61 74  Here, _ = os.pat
02e0: 68 2e 73 70 6c 69 74 28 5f 5f 66 69 6c 65 5f 5f  h.split(__file__
02f0: 29 0a 20 20 20 20 73 70 66 50 65 72 66 54 65 73  ).    spfPerfTes
0300: 74 20 3d 20 6f 73 2e 70 61 74 68 2e 6a 6f 69 6e  t = os.path.join
0310: 28 73 70 48 65 72 65 2c 20 22 70 65 72 66 2e 74  (spHere, "perf.t
0320: 78 74 22 29 0a 20 20 20 20 69 66 20 6e 6f 74 20  xt").    if not 
0330: 6f 73 2e 70 61 74 68 2e 65 78 69 73 74 73 28 73  os.path.exists(s
0340: 70 66 50 65 72 66 54 65 73 74 29 3a 0a 20 20 20  pfPerfTest):.   
0350: 20 20 20 20 20 70 72 69 6e 74 28 66 22 4e 6f 20       print(f"No 
0360: 66 69 6c 65 20 3c 70 65 72 66 2e 74 78 74 3e 20  file <perf.txt> 
0370: 69 6e 20 3c 7b 73 70 48 65 72 65 7d 3e 22 29 0a  in <{spHere}>").
0380: 20 20 20 20 20 20 20 20 72 65 74 75 72 6e 0a 20          return. 
0390: 20 20 20 77 69 74 68 20 6f 70 65 6e 28 73 70 66     with open(spf
03a0: 50 65 72 66 54 65 73 74 2c 20 22 72 22 2c 20 65  PerfTest, "r", e
03b0: 6e 63 6f 64 69 6e 67 3d 22 75 74 66 2d 38 22 29  ncoding="utf-8")
03c0: 20 61 73 20 68 53 72 63 3a 0a 20 20 20 20 20 20   as hSrc:.      
03d0: 20 20 68 44 73 74 20 3d 20 6f 70 65 6e 28 73 52    hDst = open(sR
03e0: 65 73 75 6c 74 46 69 6c 65 2c 20 22 61 22 2c 20  esultFile, "a", 
03f0: 65 6e 63 6f 64 69 6e 67 3d 22 75 74 66 2d 38 22  encoding="utf-8"
0400: 2c 20 6e 65 77 6c 69 6e 65 3d 22 5c 6e 22 29 20  , newline="\n") 
0410: 20 69 66 20 73 52 65 73 75 6c 74 46 69 6c 65 20   if sResultFile 
0420: 20 65 6c 73 65 20 4e 6f 6e 65 0a 20 20 20 20 20   else None.     
0430: 20 20 20 69 66 20 68 44 73 74 3a 0a 20 20 20 20     if hDst:.    
0440: 20 20 20 20 20 20 20 20 68 44 73 74 2e 77 72 69          hDst.wri
0450: 74 65 28 22 7b 3a 3c 31 32 7d 7b 3a 3c 32 30 7d  te("{:<12}{:<20}
0460: 22 2e 66 6f 72 6d 61 74 28 73 56 65 72 73 69 6f  ".format(sVersio
0470: 6e 2c 20 74 69 6d 65 2e 73 74 72 66 74 69 6d 65  n, time.strftime
0480: 28 22 25 59 2e 25 6d 2e 25 64 20 25 48 3a 25 4d  ("%Y.%m.%d %H:%M
0490: 22 29 29 29 0a 20 20 20 20 20 20 20 20 66 6f 72  "))).        for
04a0: 20 73 54 65 78 74 20 69 6e 20 28 20 73 2e 73 74   sText in ( s.st
04b0: 72 69 70 28 29 20 66 6f 72 20 73 20 69 6e 20 68  rip() for s in h
04c0: 53 72 63 20 69 66 20 6e 6f 74 20 73 2e 73 74 61  Src if not s.sta
04d0: 72 74 73 77 69 74 68 28 22 23 22 29 20 61 6e 64  rtswith("#") and
04e0: 20 73 2e 73 74 72 69 70 28 29 20 29 3a 0a 20 20   s.strip() ):.  
04f0: 20 20 20 20 20 20 20 20 20 20 77 69 74 68 20 74            with t
0500: 69 6d 65 62 6c 6f 63 6b 28 73 54 65 78 74 5b 3a  imeblock(sText[:
0510: 73 54 65 78 74 2e 66 69 6e 64 28 22 2e 22 29 5d  sText.find(".")]
0520: 2c 20 68 44 73 74 29 3a 0a 20 20 20 20 20 20 20  , hDst):.       
0530: 20 20 20 20 20 20 20 20 20 67 63 5f 65 6e 67 69           gc_engi
0540: 6e 65 2e 70 61 72 73 65 28 73 54 65 78 74 29 0a  ne.parse(sText).
0550: 20 20 20 20 20 20 20 20 69 66 20 68 44 73 74 3a          if hDst:
0560: 0a 20 20 20 20 20 20 20 20 20 20 20 20 68 44 73  .            hDs
0570: 74 2e 77 72 69 74 65 28 22 5c 6e 22 29 0a 0a 0a  t.write("\n")...
0580: 64 65 66 20 5f 66 75 63 6b 42 61 63 6b 73 6c 61  def _fuckBacksla
0590: 73 68 55 54 46 38 20 28 73 29 3a 0a 20 20 20 20  shUTF8 (s):.    
05a0: 22 66 75 63 6b 20 74 68 61 74 20 73 68 69 74 22  "fuck that shit"
05b0: 0a 20 20 20 20 72 65 74 75 72 6e 20 73 2e 72 65  .    return s.re
05c0: 70 6c 61 63 65 28 22 5c 75 32 30 31 39 22 2c 20  place("\u2019", 
05d0: 22 27 22 29 2e 72 65 70 6c 61 63 65 28 22 5c 75  "'").replace("\u
05e0: 32 30 31 33 22 2c 20 22 e2 80 93 22 29 2e 72 65  2013", "...").re
05f0: 70 6c 61 63 65 28 22 5c 75 32 30 31 34 22 2c 20  place("\u2014", 
0600: 22 e2 80 94 22 29 0a 0a 0a 63 6c 61 73 73 20 54  "...")...class T
0610: 65 73 74 47 72 61 6d 6d 61 72 43 68 65 63 6b 69  estGrammarChecki
0620: 6e 67 20 28 75 6e 69 74 74 65 73 74 2e 54 65 73  ng (unittest.Tes
0630: 74 43 61 73 65 29 3a 0a 20 20 20 20 22 54 65 73  tCase):.    "Tes
0640: 74 73 20 64 75 20 63 6f 72 72 65 63 74 65 75 72  ts du correcteur
0650: 20 67 72 61 6d 6d 61 74 69 63 61 6c 22 0a 0a 20   grammatical".. 
0660: 20 20 20 40 63 6c 61 73 73 6d 65 74 68 6f 64 0a     @classmethod.
0670: 20 20 20 20 64 65 66 20 73 65 74 55 70 43 6c 61      def setUpCla
0680: 73 73 20 28 63 6c 73 29 3a 0a 20 20 20 20 20 20  ss (cls):.      
0690: 20 20 67 63 5f 65 6e 67 69 6e 65 2e 6c 6f 61 64    gc_engine.load
06a0: 28 29 0a 20 20 20 20 20 20 20 20 63 6c 73 2e 5f  ().        cls._
06b0: 7a 45 72 72 6f 72 20 3d 20 72 65 2e 63 6f 6d 70  zError = re.comp
06c0: 69 6c 65 28 72 22 5c 7b 5c 7b 2e 2a 3f 5c 7d 5c  ile(r"\{\{.*?\}\
06d0: 7d 22 29 0a 20 20 20 20 20 20 20 20 63 6c 73 2e  }").        cls.
06e0: 5f 7a 52 75 6c 65 45 6e 64 20 3d 20 72 65 2e 63  _zRuleEnd = re.c
06f0: 6f 6d 70 69 6c 65 28 72 22 5f 61 5c 64 2b 5f 5c  ompile(r"_a\d+_\
0700: 64 2b 24 22 29 0a 20 20 20 20 20 20 20 20 63 6c  d+$").        cl
0710: 73 2e 5f 61 54 65 73 74 65 64 52 75 6c 65 73 20  s._aTestedRules 
0720: 3d 20 73 65 74 28 29 0a 20 20 20 20 20 20 20 20  = set().        
0730: 63 6c 73 2e 5f 6f 53 70 65 6c 6c 43 68 65 63 6b  cls._oSpellCheck
0740: 65 72 20 3d 20 67 63 5f 65 6e 67 69 6e 65 2e 67  er = gc_engine.g
0750: 65 74 53 70 65 6c 6c 43 68 65 63 6b 65 72 28 29  etSpellChecker()
0760: 0a 0a 20 20 20 20 64 65 66 20 74 65 73 74 5f 70  ..    def test_p
0770: 61 72 73 65 20 28 73 65 6c 66 29 3a 0a 20 20 20  arse (self):.   
0780: 20 20 20 20 20 7a 4f 70 74 69 6f 6e 20 3d 20 72       zOption = r
0790: 65 2e 63 6f 6d 70 69 6c 65 28 22 5e 5f 5f 28 5b  e.compile("^__([
07a0: 61 2d 7a 41 2d 5a 30 2d 39 5d 2b 29 5f 5f 20 22  a-zA-Z0-9]+)__ "
07b0: 29 0a 20 20 20 20 20 20 20 20 73 70 48 65 72 65  ).        spHere
07c0: 2c 20 5f 20 3d 20 6f 73 2e 70 61 74 68 2e 73 70  , _ = os.path.sp
07d0: 6c 69 74 28 5f 5f 66 69 6c 65 5f 5f 29 0a 20 20  lit(__file__).  
07e0: 20 20 20 20 20 20 73 70 66 50 61 72 73 69 6e 67        spfParsing
07f0: 54 65 73 74 20 3d 20 6f 73 2e 70 61 74 68 2e 6a  Test = os.path.j
0800: 6f 69 6e 28 73 70 48 65 72 65 2c 20 22 67 63 5f  oin(spHere, "gc_
0810: 74 65 73 74 2e 74 78 74 22 29 0a 20 20 20 20 20  test.txt").     
0820: 20 20 20 69 66 20 6e 6f 74 20 6f 73 2e 70 61 74     if not os.pat
0830: 68 2e 65 78 69 73 74 73 28 73 70 66 50 61 72 73  h.exists(spfPars
0840: 69 6e 67 54 65 73 74 29 3a 0a 20 20 20 20 20 20  ingTest):.      
0850: 20 20 20 20 20 20 70 72 69 6e 74 28 66 22 4e 6f        print(f"No
0860: 20 66 69 6c 65 20 3c 67 63 5f 74 65 73 74 2e 74   file <gc_test.t
0870: 78 74 3e 20 69 6e 20 3c 7b 73 70 48 65 72 65 7d  xt> in <{spHere}
0880: 3e 22 29 0a 20 20 20 20 20 20 20 20 20 20 20 20  >").            
0890: 72 65 74 75 72 6e 0a 20 20 20 20 20 20 20 20 77  return.        w
08a0: 69 74 68 20 6f 70 65 6e 28 73 70 66 50 61 72 73  ith open(spfPars
08b0: 69 6e 67 54 65 73 74 2c 20 22 72 22 2c 20 65 6e  ingTest, "r", en
08c0: 63 6f 64 69 6e 67 3d 22 75 74 66 2d 38 22 29 20  coding="utf-8") 
08d0: 61 73 20 68 53 72 63 3a 0a 20 20 20 20 20 20 20  as hSrc:.       
08e0: 20 20 20 20 20 6e 55 6e 65 78 70 65 63 74 65 64       nUnexpected
08f0: 45 72 72 6f 72 73 20 3d 20 30 0a 20 20 20 20 20  Errors = 0.     
0900: 20 20 20 20 20 20 20 6e 54 65 73 74 57 69 74 68         nTestWith
0910: 45 78 70 65 63 74 65 64 45 72 72 6f 72 20 3d 20  ExpectedError = 
0920: 30 0a 20 20 20 20 20 20 20 20 20 20 20 20 6e 54  0.            nT
0930: 65 73 74 57 69 74 68 45 78 70 65 63 74 65 64 45  estWithExpectedE
0940: 72 72 6f 72 41 6e 64 53 75 67 67 20 3d 20 30 0a  rrorAndSugg = 0.
0950: 20 20 20 20 20 20 20 20 20 20 20 20 66 6f 72 20              for 
0960: 69 2c 20 73 4c 69 6e 65 20 69 6e 20 65 6e 75 6d  i, sLine in enum
0970: 65 72 61 74 65 28 20 73 20 66 6f 72 20 73 20 69  erate( s for s i
0980: 6e 20 68 53 72 63 20 69 66 20 6e 6f 74 20 73 2e  n hSrc if not s.
0990: 73 74 61 72 74 73 77 69 74 68 28 22 23 22 29 20  startswith("#") 
09a0: 61 6e 64 20 73 2e 73 74 72 69 70 28 29 20 29 3a  and s.strip() ):
09b0: 0a 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20  .               
09c0: 20 73 4c 69 6e 65 4e 75 6d 20 3d 20 73 4c 69 6e   sLineNum = sLin
09d0: 65 5b 3a 31 30 5d 2e 73 74 72 69 70 28 29 0a 20  e[:10].strip(). 
09e0: 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 73                 s
09f0: 4c 69 6e 65 20 3d 20 73 4c 69 6e 65 5b 31 30 3a  Line = sLine[10:
0a00: 5d 2e 73 74 72 69 70 28 29 0a 20 20 20 20 20 20  ].strip().      
0a10: 20 20 20 20 20 20 20 20 20 20 73 4f 70 74 69 6f            sOptio
0a20: 6e 20 3d 20 4e 6f 6e 65 0a 20 20 20 20 20 20 20  n = None.       
0a30: 20 20 20 20 20 20 20 20 20 6d 20 3d 20 7a 4f 70           m = zOp
0a40: 74 69 6f 6e 2e 73 65 61 72 63 68 28 73 4c 69 6e  tion.search(sLin
0a50: 65 29 0a 20 20 20 20 20 20 20 20 20 20 20 20 20  e).             
0a60: 20 20 20 69 66 20 6d 3a 0a 20 20 20 20 20 20 20     if m:.       
0a70: 20 20 20 20 20 20 20 20 20 20 20 20 20 73 4c 69               sLi
0a80: 6e 65 20 3d 20 73 4c 69 6e 65 5b 6d 2e 65 6e 64  ne = sLine[m.end
0a90: 28 29 3a 5d 0a 20 20 20 20 20 20 20 20 20 20 20  ():].           
0aa0: 20 20 20 20 20 20 20 20 20 73 4f 70 74 69 6f 6e           sOption
0ab0: 20 3d 20 6d 2e 67 72 6f 75 70 28 31 29 0a 20 20   = m.group(1).  
0ac0: 20 20 20 20 20 20 20 20 20 20 20 20 20 20 69 66                if
0ad0: 20 22 2d 3e 3e 22 20 69 6e 20 73 4c 69 6e 65 3a   "->>" in sLine:
0ae0: 0a 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20  .               
0af0: 20 20 20 20 20 73 45 72 72 6f 72 54 65 78 74 2c       sErrorText,
0b00: 20 73 45 78 63 65 70 74 65 64 53 75 67 67 73 20   sExceptedSuggs 
0b10: 3d 20 73 65 6c 66 2e 5f 73 70 6c 69 74 54 65 73  = self._splitTes
0b20: 74 4c 69 6e 65 28 73 4c 69 6e 65 29 0a 20 20 20  tLine(sLine).   
0b30: 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20                  
0b40: 20 6e 54 65 73 74 57 69 74 68 45 78 70 65 63 74   nTestWithExpect
0b50: 65 64 45 72 72 6f 72 41 6e 64 53 75 67 67 20 2b  edErrorAndSugg +
0b60: 3d 20 31 0a 20 20 20 20 20 20 20 20 20 20 20 20  = 1.            
0b70: 20 20 20 20 65 6c 73 65 3a 0a 20 20 20 20 20 20      else:.      
0b80: 20 20 20 20 20 20 20 20 20 20 20 20 20 20 73 45                sE
0b90: 72 72 6f 72 54 65 78 74 20 3d 20 73 4c 69 6e 65  rrorText = sLine
0ba0: 2e 73 74 72 69 70 28 29 0a 20 20 20 20 20 20 20  .strip().       
0bb0: 20 20 20 20 20 20 20 20 20 20 20 20 20 73 45 78               sEx
0bc0: 63 65 70 74 65 64 53 75 67 67 73 20 3d 20 22 22  ceptedSuggs = ""
0bd0: 0a 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20  .               
0be0: 20 73 45 78 70 65 63 74 65 64 45 72 72 6f 72 73   sExpectedErrors
0bf0: 20 3d 20 73 65 6c 66 2e 5f 67 65 74 45 78 70 65   = self._getExpe
0c00: 63 74 65 64 45 72 72 6f 72 73 28 73 45 72 72 6f  ctedErrors(sErro
0c10: 72 54 65 78 74 29 0a 20 20 20 20 20 20 20 20 20  rText).         
0c20: 20 20 20 20 20 20 20 69 66 20 73 45 78 70 65 63         if sExpec
0c30: 74 65 64 45 72 72 6f 72 73 2e 73 74 72 69 70 28  tedErrors.strip(
0c40: 29 20 21 3d 20 22 22 3a 0a 20 20 20 20 20 20 20  ) != "":.       
0c50: 20 20 20 20 20 20 20 20 20 20 20 20 20 6e 54 65               nTe
0c60: 73 74 57 69 74 68 45 78 70 65 63 74 65 64 45 72  stWithExpectedEr
0c70: 72 6f 72 20 2b 3d 20 31 0a 20 20 20 20 20 20 20  ror += 1.       
0c80: 20 20 20 20 20 20 20 20 20 73 54 65 78 74 54 6f           sTextTo
0c90: 43 68 65 63 6b 20 3d 20 73 45 72 72 6f 72 54 65  Check = sErrorTe
0ca0: 78 74 2e 72 65 70 6c 61 63 65 28 22 7d 7d 22 2c  xt.replace("}}",
0cb0: 20 22 22 29 2e 72 65 70 6c 61 63 65 28 22 7b 7b   "").replace("{{
0cc0: 22 2c 20 22 22 29 0a 20 20 20 20 20 20 20 20 20  ", "").         
0cd0: 20 20 20 20 20 20 20 73 46 6f 75 6e 64 45 72 72         sFoundErr
0ce0: 6f 72 73 2c 20 73 4c 69 73 74 45 72 72 2c 20 73  ors, sListErr, s
0cf0: 46 6f 75 6e 64 53 75 67 67 73 20 3d 20 73 65 6c  FoundSuggs = sel
0d00: 66 2e 5f 67 65 74 46 6f 75 6e 64 45 72 72 6f 72  f._getFoundError
0d10: 73 28 73 54 65 78 74 54 6f 43 68 65 63 6b 2c 20  s(sTextToCheck, 
0d20: 73 4f 70 74 69 6f 6e 29 0a 20 20 20 20 20 20 20  sOption).       
0d30: 20 20 20 20 20 20 20 20 20 23 20 74 65 73 74 73           # tests
0d40: 0a 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20  .               
0d50: 20 69 66 20 73 45 78 70 65 63 74 65 64 45 72 72   if sExpectedErr
0d60: 6f 72 73 20 21 3d 20 73 46 6f 75 6e 64 45 72 72  ors != sFoundErr
0d70: 6f 72 73 3a 0a 20 20 20 20 20 20 20 20 20 20 20  ors:.           
0d80: 20 20 20 20 20 20 20 20 20 70 72 69 6e 74 28 22           print("
0d90: 5c 6e 23 20 4c 69 6e 65 20 6e 75 6d 3a 20 22 20  \n# Line num: " 
0da0: 2b 20 73 4c 69 6e 65 4e 75 6d 20 2b 20 5c 0a 20  + sLineNum + \. 
0db0: 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20                  
0dc0: 20 20 20 20 20 20 20 20 20 22 5c 6e 3e 20 74 6f           "\n> to
0dd0: 20 63 68 65 63 6b 3a 20 22 20 2b 20 5f 66 75 63   check: " + _fuc
0de0: 6b 42 61 63 6b 73 6c 61 73 68 55 54 46 38 28 73  kBackslashUTF8(s
0df0: 54 65 78 74 54 6f 43 68 65 63 6b 29 20 2b 20 5c  TextToCheck) + \
0e00: 0a 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20  .               
0e10: 20 20 20 20 20 20 20 20 20 20 20 22 5c 6e 20 20             "\n  
0e20: 65 78 70 65 63 74 65 64 3a 20 22 20 2b 20 73 45  expected: " + sE
0e30: 78 70 65 63 74 65 64 45 72 72 6f 72 73 20 2b 20  xpectedErrors + 
0e40: 5c 0a 20 20 20 20 20 20 20 20 20 20 20 20 20 20  \.              
0e50: 20 20 20 20 20 20 20 20 20 20 20 20 22 5c 6e 20              "\n 
0e60: 20 66 6f 75 6e 64 3a 20 20 20 20 22 20 2b 20 73   found:    " + s
0e70: 46 6f 75 6e 64 45 72 72 6f 72 73 20 2b 20 5c 0a  FoundErrors + \.
0e80: 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20                  
0e90: 20 20 20 20 20 20 20 20 20 20 22 5c 6e 20 20 65            "\n  e
0ea0: 72 72 6f 72 73 3a 20 20 20 5c 6e 22 20 2b 20 73  rrors:   \n" + s
0eb0: 4c 69 73 74 45 72 72 29 0a 20 20 20 20 20 20 20  ListErr).       
0ec0: 20 20 20 20 20 20 20 20 20 20 20 20 20 6e 55 6e               nUn
0ed0: 65 78 70 65 63 74 65 64 45 72 72 6f 72 73 20 2b  expectedErrors +
0ee0: 3d 20 31 0a 20 20 20 20 20 20 20 20 20 20 20 20  = 1.            
0ef0: 20 20 20 20 65 6c 69 66 20 73 45 78 63 65 70 74      elif sExcept
0f00: 65 64 53 75 67 67 73 3a 0a 20 20 20 20 20 20 20  edSuggs:.       
0f10: 20 20 20 20 20 20 20 20 20 20 20 20 20 69 66 20               if 
0f20: 6e 6f 74 20 73 65 6c 66 2e 5f 63 68 65 63 6b 53  not self._checkS
0f30: 75 67 67 65 73 74 69 6f 6e 73 28 73 45 78 63 65  uggestions(sExce
0f40: 70 74 65 64 53 75 67 67 73 2c 20 73 46 6f 75 6e  ptedSuggs, sFoun
0f50: 64 53 75 67 67 73 29 3a 0a 20 20 20 20 20 20 20  dSuggs):.       
0f60: 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20                  
0f70: 20 70 72 69 6e 74 28 22 5c 6e 23 20 4c 69 6e 65   print("\n# Line
0f80: 20 6e 75 6d 3a 20 22 20 2b 20 73 4c 69 6e 65 4e   num: " + sLineN
0f90: 75 6d 20 2b 20 5c 0a 20 20 20 20 20 20 20 20 20  um + \.         
0fa0: 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20                  
0fb0: 20 20 20 20 20 22 5c 6e 3e 20 74 6f 20 63 68 65       "\n> to che
0fc0: 63 6b 3a 20 22 20 2b 20 5f 66 75 63 6b 42 61 63  ck: " + _fuckBac
0fd0: 6b 73 6c 61 73 68 55 54 46 38 28 73 54 65 78 74  kslashUTF8(sText
0fe0: 54 6f 43 68 65 63 6b 29 20 2b 20 5c 0a 20 20 20  ToCheck) + \.   
0ff0: 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20                  
1000: 20 20 20 20 20 20 20 20 20 20 20 22 5c 6e 20 20             "\n  
1010: 65 78 70 65 63 74 65 64 3a 20 22 20 2b 20 73 45  expected: " + sE
1020: 78 63 65 70 74 65 64 53 75 67 67 73 20 2b 20 5c  xceptedSuggs + \
1030: 0a 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20  .               
1040: 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 22                 "
1050: 5c 6e 20 20 66 6f 75 6e 64 3a 20 20 20 20 22 20  \n  found:    " 
1060: 2b 20 73 46 6f 75 6e 64 53 75 67 67 73 20 2b 20  + sFoundSuggs + 
1070: 5c 0a 20 20 20 20 20 20 20 20 20 20 20 20 20 20  \.              
1080: 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20                  
1090: 22 5c 6e 20 20 65 72 72 6f 72 73 3a 20 20 20 5c  "\n  errors:   \
10a0: 6e 22 20 2b 20 73 4c 69 73 74 45 72 72 29 0a 20  n" + sListErr). 
10b0: 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20                  
10c0: 20 20 20 20 20 20 20 6e 55 6e 65 78 70 65 63 74         nUnexpect
10d0: 65 64 45 72 72 6f 72 73 20 2b 3d 20 31 0a 20 20  edErrors += 1.  
10e0: 20 20 20 20 20 20 20 20 20 20 70 72 69 6e 74 28            print(
10f0: 22 54 65 73 74 73 20 77 69 74 68 20 65 78 70 65  "Tests with expe
1100: 63 74 65 64 20 65 72 72 6f 72 73 3a 22 2c 20 6e  cted errors:", n
1110: 54 65 73 74 57 69 74 68 45 78 70 65 63 74 65 64  TestWithExpected
1120: 45 72 72 6f 72 2c 20 22 20 61 6e 64 20 73 75 67  Error, " and sug
1130: 67 65 73 74 69 6f 6e 73 3a 22 2c 20 6e 54 65 73  gestions:", nTes
1140: 74 57 69 74 68 45 78 70 65 63 74 65 64 45 72 72  tWithExpectedErr
1150: 6f 72 41 6e 64 53 75 67 67 2c 20 22 3a 7b 3a 2e  orAndSugg, ":{:.
1160: 34 7d 25 22 2e 66 6f 72 6d 61 74 28 6e 54 65 73  4}%".format(nTes
1170: 74 57 69 74 68 45 78 70 65 63 74 65 64 45 72 72  tWithExpectedErr
1180: 6f 72 41 6e 64 53 75 67 67 2f 6e 54 65 73 74 57  orAndSugg/nTestW
1190: 69 74 68 45 78 70 65 63 74 65 64 45 72 72 6f 72  ithExpectedError
11a0: 2a 31 30 30 29 29 0a 20 20 20 20 20 20 20 20 20  *100)).         
11b0: 20 20 20 69 66 20 6e 55 6e 65 78 70 65 63 74 65     if nUnexpecte
11c0: 64 45 72 72 6f 72 73 3a 0a 20 20 20 20 20 20 20  dErrors:.       
11d0: 20 20 20 20 20 20 20 20 20 70 72 69 6e 74 28 22           print("
11e0: 55 6e 65 78 70 65 63 74 65 64 20 65 72 72 6f 72  Unexpected error
11f0: 73 3a 22 2c 20 6e 55 6e 65 78 70 65 63 74 65 64  s:", nUnexpected
1200: 45 72 72 6f 72 73 29 0a 20 20 20 20 20 20 20 20  Errors).        
1210: 23 20 75 6e 74 65 73 74 65 64 20 72 75 6c 65 73  # untested rules
1220: 0a 20 20 20 20 20 20 20 20 61 55 6e 74 65 73 74  .        aUntest
1230: 65 64 52 75 6c 65 73 20 3d 20 73 65 74 28 29 0a  edRules = set().
1240: 20 20 20 20 20 20 20 20 66 6f 72 20 5f 2c 20 73          for _, s
1250: 4f 70 74 2c 20 73 4c 69 6e 65 49 64 2c 20 73 52  Opt, sLineId, sR
1260: 75 6c 65 49 64 20 69 6e 20 67 63 5f 65 6e 67 69  uleId in gc_engi
1270: 6e 65 2e 6c 69 73 74 52 75 6c 65 73 28 29 3a 0a  ne.listRules():.
1280: 20 20 20 20 20 20 20 20 20 20 20 20 73 52 75 6c              sRul
1290: 65 49 64 20 3d 20 73 52 75 6c 65 49 64 2e 72 73  eId = sRuleId.rs
12a0: 74 72 69 70 28 22 30 31 32 33 34 35 36 37 38 39  trip("0123456789
12b0: 22 29 0a 20 20 20 20 20 20 20 20 20 20 20 20 69  ").            i
12c0: 66 20 73 4f 70 74 20 21 3d 20 22 40 40 40 40 22  f sOpt != "@@@@"
12d0: 20 61 6e 64 20 73 52 75 6c 65 49 64 20 6e 6f 74   and sRuleId not
12e0: 20 69 6e 20 73 65 6c 66 2e 5f 61 54 65 73 74 65   in self._aTeste
12f0: 64 52 75 6c 65 73 20 61 6e 64 20 6e 6f 74 20 72  dRules and not r
1300: 65 2e 73 65 61 72 63 68 28 22 5e 5b 30 2d 39 5d  e.search("^[0-9]
1310: 2b 5b 73 70 5d 24 7c 5e 5b 70 64 5d 5f 22 2c 20  +[sp]$|^[pd]_", 
1320: 73 52 75 6c 65 49 64 29 3a 0a 20 20 20 20 20 20  sRuleId):.      
1330: 20 20 20 20 20 20 20 20 20 20 61 55 6e 74 65 73            aUntes
1340: 74 65 64 52 75 6c 65 73 2e 61 64 64 28 66 22 7b  tedRules.add(f"{
1350: 73 4c 69 6e 65 49 64 7d 2f 7b 73 52 75 6c 65 49  sLineId}/{sRuleI
1360: 64 7d 22 29 0a 20 20 20 20 20 20 20 20 69 66 20  d}").        if 
1370: 61 55 6e 74 65 73 74 65 64 52 75 6c 65 73 3a 0a  aUntestedRules:.
1380: 20 20 20 20 20 20 20 20 20 20 20 20 70 72 69 6e              prin
1390: 74 28 29 0a 20 20 20 20 20 20 20 20 20 20 20 20  t().            
13a0: 66 6f 72 20 73 52 75 6c 65 20 69 6e 20 73 6f 72  for sRule in sor
13b0: 74 65 64 28 61 55 6e 74 65 73 74 65 64 52 75 6c  ted(aUntestedRul
13c0: 65 73 29 3a 0a 20 20 20 20 20 20 20 20 20 20 20  es):.           
13d0: 20 20 20 20 20 65 63 68 6f 28 73 52 75 6c 65 29       echo(sRule)
13e0: 0a 20 20 20 20 20 20 20 20 20 20 20 20 65 63 68  .            ech
13f0: 6f 28 22 20 20 5b 7b 7d 20 75 6e 74 65 73 74 65  o("  [{} unteste
1400: 64 20 72 75 6c 65 73 5d 22 2e 66 6f 72 6d 61 74  d rules]".format
1410: 28 6c 65 6e 28 61 55 6e 74 65 73 74 65 64 52 75  (len(aUntestedRu
1420: 6c 65 73 29 29 29 0a 0a 20 20 20 20 64 65 66 20  les)))..    def 
1430: 5f 73 70 6c 69 74 54 65 73 74 4c 69 6e 65 20 28  _splitTestLine (
1440: 73 65 6c 66 2c 20 73 4c 69 6e 65 29 3a 0a 20 20  self, sLine):.  
1450: 20 20 20 20 20 20 73 54 65 78 74 2c 20 73 53 75        sText, sSu
1460: 67 67 20 3d 20 73 4c 69 6e 65 2e 73 70 6c 69 74  gg = sLine.split
1470: 28 22 2d 3e 3e 22 29 0a 20 20 20 20 20 20 20 20  ("->>").        
1480: 73 53 75 67 67 20 3d 20 73 53 75 67 67 2e 73 74  sSugg = sSugg.st
1490: 72 69 70 28 29 0a 20 20 20 20 20 20 20 20 69 66  rip().        if
14a0: 20 73 53 75 67 67 2e 73 74 61 72 74 73 77 69 74   sSugg.startswit
14b0: 68 28 27 22 27 29 20 61 6e 64 20 73 53 75 67 67  h('"') and sSugg
14c0: 2e 65 6e 64 73 77 69 74 68 28 27 22 27 29 3a 0a  .endswith('"'):.
14d0: 20 20 20 20 20 20 20 20 20 20 20 20 73 53 75 67              sSug
14e0: 67 20 3d 20 73 53 75 67 67 5b 31 3a 2d 31 5d 0a  g = sSugg[1:-1].
14f0: 20 20 20 20 20 20 20 20 72 65 74 75 72 6e 20 28          return (
1500: 73 54 65 78 74 2e 73 74 72 69 70 28 29 2c 20 73  sText.strip(), s
1510: 53 75 67 67 29 0a 0a 20 20 20 20 64 65 66 20 5f  Sugg)..    def _
1520: 67 65 74 46 6f 75 6e 64 45 72 72 6f 72 73 20 28  getFoundErrors (
1530: 73 65 6c 66 2c 20 73 4c 69 6e 65 2c 20 73 4f 70  self, sLine, sOp
1540: 74 69 6f 6e 29 3a 0a 20 20 20 20 20 20 20 20 69  tion):.        i
1550: 66 20 73 4f 70 74 69 6f 6e 3a 0a 20 20 20 20 20  f sOption:.     
1560: 20 20 20 20 20 20 20 67 63 5f 65 6e 67 69 6e 65         gc_engine
1570: 2e 73 65 74 4f 70 74 69 6f 6e 28 73 4f 70 74 69  .setOption(sOpti
1580: 6f 6e 2c 20 54 72 75 65 29 0a 20 20 20 20 20 20  on, True).      
1590: 20 20 20 20 20 20 61 45 72 72 73 20 3d 20 67 63        aErrs = gc
15a0: 5f 65 6e 67 69 6e 65 2e 70 61 72 73 65 28 73 4c  _engine.parse(sL
15b0: 69 6e 65 29 0a 20 20 20 20 20 20 20 20 20 20 20  ine).           
15c0: 20 67 63 5f 65 6e 67 69 6e 65 2e 73 65 74 4f 70   gc_engine.setOp
15d0: 74 69 6f 6e 28 73 4f 70 74 69 6f 6e 2c 20 46 61  tion(sOption, Fa
15e0: 6c 73 65 29 0a 20 20 20 20 20 20 20 20 65 6c 73  lse).        els
15f0: 65 3a 0a 20 20 20 20 20 20 20 20 20 20 20 20 61  e:.            a
1600: 45 72 72 73 20 3d 20 67 63 5f 65 6e 67 69 6e 65  Errs = gc_engine
1610: 2e 70 61 72 73 65 28 73 4c 69 6e 65 29 0a 20 20  .parse(sLine).  
1620: 20 20 20 20 20 20 73 52 65 73 20 3d 20 22 20 22        sRes = " "
1630: 20 2a 20 6c 65 6e 28 73 4c 69 6e 65 29 0a 20 20   * len(sLine).  
1640: 20 20 20 20 20 20 73 4c 69 73 74 45 72 72 20 3d        sListErr =
1650: 20 22 22 0a 20 20 20 20 20 20 20 20 6c 41 6c 6c   "".        lAll
1660: 53 75 67 67 20 3d 20 5b 5d 0a 20 20 20 20 20 20  Sugg = [].      
1670: 20 20 66 6f 72 20 64 45 72 72 20 69 6e 20 73 6f    for dErr in so
1680: 72 74 65 64 28 61 45 72 72 73 2c 20 6b 65 79 3d  rted(aErrs, key=
1690: 6c 61 6d 62 64 61 20 64 3a 20 64 5b 22 6e 53 74  lambda d: d["nSt
16a0: 61 72 74 22 5d 29 3a 0a 20 20 20 20 20 20 20 20  art"]):.        
16b0: 20 20 20 20 73 52 65 73 20 3d 20 73 52 65 73 5b      sRes = sRes[
16c0: 3a 64 45 72 72 5b 22 6e 53 74 61 72 74 22 5d 5d  :dErr["nStart"]]
16d0: 20 2b 20 22 7e 22 20 2a 20 28 64 45 72 72 5b 22   + "~" * (dErr["
16e0: 6e 45 6e 64 22 5d 20 2d 20 64 45 72 72 5b 22 6e  nEnd"] - dErr["n
16f0: 53 74 61 72 74 22 5d 29 20 2b 20 73 52 65 73 5b  Start"]) + sRes[
1700: 64 45 72 72 5b 22 6e 45 6e 64 22 5d 3a 5d 0a 20  dErr["nEnd"]:]. 
1710: 20 20 20 20 20 20 20 20 20 20 20 73 4c 69 73 74             sList
1720: 45 72 72 20 2b 3d 20 22 20 20 20 20 2a 20 7b 73  Err += "    * {s
1730: 4c 69 6e 65 49 64 7d 20 2f 20 7b 73 52 75 6c 65  LineId} / {sRule
1740: 49 64 7d 20 20 61 74 20 20 7b 6e 53 74 61 72 74  Id}  at  {nStart
1750: 7d 3a 7b 6e 45 6e 64 7d 5c 6e 22 2e 66 6f 72 6d  }:{nEnd}\n".form
1760: 61 74 28 2a 2a 64 45 72 72 29 0a 20 20 20 20 20  at(**dErr).     
1770: 20 20 20 20 20 20 20 6c 41 6c 6c 53 75 67 67 2e         lAllSugg.
1780: 61 70 70 65 6e 64 28 22 7c 22 2e 6a 6f 69 6e 28  append("|".join(
1790: 64 45 72 72 5b 22 61 53 75 67 67 65 73 74 69 6f  dErr["aSuggestio
17a0: 6e 73 22 5d 29 29 0a 20 20 20 20 20 20 20 20 20  ns"])).         
17b0: 20 20 20 73 65 6c 66 2e 5f 61 54 65 73 74 65 64     self._aTested
17c0: 52 75 6c 65 73 2e 61 64 64 28 64 45 72 72 5b 22  Rules.add(dErr["
17d0: 73 52 75 6c 65 49 64 22 5d 2e 72 73 74 72 69 70  sRuleId"].rstrip
17e0: 28 22 30 31 32 33 34 35 36 37 38 39 22 29 29 0a  ("0123456789")).
17f0: 20 20 20 20 20 20 20 20 20 20 20 20 23 20 74 65              # te
1800: 73 74 20 6d 65 73 73 61 67 65 73 0a 20 20 20 20  st messages.    
1810: 20 20 20 20 20 20 20 20 61 47 72 61 6d 45 72 72          aGramErr
1820: 73 20 3d 20 67 63 5f 65 6e 67 69 6e 65 2e 70 61  s = gc_engine.pa
1830: 72 73 65 28 70 75 72 67 65 4d 65 73 73 61 67 65  rse(purgeMessage
1840: 28 64 45 72 72 5b 22 73 4d 65 73 73 61 67 65 22  (dErr["sMessage"
1850: 5d 29 29 0a 20 20 20 20 20 20 20 20 20 20 20 20  ])).            
1860: 61 47 72 61 6d 45 72 72 73 20 3d 20 5b 20 64 4d  aGramErrs = [ dM
1870: 73 67 45 72 72 20 20 66 6f 72 20 64 4d 73 67 45  sgErr  for dMsgE
1880: 72 72 20 69 6e 20 73 6f 72 74 65 64 28 61 47 72  rr in sorted(aGr
1890: 61 6d 45 72 72 73 2c 20 6b 65 79 3d 6c 61 6d 62  amErrs, key=lamb
18a0: 64 61 20 64 3a 20 64 5b 22 6e 53 74 61 72 74 22  da d: d["nStart"
18b0: 5d 29 20 20 69 66 20 73 65 6c 66 2e 5f 7a 52 75  ])  if self._zRu
18c0: 6c 65 45 6e 64 2e 73 75 62 28 22 22 2c 20 64 4d  leEnd.sub("", dM
18d0: 73 67 45 72 72 5b 22 73 52 75 6c 65 49 64 22 5d  sgErr["sRuleId"]
18e0: 29 20 21 3d 20 73 65 6c 66 2e 5f 7a 52 75 6c 65  ) != self._zRule
18f0: 45 6e 64 2e 73 75 62 28 22 22 2c 20 64 45 72 72  End.sub("", dErr
1900: 5b 22 73 52 75 6c 65 49 64 22 5d 29 20 5d 0a 20  ["sRuleId"]) ]. 
1910: 20 20 20 20 20 20 20 20 20 20 20 61 53 70 65 6c             aSpel
1920: 6c 45 72 72 73 20 3d 20 73 65 6c 66 2e 5f 6f 53  lErrs = self._oS
1930: 70 65 6c 6c 43 68 65 63 6b 65 72 2e 70 61 72 73  pellChecker.pars
1940: 65 50 61 72 61 67 72 61 70 68 28 72 65 2e 73 75  eParagraph(re.su
1950: 62 28 22 e2 80 b9 5b 5e e2 80 ba 5d 2b e2 80 ba  b("...[^...]+...
1960: 22 2c 20 6c 61 6d 62 64 61 20 6d 3a 20 22 20 22  ", lambda m: " "
1970: 20 2a 20 6c 65 6e 28 6d 2e 67 72 6f 75 70 28 30   * len(m.group(0
1980: 29 29 2c 20 64 45 72 72 5b 22 73 4d 65 73 73 61  )), dErr["sMessa
1990: 67 65 22 5d 29 29 0a 20 20 20 20 20 20 20 20 20  ge"])).         
19a0: 20 20 20 69 66 20 61 47 72 61 6d 45 72 72 73 20     if aGramErrs 
19b0: 6f 72 20 61 53 70 65 6c 6c 45 72 72 73 20 6f 72  or aSpellErrs or
19c0: 20 22 3c 73 74 61 72 74 3e 22 20 69 6e 20 64 45   "<start>" in dE
19d0: 72 72 5b 22 73 4d 65 73 73 61 67 65 22 5d 20 6f  rr["sMessage"] o
19e0: 72 20 22 3c 65 6e 64 3e 22 20 69 6e 20 64 45 72  r "<end>" in dEr
19f0: 72 5b 22 73 4d 65 73 73 61 67 65 22 5d 3a 0a 20  r["sMessage"]:. 
1a00: 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 70                 p
1a10: 72 69 6e 74 28 22 5c 6e 23 20 45 72 72 6f 72 20  rint("\n# Error 
1a20: 69 6e 3a 20 3c 22 20 2b 20 64 45 72 72 5b 22 73  in: <" + dErr["s
1a30: 4d 65 73 73 61 67 65 22 5d 20 2b 20 22 3e 5c 6e  Message"] + ">\n
1a40: 20 20 20 20 22 20 2b 20 64 45 72 72 5b 22 73 4c      " + dErr["sL
1a50: 69 6e 65 49 64 22 5d 20 2b 20 22 20 2f 20 22 20  ineId"] + " / " 
1a60: 2b 20 64 45 72 72 5b 22 73 52 75 6c 65 49 64 22  + dErr["sRuleId"
1a70: 5d 29 0a 20 20 20 20 20 20 20 20 20 20 20 20 20  ]).             
1a80: 20 20 20 66 6f 72 20 64 4d 73 67 45 72 72 20 69     for dMsgErr i
1a90: 6e 20 61 47 72 61 6d 45 72 72 73 3a 0a 20 20 20  n aGramErrs:.   
1aa0: 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20                  
1ab0: 20 70 72 69 6e 74 28 22 20 20 20 20 20 20 20 20   print("        
1ac0: 65 72 72 6f 72 3a 20 7b 73 4c 69 6e 65 49 64 7d  error: {sLineId}
1ad0: 20 2f 20 7b 73 52 75 6c 65 49 64 7d 20 20 61 74   / {sRuleId}  at
1ae0: 20 20 7b 6e 53 74 61 72 74 7d 3a 7b 6e 45 6e 64    {nStart}:{nEnd
1af0: 7d 22 2e 66 6f 72 6d 61 74 28 2a 2a 64 4d 73 67  }".format(**dMsg
1b00: 45 72 72 29 29 0a 20 20 20 20 20 20 20 20 20 20  Err)).          
1b10: 20 20 20 20 20 20 66 6f 72 20 64 4d 73 67 45 72        for dMsgEr
1b20: 72 20 69 6e 20 61 53 70 65 6c 6c 45 72 72 73 3a  r in aSpellErrs:
1b30: 0a 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20  .               
1b40: 20 20 20 20 20 70 72 69 6e 74 28 22 20 20 20 20       print("    
1b50: 20 20 20 20 73 70 65 6c 6c 69 6e 67 20 6d 69 73      spelling mis
1b60: 74 61 6b 65 3a 20 3c 7b 73 56 61 6c 75 65 7d 3e  take: <{sValue}>
1b70: 20 20 61 74 20 20 7b 6e 53 74 61 72 74 7d 3a 7b    at  {nStart}:{
1b80: 6e 45 6e 64 7d 22 2e 66 6f 72 6d 61 74 28 2a 2a  nEnd}".format(**
1b90: 64 4d 73 67 45 72 72 29 29 0a 20 20 20 20 20 20  dMsgErr)).      
1ba0: 20 20 72 65 74 75 72 6e 20 73 52 65 73 2c 20 73    return sRes, s
1bb0: 4c 69 73 74 45 72 72 2c 20 22 7c 7c 7c 22 2e 6a  ListErr, "|||".j
1bc0: 6f 69 6e 28 6c 41 6c 6c 53 75 67 67 29 0a 0a 20  oin(lAllSugg).. 
1bd0: 20 20 20 64 65 66 20 5f 67 65 74 45 78 70 65 63     def _getExpec
1be0: 74 65 64 45 72 72 6f 72 73 20 28 73 65 6c 66 2c  tedErrors (self,
1bf0: 20 73 4c 69 6e 65 29 3a 0a 20 20 20 20 20 20 20   sLine):.       
1c00: 20 73 52 65 73 20 3d 20 22 20 22 20 2a 20 6c 65   sRes = " " * le
1c10: 6e 28 73 4c 69 6e 65 29 0a 20 20 20 20 20 20 20  n(sLine).       
1c20: 20 66 6f 72 20 69 2c 20 6d 20 69 6e 20 65 6e 75   for i, m in enu
1c30: 6d 65 72 61 74 65 28 73 65 6c 66 2e 5f 7a 45 72  merate(self._zEr
1c40: 72 6f 72 2e 66 69 6e 64 69 74 65 72 28 73 4c 69  ror.finditer(sLi
1c50: 6e 65 29 29 3a 0a 20 20 20 20 20 20 20 20 20 20  ne)):.          
1c60: 20 20 6e 53 74 61 72 74 20 3d 20 6d 2e 73 74 61    nStart = m.sta
1c70: 72 74 28 29 20 2d 20 28 34 20 2a 20 69 29 0a 20  rt() - (4 * i). 
1c80: 20 20 20 20 20 20 20 20 20 20 20 6e 45 6e 64 20             nEnd 
1c90: 3d 20 6d 2e 65 6e 64 28 29 20 2d 20 28 34 20 2a  = m.end() - (4 *
1ca0: 20 28 69 2b 31 29 29 0a 20 20 20 20 20 20 20 20   (i+1)).        
1cb0: 20 20 20 20 73 52 65 73 20 3d 20 73 52 65 73 5b      sRes = sRes[
1cc0: 3a 6e 53 74 61 72 74 5d 20 2b 20 22 7e 22 20 2a  :nStart] + "~" *
1cd0: 20 28 6e 45 6e 64 20 2d 20 6e 53 74 61 72 74 29   (nEnd - nStart)
1ce0: 20 2b 20 73 52 65 73 5b 6e 45 6e 64 3a 2d 34 5d   + sRes[nEnd:-4]
1cf0: 0a 20 20 20 20 20 20 20 20 72 65 74 75 72 6e 20  .        return 
1d00: 73 52 65 73 0a 0a 20 20 20 20 64 65 66 20 5f 63  sRes..    def _c
1d10: 68 65 63 6b 53 75 67 67 65 73 74 69 6f 6e 73 20  heckSuggestions 
1d20: 28 73 65 6c 66 2c 20 73 45 78 63 65 70 74 65 64  (self, sExcepted
1d30: 53 75 67 67 73 2c 20 73 46 6f 75 6e 64 53 75 67  Suggs, sFoundSug
1d40: 67 73 29 3a 0a 20 20 20 20 20 20 20 20 6c 41 6c  gs):.        lAl
1d50: 6c 45 78 70 65 63 74 65 64 53 75 67 67 73 20 3d  lExpectedSuggs =
1d60: 20 73 45 78 63 65 70 74 65 64 53 75 67 67 73 2e   sExceptedSuggs.
1d70: 73 70 6c 69 74 28 22 7c 7c 7c 22 29 0a 20 20 20  split("|||").   
1d80: 20 20 20 20 20 6c 41 6c 6c 46 6f 75 6e 64 53 75       lAllFoundSu
1d90: 67 67 73 20 3d 20 73 46 6f 75 6e 64 53 75 67 67  ggs = sFoundSugg
1da0: 73 2e 73 70 6c 69 74 28 22 7c 7c 7c 22 29 0a 20  s.split("|||"). 
1db0: 20 20 20 20 20 20 20 69 66 20 6c 65 6e 28 6c 41         if len(lA
1dc0: 6c 6c 45 78 70 65 63 74 65 64 53 75 67 67 73 29  llExpectedSuggs)
1dd0: 20 21 3d 20 6c 65 6e 28 6c 41 6c 6c 46 6f 75 6e   != len(lAllFoun
1de0: 64 53 75 67 67 73 29 3a 0a 20 20 20 20 20 20 20  dSuggs):.       
1df0: 20 20 20 20 20 72 65 74 75 72 6e 20 46 61 6c 73       return Fals
1e00: 65 0a 20 20 20 20 20 20 20 20 66 6f 72 20 73 45  e.        for sE
1e10: 78 63 65 70 74 65 64 53 75 67 67 73 2c 20 73 46  xceptedSuggs, sF
1e20: 6f 75 6e 64 53 75 67 67 73 20 69 6e 20 7a 69 70  oundSuggs in zip
1e30: 28 6c 41 6c 6c 45 78 70 65 63 74 65 64 53 75 67  (lAllExpectedSug
1e40: 67 73 2c 20 6c 41 6c 6c 46 6f 75 6e 64 53 75 67  gs, lAllFoundSug
1e50: 67 73 29 3a 0a 20 20 20 20 20 20 20 20 20 20 20  gs):.           
1e60: 20 69 66 20 73 65 74 28 73 45 78 63 65 70 74 65   if set(sExcepte
1e70: 64 53 75 67 67 73 2e 73 70 6c 69 74 28 22 7c 22  dSuggs.split("|"
1e80: 29 29 20 21 3d 20 73 65 74 28 73 46 6f 75 6e 64  )) != set(sFound
1e90: 53 75 67 67 73 2e 73 70 6c 69 74 28 22 7c 22 29  Suggs.split("|")
1ea0: 29 3a 0a 20 20 20 20 20 20 20 20 20 20 20 20 20  ):.             
1eb0: 20 20 20 72 65 74 75 72 6e 20 46 61 6c 73 65 0a     return False.
1ec0: 20 20 20 20 20 20 20 20 72 65 74 75 72 6e 20 54          return T
1ed0: 72 75 65 0a 0a 0a 64 65 66 20 70 75 72 67 65 4d  rue...def purgeM
1ee0: 65 73 73 61 67 65 20 28 73 4d 65 73 73 61 67 65  essage (sMessage
1ef0: 29 3a 0a 20 20 20 20 66 6f 72 20 73 54 6f 52 65  ):.    for sToRe
1f00: 70 6c 61 63 65 2c 20 73 52 65 70 6c 61 63 65 6d  place, sReplacem
1f10: 65 6e 74 20 69 6e 20 5b 0a 20 20 20 20 20 20 20  ent in [.       
1f20: 20 28 22 6c e2 80 99 20 22 2c 20 22 6c e2 80 99   ("l... ", "l...
1f30: 22 29 2c 20 28 22 64 e2 80 99 20 22 2c 20 22 64  "), ("d... ", "d
1f40: e2 80 99 22 29 2c 20 28 22 6e e2 80 99 20 22 2c  ..."), ("n... ",
1f50: 20 22 6e e2 80 99 22 29 2c 20 28 22 6a e2 80 99   "n..."), ("j...
1f60: 20 22 2c 20 22 6a e2 80 99 22 29 2c 20 28 22 6d   ", "j..."), ("m
1f70: e2 80 99 20 22 2c 20 22 6d e2 80 99 22 29 2c 20  ... ", "m..."), 
1f80: 28 22 74 e2 80 99 20 22 2c 20 22 74 e2 80 99 22  ("t... ", "t..."
1f90: 29 2c 20 28 22 73 e2 80 99 20 22 2c 20 22 73 e2  ), ("s... ", "s.
1fa0: 80 99 22 29 2c 20 28 22 71 75 e2 80 99 20 22 2c  .."), ("qu... ",
1fb0: 20 22 71 75 e2 80 99 22 29 2c 0a 20 20 20 20 20   "qu..."),.     
1fc0: 20 20 20 28 22 4c e2 80 99 20 22 2c 20 22 4c e2     ("L... ", "L.
1fd0: 80 99 22 29 2c 20 28 22 44 e2 80 99 20 22 2c 20  .."), ("D... ", 
1fe0: 22 44 e2 80 99 22 29 2c 20 28 22 4e e2 80 99 20  "D..."), ("N... 
1ff0: 22 2c 20 22 4e e2 80 99 22 29 2c 20 28 22 4a e2  ", "N..."), ("J.
2000: 80 99 20 22 2c 20 22 4a e2 80 99 22 29 2c 20 28  .. ", "J..."), (
2010: 22 4d e2 80 99 20 22 2c 20 22 4d e2 80 99 22 29  "M... ", "M...")
2020: 2c 20 28 22 54 e2 80 99 20 22 2c 20 22 54 e2 80  , ("T... ", "T..
2030: 99 22 29 2c 20 28 22 53 e2 80 99 20 22 2c 20 22  ."), ("S... ", "
2040: 53 e2 80 99 22 29 2c 20 28 22 51 55 e2 80 99 20  S..."), ("QU... 
2050: 22 2c 20 22 51 55 e2 80 99 22 29 0a 20 20 20 20  ", "QU...").    
2060: 5d 3a 0a 20 20 20 20 20 20 20 20 73 4d 65 73 73  ]:.        sMess
2070: 61 67 65 20 3d 20 73 4d 65 73 73 61 67 65 2e 72  age = sMessage.r
2080: 65 70 6c 61 63 65 28 73 54 6f 52 65 70 6c 61 63  eplace(sToReplac
2090: 65 2c 20 73 52 65 70 6c 61 63 65 6d 65 6e 74 29  e, sReplacement)
20a0: 0a 20 20 20 20 72 65 74 75 72 6e 20 73 4d 65 73  .    return sMes
20b0: 73 61 67 65 0a 0a 0a 64 65 66 20 6d 61 69 6e 28  sage...def main(
20c0: 29 3a 0a 20 20 20 20 22 73 74 61 72 74 20 66 75  ):.    "start fu
20d0: 6e 63 74 69 6f 6e 22 0a 20 20 20 20 75 6e 69 74  nction".    unit
20e0: 74 65 73 74 2e 6d 61 69 6e 28 29 0a 0a 0a 69 66  test.main()...if
20f0: 20 5f 5f 6e 61 6d 65 5f 5f 20 3d 3d 20 27 5f 5f   __name__ == '__
2100: 6d 61 69 6e 5f 5f 27 3a 0a 20 20 20 20 6d 61 69  main__':.    mai
2110: 6e 28 29 0a                                      n().