Source code for sympy.printing.pretty.pretty_symbology

"""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.values(): symbols.append(g) symbols.append(G) # and atoms symbols += list(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 str(*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') }
[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, '\u250c\u2500\n\u251c\u2500\n\u2575'), 'G': (3, 0, 3, 1, '\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]