Ticket #5079: 5079-preparse-1.patch

File 5079-preparse-1.patch, 7.2 KB (added by robertwb, 12 years ago)
  • sage/misc/preparser.py

    # HG changeset patch
    # User Robert Bradshaw <robertwb@math.washington.edu>
    # Date 1232777584 28800
    # Node ID c2c72812a6267570c06b20e74954fefc638cdf05
    # Parent  95ddaf3f256598b3a656d4693c4fa8f5a6ac9fb8
    Ticket #5079, better preparsing of literal numbers part 1
    
    diff -r 95ddaf3f2565 -r c2c72812a626 sage/misc/preparser.py
    a b  
    431431        '[_sage_const_1 , _sage_const_1p1 , _sage_const_1e1 , -_sage_const_1en1 , _sage_const_1p ]'
    432432       
    433433        sage: extract_numeric_literals("[1.sqrt(), 1.2.sqrt(), 1r, 1.2r, R.1, R0.1, (1..5)]")[0]
    434         '[_sage_const_1 .sqrt(), _sage_const_1p2 .sqrt(), 1r, 1.2r, R.1, R0.1, (_sage_const_1 .._sage_const_5 )]'
     434        '[_sage_const_1 .sqrt(), _sage_const_1p2 .sqrt(), 1 , 1.2 , R.1, R0.1, (_sage_const_1 .._sage_const_5 )]'
     435    """
     436    return preparse_numeric_literals(code, True)
     437   
     438def preparse_numeric_literals(code, extract=False):
     439    """
     440    This preparses numerical literals into their sage counterparts,
     441    e.g. Integer, RealNumber, and ComplexNumber.
     442    If extract is true, then it will create names for the literals
     443    and return a dict of name-construction pairs along with the
     444    processed code.
     445   
     446    EXAMPLES:
     447        sage: from sage.misc.preparser import preparse_numeric_literals
     448        sage: preparse_numeric_literals("5")
     449        'Integer(5)'
     450        sage: preparse_numeric_literals("5j")
     451        "ComplexNumber(0, '5')"
     452        sage: preparse_numeric_literals("5jr")
     453        '5J'
     454        sage: preparse_numeric_literals("5l")
     455        '5l'
     456        sage: preparse_numeric_literals("5L")
     457        '5L'
     458        sage: preparse_numeric_literals("1.5")
     459        "RealNumber('1.5')"
     460        sage: preparse_numeric_literals("1.5j")
     461        "ComplexNumber(0, '1.5')"
     462        sage: preparse_numeric_literals(".5j")
     463        "ComplexNumber(0, '.5')"
     464        sage: preparse_numeric_literals("5e9j")
     465        "ComplexNumber(0, '5e9')"
     466        sage: preparse_numeric_literals("5.")
     467        "RealNumber('5.')"
     468        sage: preparse_numeric_literals("5.j")
     469        "ComplexNumber(0, '5.')"
     470        sage: preparse_numeric_literals("5.foo()")
     471        'Integer(5).foo()'
     472        sage: preparse_numeric_literals("5.5.foo()")
     473        "RealNumber('5.5').foo()"
     474        sage: preparse_numeric_literals("5.5j.foo()")
     475        "ComplexNumber(0, '5.5').foo()"
     476        sage: preparse_numeric_literals("5j.foo()")
     477        "ComplexNumber(0, '5').foo()"
     478        sage: preparse_numeric_literals("1.exp()")
     479        'Integer(1).exp()'
     480        sage: preparse_numeric_literals("1e+10")
     481        "RealNumber('1e+10')"
     482       
     483    It may be overly forgiving in some cases:
     484        sage: preparse_numeric_literals("0xArrrrr")
     485        '0xA'
    435486    """
    436487    literals = {}
    437488    last = 0
    438489    new_code = []
    439     dec_num = r"\b\d+\b"
    440     hex_num = r"\b0x[0-9a-fA-F]\b"
     490    dec_num = r"\b\d+"
     491    hex_num = r"\b0x[0-9a-fA-F]"
    441492    # This is slightly annoying as floating point numbers may start
    442493    # with a decimal point, but if they do the \b will not match.
    443     float_num = r"((\b\d+([.]\d*)?)|([.]\d+))([eE][-+]?\d+)?\b"
    444     all_num = r"(%s)|(%s)|(%s)" % (float_num, dec_num, hex_num)
     494    float_num = r"((\b\d+([.]\d*)?)|([.]\d+))([eE][-+]?\d+)?"
     495    all_num = r"((%s)|(%s)|(%s))([RrLlJj]*)\b" % (float_num, dec_num, hex_num)
    445496    for m in re.finditer(all_num, code):
    446         num, start, end = m.group(), m.start(), m.end()
    447 
    448         # The Sage preparser does extra things with numbers, which we need to handle here.
    449         if '.' in num:
    450             if start > 0 and num[0] == '.':
    451                 if code[start-1] == '.':
    452                     # handle Ellipsis
    453                     start += 1
    454                     num = num[1:]
    455                 elif re.match(r'[a-zA-Z0-9_\])]', code[start-1]):
    456                     # handle R.0
    457                     continue
    458             elif end < len(code) and num[-1] == '.':
    459                 if re.match('[a-zA-Z]', code[end]):
    460                     # handle 4.sqrt()
    461                     end -= 1
    462                     num = num[:-1]
    463                 elif re.match(r'\d', code[end]):
    464                     # handle 4.0r
    465                     continue
    466         elif end < len(code) and code[end] == '.':
    467             # \b does not match after the .
    468             # two dots in a row would be an ellipsis
    469             if end+1 == len(code) or code[end+1] != '.':
    470                 end += 1
    471                 num += '.'
     497        start, end = m.start(), m.end()
     498        num = m.group(1)
     499        postfix = m.groups()[-1].upper()
     500       
     501        if 'R' in postfix:
     502            num_name = num_make = num + postfix.replace('R', '')
     503        elif 'L' in postfix:
     504            continue
     505        else:
     506       
     507            # The Sage preparser does extra things with numbers, which we need to handle here.
     508            if '.' in num:
     509                if start > 0 and num[0] == '.':
     510                    if code[start-1] == '.':
     511                        # handle Ellipsis
     512                        start += 1
     513                        num = num[1:]
     514                    elif re.match(r'[a-zA-Z0-9_\])]', code[start-1]):
     515                        # handle R.0
     516                        continue
     517                elif end < len(code) and num[-1] == '.':
     518                    if re.match('[a-zA-Z]', code[end]):
     519                        # handle 4.sqrt()
     520                        end -= 1
     521                        num = num[:-1]
     522            elif end < len(code) and code[end] == '.' and not postfix:
     523                # \b does not match after the .
     524                # two dots in a row would be an ellipsis
     525                if end+1 == len(code) or code[end+1] != '.':
     526                    end += 1
     527                    num += '.'
     528               
     529            if '.' in num or 'e' in num or 'E' in num or 'J' in postfix:
     530                num_name = numeric_literal_prefix + num.replace('.', 'p').replace('-', 'n').replace('+', '')
     531                if 'J' in postfix:
     532                    num_make = "ComplexNumber(0, '%s')" % num
     533                    num_name += 'j'
     534                else:
     535                    num_make = "RealNumber('%s')" % num
     536            else:
     537                num_name = numeric_literal_prefix + num
     538                num_make = "Integer(%s)" % num
     539       
     540            literals[num_name] = num_make
    472541           
    473         if '.' in num or 'e' in num or 'E' in num:
    474             num_name = numeric_literal_prefix + num.replace('.', 'p').replace('-', 'n').replace('+', '')
    475             num_make = "RealNumber('%s')" % num
     542        new_code.append(code[last:start])
     543        if extract:
     544            new_code.append(num_name+' ')
    476545        else:
    477             num_name = numeric_literal_prefix + num
    478             num_make = "Integer(%s)" % num
    479         literals[num_name] = num_make
    480         new_code.append(code[last:start])
    481         new_code.append(num_name+' ')
     546            new_code.append(num_make)
    482547        last = end
    483548       
    484549    new_code.append(code[last:])
    485     return ''.join(new_code), literals
     550    code = ''.join(new_code)
     551    if extract:
     552        return code, literals
     553    else:
     554        return code
     555
    486556
    487557def strip_prompts(line):
    488558    r"""Get rid of leading sage: and >>> prompts so that pasting of examples from