sugar-toolkit-gtk3/services/console/lib/purk/parse_mirc.py

458 lines
14 KiB
Python
Raw Normal View History

2007-08-21 18:18:38 +02:00
try:
from conf import conf
except ImportError:
conf = {}
BOLD = '\x02'
UNDERLINE = '\x1F'
MIRC_COLOR = '\x03'
MIRC_COLOR_BG = MIRC_COLOR, MIRC_COLOR
BERS_COLOR = '\x04'
RESET = '\x0F'
colors = (
'#FFFFFF', '#000000', '#00007F', '#009300',
'#FF0000', '#7F0000', '#9C009C', '#FF7F00',
'#FFFF00', '#00FF00', '#009393', '#00FFFF',
'#0000FF', '#FF00FF', '#7F7F7F', '#D2D2D2'
)
def get_mirc_color(number):
if number == '99':
return None
number = int(number) & 15
confcolors = conf.get('colors', colors)
try:
return confcolors[number]
except:
# someone edited their colors wrongly
return colors[number]
DEC_DIGITS, HEX_DIGITS = set('0123456789'), set('0123456789abcdefABCDEF')
def parse_mirc_color(string, pos, open_tags, tags):
color_chars = 1
if MIRC_COLOR in open_tags:
fgtag = open_tags.pop(MIRC_COLOR)
fgtag['to'] = pos
tags.append(fgtag)
if MIRC_COLOR_BG in open_tags:
bgtag = open_tags.pop(MIRC_COLOR_BG)
bgtag['to'] = pos
tags.append(bgtag)
bg = bgtag['data'][1]
else:
bg = None
if string[0] in DEC_DIGITS:
if string[1] in DEC_DIGITS:
fg = get_mirc_color(string[:2])
string = string[1:]
color_chars += 2
else:
fg = get_mirc_color(string[0])
color_chars += 1
if string[1] == "," and string[2] in DEC_DIGITS:
if string[3] in DEC_DIGITS:
bg = get_mirc_color(string[2:4])
color_chars += 3
else:
bg = get_mirc_color(string[2])
color_chars += 2
else:
fg = bg = None
if fg:
open_tags[MIRC_COLOR] = {'data': ("foreground",fg), 'from': pos}
else:
open_tags.pop(MIRC_COLOR,None)
if bg:
open_tags[MIRC_COLOR_BG] = {'data': ("background",bg), 'from': pos}
else:
open_tags.pop(MIRC_COLOR_BG,None)
return color_chars
def parse_bersirc_color(string, pos, open_tags, tags):
bg = None
if MIRC_COLOR in open_tags:
tag = open_tags.pop(MIRC_COLOR)
tag['to'] = pos
tags.append(tag)
if MIRC_COLOR_BG in open_tags:
bgtag = open_tags.pop(MIRC_COLOR_BG)
bgtag['to'] = pos
tags.append(bgtag)
bg = bgtag['data'][1]
for c in (0, 1, 2, 3, 4, 5):
if string[c] not in HEX_DIGITS:
return 1
fg = '#' + string[:6].upper()
color_chars = 7
for c in (7, 8, 9, 10, 11, 12):
if string[c] not in HEX_DIGITS:
break
else:
if string[6] == ",":
bg = '#' + string[7:13].upper()
color_chars = 14
if fg:
open_tags[MIRC_COLOR] = {'data': ("foreground",fg), 'from': pos}
else:
open_tags.pop(MIRC_COLOR,None)
if bg:
open_tags[MIRC_COLOR_BG] = {'data': ("background",bg), 'from': pos}
else:
open_tags.pop(MIRC_COLOR_BG,None)
return color_chars
def parse_bold(string, pos, open_tags, tags):
if BOLD in open_tags:
tag = open_tags.pop(BOLD)
tag['to'] = pos
tags.append(tag)
else:
open_tags[BOLD] = {'data': ('weight', BOLD), 'from': pos}
return 1
def parse_underline(string, pos, open_tags, tags):
if UNDERLINE in open_tags:
tag = open_tags.pop(UNDERLINE)
tag['to'] = pos
tags.append(tag)
else:
open_tags[UNDERLINE] = {'data': ('underline', UNDERLINE), 'from': pos}
return 1
def parse_reset(string, pos, open_tags, tags):
for t in open_tags:
tag = open_tags[t]
tag['to'] = pos
tags.append(tag)
open_tags.clear()
return 1
tag_parser = {
MIRC_COLOR: parse_mirc_color,
BERS_COLOR: parse_bersirc_color,
BOLD: parse_bold,
UNDERLINE: parse_underline,
RESET: parse_reset
}
def parse_mirc(string):
string += RESET
out = ''
open_tags = {}
tags = []
text_i = outtext_i = 0
for tag_i, char in enumerate(string):
if char in tag_parser:
out += string[text_i:tag_i]
outtext_i += tag_i - text_i
text_i = tag_i + tag_parser[char](
string[tag_i+1:],
outtext_i,
open_tags,
tags
)
return tags, out
#transforms for unparse_mirc
#^O
def transform_reset(start, end):
return RESET, '', {}
#^K
def transform_color_reset(start, end):
if ('foreground' in start and 'foreground' not in end) or \
('background' in start and 'background' not in end):
result = start.copy()
result.pop("foreground",None)
result.pop("background",None)
return MIRC_COLOR, DEC_DIGITS, result
else:
return '','',start
#^KXX
def transform_color(start, end):
if (start.get('foreground',99) != end.get('foreground',99)):
confcolors = conf.get('colors', colors)
result = start.copy()
if 'foreground' in end:
try:
index = list(confcolors).index(end['foreground'].upper())
except ValueError:
return '','',start
result['foreground'] = end['foreground']
else:
index = 99
del result['foreground']
return '\x03%02i' % index, ',', result
else:
return '','',start
#^KXX,YY
def transform_bcolor(start, end):
if (start.get('background',99) != end.get('background',99)):
confcolors = conf.get('colors', colors)
result = start.copy()
if 'foreground' in end:
try:
fg_index = list(confcolors).index(end['foreground'].upper())
except ValueError:
return '','',start
result['foreground'] = end['foreground']
else:
fg_index = 99
result.pop('foreground',None)
if 'background' in end:
try:
bg_index = list(confcolors).index(end['background'].upper())
except ValueError:
return '','',start
result['background'] = end['background']
else:
bg_index = 99
del result['background']
return '\x03%02i,%02i' % (fg_index, bg_index), ',', result
else:
return '','',start
#^LXXXXXX
def transform_bersirc(start, end):
if 'foreground' in end and end['foreground'] != start.get('foreground'):
result = start.copy()
result['foreground'] = end['foreground']
return "\x04%s" % end['foreground'][1:], ',', result
else:
return '','',start
#^LXXXXXX,YYYYYY
def transform_bbersirc(start, end):
if 'foreground' in end and 'background' in end and (
end['foreground'] != start.get('foreground') or
end['background'] != start.get('background')):
result = start.copy()
result['foreground'] = end['foreground']
result['background'] = end['background']
return "\x04%s,%s" % (end['foreground'][1:], end['background'][1:]), ',', result
else:
return '','',start
#^B
def transform_underline(start, end):
if ('underline' in start) != ('underline' in end):
result = start.copy()
if 'underline' in start:
del result['underline']
else:
result['underline'] = UNDERLINE
return UNDERLINE, '', result
else:
return '','',start
#^U
def transform_bold(start, end):
if ('weight' in start) != ('weight' in end):
result = start.copy()
if 'weight' in start:
del result['weight']
else:
result['weight'] = BOLD
return BOLD, '', result
else:
return '','',start
#^B^B
#In some rare circumstances, we HAVE to do this to generate a working string
def transform_dbold(start, end):
return BOLD*2, '', start
#return the formatting needed to transform one set of format tags to another
def transform(start, end, nextchar=" "):
transform_functions = (
transform_reset, transform_color_reset, transform_color, transform_bcolor,
transform_bersirc, transform_bbersirc, transform_underline,
transform_bold, transform_dbold,
)
candidates = [('','',start)]
result = None
for f in transform_functions:
for string, badchars, s in candidates[:]:
newstring, badchars, s = f(s, end)
string += newstring
if newstring and (result == None or len(string) < len(result)):
if nextchar not in badchars and s == end:
result = string
else:
candidates.append((string, badchars, s))
return result
def unparse_mirc(tagsandtext):
lasttags, lastchar = {}, ''
string = []
for tags, char in tagsandtext:
if tags != lasttags:
string.append(transform(lasttags, tags, char[0]))
string.append(char)
lasttags, lastchar = tags, char
return ''.join(string)
if __name__ == "__main__":
tests = [
'not\x02bold\x02not',
'not\x1Funderline\x1Fnot',
"\x02\x1FHi\x0F",
'not\x030,17white-on-black\x0304red-on-black\x03nothing',
"\x040000CC<\x04nick\x040000CC>\x04 text",
'\x04770077,FFFFFFbersirc color with background! \x04000077setting foreground! \x04reset!',
'\x047700,FFFFbersirc',
"\x03123Hello",
"\x0312,Hello",
"\x034Hello",
"Bo\x02ld",
"\x034,5Hello\x036Goodbye",
"\x04ff0000,00ff00Hello\x040000ffGoodbye",
"\x04777777(\x0400CCCCstuff\x04777777)\x04",
'\x0307orange\x04CCCCCCgrey\x0307orange',
'\x04CCCCCC,444444sdf\x0304jkl',
'\x0403\x02\x02,trixy',
'\x04FFFFFF\x02\x02,000000trixy for bersirc',
]
results = [
([{'from': 3, 'data': ('weight', '\x02'), 'to': 7}], 'notboldnot'),
([{'from': 3, 'data': ('underline', '\x1f'), 'to': 12}], 'notunderlinenot'),
([{'from': 0, 'data': ('weight', '\x02'), 'to': 2}, {'from': 0, 'data': ('underline', '\x1f'), 'to': 2}], 'Hi'),
([{'from': 3, 'data': ('foreground', '#FFFFFF'), 'to': 17}, {'from': 3, 'data': ('background', '#000000'), 'to': 17}, {'from': 17, 'data': ('foreground', '#FF0000'), 'to': 29}, {'from': 17, 'data': ('background', '#000000'), 'to': 29}], 'notwhite-on-blackred-on-blacknothing'),
([{'from': 0, 'data': ('foreground', '#0000CC'), 'to': 1}, {'from': 5, 'data': ('foreground', '#0000CC'), 'to': 6}], '<nick> text'),
([{'from': 0, 'data': ('foreground', '#770077'), 'to': 31}, {'from': 0, 'data': ('background', '#FFFFFF'), 'to': 31}, {'from': 31, 'data': ('foreground', '#000077'), 'to': 51}, {'from': 31, 'data': ('background', '#FFFFFF'), 'to': 51}], 'bersirc color with background! setting foreground! reset!'),
([], '7700,FFFFbersirc'),
([{'from': 0, 'data': ('foreground', '#0000FF'), 'to': 6}], '3Hello'),
([{'from': 0, 'data': ('foreground', '#0000FF'), 'to': 6}], ',Hello'),
([{'from': 0, 'data': ('foreground', '#FF0000'), 'to': 5}], 'Hello'),
([{'from': 2, 'data': ('weight', '\x02'), 'to': 4}], 'Bold'),
([{'from': 0, 'data': ('foreground', '#FF0000'), 'to': 5}, {'from': 0, 'data': ('background', '#7F0000'), 'to': 5}, {'from': 5, 'data': ('foreground', '#9C009C'), 'to': 12}, {'from': 5, 'data': ('background', '#7F0000'), 'to': 12}], 'HelloGoodbye'),
([{'from': 0, 'data': ('foreground', '#FF0000'), 'to': 5}, {'from': 0, 'data': ('background', '#00FF00'), 'to': 5}, {'from': 5, 'data': ('foreground', '#0000FF'), 'to': 12}, {'from': 5, 'data': ('background', '#00FF00'), 'to': 12}], 'HelloGoodbye'),
([{'from': 0, 'data': ('foreground', '#777777'), 'to': 1}, {'from': 1, 'data': ('foreground', '#00CCCC'), 'to': 6}, {'from': 6, 'data': ('foreground', '#777777'), 'to': 7}], '(stuff)'),
([{'from': 0, 'data': ('foreground', '#FF7F00'), 'to': 6}, {'from': 6, 'data': ('foreground', '#CCCCCC'), 'to': 10}, {'from': 10, 'data': ('foreground', '#FF7F00'), 'to': 16}], 'orangegreyorange'),
([{'from': 0, 'data': ('foreground', '#CCCCCC'), 'to': 3}, {'from': 0, 'data': ('background', '#444444'), 'to': 3}, {'from': 3, 'data': ('foreground', '#FF0000'), 'to': 6}, {'from': 3, 'data': ('background', '#444444'), 'to': 6}], 'sdfjkl'),
([{'from': 2, 'data': ('weight', '\x02'), 'to': 2}], '03,trixy'),
([{'from': 0, 'data': ('weight', '\x02'), 'to': 0}, {'from': 0, 'data': ('foreground', '#FFFFFF'), 'to': 24}], ',000000trixy for bersirc'),
]
#"""
#r = range(20000)
#for i in r:
# for test in tests:
# parse_mirc(test)
"""
lines = [eval(line.strip()) for line in file("parse_mirc_torture_test.txt")]
for r in range(100):
for line in lines:
parse_mirc(line)
#"""
def setify_tags(tags):
return set(frozenset(tag.iteritems()) for tag in tags if tag['from'] != tag['to'])
def parsed_eq((tags1, text1), (tags2, text2)):
return setify_tags(tags1) == setify_tags(tags2) and text1 == text2
def parsed_to_unparsed((tags, text)):
result = []
for i, char in enumerate(text):
result.append((
dict(tag['data'] for tag in tags if tag['from'] <= i < tag['to']),
char))
return result
for i, (test, result) in enumerate(zip(tests, results)):
if not parsed_eq(parse_mirc(test), result):
print "parse_mirc failed test %s:" % i
print repr(test)
print parse_mirc(test)
print result
print
elif not parsed_eq(parse_mirc(unparse_mirc(parsed_to_unparsed(result))), result):
print "unparse_mirc failed test %s:" % i
print repr(test)
print unparse_mirc(test)
print
#import dis
#dis.dis(parse_mirc)