https://gitorious.org/jbotci/jbotci.git
Revision 31ca832f37cd9851b60b75be5dfd0cfcb3f42658 authored by David Gowers on 08 September 2010, 09:06:29 UTC, committed by David Gowers on 08 September 2010, 09:06:29 UTC
0 parent
Raw File
Tip revision: 31ca832f37cd9851b60b75be5dfd0cfcb3f42658 authored by David Gowers on 08 September 2010, 09:06:29 UTC
initial commit
Tip revision: 31ca832
jbofacki
#!/usr/bin/env python
# Accept lojban input, tab-complete words and -raf-si,
# feed jbofihe or camxes.
# Provide quick colorized word lookups and approximate gloss/notes/def search.
#
# Req: Python 2.5, readline. Also a valid makfa-cli database (run 'makfa update' to get one)
#
# Probably also requires Linux or OSX. May be able to be made more portable.
#
# Bugs/Lacking aspects:
#
# XXX define a matches-displayer that shows word type (or class, in case of cmavo)
# XXX parentheses handling is incomplete in some circumstances
# XXX some python functions like sum, dict, etc show up in the completion list
# XXX colorize jbofihe (and camxes?) output
# XXX completion could be context sensitive (some way to prioritize terminators when a
#     scope is open?)
# XXX glosses should be completeable when the input string contains '/'
# XXX wildcards in search (shell style)
# XXX quoting in search (extract quoted sections first. disallow searching for '"' lerfu
# XXX lockfile to prevent log/notes mangling.
#
# le se ckire:
#
#   la donri
#   la tueis
#

import os
from subprocess import Popen, PIPE, call
import readline
import rlcompleter
import sys
import re

##### se cuxna
#
#
logfile = os.path.expanduser ('~/jbofacki-log.rst')
notcyvreji = os.path.expanduser ('~/jbofacki-notci.rst')
camxes = ['java','-jar',os.path.expanduser ('~/lojban/lojban_peg_parser.jar')]
jbofihe = ['jbofihe','-x','-se']
default = jbofihe
jarco_tuha_le_sidju = True

mipri_ro_fuhivla = False # na ca ka pilno

#####

# > from pygments.console import codes
# > print codes

codes = {'': '',
'yellow': '\x1b[33;01m',
'blink': '\x1b[05m',
'lightgray': '\x1b[37m',
'underline': '\x1b[04m',
'darkyellow': '\x1b[33m',
'blue': '\x1b[34;01m',
'darkblue': '\x1b[34m',
'faint': '\x1b[02m',
'fuchsia': '\x1b[35;01m',
'black': '\x1b[30m',
'white': '\x1b[01m',
'red': '\x1b[31;01m',
'brown': '\x1b[33m',
'turquoise': '\x1b[36;01m',
'bold': '\x1b[01m',
'darkred': '\x1b[31m',
'darkgreen': '\x1b[32m',
'reset': '\x1b[39;49;00m',
'standout': '\x1b[03m',
'darkteal': '\x1b[36;01m',
'darkgray': '\x1b[30;01m',
'overline': '\x1b[06m',
'purple': '\x1b[35m',
'green': '\x1b[32;01m',
'teal': '\x1b[36m',
'fuscia': '\x1b[35;01m'}

# lo nu lo mifra ku cenba ku cumki ..
#
try:
    from pygments.console import codes
except:
    # pilno le mi mifra
    pass

tersuhi_skari = codes['purple']
valsi_skari = codes['white']
klesi_skari = codes['darkgreen']
rafsi_skari = codes['teal']
tcita_skari = codes['brown']
cmavo_skari = codes['blue']
gismu_skari = codes['darkgreen']
lujvo_skari = codes['green']
jorne_skari = codes['darkgreen']
troci_skari = codes['red']
selmaho_skari = codes['darkred']
fuhivla_skari = codes['fuchsia']
pinka_skari = codes['darkgray']
xruti = codes['reset']

# cpacu le jorne la makfa
manri_jorne = {'A': ((14, 6),),
            'BAI': ((9, 6),),
            'BAhE': ((19, 11),),
            'BE': ((5, 7),),
            'BEI': ((5, 7),),
            'BEhO': ((5, 7),),
            'BIhE': ((18, 5),),
            'BIhI': ((14, 16),),
            'BO': ((5, 3), (15, 6), (18, 17),),
            'BOI': ((18, 6),),
            'BU': ((17, 4),),
            'BY': ((17, 2),),
            'CAI': ((13, 4),),
            'CAhA': ((10, 19),),
            'CEI': ((7, 5),),
            'CEhE': ((14, 11), (16, 7),),
            'CO': ((5, 8),),
            'COI': ((6, 11), (13, 14),),
            'CU': ((9, 2),),
            'CUhE': ((10, 24),),
            'DAhO': ((7, 13),),
            'DOI': ((13, 14),),
            'DOhU': ((13, 14),),
            'FA': ((9, 3),),
            'FAhA': ((10, 2),),
            'FAhO': ((19, 15),),
            'FEhE': ((10, 11),),
            'FEhU': ((9, 5),),
            'FIhO': ((9, 5),),
            'FOI': ((17, 6),),
            'FUhA': ((18, 16),),
            'FUhE': ((19, 8),),
            'FUhO': ((19, 8),),
            'GA': ((14, 5),),
            'GAhO': ((14, 16),),
            'GEhU': ((8, 3),),
            'GI': ((14, 5),),
            'GIhA': ((14, 3),),
            'GOI': ((8, 3),),
            'GOhA': ((7, 6),),
            'GUhA': ((14, 3),),
            'I': ((19, 2),),
            'JA': ((14, 3),),
            'JAI': ((9, 12),),
            'JOI': ((14, 14),),
            'JOhI': ((18, 15),),
            'KE': ((5, 5),),
            'KEI': ((11, 1),),
            'KEhE': ((5, 5),),
            'KI': ((10, 13),),
            'KOhA': ((7, 1),),
            'KU': ((6, 2), (10, 1),),
            'KUhE': ((18, 6),),
            'KUhO': ((8, 1),),
            'LA': ((6, 2),),
            'LAU': ((17, 14),),
            'LAhE': ((6, 10),),
            'LE': ((6, 2),),
            'LEhU': ((19, 9),),
            'LI': ((18, 5),),
            'LIhU': ((19, 9),),
            'LOhO': ((18, 17),),
            'LOhU': ((19, 9),),
            'LU': ((19, 9),),
            'LUhU': ((6, 10),),
            'MAI': ((18, 19), (19, 1),),
            'MAhO': ((18, 6),),
            'ME': ((5, 10), (18, 1),),
            'MEhU': ((5, 11),),
            'MOI': ((5, 11),),
            'MOhE': ((18, 18),),
            'MOhI': ((10, 8),),
            'NA': ((14, 3), (15, 7),),
            'NAI': ((14, 3), (15, 7),),
            'NAhE': ((15, 4),),
            'NAhU': ((18, 18),),
            'NIhE': ((18, 18),),
            'NIhO': ((19, 3),),
            'NOI': ((8, 1),),
            'NU': ((11, 1),),
            'NUhA': ((18, 19),),
            'NUhI': ((14, 11), (16, 7),),
            'NUhU': ((14, 11),),
            'PA': ((18, 2),),
            'PEhE': ((14, 11),),
            'PEhO': ((18, 6),),
            'PU': ((10, 4),),
            'RAhO': ((7, 6),),
            'ROI': ((10, 9),),
            'SA': ((19, 13),),
            'SE': ((5, 1), (9, 4),),
            'SEI': ((19, 12),),
            'SEhU': ((19, 12),),
            'SI': ((19, 13),),
            'SOI': ((7, 8),),
            'SU': ((19, 13),),
            'TAhE': ((10, 9),),
            'TEI': ((17, 6),),
            'TEhU': ((18, 15),),
            'TO': ((19, 12),),
            'TOI': ((19, 12),),
            'TUhE': ((19, 2),),
            'TUhU': ((19, 2),),
            'UI': ((13, 1),),
            'VA': ((10, 2),),
            'VAU': ((14, 9),),
            'VEI': ((18, 5),),
            'VEhA': ((10, 5),),
            'VEhO': ((19, 5),),
            'VIhA': ((10, 7),),
            'VUhO': ((8, 8),),
            'VUhU': ((18, 5),),
            'XI': ((18, 13),),
            'Y': ((19, 14),),
            'ZAhO': ((10, 10),),
            'ZEI': ((4, 6),),
            'ZEhA': ((10, 5),),
            'ZI': ((10, 4),),
            'ZIhE': ((8, 4),),
            'ZO': ((19, 10),),
            'ZOI': ((19, 10),),
            'ZOhU': ((16, 2), (19, 4),)}

# .ije cpacu le mulno kuspe datni la makfa

mulno_selmaho = {'BE': 'BEhO',
                 'PA': 'BOI',
                 'BY': 'BOI',
                 'COI': 'DOhU',
                 'DOI': 'DOhU',
                 'FIhO': 'FEhU',
                 'GOI': 'GEhU',
                 'NU': 'KEI',
                 'KE': 'KEhE',
                 'LE': 'KU',
                 'LA': 'KU',
                 'PEhO': 'KUhE',
                 'NOI': 'KUhO',
                 'LU': 'LIhU',
                 'LI': 'LOhO',
                 'LAhE': 'LUhU',
                 'NAhE+BO': 'LUhU',
                 'ME': 'MEhU',
                 'NUhI': 'NUhU',
                 'SEI': 'SEhU',
                 'SOI': 'SEhU',
                 'TO': 'TOI',
                 'TUhE': 'TUhU',
                 'VEI': 'VEhO'}

def facki_fi_lo_valsi (valsi):
    return [v in ro_valsi for v in valsi]

def galfi_le_porsi (valsi, se_porsi = 'cmene'):
    valsi = list (valsi)
    if se_porsi == 'cmene':
        valsi.sort()
    elif se_porsi == 'rafsi':
        valsi.sort(key = lambda v:"".join(valsi_datni['rafsi']))
    else:
        valsi.sort(key = lambda v:valsi_datni[se_porsi])
    return valsi

tersuhi_rex = re.compile ('([a-z][0-9])')
pinka_rex = re.compile ('([([][^\])]+[)\]])')
valsi_rex = re.compile ('(\{[a-z \']+\})')
se_ciska_rex = re.compile ('("[^"]+")')
selmaho_rex = re.compile ('([A-Zh]+)(\\.+)?([A-Zh]+)?') #cumki srera sera'a eg LU.. LI'U
jorne_rex = re.compile('(http://dag.github.com/cll/[0-9/]+)')


def skagau (se_ciska, rex, skari):
    if not se_ciska:
        return ''
    return rex.sub (skari + '\\1' + xruti, se_ciska)

def klesi_skagau (klesi):
    klesi = klesi.replace ("experimental", troci_skari + "experimental" + xruti)
    klesi = klesi.replace ("lujvo", lujvo_skari + 'lujvo' + xruti)
    klesi = klesi.replace ("gismu", gismu_skari + 'gismu' + xruti)
    klesi = klesi.replace ("cmavo", cmavo_skari + 'cmavo' + xruti)
    klesi = klesi.replace ("fu'ivla", fuhivla_skari + "fu'ivla" + xruti)
    return klesi

def selmaho_skagau (selmaho):
    """XXX doesn't handle eg LU..LI'U correctly"""
    if not selmaho:
        return ''
    if '...' in selmaho:
        return "%s%s%s...%s%s%s" % (selmaho_skari, selmaho[:selmaho.find('.')], xruti,
                                    selmaho_skari, selmaho[selmaho.rfind('.')+1:], xruti)
        #return selmaho_rex.sub (selmaho_skari + '\\1' + xruti + '\\2' + selmaho_skari + '\\3' + xruti, selmaho)
    return selmaho_skari + selmaho + xruti

def rafsi_skagau (rafsi):
    if len(rafsi) == 0:
        return ''
    return rafsi_skari + "\t" + "\t".join(rafsi) + xruti

def tersuhi_skagau (tersuhi):
    import pygments.console as pc
    te_pruce = []
    zasni = []
    for ts, ts_tcita in tersuhi.items():
        #print ts, ';',ts_tcita
        for da, de in ts_tcita:
            if da and (not de):
                zasni.append (' %s%r%s' % ( tcita_skari, da,
                                            xruti))
            else:
                zasni.append ('%s%r%s in sense %s%r%s' % (tcita_skari,
                                                da, xruti,
                                                tcita_skari,
                                                de, xruti))
        te_pruce.append (("%sx%d%s " % (tersuhi_skari,
                                         ts,xruti) +
                                         ", ".join(zasni)))
    return "; ".join(te_pruce)


def sidju ():
    s = """\t\t%Wla jbofacki%/
    \t\t===========

    "le broda lo brode noi gerku ku selbri"
    \t\t-> %Wget verbose jbofihe output.%/
    "!"
    \t\t-> %Wget camxes output WRT the previous input%/.
    "le broda lo brode noi gerku ku selbri?" or
    "?le broda lo brode noi gerku ku selbri"
    \t\t-> %Wshow colored definitions
    \t\t   of all unique valsi in that input
    \t\t   (%/%Gbroda brode gerku noi
    \t\t   ku le lo selbri%/%W)%/
    "broda CAhA -raf- -bro-?"
    \t\t-> %Wsimilar to above, demonstrating
    \t\t   cmavo class expansion:%/
    \t\t        %GCAhA%/ -> %Gca'a ka'e nu'o pu'i%/
    \t\t        %G-raf-%/ -> %Grafsi%/ (rafsi expansion) also occurs
    "crystal/" | "/crystal"
    \t\t-> %Wsearch through all fields for 'crystal'%/
    \t\t   (natlang or lojban word fragments)
    ":ni x1 is filled with ce'u if ce'u isn't explicitly specified."
    \t\t-> %WWrite the above text "ni x1[...]" to ~/jbofacki-notci.rst%/
    "??"-> %WShow this help%/
    Tab-completion of lojban words and -raf-si is available at all times,
    as are the usual Readline niceties.
    ------
    (word lookup requires a makfa db. Installing makfa-cli and
     running 'makfa update' should make one.)

    (remember to edit the variable definitions in the 'se cuxna'
    section of the jbominde script file.
    You can configure jbofihe and camxes parameters,
    the path to camxes, whether camxes or jbofihe is default,
    and whether this help is shown at startup.)
    ------...
    .i ai cfari
    """

    s = s.replace ('%W', codes['white'])
    s = s.replace ('%G', codes['green'])
    s = s.replace ('%/', xruti)
    print s

def tersuhi_sisku (datni, valsi):
    tersuhi = datni['places']
    for tersuhi, tcita in tersuhi.items():
        for a, b in tcita:
            if valsi in a:
                return True
            if not b:
                continue
            if valsi in b:
                return True
    return False


def naljbo_sisku (pagbu):
    te_pruce = []
    for cmene, datni in valsi_datni.items():
        for p in pagbu:
            if p in cmene or (datni['notes'] and p in datni['notes']) \
               or p in datni['definition'] or tersuhi_sisku (datni, p):
                te_pruce.append (cmene)
    return te_pruce

def jarco (valsi, se_porsi = 'cmene'):
    te_pruce = []
    # remove
    ro_rapli = [v for v in valsi if valsi.count(v) > 1]
    for r in ro_rapli:
        while valsi.count(r) > 1:
            valsi.remove (r)
    valsi = galfi_le_porsi (valsi, se_porsi)
    # banro le selma'o ja rafsi
    valsi2 = []
    for v in valsi:
        if selmaho_rex.match(v):
            if '..' in v:
                purci = v[:v.find('.')]
                balvi = v[v.rfind('.')+1:]
            else:
                purci = v
                balvi = None
            for v2 in (purci, balvi):
                if not v2:
                    continue
                for cmene, datni in valsi_datni.items():
                    if not datni['selmaho']:
                        continue
                    selmaho = datni['selmaho']
                    if sm == v2 or sm.startswith (v2):
                        # UI -> UI[12345678][abc]?
                        # UI3 facki [UI3a, UI3b, ...]
                        valsi2.append (cmene)
        elif len(v) in (5,6) and v.startswith ('-') and v.endswith ('-'):
            rafsi = v[1:-1]
            for cmene, datni in valsi_datni.items():
                    if rafsi in datni['rafsi']:
                        valsi2.append (cmene)
                        break; # no more than one valsi should be assigned the same rafsi
        else:
            valsi2.append (v)
    for v in valsi2:
        try:
            datni = valsi_datni[v]
        except KeyError:
            te_pruce.append ((v, selmaho_skari + 'na facki' + xruti))
            continue
        se_valsi = datni['definition']
        se_valsi = skagau (se_valsi, pinka_rex, pinka_skari)
        se_valsi = skagau (se_valsi, tersuhi_rex, tersuhi_skari)
        # skagau [a-z][0-9]
        # skagau (qualifiers)
        notci = datni['notes']
        notci = skagau (notci, pinka_rex, pinka_skari)
        notci = skagau (notci, valsi_rex, jorne_skari)
        notci = skagau (notci, tersuhi_rex, tersuhi_skari)
        rafsi = rafsi_skagau (datni['rafsi'])
        tersuhi = tersuhi_skagau (datni ['places'])
        # skagau {valsi}
        jorne = ''
        selmaho = datni['selmaho']
        if selmaho in manri_jorne:
            jorne = ", ".join(['<http://dag.github.com/cll/%d/%d/>' % (a,b) for a,b in manri_jorne[selmaho]])
            jorne = skagau (jorne, jorne_rex, jorne_skari)
        if selmaho in mulno_selmaho:
            selmaho += '...' + mulno_selmaho[selmaho]
        selmaho = selmaho_skagau (selmaho)
        klesi = klesi_skagau (datni['type'])
        zasni = " ".join ([klesi,selmaho,rafsi])
        te_pruce.append ( (v, zasni, se_valsi, tersuhi, notci, jorne))
    return te_pruce

def selmaho (valsi_ja_selmaho):
    if type(valsi_ja_selmaho) == list or valsi_ja_selmaho in ro_valsi:
        valsi = valsi_ja_selmaho
        if type(valsi) != list:
            valsi = [valsi]
        valsi = facki_fi_lo_valsi (valsi)
        # XXX look up all the valsi, return their selmaho
    else:
        raise NotImplementedError ('selmaho klesi zgana')
        # XXX look up all the selmaho in the specified class

def jarco_xire (tcita):
    j = tcita
    for da in j:
        print ''
        print (codes['white'] + da[0] + xruti
              + "\t" + da[1])
        for i, de in enumerate (da[2:]):
            if len(de) > 1:
                print ("\t" + de)
    print ''


log = open (logfile, 'a')
notci = open (notcyvreji, 'a')
from cPickle import load
f = open (os.path.expanduser('~/.makfa.dump'), 'rb')
valsi_datni = load(f)
ro_valsi = valsi_datni['order']
valsi_datni = valsi_datni['entries']
last = None

mulno_selcmi = {}
for valsi in ro_valsi:
    mulno_selcmi[valsi] = True

for valsi, datni in valsi_datni.items():
    if datni['rafsi'] != None and len(datni['rafsi']) > 0:
        for r in datni['rafsi']:
            mulno_selcmi["-%s-" % r] = True

def main ():
    from rlcompleter import Completer
    import readline
    m = Completer (mulno_selcmi)
    readline.parse_and_bind ('tab: complete')
    readline.set_completer (m.complete)
    drata_lerfu = "?/:"
    readline.set_completer_delims (drata_lerfu + ". ")
    if jarco_tuha_le_sidju:
        sidju ()
    while 1:
        sepuhe = raw_input('se pu\'e: ')
        command = default
        if sepuhe.startswith ('!') and last != None:
            if default == jbofihe:
                command = camxes
            else:
                command = jbofihe
            f = Popen (command, stdin = PIPE, stdout = PIPE)
            f.stdin.write(last)
        elif sepuhe[0] in drata_lerfu or sepuhe[-1] in drata_lerfu:
            # ? (look up a word)
            #
            if sepuhe[0] in drata_lerfu:
                char = sepuhe[0]
                sepuhe = sepuhe[1:]
            else:
                char = sepuhe[-1]
                sepuhe = sepuhe[:-1]
            if char == '?':
                if len (sepuhe) == 1 and sepuhe == '?':
                    sidju()
                else:
                    valsi = sepuhe.strip().split()
                    j = jarco (valsi)
                    jarco_xire (j)
            elif char == '/':
                valsi = sepuhe.strip().split()
                j = naljbo_sisku (valsi)
                if len(j) > 100:
                    print (codes['red'] +
                           ('\nNot displaying excessive number (%d) of search results.\n' %
                           len(j)) + xruti + 'Please refine your search.\n')
                else:
                    j = jarco (j)
                    jarco_xire (j)
            elif char == ':':
                if sepuhe [0] != '?':
                    notci.write (sepuhe)
                    notci.write ('\n')
                else:
                    notci.flush()
                    call (['tail', notcyvreji])
            log.write(sepuhe)
            log.write('\n')
            continue
        else:
            f = Popen (default, stdin = PIPE, stdout = PIPE)
            f.stdin.write(sepuhe)
        last = sepuhe
        f.stdin.close()
        tepuhe = f.stdout.read()
        f.stdout.close()

        sys.stdout.write (tepuhe)
        sys.stdout.write ('\n')
        sys.stdout.flush ()

        log.write (sepuhe)
        log.write ('\n')
        log.write (tepuhe)
        log.write ('\n')

if __name__ == "__main__":
    main()
back to top