Grammalecte  Hex Artifact Content

Artifact b3d63790199dd932f19e303ca618c4b3a92ac45e614817ccbadf0a5217a66a96:


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 61 54 65 73 74 65 64 52 75 6c 65 73 20 3d 20  _aTestedRules = 
06f0: 73 65 74 28 29 0a 0a 20 20 20 20 64 65 66 20 74  set()..    def t
0700: 65 73 74 5f 70 61 72 73 65 20 28 73 65 6c 66 29  est_parse (self)
0710: 3a 0a 20 20 20 20 20 20 20 20 7a 4f 70 74 69 6f  :.        zOptio
0720: 6e 20 3d 20 72 65 2e 63 6f 6d 70 69 6c 65 28 22  n = re.compile("
0730: 5e 5f 5f 28 5b 61 2d 7a 41 2d 5a 30 2d 39 5d 2b  ^__([a-zA-Z0-9]+
0740: 29 5f 5f 20 22 29 0a 20 20 20 20 20 20 20 20 73  )__ ").        s
0750: 70 48 65 72 65 2c 20 5f 20 3d 20 6f 73 2e 70 61  pHere, _ = os.pa
0760: 74 68 2e 73 70 6c 69 74 28 5f 5f 66 69 6c 65 5f  th.split(__file_
0770: 5f 29 0a 20 20 20 20 20 20 20 20 73 70 66 50 61  _).        spfPa
0780: 72 73 69 6e 67 54 65 73 74 20 3d 20 6f 73 2e 70  rsingTest = os.p
0790: 61 74 68 2e 6a 6f 69 6e 28 73 70 48 65 72 65 2c  ath.join(spHere,
07a0: 20 22 67 63 5f 74 65 73 74 2e 74 78 74 22 29 0a   "gc_test.txt").
07b0: 20 20 20 20 20 20 20 20 69 66 20 6e 6f 74 20 6f          if not o
07c0: 73 2e 70 61 74 68 2e 65 78 69 73 74 73 28 73 70  s.path.exists(sp
07d0: 66 50 61 72 73 69 6e 67 54 65 73 74 29 3a 0a 20  fParsingTest):. 
07e0: 20 20 20 20 20 20 20 20 20 20 20 70 72 69 6e 74             print
07f0: 28 66 22 4e 6f 20 66 69 6c 65 20 3c 67 63 5f 74  (f"No file <gc_t
0800: 65 73 74 2e 74 78 74 3e 20 69 6e 20 3c 7b 73 70  est.txt> in <{sp
0810: 48 65 72 65 7d 3e 22 29 0a 20 20 20 20 20 20 20  Here}>").       
0820: 20 20 20 20 20 72 65 74 75 72 6e 0a 20 20 20 20       return.    
0830: 20 20 20 20 77 69 74 68 20 6f 70 65 6e 28 73 70      with open(sp
0840: 66 50 61 72 73 69 6e 67 54 65 73 74 2c 20 22 72  fParsingTest, "r
0850: 22 2c 20 65 6e 63 6f 64 69 6e 67 3d 22 75 74 66  ", encoding="utf
0860: 2d 38 22 29 20 61 73 20 68 53 72 63 3a 0a 20 20  -8") as hSrc:.  
0870: 20 20 20 20 20 20 20 20 20 20 6e 45 72 72 6f 72            nError
0880: 20 3d 20 30 0a 20 20 20 20 20 20 20 20 20 20 20   = 0.           
0890: 20 66 6f 72 20 73 4c 69 6e 65 20 69 6e 20 28 20   for sLine in ( 
08a0: 73 20 66 6f 72 20 73 20 69 6e 20 68 53 72 63 20  s for s in hSrc 
08b0: 69 66 20 6e 6f 74 20 73 2e 73 74 61 72 74 73 77  if not s.startsw
08c0: 69 74 68 28 22 23 22 29 20 61 6e 64 20 73 2e 73  ith("#") and s.s
08d0: 74 72 69 70 28 29 20 29 3a 0a 20 20 20 20 20 20  trip() ):.      
08e0: 20 20 20 20 20 20 20 20 20 20 73 4c 69 6e 65 4e            sLineN
08f0: 75 6d 20 3d 20 73 4c 69 6e 65 5b 3a 31 30 5d 2e  um = sLine[:10].
0900: 73 74 72 69 70 28 29 0a 20 20 20 20 20 20 20 20  strip().        
0910: 20 20 20 20 20 20 20 20 73 4c 69 6e 65 20 3d 20          sLine = 
0920: 73 4c 69 6e 65 5b 31 30 3a 5d 2e 73 74 72 69 70  sLine[10:].strip
0930: 28 29 0a 20 20 20 20 20 20 20 20 20 20 20 20 20  ().             
0940: 20 20 20 73 4f 70 74 69 6f 6e 20 3d 20 4e 6f 6e     sOption = Non
0950: 65 0a 20 20 20 20 20 20 20 20 20 20 20 20 20 20  e.              
0960: 20 20 6d 20 3d 20 7a 4f 70 74 69 6f 6e 2e 73 65    m = zOption.se
0970: 61 72 63 68 28 73 4c 69 6e 65 29 0a 20 20 20 20  arch(sLine).    
0980: 20 20 20 20 20 20 20 20 20 20 20 20 69 66 20 6d              if m
0990: 3a 0a 20 20 20 20 20 20 20 20 20 20 20 20 20 20  :.              
09a0: 20 20 20 20 20 20 73 4c 69 6e 65 20 3d 20 73 4c        sLine = sL
09b0: 69 6e 65 5b 6d 2e 65 6e 64 28 29 3a 5d 0a 20 20  ine[m.end():].  
09c0: 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20                  
09d0: 20 20 73 4f 70 74 69 6f 6e 20 3d 20 6d 2e 67 72    sOption = m.gr
09e0: 6f 75 70 28 31 29 0a 20 20 20 20 20 20 20 20 20  oup(1).         
09f0: 20 20 20 20 20 20 20 69 66 20 22 2d 3e 3e 22 20         if "->>" 
0a00: 69 6e 20 73 4c 69 6e 65 3a 0a 20 20 20 20 20 20  in sLine:.      
0a10: 20 20 20 20 20 20 20 20 20 20 20 20 20 20 73 45                sE
0a20: 72 72 6f 72 54 65 78 74 2c 20 73 45 78 63 65 70  rrorText, sExcep
0a30: 74 65 64 53 75 67 67 73 20 3d 20 73 65 6c 66 2e  tedSuggs = self.
0a40: 5f 73 70 6c 69 74 54 65 73 74 4c 69 6e 65 28 73  _splitTestLine(s
0a50: 4c 69 6e 65 29 0a 20 20 20 20 20 20 20 20 20 20  Line).          
0a60: 20 20 20 20 20 20 65 6c 73 65 3a 0a 20 20 20 20        else:.    
0a70: 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20                  
0a80: 73 45 72 72 6f 72 54 65 78 74 20 3d 20 73 4c 69  sErrorText = sLi
0a90: 6e 65 2e 73 74 72 69 70 28 29 0a 20 20 20 20 20  ne.strip().     
0aa0: 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 73                 s
0ab0: 45 78 63 65 70 74 65 64 53 75 67 67 73 20 3d 20  ExceptedSuggs = 
0ac0: 22 22 0a 20 20 20 20 20 20 20 20 20 20 20 20 20  "".             
0ad0: 20 20 20 73 45 78 70 65 63 74 65 64 45 72 72 6f     sExpectedErro
0ae0: 72 73 20 3d 20 73 65 6c 66 2e 5f 67 65 74 45 78  rs = self._getEx
0af0: 70 65 63 74 65 64 45 72 72 6f 72 73 28 73 45 72  pectedErrors(sEr
0b00: 72 6f 72 54 65 78 74 29 0a 20 20 20 20 20 20 20  rorText).       
0b10: 20 20 20 20 20 20 20 20 20 73 54 65 78 74 54 6f           sTextTo
0b20: 43 68 65 63 6b 20 3d 20 73 45 72 72 6f 72 54 65  Check = sErrorTe
0b30: 78 74 2e 72 65 70 6c 61 63 65 28 22 7d 7d 22 2c  xt.replace("}}",
0b40: 20 22 22 29 2e 72 65 70 6c 61 63 65 28 22 7b 7b   "").replace("{{
0b50: 22 2c 20 22 22 29 0a 20 20 20 20 20 20 20 20 20  ", "").         
0b60: 20 20 20 20 20 20 20 73 46 6f 75 6e 64 45 72 72         sFoundErr
0b70: 6f 72 73 2c 20 73 4c 69 73 74 45 72 72 2c 20 73  ors, sListErr, s
0b80: 46 6f 75 6e 64 53 75 67 67 73 20 3d 20 73 65 6c  FoundSuggs = sel
0b90: 66 2e 5f 67 65 74 46 6f 75 6e 64 45 72 72 6f 72  f._getFoundError
0ba0: 73 28 73 54 65 78 74 54 6f 43 68 65 63 6b 2c 20  s(sTextToCheck, 
0bb0: 73 4f 70 74 69 6f 6e 29 0a 20 20 20 20 20 20 20  sOption).       
0bc0: 20 20 20 20 20 20 20 20 20 23 20 74 65 73 74 73           # tests
0bd0: 0a 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20  .               
0be0: 20 69 66 20 73 45 78 70 65 63 74 65 64 45 72 72   if sExpectedErr
0bf0: 6f 72 73 20 21 3d 20 73 46 6f 75 6e 64 45 72 72  ors != sFoundErr
0c00: 6f 72 73 3a 0a 20 20 20 20 20 20 20 20 20 20 20  ors:.           
0c10: 20 20 20 20 20 20 20 20 20 70 72 69 6e 74 28 22           print("
0c20: 5c 6e 23 20 4c 69 6e 65 20 6e 75 6d 3a 20 22 20  \n# Line num: " 
0c30: 2b 20 73 4c 69 6e 65 4e 75 6d 20 2b 20 5c 0a 20  + sLineNum + \. 
0c40: 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20                  
0c50: 20 20 20 20 20 20 20 20 20 22 5c 6e 3e 20 74 6f           "\n> to
0c60: 20 63 68 65 63 6b 3a 20 22 20 2b 20 5f 66 75 63   check: " + _fuc
0c70: 6b 42 61 63 6b 73 6c 61 73 68 55 54 46 38 28 73  kBackslashUTF8(s
0c80: 54 65 78 74 54 6f 43 68 65 63 6b 29 20 2b 20 5c  TextToCheck) + \
0c90: 0a 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20  .               
0ca0: 20 20 20 20 20 20 20 20 20 20 20 22 5c 6e 20 20             "\n  
0cb0: 65 78 70 65 63 74 65 64 3a 20 22 20 2b 20 73 45  expected: " + sE
0cc0: 78 70 65 63 74 65 64 45 72 72 6f 72 73 20 2b 20  xpectedErrors + 
0cd0: 5c 0a 20 20 20 20 20 20 20 20 20 20 20 20 20 20  \.              
0ce0: 20 20 20 20 20 20 20 20 20 20 20 20 22 5c 6e 20              "\n 
0cf0: 20 66 6f 75 6e 64 3a 20 20 20 20 22 20 2b 20 73   found:    " + s
0d00: 46 6f 75 6e 64 45 72 72 6f 72 73 20 2b 20 5c 0a  FoundErrors + \.
0d10: 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20                  
0d20: 20 20 20 20 20 20 20 20 20 20 22 5c 6e 20 20 65            "\n  e
0d30: 72 72 6f 72 73 3a 20 20 20 5c 6e 22 20 2b 20 73  rrors:   \n" + s
0d40: 4c 69 73 74 45 72 72 29 0a 20 20 20 20 20 20 20  ListErr).       
0d50: 20 20 20 20 20 20 20 20 20 20 20 20 20 6e 45 72               nEr
0d60: 72 6f 72 20 2b 3d 20 31 0a 20 20 20 20 20 20 20  ror += 1.       
0d70: 20 20 20 20 20 20 20 20 20 65 6c 69 66 20 73 45           elif sE
0d80: 78 63 65 70 74 65 64 53 75 67 67 73 3a 0a 20 20  xceptedSuggs:.  
0d90: 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20                  
0da0: 20 20 69 66 20 73 45 78 63 65 70 74 65 64 53 75    if sExceptedSu
0db0: 67 67 73 20 21 3d 20 73 46 6f 75 6e 64 53 75 67  ggs != sFoundSug
0dc0: 67 73 3a 0a 20 20 20 20 20 20 20 20 20 20 20 20  gs:.            
0dd0: 20 20 20 20 20 20 20 20 20 20 20 20 70 72 69 6e              prin
0de0: 74 28 22 5c 6e 23 20 4c 69 6e 65 20 6e 75 6d 3a  t("\n# Line num:
0df0: 20 22 20 2b 20 73 4c 69 6e 65 4e 75 6d 20 2b 20   " + sLineNum + 
0e00: 5c 0a 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 20 20 20 20 20                  
0e20: 22 5c 6e 3e 20 74 6f 20 63 68 65 63 6b 3a 20 22  "\n> to check: "
0e30: 20 2b 20 5f 66 75 63 6b 42 61 63 6b 73 6c 61 73   + _fuckBackslas
0e40: 68 55 54 46 38 28 73 54 65 78 74 54 6f 43 68 65  hUTF8(sTextToChe
0e50: 63 6b 29 20 2b 20 5c 0a 20 20 20 20 20 20 20 20  ck) + \.        
0e60: 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20                  
0e70: 20 20 20 20 20 20 22 5c 6e 20 20 65 78 70 65 63        "\n  expec
0e80: 74 65 64 3a 20 22 20 2b 20 73 45 78 63 65 70 74  ted: " + sExcept
0e90: 65 64 53 75 67 67 73 20 2b 20 5c 0a 20 20 20 20  edSuggs + \.    
0ea0: 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20                  
0eb0: 20 20 20 20 20 20 20 20 20 20 22 5c 6e 20 20 66            "\n  f
0ec0: 6f 75 6e 64 3a 20 20 20 20 22 20 2b 20 73 46 6f  ound:    " + sFo
0ed0: 75 6e 64 53 75 67 67 73 20 2b 20 5c 0a 20 20 20  undSuggs + \.   
0ee0: 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20                  
0ef0: 20 20 20 20 20 20 20 20 20 20 20 22 5c 6e 20 20             "\n  
0f00: 65 72 72 6f 72 73 3a 20 20 20 5c 6e 22 20 2b 20  errors:   \n" + 
0f10: 73 4c 69 73 74 45 72 72 29 0a 20 20 20 20 20 20  sListErr).      
0f20: 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20                  
0f30: 20 20 6e 45 72 72 6f 72 20 2b 3d 20 31 0a 20 20    nError += 1.  
0f40: 20 20 20 20 20 20 20 20 20 20 69 66 20 6e 45 72            if nEr
0f50: 72 6f 72 3a 0a 20 20 20 20 20 20 20 20 20 20 20  ror:.           
0f60: 20 20 20 20 20 70 72 69 6e 74 28 22 55 6e 65 78       print("Unex
0f70: 70 65 63 74 65 64 20 65 72 72 6f 72 73 3a 22 2c  pected errors:",
0f80: 20 6e 45 72 72 6f 72 29 0a 20 20 20 20 20 20 20   nError).       
0f90: 20 23 20 75 6e 74 65 73 74 65 64 20 72 75 6c 65   # untested rule
0fa0: 73 0a 20 20 20 20 20 20 20 20 61 55 6e 74 65 73  s.        aUntes
0fb0: 74 65 64 52 75 6c 65 73 20 3d 20 73 65 74 28 29  tedRules = set()
0fc0: 0a 20 20 20 20 20 20 20 20 66 6f 72 20 5f 2c 20  .        for _, 
0fd0: 73 4f 70 74 2c 20 73 4c 69 6e 65 49 64 2c 20 73  sOpt, sLineId, s
0fe0: 52 75 6c 65 49 64 20 69 6e 20 67 63 5f 65 6e 67  RuleId in gc_eng
0ff0: 69 6e 65 2e 6c 69 73 74 52 75 6c 65 73 28 29 3a  ine.listRules():
1000: 0a 20 20 20 20 20 20 20 20 20 20 20 20 73 52 75  .            sRu
1010: 6c 65 49 64 20 3d 20 73 52 75 6c 65 49 64 2e 72  leId = sRuleId.r
1020: 73 74 72 69 70 28 22 30 31 32 33 34 35 36 37 38  strip("012345678
1030: 39 22 29 0a 20 20 20 20 20 20 20 20 20 20 20 20  9").            
1040: 69 66 20 73 4f 70 74 20 21 3d 20 22 40 40 40 40  if sOpt != "@@@@
1050: 22 20 61 6e 64 20 73 52 75 6c 65 49 64 20 6e 6f  " and sRuleId no
1060: 74 20 69 6e 20 73 65 6c 66 2e 5f 61 54 65 73 74  t in self._aTest
1070: 65 64 52 75 6c 65 73 20 61 6e 64 20 6e 6f 74 20  edRules and not 
1080: 72 65 2e 73 65 61 72 63 68 28 22 5e 5b 30 2d 39  re.search("^[0-9
1090: 5d 2b 5b 73 70 5d 24 7c 5e 5b 70 64 5d 5f 22 2c  ]+[sp]$|^[pd]_",
10a0: 20 73 52 75 6c 65 49 64 29 3a 0a 20 20 20 20 20   sRuleId):.     
10b0: 20 20 20 20 20 20 20 20 20 20 20 61 55 6e 74 65             aUnte
10c0: 73 74 65 64 52 75 6c 65 73 2e 61 64 64 28 66 22  stedRules.add(f"
10d0: 7b 73 4c 69 6e 65 49 64 7d 2f 7b 73 52 75 6c 65  {sLineId}/{sRule
10e0: 49 64 7d 22 29 0a 20 20 20 20 20 20 20 20 69 66  Id}").        if
10f0: 20 61 55 6e 74 65 73 74 65 64 52 75 6c 65 73 3a   aUntestedRules:
1100: 0a 20 20 20 20 20 20 20 20 20 20 20 20 70 72 69  .            pri
1110: 6e 74 28 29 0a 20 20 20 20 20 20 20 20 20 20 20  nt().           
1120: 20 66 6f 72 20 73 52 75 6c 65 20 69 6e 20 61 55   for sRule in aU
1130: 6e 74 65 73 74 65 64 52 75 6c 65 73 3a 0a 20 20  ntestedRules:.  
1140: 20 20 20 20 20 20 20 20 20 20 20 20 20 20 65 63                ec
1150: 68 6f 28 73 52 75 6c 65 29 0a 20 20 20 20 20 20  ho(sRule).      
1160: 20 20 20 20 20 20 65 63 68 6f 28 22 20 20 5b 7b        echo("  [{
1170: 7d 20 75 6e 74 65 73 74 65 64 20 72 75 6c 65 73  } untested rules
1180: 5d 22 2e 66 6f 72 6d 61 74 28 6c 65 6e 28 61 55  ]".format(len(aU
1190: 6e 74 65 73 74 65 64 52 75 6c 65 73 29 29 29 0a  ntestedRules))).
11a0: 0a 20 20 20 20 64 65 66 20 5f 73 70 6c 69 74 54  .    def _splitT
11b0: 65 73 74 4c 69 6e 65 20 28 73 65 6c 66 2c 20 73  estLine (self, s
11c0: 4c 69 6e 65 29 3a 0a 20 20 20 20 20 20 20 20 73  Line):.        s
11d0: 54 65 78 74 2c 20 73 53 75 67 67 20 3d 20 73 4c  Text, sSugg = sL
11e0: 69 6e 65 2e 73 70 6c 69 74 28 22 2d 3e 3e 22 29  ine.split("->>")
11f0: 0a 20 20 20 20 20 20 20 20 73 53 75 67 67 20 3d  .        sSugg =
1200: 20 73 53 75 67 67 2e 73 74 72 69 70 28 29 0a 20   sSugg.strip(). 
1210: 20 20 20 20 20 20 20 69 66 20 73 53 75 67 67 2e         if sSugg.
1220: 73 74 61 72 74 73 77 69 74 68 28 27 22 27 29 20  startswith('"') 
1230: 61 6e 64 20 73 53 75 67 67 2e 65 6e 64 73 77 69  and sSugg.endswi
1240: 74 68 28 27 22 27 29 3a 0a 20 20 20 20 20 20 20  th('"'):.       
1250: 20 20 20 20 20 73 53 75 67 67 20 3d 20 73 53 75       sSugg = sSu
1260: 67 67 5b 31 3a 2d 31 5d 0a 20 20 20 20 20 20 20  gg[1:-1].       
1270: 20 72 65 74 75 72 6e 20 28 73 54 65 78 74 2e 73   return (sText.s
1280: 74 72 69 70 28 29 2c 20 73 53 75 67 67 29 0a 0a  trip(), sSugg)..
1290: 20 20 20 20 64 65 66 20 5f 67 65 74 46 6f 75 6e      def _getFoun
12a0: 64 45 72 72 6f 72 73 20 28 73 65 6c 66 2c 20 73  dErrors (self, s
12b0: 4c 69 6e 65 2c 20 73 4f 70 74 69 6f 6e 29 3a 0a  Line, sOption):.
12c0: 20 20 20 20 20 20 20 20 69 66 20 73 4f 70 74 69          if sOpti
12d0: 6f 6e 3a 0a 20 20 20 20 20 20 20 20 20 20 20 20  on:.            
12e0: 67 63 5f 65 6e 67 69 6e 65 2e 73 65 74 4f 70 74  gc_engine.setOpt
12f0: 69 6f 6e 28 73 4f 70 74 69 6f 6e 2c 20 54 72 75  ion(sOption, Tru
1300: 65 29 0a 20 20 20 20 20 20 20 20 20 20 20 20 61  e).            a
1310: 45 72 72 73 20 3d 20 67 63 5f 65 6e 67 69 6e 65  Errs = gc_engine
1320: 2e 70 61 72 73 65 28 73 4c 69 6e 65 29 0a 20 20  .parse(sLine).  
1330: 20 20 20 20 20 20 20 20 20 20 67 63 5f 65 6e 67            gc_eng
1340: 69 6e 65 2e 73 65 74 4f 70 74 69 6f 6e 28 73 4f  ine.setOption(sO
1350: 70 74 69 6f 6e 2c 20 46 61 6c 73 65 29 0a 20 20  ption, False).  
1360: 20 20 20 20 20 20 65 6c 73 65 3a 0a 20 20 20 20        else:.    
1370: 20 20 20 20 20 20 20 20 61 45 72 72 73 20 3d 20          aErrs = 
1380: 67 63 5f 65 6e 67 69 6e 65 2e 70 61 72 73 65 28  gc_engine.parse(
1390: 73 4c 69 6e 65 29 0a 20 20 20 20 20 20 20 20 73  sLine).        s
13a0: 52 65 73 20 3d 20 22 20 22 20 2a 20 6c 65 6e 28  Res = " " * len(
13b0: 73 4c 69 6e 65 29 0a 20 20 20 20 20 20 20 20 73  sLine).        s
13c0: 4c 69 73 74 45 72 72 20 3d 20 22 22 0a 20 20 20  ListErr = "".   
13d0: 20 20 20 20 20 6c 41 6c 6c 53 75 67 67 20 3d 20       lAllSugg = 
13e0: 5b 5d 0a 20 20 20 20 20 20 20 20 66 6f 72 20 64  [].        for d
13f0: 45 72 72 20 69 6e 20 73 6f 72 74 65 64 28 61 45  Err in sorted(aE
1400: 72 72 73 2c 20 6b 65 79 3d 6c 61 6d 62 64 61 20  rrs, key=lambda 
1410: 64 3a 20 64 5b 22 6e 53 74 61 72 74 22 5d 29 3a  d: d["nStart"]):
1420: 0a 20 20 20 20 20 20 20 20 20 20 20 20 73 52 65  .            sRe
1430: 73 20 3d 20 73 52 65 73 5b 3a 64 45 72 72 5b 22  s = sRes[:dErr["
1440: 6e 53 74 61 72 74 22 5d 5d 20 2b 20 22 7e 22 20  nStart"]] + "~" 
1450: 2a 20 28 64 45 72 72 5b 22 6e 45 6e 64 22 5d 20  * (dErr["nEnd"] 
1460: 2d 20 64 45 72 72 5b 22 6e 53 74 61 72 74 22 5d  - dErr["nStart"]
1470: 29 20 2b 20 73 52 65 73 5b 64 45 72 72 5b 22 6e  ) + sRes[dErr["n
1480: 45 6e 64 22 5d 3a 5d 0a 20 20 20 20 20 20 20 20  End"]:].        
1490: 20 20 20 20 73 4c 69 73 74 45 72 72 20 2b 3d 20      sListErr += 
14a0: 22 20 20 20 20 2a 20 7b 73 4c 69 6e 65 49 64 7d  "    * {sLineId}
14b0: 20 2f 20 7b 73 52 75 6c 65 49 64 7d 20 20 61 74   / {sRuleId}  at
14c0: 20 20 7b 6e 53 74 61 72 74 7d 3a 7b 6e 45 6e 64    {nStart}:{nEnd
14d0: 7d 5c 6e 22 2e 66 6f 72 6d 61 74 28 2a 2a 64 45  }\n".format(**dE
14e0: 72 72 29 0a 20 20 20 20 20 20 20 20 20 20 20 20  rr).            
14f0: 6c 41 6c 6c 53 75 67 67 2e 61 70 70 65 6e 64 28  lAllSugg.append(
1500: 22 7c 22 2e 6a 6f 69 6e 28 64 45 72 72 5b 22 61  "|".join(dErr["a
1510: 53 75 67 67 65 73 74 69 6f 6e 73 22 5d 29 29 0a  Suggestions"])).
1520: 20 20 20 20 20 20 20 20 20 20 20 20 73 65 6c 66              self
1530: 2e 5f 61 54 65 73 74 65 64 52 75 6c 65 73 2e 61  ._aTestedRules.a
1540: 64 64 28 64 45 72 72 5b 22 73 52 75 6c 65 49 64  dd(dErr["sRuleId
1550: 22 5d 2e 72 73 74 72 69 70 28 22 30 31 32 33 34  "].rstrip("01234
1560: 35 36 37 38 39 22 29 29 0a 20 20 20 20 20 20 20  56789")).       
1570: 20 20 20 20 20 23 20 74 65 73 74 20 6d 65 73 73       # test mess
1580: 61 67 65 73 0a 20 20 20 20 20 20 20 20 20 20 20  ages.           
1590: 20 69 66 20 46 61 6c 73 65 3a 0a 20 20 20 20 20   if False:.     
15a0: 20 20 20 20 20 20 20 20 20 20 20 61 4d 73 67 45             aMsgE
15b0: 72 72 73 20 3d 20 67 63 5f 65 6e 67 69 6e 65 2e  rrs = gc_engine.
15c0: 70 61 72 73 65 28 70 75 72 67 65 4d 65 73 73 61  parse(purgeMessa
15d0: 67 65 28 64 45 72 72 5b 22 73 4d 65 73 73 61 67  ge(dErr["sMessag
15e0: 65 22 5d 29 29 0a 20 20 20 20 20 20 20 20 20 20  e"])).          
15f0: 20 20 20 20 20 20 69 66 20 61 4d 73 67 45 72 72        if aMsgErr
1600: 73 20 6f 72 20 22 3c 73 74 61 72 74 3e 22 20 69  s or "<start>" i
1610: 6e 20 64 45 72 72 5b 22 73 4d 65 73 73 61 67 65  n dErr["sMessage
1620: 22 5d 20 6f 72 20 22 3c 65 6e 64 3e 22 20 69 6e  "] or "<end>" in
1630: 20 64 45 72 72 5b 22 73 4d 65 73 73 61 67 65 22   dErr["sMessage"
1640: 5d 3a 0a 20 20 20 20 20 20 20 20 20 20 20 20 20  ]:.             
1650: 20 20 20 20 20 20 20 70 72 69 6e 74 28 22 5c 6e         print("\n
1660: 23 20 49 6e 63 6f 72 72 65 63 74 20 6d 65 73 73  # Incorrect mess
1670: 61 67 65 3a 20 3c 22 20 2b 20 64 45 72 72 5b 22  age: <" + dErr["
1680: 73 4d 65 73 73 61 67 65 22 5d 20 2b 20 22 3e 5c  sMessage"] + ">\
1690: 6e 20 20 20 20 22 20 2b 20 64 45 72 72 5b 22 73  n    " + dErr["s
16a0: 4c 69 6e 65 49 64 22 5d 20 2b 20 22 20 2f 20 22  LineId"] + " / "
16b0: 20 2b 20 64 45 72 72 5b 22 73 52 75 6c 65 49 64   + dErr["sRuleId
16c0: 22 5d 29 0a 20 20 20 20 20 20 20 20 20 20 20 20  "]).            
16d0: 20 20 20 20 20 20 20 20 69 66 20 61 4d 73 67 45          if aMsgE
16e0: 72 72 73 3a 0a 20 20 20 20 20 20 20 20 20 20 20  rrs:.           
16f0: 20 20 20 20 20 20 20 20 20 20 20 20 20 66 6f 72               for
1700: 20 64 4d 73 67 45 72 72 20 69 6e 20 73 6f 72 74   dMsgErr in sort
1710: 65 64 28 61 4d 73 67 45 72 72 73 2c 20 6b 65 79  ed(aMsgErrs, key
1720: 3d 6c 61 6d 62 64 61 20 64 3a 20 64 5b 22 6e 53  =lambda d: d["nS
1730: 74 61 72 74 22 5d 29 3a 0a 20 20 20 20 20 20 20  tart"]):.       
1740: 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20                  
1750: 20 20 20 20 20 70 72 69 6e 74 28 22 20 20 20 20       print("    
1760: 20 20 20 20 65 72 72 6f 72 3a 20 7b 73 4c 69 6e      error: {sLin
1770: 65 49 64 7d 20 2f 20 7b 73 52 75 6c 65 49 64 7d  eId} / {sRuleId}
1780: 20 20 61 74 20 20 7b 6e 53 74 61 72 74 7d 3a 7b    at  {nStart}:{
1790: 6e 45 6e 64 7d 22 2e 66 6f 72 6d 61 74 28 2a 2a  nEnd}".format(**
17a0: 64 4d 73 67 45 72 72 29 29 0a 20 20 20 20 20 20  dMsgErr)).      
17b0: 20 20 72 65 74 75 72 6e 20 73 52 65 73 2c 20 73    return sRes, s
17c0: 4c 69 73 74 45 72 72 2c 20 22 7c 7c 7c 22 2e 6a  ListErr, "|||".j
17d0: 6f 69 6e 28 6c 41 6c 6c 53 75 67 67 29 0a 0a 20  oin(lAllSugg).. 
17e0: 20 20 20 64 65 66 20 5f 67 65 74 45 78 70 65 63     def _getExpec
17f0: 74 65 64 45 72 72 6f 72 73 20 28 73 65 6c 66 2c  tedErrors (self,
1800: 20 73 4c 69 6e 65 29 3a 0a 20 20 20 20 20 20 20   sLine):.       
1810: 20 73 52 65 73 20 3d 20 22 20 22 20 2a 20 6c 65   sRes = " " * le
1820: 6e 28 73 4c 69 6e 65 29 0a 20 20 20 20 20 20 20  n(sLine).       
1830: 20 66 6f 72 20 69 2c 20 6d 20 69 6e 20 65 6e 75   for i, m in enu
1840: 6d 65 72 61 74 65 28 73 65 6c 66 2e 5f 7a 45 72  merate(self._zEr
1850: 72 6f 72 2e 66 69 6e 64 69 74 65 72 28 73 4c 69  ror.finditer(sLi
1860: 6e 65 29 29 3a 0a 20 20 20 20 20 20 20 20 20 20  ne)):.          
1870: 20 20 6e 53 74 61 72 74 20 3d 20 6d 2e 73 74 61    nStart = m.sta
1880: 72 74 28 29 20 2d 20 28 34 20 2a 20 69 29 0a 20  rt() - (4 * i). 
1890: 20 20 20 20 20 20 20 20 20 20 20 6e 45 6e 64 20             nEnd 
18a0: 3d 20 6d 2e 65 6e 64 28 29 20 2d 20 28 34 20 2a  = m.end() - (4 *
18b0: 20 28 69 2b 31 29 29 0a 20 20 20 20 20 20 20 20   (i+1)).        
18c0: 20 20 20 20 73 52 65 73 20 3d 20 73 52 65 73 5b      sRes = sRes[
18d0: 3a 6e 53 74 61 72 74 5d 20 2b 20 22 7e 22 20 2a  :nStart] + "~" *
18e0: 20 28 6e 45 6e 64 20 2d 20 6e 53 74 61 72 74 29   (nEnd - nStart)
18f0: 20 2b 20 73 52 65 73 5b 6e 45 6e 64 3a 2d 34 5d   + sRes[nEnd:-4]
1900: 0a 20 20 20 20 20 20 20 20 72 65 74 75 72 6e 20  .        return 
1910: 73 52 65 73 0a 0a 0a 64 65 66 20 70 75 72 67 65  sRes...def purge
1920: 4d 65 73 73 61 67 65 20 28 73 4d 65 73 73 61 67  Message (sMessag
1930: 65 29 3a 0a 20 20 20 20 73 4d 65 73 73 61 67 65  e):.    sMessage
1940: 20 3d 20 73 4d 65 73 73 61 67 65 2e 72 65 70 6c   = sMessage.repl
1950: 61 63 65 28 22 6c e2 80 99 20 22 2c 20 22 6c e2  ace("l... ", "l.
1960: 80 99 22 29 2e 72 65 70 6c 61 63 65 28 22 64 e2  ..").replace("d.
1970: 80 99 20 22 2c 20 22 64 e2 80 99 22 29 2e 72 65  .. ", "d...").re
1980: 70 6c 61 63 65 28 22 6d e2 80 99 20 22 2c 20 22  place("m... ", "
1990: 6d e2 80 99 22 29 2e 72 65 70 6c 61 63 65 28 22  m...").replace("
19a0: 74 e2 80 99 20 22 2c 20 22 74 e2 80 99 22 29 2e  t... ", "t...").
19b0: 72 65 70 6c 61 63 65 28 22 73 e2 80 99 20 22 2c  replace("s... ",
19c0: 20 22 73 e2 80 99 22 29 2e 72 65 70 6c 61 63 65   "s...").replace
19d0: 28 22 71 75 e2 80 99 20 22 2c 20 22 71 75 e2 80  ("qu... ", "qu..
19e0: 99 22 29 0a 20 20 20 20 73 4d 65 73 73 61 67 65  .").    sMessage
19f0: 20 3d 20 73 4d 65 73 73 61 67 65 2e 72 65 70 6c   = sMessage.repl
1a00: 61 63 65 28 22 4c e2 80 99 20 22 2c 20 22 4c e2  ace("L... ", "L.
1a10: 80 99 22 29 2e 72 65 70 6c 61 63 65 28 22 44 e2  ..").replace("D.
1a20: 80 99 20 22 2c 20 22 44 e2 80 99 22 29 2e 72 65  .. ", "D...").re
1a30: 70 6c 61 63 65 28 22 4d e2 80 99 20 22 2c 20 22  place("M... ", "
1a40: 4d e2 80 99 22 29 2e 72 65 70 6c 61 63 65 28 22  M...").replace("
1a50: 54 e2 80 99 20 22 2c 20 22 54 e2 80 99 22 29 2e  T... ", "T...").
1a60: 72 65 70 6c 61 63 65 28 22 53 e2 80 99 20 22 2c  replace("S... ",
1a70: 20 22 53 e2 80 99 22 29 2e 72 65 70 6c 61 63 65   "S...").replace
1a80: 28 22 51 55 e2 80 99 20 22 2c 20 22 51 55 e2 80  ("QU... ", "QU..
1a90: 99 22 29 0a 20 20 20 20 72 65 74 75 72 6e 20 73  .").    return s
1aa0: 4d 65 73 73 61 67 65 0a 0a 0a 64 65 66 20 6d 61  Message...def ma
1ab0: 69 6e 28 29 3a 0a 20 20 20 20 22 73 74 61 72 74  in():.    "start
1ac0: 20 66 75 6e 63 74 69 6f 6e 22 0a 20 20 20 20 75   function".    u
1ad0: 6e 69 74 74 65 73 74 2e 6d 61 69 6e 28 29 0a 0a  nittest.main()..
1ae0: 0a 69 66 20 5f 5f 6e 61 6d 65 5f 5f 20 3d 3d 20  .if __name__ == 
1af0: 27 5f 5f 6d 61 69 6e 5f 5f 27 3a 0a 20 20 20 20  '__main__':.    
1b00: 6d 61 69 6e 28 29 0a                             main().