view repair.py @ 66:53c37a02d471

working, I think/hope
author Henry S. Thompson <ht@inf.ed.ac.uk>
date Thu, 14 Dec 2023 20:17:48 +0000
parents 5e5feacb730d
children
line wrap: on
line source

#!/usr/bin/python3
import sys

import lisparser

def readAlist(fn):
  with open(fn,'r') as f:
    sline = f.readline()
    alines = (l for l in f if (L:=l).startswith("("))
    return sline, alist(alines), L

# alist fields are: group, rank, read, [marks, [method, [params]]]
#                   string  int [list]  [plist]  expr    plist                                                     
def alist(lines):
  res = {}
  for l in lines:
    ll = lisparser.get_ast(lisparser.normalize_str(l))[0]
    k = ll.pop(0)
    n = int(ll.pop(0))
    res[eval(k)]=[n]+al2p(ll)
  return res

def al2p(ll):
  '''handle read (list or nil), maybe marks (plist or nil), usually method,
     maybe params (plist)'''
  res=[ll.pop(0)] # read
  if ll:
    marks = ll.pop(0)
    if marks != 'nil':
      marks = dict((e[0],e[1:]) for e in marks)
    res.append(marks)
  else:
    return res
  if ll:
    res.append(ll.pop(0)) # method
  else:
    return res
  if ll:
    params = ll.pop(0)
    if params == 'nil':
      res.append(params)
    else:
      res.append(dict((e[0],e[1:]) for e in params)) # params
  else:
    return res
  if ll:
    raise ValueError("too many args: %s"%ll)
  return res
    
def p2l(pl, f):
  if isinstance(pl,list) or isinstance(pl,tuple):
    if len(pl) == 0:
      f.write('nil')
    else:
      f.write('(')
      space = False
      for e in pl:
        if space:
          f.write(' ')
          p2l(e,f)
        else:
          p2l(e,f)
          space = True
      f.write(')')
  elif isinstance(pl,dict):
    f.write('(')
    space = False
    for k, v in pl.items():
      if space:
        f.write(' (')
      else:
        f.write('(')
        space = True
      f.write(k)
      for e in v:
        f.write(' ')
        p2l(e,f)
      f.write(')')
    f.write(')')
#   elif isinstance(pl,str):
#     if pl in ['.','nil']:
#       f.write(pl)
#     else:
#       try:
#         int(pl)
#         f.write(pl)
#       except ValueError:
#         f.write(pl)
  else:
    f.write(pl)

def mergeOne(gv,mv):
  '''rank: deeper in gnus wins
   read: if unequal
           if mail is nil, use gnus
           otherwise use mail
   marks: merge unseen keys, unequal values for same key prefer gnus
          EXCEPT bogus, w3c-ac-forum, handle by hand DONE
   method: change "ht" to "nnml+ht"
           FIX group-2002-07 in gnus by hand DONE
   params: merge by keys
'''
  res = mv.copy()
  if gv[0] > res[0]: # rank
    res[0] = gv[0]
  if res[1] == 'nil': # read
    res[1] = gv[1]
  if res[2] != gv[2]: # marks
    if res[2] == 'nil':
      res[2] = gv[2]
    else:
      for k, v in gv[2].items():
        # fill in missing, override unequal, if equal no harm done
        res[2][k] = v
  # we prefer m to g for method
  if len(res) == 4:
    # no params
    if len(gv) == 5:
      res.append(gv[4])
  elif len(gv) == 5:
    if res[4] != gv[4]:
      for k, v in gv[4].items():
        # fill in missing, override unequal, if equal no harm done
        res[4][k] = v
  return res
      
def lineout(k,res,outf):
  outf.write('("%s" '%k)
  outf.write(str(res[0])) # rank
  for v in res[1:]:
    outf.write(' ')
    p2l(v,outf)
  outf.write(')\n')

def merge(gnus, mail, out):
  '''
   rank, read, marks, method, params
   
   read is everything that is unmarked (nothing in left column)
   marks.seen is everything that has ever been looked it and not DELETED
   marks.tick is a !, marks.forward is F, marks.reply is A.
''' # '
  global g, m
  g1, g, g2 = readAlist(gnus)
  _, m, _ = readAlist(mail)
  with open(out, "w") as outf:
    outf.write(g1)
    for k, v in g.items():
      if k in m:
        res = mergeOne(v, m[k])
      else:
        res = v
      lineout(k,res,outf)
    for k, v in m.items():
      if k not in g:
        lineout(k,v,outf)
    outf.write(g2)

def stale():
  '''

 Comparison tool:

 Watch out for tabs!
 To look for overlap, change 2nd \n in printf to \t

 export T=$'\t'
 export B1="(\(\([^()]\+\|([^)]*)\) \?\)*)"
 export P1="\(nil\|$B1\)"
 export P2="\(nil\|\((\($B1 \?\)*)\)\)"
 export MA='\("[^"]*"\|(nndraft "")\)'
 fgrep -hf shared {gnus,mail}/alist.fixed | sed 's/ \([0-9]\) '"$P1 $P2 / \1     \2      \5      /;s/\(.*${T}.*${T}.*${T}\)$MA $P2/\1\2${T}\4/"
   paste <(cat shared) <(fgrep -f shared mail/alist.fixed | sed 's/ \([0-9]\) '"$P $P/ \1  \2      \5      /g" | cut -f 3)  <(fgrep -f shared gnus/alist.fixed | sed 's/ \([0-9]\) '"$P $P/ \1     \2      \5      /g" | cut -f 3) | { IFS='       ' ; while read gn g m; do if [ "$g" != "$m" ]; then printf "=----%s------\n%s\n%s\n" "$gn" "$g" "$m"; fi; done ; } | less
'''

if __name__ == '__main__':
  merge(*sys.argv[1:])