"""Symbolic primitives + unicode/ASCII abstraction for pretty.py"""
import sys
warnings = ''
# first, setup unicodedate environment
try:
import unicodedata
[docs] def U(name):
"""unicode character by name or None if not found"""
try:
u = unicodedata.lookup(name)
except KeyError:
u = None
global warnings
warnings += 'W: no \'%s\' in unocodedata\n' % name
return u
except ImportError:
warnings += 'W: no unicodedata available\n'
U = lambda name: None
from sympy.printing.conventions import split_super_sub
# prefix conventions when constructing tables
# L - LATIN i
# G - GREEK beta
# D - DIGIT 0
# S - SYMBOL +
__all__ = ['greek', 'sub', 'sup', 'xsym', 'vobj', 'hobj', 'pretty_symbol',
'annotated']
_use_unicode = False
[docs]def pretty_use_unicode(flag=None):
"""Set whether pretty-printer should use unicode by default"""
global _use_unicode
global warnings
if flag is None:
return _use_unicode
if flag and warnings:
# print warnings (if any) on first unicode usage
print "I: pprint -- we are going to use unicode, but there are following problems:"
print warnings
warnings = ''
use_unicode_prev = _use_unicode
_use_unicode = flag
return use_unicode_prev
[docs]def pretty_try_use_unicode():
"""See if unicode output is available and leverage it if possible"""
try:
symbols = []
# see, if we can represent greek alphabet
for g, G in greek.itervalues():
symbols.append(g)
symbols.append(G)
# and atoms
symbols += atoms_table.values()
for s in symbols:
if s is None:
return # common symbols not present!
encoding = getattr(sys.stdout, 'encoding', None)
# this happens when e.g. stdout is redirected through a pipe, or is
# e.g. a cStringIO.StringO
if encoding is None:
return # sys.stdout has no encoding
# try to encode
s.encode(encoding)
except UnicodeEncodeError:
pass
else:
pretty_use_unicode(True)
[docs]def xstr(*args):
"""call str or unicode depending on current mode"""
if _use_unicode:
return unicode(*args)
else:
return str(*args)
# GREEK
g = lambda l: U('GREEK SMALL LETTER %s' % l.upper())
G = lambda l: U('GREEK CAPITAL LETTER %s' % l.upper())
greek_letters = [
'alpha', 'beta', 'gamma', 'delta', 'epsilon', 'zeta', 'eta', 'theta',
'iota', 'kappa', 'lamda', 'mu', 'nu', 'xi', 'omicron', 'pi', 'rho',
'sigma', 'tau', 'upsilon', 'phi', 'chi', 'psi', 'omega' ]
# {} greek letter -> (g,G)
greek = dict([(l, (g(l), G(l))) for l in greek_letters])
# aliases
greek['lambda'] = greek['lamda']
digit_2txt = {
'0': 'ZERO',
'1': 'ONE',
'2': 'TWO',
'3': 'THREE',
'4': 'FOUR',
'5': 'FIVE',
'6': 'SIX',
'7': 'SEVEN',
'8': 'EIGHT',
'9': 'NINE',
}
symb_2txt = {
'+': 'PLUS SIGN',
'-': 'MINUS',
'=': 'EQUALS SIGN',
'(': 'LEFT PARENTHESIS',
')': 'RIGHT PARENTHESIS',
'[': 'LEFT SQUARE BRACKET',
']': 'RIGHT SQUARE BRACKET',
'{': 'LEFT CURLY BRACKET',
'}': 'RIGHT CURLY BRACKET',
# non-std
'{}': 'CURLY BRACKET',
'sum': 'SUMMATION',
'int': 'INTEGRAL',
}
# SUBSCRIPT & SUPERSCRIPT
LSUB = lambda letter: U('LATIN SUBSCRIPT SMALL LETTER %s' % letter.upper())
GSUB = lambda letter: U('GREEK SUBSCRIPT SMALL LETTER %s' % letter.upper())
DSUB = lambda digit: U('SUBSCRIPT %s' % digit_2txt[digit])
SSUB = lambda symb: U('SUBSCRIPT %s' % symb_2txt[symb])
LSUP = lambda letter: U('SUPERSCRIPT LATIN SMALL LETTER %s' % letter.upper())
DSUP = lambda digit: U('SUPERSCRIPT %s' % digit_2txt[digit])
SSUP = lambda symb: U('SUPERSCRIPT %s' % symb_2txt[symb])
sub = {} # symb -> subscript symbol
sup = {} # symb -> superscript symbol
# latin subscripts
for l in 'aeioruvx':
sub[l] = LSUB(l)
for l in 'in':
sup[l] = LSUP(l)
for gl in ['beta', 'gamma', 'rho', 'phi', 'chi']:
sub[gl] = GSUB(gl)
for d in [str(i) for i in range(10)]:
sub[d] = DSUB(d)
sup[d] = DSUP(d)
for s in '+-=()':
sub[s] = SSUB(s)
sup[s] = SSUP(s)
# VERTICAL OBJECTS
HUP = lambda symb: U('%s UPPER HOOK' % symb_2txt[symb])
CUP = lambda symb: U('%s UPPER CORNER' % symb_2txt[symb])
MID = lambda symb: U('%s MIDDLE PIECE' % symb_2txt[symb])
EXT = lambda symb: U('%s EXTENSION' % symb_2txt[symb])
HLO = lambda symb: U('%s LOWER HOOK' % symb_2txt[symb])
CLO = lambda symb: U('%s LOWER CORNER' % symb_2txt[symb])
TOP = lambda symb: U('%s TOP' % symb_2txt[symb])
BOT = lambda symb: U('%s BOTTOM' % symb_2txt[symb])
# {} '(' -> (extension, start, end, middle) 1-character
_xobj_unicode = {
# vertical symbols
# (( ext, top, bot, mid ), c1)
'(': (( EXT('('), HUP('('), HLO('(') ), '('),
')': (( EXT(')'), HUP(')'), HLO(')') ), ')'),
'[': (( EXT('['), CUP('['), CLO('[') ), '['),
']': (( EXT(']'), CUP(']'), CLO(']') ), ']'),
'{': (( EXT('{}'), HUP('{'), HLO('{'), MID('{') ), '{'),
'}': (( EXT('{}'), HUP('}'), HLO('}'), MID('}') ), '}'),
'|': U('BOX DRAWINGS LIGHT VERTICAL'),
'<': ((U('BOX DRAWINGS LIGHT VERTICAL'),
U('BOX DRAWINGS LIGHT DIAGONAL UPPER RIGHT TO LOWER LEFT'),
U('BOX DRAWINGS LIGHT DIAGONAL UPPER LEFT TO LOWER RIGHT')), '<'),
'>': ((U('BOX DRAWINGS LIGHT VERTICAL'),
U('BOX DRAWINGS LIGHT DIAGONAL UPPER LEFT TO LOWER RIGHT'),
U('BOX DRAWINGS LIGHT DIAGONAL UPPER RIGHT TO LOWER LEFT')), '>'),
'lfloor': (( EXT('['), EXT('['), CLO('[') ), U('LEFT FLOOR')),
'rfloor': (( EXT(']'), EXT(']'), CLO(']') ), U('RIGHT FLOOR')),
'lceil': (( EXT('['), CUP('['), EXT('[') ), U('LEFT CEILING')),
'rceil': (( EXT(']'), CUP(']'), EXT(']') ), U('RIGHT CEILING')),
'int': (( EXT('int'), U('TOP HALF INTEGRAL'), U('BOTTOM HALF INTEGRAL') ), U('INTEGRAL')),
'sum': (( U('BOX DRAWINGS LIGHT DIAGONAL UPPER LEFT TO LOWER RIGHT'), '_', U('OVERLINE'), U('BOX DRAWINGS LIGHT DIAGONAL UPPER RIGHT TO LOWER LEFT')), U('N-ARY SUMMATION')),
# horizontal objects
#'-': '-',
'-': U('BOX DRAWINGS LIGHT HORIZONTAL'),
'_': U('LOW LINE'),
# We used to use this, but LOW LINE looks better for roots, as it's a
# little lower (i.e., it lines up with the / perfectly. But perhaps this
# one would still be wanted for some cases?
# '_': U('HORIZONTAL SCAN LINE-9'),
# diagonal objects '\' & '/' ?
'/': U('BOX DRAWINGS LIGHT DIAGONAL UPPER RIGHT TO LOWER LEFT'),
'\\': U('BOX DRAWINGS LIGHT DIAGONAL UPPER LEFT TO LOWER RIGHT'),
}
_xobj_ascii = {
# vertical symbols
# (( ext, top, bot, mid ), c1)
'(': (( '|', '/', '\\' ), '('),
')': (( '|', '\\', '/' ), ')'),
# XXX this looks ugly
# '[': (( '|', '-', '-' ), '['),
# ']': (( '|', '-', '-' ), ']'),
# XXX not so ugly :(
'[': (( '[', '[', '[' ), '['),
']': (( ']', ']', ']' ), ']'),
'{': (( '|', '/', '\\', '<' ), '{'),
'}': (( '|', '\\', '/', '>' ), '}'),
'|': '|',
'<': (( '|', '/', '\\' ), '<'),
'>': (( '|', '\\', '/' ), '>'),
'int': ( ' | ', ' /', '/ ' ),
# horizontal objects
'-': '-',
'_': '_',
# diagonal objects '\' & '/' ?
'/': '/',
'\\': '\\',
}
[docs]def xobj(symb, length):
"""Construct spatial object of given length.
return: [] of equal-length strings
"""
assert length > 0
# TODO robustify when no unicodedat available
if _use_unicode:
_xobj = _xobj_unicode
else:
_xobj = _xobj_ascii
vinfo = _xobj[symb]
c1 = top = bot = mid = None
if not isinstance(vinfo, tuple): # 1 entry
ext = vinfo
else:
if isinstance(vinfo[0], tuple): # (vlong), c1
vlong = vinfo[0]
c1 = vinfo[1]
else: # (vlong), c1
vlong = vinfo
ext = vlong[0]
try:
top = vlong[1]
bot = vlong[2]
mid = vlong[3]
except IndexError:
pass
if c1 is None:
c1 = ext
if top is None:
top = ext
if bot is None:
bot = ext
if mid is not None:
if (length % 2) == 0:
# even height, but we have to print it somehow anyway...
# XXX is it ok?
length += 1
else:
mid = ext
if length == 1:
return c1
res = []
next = (length - 2)//2
nmid = (length - 2) - next*2
res += [top]
res += [ext]*next
res += [mid]*nmid
res += [ext]*next
res += [bot]
return res
[docs]def vobj(symb, height):
"""Construct vertical object of a given height
see: xobj
"""
return '\n'.join( xobj(symb, height) )
[docs]def hobj(symb, width):
"""Construct horizontal object of a given width
see: xobj
"""
return ''.join( xobj(symb, width) )
# RADICAL
# n -> symbol
root = {
2: U('SQUARE ROOT'), # U('RADICAL SYMBOL BOTTOM')
3: U('CUBE ROOT'),
4: U('FOURTH ROOT'),
}
# RATIONAL
VF = lambda txt: U('VULGAR FRACTION %s' % txt)
# (p,q) -> symbol
frac = {
(1, 2): VF('ONE HALF'),
(1, 3): VF('ONE THIRD'),
(2, 3): VF('TWO THIRDS'),
(1, 4): VF('ONE QUARTER'),
(3, 4): VF('THREE QUARTERS'),
(1, 5): VF('ONE FIFTH'),
(2, 5): VF('TWO FIFTHS'),
(3, 5): VF('THREE FIFTHS'),
(4, 5): VF('FOUR FIFTHS'),
(1, 6): VF('ONE SIXTH'),
(5, 6): VF('FIVE SIXTHS'),
(1, 8): VF('ONE EIGHTH'),
(3, 8): VF('THREE EIGHTHS'),
(5, 8): VF('FIVE EIGHTHS'),
(7, 8): VF('SEVEN EIGHTHS'),
}
# atom symbols
_xsym = {
'==': ('=', '='),
'<': ('<', '<'),
'>': ('>', '>'),
'<=': ('<=', U('LESS-THAN OR EQUAL TO')),
'>=': ('>=', U('GREATER-THAN OR EQUAL TO')),
'!=': ('!=', U('NOT EQUAL TO')),
'*': ('*', U('DOT OPERATOR')),
'-->': ('-->', U('EM DASH') + U('EM DASH') +
U('BLACK RIGHT-POINTING TRIANGLE')),
'==>': ('==>', U('BOX DRAWINGS DOUBLE HORIZONTAL') +
U('BOX DRAWINGS DOUBLE HORIZONTAL') +
U('BLACK RIGHT-POINTING TRIANGLE')),
'.': ('*', U('RING OPERATOR')),
}
[docs]def xsym(sym):
"""get symbology for a 'character'"""
op = _xsym[sym]
if _use_unicode:
return op[1]
else:
return op[0]
# SYMBOLS
atoms_table = {
# class how-to-display
'Exp1': U('SCRIPT SMALL E'),
'Pi': U('GREEK SMALL LETTER PI'),
'Infinity': U('INFINITY'),
'NegativeInfinity': U('INFINITY') and ('-' + U('INFINITY')), # XXX what to do here
#'ImaginaryUnit': U('GREEK SMALL LETTER IOTA'),
#'ImaginaryUnit': U('MATHEMATICAL ITALIC SMALL I'),
'ImaginaryUnit': U('DOUBLE-STRUCK ITALIC SMALL I'),
'EmptySet': U('EMPTY SET'),
'Naturals': U('DOUBLE-STRUCK CAPITAL N'),
'Integers': U('DOUBLE-STRUCK CAPITAL Z'),
'Reals': U('DOUBLE-STRUCK CAPITAL R'),
'Union': U('UNION'),
'Intersection': U('INTERSECTION'),
'Ring': U('RING OPERATOR')
}
[docs]def pretty_atom(atom_name, default=None):
"""return pretty representation of an atom"""
if _use_unicode:
return atoms_table[atom_name]
else:
if default is not None:
return default
raise KeyError('only unicode') # send it default printer
[docs]def pretty_symbol(symb_name):
"""return pretty representation of a symbol"""
# let's split symb_name into symbol + index
# UC: beta1
# UC: f_beta
if not _use_unicode:
return symb_name
name, sups, subs = split_super_sub(symb_name)
# let's prettify name
gG = greek.get(name.lower())
if gG is not None:
if name.islower():
greek_name = greek.get(name.lower())[0]
else:
greek_name = greek.get(name.lower())[1]
# some letters may not be available
if greek_name is not None:
name = greek_name
# Let's prettify sups/subs. If it fails at one of them, pretty sups/subs are
# not used at all.
def pretty_list(l, mapping):
result = []
for s in l:
pretty = mapping.get(s)
if pretty is None:
try: # match by separate characters
pretty = ''.join([mapping[c] for c in s])
except KeyError:
return None
result.append(pretty)
return result
pretty_sups = pretty_list(sups, sup)
if pretty_sups is not None:
pretty_subs = pretty_list(subs, sub)
else:
pretty_subs = None
# glue the results into one string
if pretty_subs is None: # nice formatting of sups/subs did not work
return symb_name
else:
sups_result = ' '.join(pretty_sups)
subs_result = ' '.join(pretty_subs)
return ''.join([name, sups_result, subs_result])
[docs]def annotated(letter):
"""
Return a stylised drawing of the letter ``letter``, together with
information on how to put annotations (super- and subscripts to the
left and to the right) on it.
See pretty.py functions _print_meijerg, _print_hyper on how to use this
information.
"""
ucode_pics = {
'F': (2, 0, 2, 0, u'\u250c\u2500\n\u251c\u2500\n\u2575'),
'G': (3, 0, 3, 1,
u'\u256d\u2500\u256e\n\u2502\u2576\u2510\n\u2570\u2500\u256f')
}
ascii_pics = {
'F': (3, 0, 3, 0, ' _\n|_\n|\n'),
'G': (3, 0, 3, 1, ' __\n/__\n\_|')
}
if _use_unicode:
return ucode_pics[letter]
else:
return ascii_pics[letter]