Ticket #2347: 2347-5.patch

File 2347-5.patch, 8.3 KB (added by robertwb, 14 years ago)
  • sage/calculus/calculus.py

    # HG changeset patch
    # User Robert Bradshaw <robertwb@math.washington.edu>
    # Date 1207895517 25200
    # Node ID cec740e603143f0ee77011ab1fee03377497ae06
    # Parent  5c7590ae6837cf1cebd5a6dc33bf373fc2aeb7db
    Remove use of sage_eval for symbolic expression parsing, allow construction via a string.
    
    diff -r 5c7590ae6837 -r cec740e60314 sage/calculus/calculus.py
    a b import sage.numerical.optimize 
    266266# separate from the default system-wide version.
    267267maxima = Maxima(init_code = ['display2d:false; domain: complex; keepfloat: true'])
    268268
    269 from sage.misc.sage_eval import sage_eval
     269from sage.misc.parser import Parser, LookupNameMaker
    270270
    271271from sage.calculus.equations import SymbolicEquation
    272272from sage.rings.real_mpfr import RealNumber
    class SymbolicExpressionRing_class(uniq, 
    439439            return x
    440440        elif hasattr(x, '_symbolic_'):
    441441            return x._symbolic_(self)
     442        elif isinstance(x, str):
     443            try:
     444                return symbolic_expression_from_string(x)
     445            except SyntaxError, err:
     446                msg, s, pos = err.args
     447                raise TypeError, "%s: %s !!! %s" % (msg, s[:pos], s[pos:])
    442448        return self._coerce_impl(x)
    443449
    444450    def _coerce_impl(self, x):
    class SymbolicExpression(RingElement): 
    33723378            try:
    33733379                f = self.polynomial(QQ)
    33743380                w = repr(f.factor())
    3375                 return sage_eval(w, _vars)
     3381                return symbolic_expression_from_string(w, _vars)
    33763382            except TypeError:
    33773383                pass
    33783384            return self.parent()(self._maxima_().factor())
    class SymbolicConstant(Symbolic_object): 
    41544160
    41554161        return SymbolicArithmetic([self, right], operator.div)       
    41564162
    4157 
     4163    def __neg__(self):
     4164        return SymbolicConstant(-self._obj)
    41584165
    41594166    def __pow__(self, right):
    41604167        """
    class SymbolicArithmetic(SymbolicOperati 
    48474854        for o in ops:
    48484855            try:
    48494856                obj = o._obj
    4850                 if isinstance(obj, Rational):
     4857                # negative numbers are not handled correctly because _is_atomic has no sense of precedence
     4858                if o is ops[0] and str(obj)[0] == '-':
     4859                    temp = SymbolicConstant(obj)
     4860                    temp._operator = operator.neg
     4861                    temp._binary = False
     4862                    temp._unary = True
     4863                    temp._precedence = 2000
     4864                    li.append(temp)
     4865                elif isinstance(obj, Rational):
    48514866                    temp = SymbolicConstant(obj)
    48524867                    if not temp._obj.is_integral():
    48534868                        temp._operator = operator.div
    def log(x, base=None): 
    73847399            return x.log(base)
    73857400        except AttributeError:
    73867401            return function_log(x) / function_log(base)
     7402           
     7403_syms['log'] = log
     7404_syms['ln'] = log
    73877405
    73887406#####################
    73897407# The polylogarithm
    def symbolic_expression_from_maxima_stri 
    81588176        # use a global flag so all expressions obtained via
    81598177        # evaluation of maxima code are assumed pre-simplified
    81608178        is_simplified = True
     8179        return symbolic_expression_from_string(s, syms, accept_sequence=True)
    81618180        last_msg = ''
    81628181        while True:
    81638182            try:
    def maxima_options(**kwds): 
    82748293    return ','.join(['%s=%s'%(key,mapped_opts(val)) for key, val in kwds.iteritems()])
    82758294
    82768295
     8296# Parser for symbolic ring elements
     8297
     8298_augmented_syms = {}
     8299
     8300def _find_var(name):
     8301    try:
     8302        return (_augmented_syms or _syms)[name]
     8303    except KeyError:
     8304        pass
     8305    try:
     8306        return SR(sage.all.__dict__[name])
     8307    except KeyError:
     8308        return var(name)
     8309
     8310SR_parser = Parser(make_int      = lambda x: SymbolicConstant(Integer(x)),
     8311                   make_float    = lambda x: SymbolicConstant(create_RealNumber(x)),
     8312                   make_var      = _find_var,
     8313                   make_function = LookupNameMaker(_syms, function))
     8314                   
     8315def symbolic_expression_from_string(s, syms=None, accept_sequence=False):
     8316    parse_func = SR_parser.parse_sequence if accept_sequence else SR_parser.parse_expression
     8317    if syms is None:
     8318        return parse_func(s)
     8319    else:
     8320        try:
     8321            global _augmented_syms
     8322            _augmented_syms = syms
     8323            return parse_func(s)
     8324        finally:
     8325            _augmented_syms = {}
  • sage/misc/parser.pyx

    diff -r 5c7590ae6837 -r cec740e60314 sage/misc/parser.pyx
    a b def token_to_str(int token): 
    9696
    9797
    9898cdef inline bint is_alphanumeric(char c):
    99     return 'a' <= c < 'z' or 'A' <= c <= 'Z' or '0' <= c <= '9' or c == '_'
     99    return 'a' <= c <= 'z' or 'A' <= c <= 'Z' or '0' <= c <= '9' or c == '_'
    100100       
    101101cdef inline bint is_whitespace(char c):
    102102    return (c != 0) & (strchr(" \t\n\r", c) != NULL)
    cdef class Tokenizer: 
    186186            token = self.next()
    187187        return all
    188188       
    189     def reset(self, int pos = 0):
     189    cpdef reset(self, int pos = 0):
    190190        """
    191191        Reset the tokenizer to a given position.
    192192       
    cdef class Parser: 
    548548                    obj = self.integer_constructor(tokens.last_token_string())
    549549                else:
    550550                    tokens.backtrack()
    551                     obj = self.p_expr(tokens)
     551                    obj = self.p_eqn(tokens)
    552552            elif token == '[':
    553553                obj = self.p_list(tokens)
    554554            elif token == '(':
    cdef class Parser: 
    559559                tokens.token = ','
    560560                return all
    561561            else:
    562                 obj = self.p_expr(tokens)
     562                obj = self.p_eqn(tokens)
    563563            PyList_Append(all, obj)
    564564            token = tokens.next()
    565565           
    566566        tokens.backtrack()
    567567        return all
    568568       
    569     cpdef p_list(self, tokens):
     569    cpdef p_list(self, Tokenizer tokens):
    570570        """
    571571        Parse a list of items.
    572572       
    cdef class Parser: 
    587587            self.parse_error(tokens, "Malformed list")
    588588        return all
    589589       
    590     cpdef p_tuple(self, tokens):
     590    cpdef p_tuple(self, Tokenizer tokens):
    591591        """
    592592        Parse a tuple of items.
    593593       
    594594        EXAMPLES:
    595595            sage: from sage.misc.parser import Parser, Tokenizer
    596596            sage: p = Parser(make_var=var)
    597             sage: p.p_tuple(Tokenizer("( (), (1), (1,), (1,2), (1,2,3), )"))
    598             ((), 1, (1,), (1, 2), (1, 2, 3))
     597            sage: p.p_tuple(Tokenizer("( (), (1), (1,), (1,2), (1,2,3), (1+2)^2, )"))
     598            ((), 1, (1,), (1, 2), (1, 2, 3), 9)
    599599        """
     600        cdef int start = tokens.pos
    600601        cdef int token = tokens.next()
    601602        cdef bint real_tuple = True
    602603        if token != '(':
    cdef class Parser: 
    611612        if real_tuple:
    612613            return tuple(all)
    613614        else:
    614             return all[0]
     615            token = tokens.peek()
     616            if token == ',' or token == EOS:
     617                return all[0]
     618            else:
     619                # we have to reparse the entire thing as an expression
     620                tokens.reset(start)
     621                return self.p_eqn(tokens)
    615622       
    616623# eqn ::= expr op expr | expr
    617624    cpdef p_eqn(self, Tokenizer tokens):
    cdef class Parser: 
    641648        """
    642649        lhs = self.p_expr(tokens)
    643650        cdef int op = tokens.next()
    644         if op == EOS:
    645             return lhs
    646         elif op == '=':
     651        if op == '=':
    647652            return lhs == self.p_expr(tokens)
    648653        elif op == NOT_EQ:
    649654            return lhs != self.p_expr(tokens)
    cdef class Parser: 
    656661        elif op == GREATER_EQ:
    657662            return lhs >= self.p_expr(tokens)
    658663        else:
    659             self.parse_error(tokens, "Malformed equation")
     664            tokens.backtrack()
     665            return lhs
    660666       
    661667# expr ::=  term | expr '+' term | expr '-' term
    662668    cpdef p_expr(self, Tokenizer tokens):
    cdef class Parser: 
    887893            return self.p_expr(tokens)
    888894           
    889895    cdef parse_error(self, Tokenizer tokens, msg="Malformed expression"):
    890         raise ValueError, (msg, tokens.s, tokens.pos)
     896        raise SyntaxError, (msg, tokens.s, tokens.pos)
    891897
    892898
    893899cdef class LookupNameMaker: