Ticket #2347: 2347-4.patch

File 2347-4.patch, 64.4 KB (added by robertwb, 14 years ago)
  • deleted file sage/calculus/parser.pyx

    # HG changeset patch
    # User Robert Bradshaw <robertwb@math.washington.edu>
    # Date 1207880056 25200
    # Node ID 5c7590ae6837cf1cebd5a6dc33bf373fc2aeb7db
    # Parent  e5b5b9eeff71c8dc42cdfdf077c1690dc35719c4
    Move parser to misc
    
    diff -r e5b5b9eeff71 -r 5c7590ae6837 sage/calculus/parser.pyx
    + -  
    1 """
    2 This module provides a parser for symbolic equations and expressions.
    3 
    4 It is both safer and more powerful than using Python's eval, as one has
    5 complete control over what names are used (including dynamically creating
    6 variables) and how integer and floating point literals are created.
    7 
    8 AUTHOR:
    9     -- Robert Bradshaw 2008-04 (initial version)
    10 """
    11 
    12 #*****************************************************************************
    13 #     Copyright (C) 2008 Robert Bradshaw <robertwb@math.washington.edu>
    14 #
    15 #  Distributed under the terms of the GNU General Public License (GPL)
    16 #
    17 #    This code is distributed in the hope that it will be useful,
    18 #    but WITHOUT ANY WARRANTY; without even the implied warranty of
    19 #    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
    20 #    General Public License for more details.
    21 #
    22 #  The full text of the GPL is available at:
    23 #
    24 #                  http://www.gnu.org/licenses/
    25 #*****************************************************************************
    26 
    27 cdef extern from "string.h":
    28     char *strchr(char *str, int ch)
    29    
    30 cdef extern from "Python.h":
    31     object PyString_FromStringAndSize(char *v, Py_ssize_t len)
    32     int PyList_Append(object list, object item) except -1
    33 
    34 import math
    35 
    36 def foo(*args, **kwds):
    37     """
    38     This is a function for testing that simply returns the arguments and
    39     keywords passed into it.
    40    
    41     EXAMPLES:
    42         sage: from sage.calculus.parser import foo
    43         sage: foo(1, 2, a=3)
    44         ((1, 2), {'a': 3})
    45     """
    46     return args, kwds
    47 
    48 fuction_map = {
    49   'foo': foo,
    50   'sqrt': math.sqrt,
    51   'sin': math.sin,
    52   'cos': math.cos,
    53   'tan': math.tan,
    54 }
    55 
    56 cdef enum token_types:
    57     # leave room for ASCII character tokens such as '+'
    58     INT = 128
    59     FLOAT
    60     NAME
    61     EOS
    62     ERROR
    63    
    64     LESS_EQ
    65     GREATER_EQ
    66     NOT_EQ
    67 
    68 enum_map = {
    69   INT:        'INT',
    70   FLOAT:      'FLOAT',
    71   NAME:       'NAME',
    72   EOS:        'EOS',
    73   ERROR:      'ERROR',
    74   LESS_EQ:    'LESS_EQ',
    75   GREATER_EQ: 'GREATER_EQ',
    76   NOT_EQ:     'NOT_EQ',
    77 }
    78 
    79 def token_to_str(int token):
    80     """
    81     For speed reasons, tokens are integers. This function returns a string
    82     representation of a given token.
    83    
    84     EXAMPLES:
    85         sage: from sage.calculus.parser import Tokenizer, token_to_str
    86         sage: t = Tokenizer("+ 2")
    87         sage: token_to_str(t.next())
    88         '+'
    89         sage: token_to_str(t.next())
    90         'INT'
    91     """
    92     try:
    93         return enum_map[token]
    94     except KeyError:
    95         return chr(token)
    96 
    97 
    98 cdef inline bint is_alphanumeric(char c):
    99     return 'a' <= c < 'z' or 'A' <= c <= 'Z' or '0' <= c <= '9' or c == '_'
    100        
    101 cdef inline bint is_whitespace(char c):
    102     return (c != 0) & (strchr(" \t\n\r", c) != NULL)
    103 
    104 
    105 cdef class Tokenizer:
    106     cdef char *s
    107     cdef string_obj
    108     cdef int token
    109     cdef int pos
    110     cdef int last_pos
    111    
    112     def __init__(self, s):
    113         """
    114         This class takes a string and turns it into a list of tokens for use
    115         by the parser.
    116        
    117         The tokenizer wraps a string object, to tokenize a different string
    118         create a new tokenizer.
    119        
    120         EXAMPLES:
    121             sage: from sage.calculus.parser import Tokenizer
    122             sage: Tokenizer("1.5+2*3^4-sin(x)").test()
    123             ['FLOAT(1.5)', '+', 'INT(2)', '*', 'INT(3)', '^', 'INT(4)', '-', 'NAME(sin)', '(', 'NAME(x)', ')']
    124            
    125         The single character tokens are given by:
    126             sage: Tokenizer("+-*/^(),=<>[]{}").test()
    127             ['+', '-', '*', '/', '^', '(', ')', ',', '=', '<', '>', '[', ']', '{', '}']
    128            
    129         Two-character comparisons accepted are:
    130             sage: Tokenizer("<= >= != == **").test()
    131             ['LESS_EQ', 'GREATER_EQ', 'NOT_EQ', '=', '^']
    132            
    133         Integers are strings of 0-9:
    134             sage: Tokenizer("1 123 9879834759873452908375013").test()
    135             ['INT(1)', 'INT(123)', 'INT(9879834759873452908375013)']
    136            
    137         Floating point numbers can contain a single decimal point and possibly exponential notation:
    138             sage: Tokenizer("1. .01 1e3 1.e-3").test()
    139             ['FLOAT(1.)', 'FLOAT(.01)', 'FLOAT(1e3)', 'FLOAT(1.e-3)']
    140            
    141         Note that negative signes are not attached to the token:
    142             sage: Tokenizer("-1 -1.2").test()
    143             ['-', 'INT(1)', '-', 'FLOAT(1.2)']
    144 
    145         Names are alphanumeric sequences not starting with a digit:
    146             sage: Tokenizer("a a1 _a_24").test()
    147             ['NAME(a)', 'NAME(a1)', 'NAME(_a_24)']
    148        
    149         Anything else is an error:
    150             sage: Tokenizer("&@!~").test()
    151             ['ERROR', 'ERROR', 'ERROR', 'ERROR']
    152 
    153         No attempt for correctness is made at this stage:
    154             sage: Tokenizer(") )( 5e5e5").test()
    155             [')', ')', '(', 'FLOAT(5e5)', 'NAME(e5)']
    156             sage: Tokenizer("?$%").test()
    157             ['ERROR', 'ERROR', 'ERROR']
    158         """
    159         self.pos = 0
    160         self.last_pos = 0
    161         self.s = s
    162         self.string_obj = s # so it doesn't get deallocated before self
    163        
    164     def test(self):
    165         """
    166         This is a utility function for easy testing of the tokenizer.
    167        
    168         Distructively read off the tokens in self, returning a list of string
    169         representations of the tokens.
    170 
    171         EXAMPLES:
    172             sage: from sage.calculus.parser import Tokenizer
    173             sage: t = Tokenizer("a b 3")
    174             sage: t.test()
    175             ['NAME(a)', 'NAME(b)', 'INT(3)']
    176             sage: t.test()
    177             []
    178         """
    179         all = []
    180         cdef int token = self.next()
    181         while token != EOS:
    182             if token in [INT, FLOAT, NAME]:
    183                 all.append("%s(%s)" % (token_to_str(token), self.last_token_string()))
    184             else:
    185                 all.append(token_to_str(token))
    186             token = self.next()
    187         return all
    188        
    189     def reset(self, int pos = 0):
    190         """
    191         Reset the tokenizer to a given position.
    192        
    193         EXAMPLES:
    194             sage: from sage.calculus.parser import Tokenizer
    195             sage: t = Tokenizer("a+b*c")
    196             sage: t.test()
    197             ['NAME(a)', '+', 'NAME(b)', '*', 'NAME(c)']
    198             sage: t.test()
    199             []
    200             sage: t.reset()
    201             sage: t.test()
    202             ['NAME(a)', '+', 'NAME(b)', '*', 'NAME(c)']
    203             sage: t.reset(3)
    204             sage: t.test()
    205             ['*', 'NAME(c)']
    206            
    207         No care is taken to make sure we don't jump in the middle of a token:
    208             sage: t = Tokenizer("12345+a")
    209             sage: t.test()
    210             ['INT(12345)', '+', 'NAME(a)']
    211             sage: t.reset(2)
    212             sage: t.test()
    213             ['INT(345)', '+', 'NAME(a)']
    214         """
    215         self.pos = self.last_pos = pos
    216        
    217     cdef int find(self) except -1:
    218         """
    219         This function actually does all the work, and extensively is tested above.
    220         """
    221         cdef bint seen_exp, seen_decimal
    222         cdef int type
    223         cdef char* s = self.s
    224         cdef int pos = self.pos
    225        
    226         # skip whitespace
    227         if is_whitespace(s[pos]):
    228             while is_whitespace(s[pos]):
    229                 pos += 1
    230             self.pos = pos
    231 
    232         # end of string
    233         if s[pos] == 0:
    234             return EOS
    235            
    236         # dipthongs
    237         if s[pos+1] == '=':
    238             if s[pos] == '<':
    239                 self.pos += 2
    240                 return LESS_EQ
    241             elif s[pos] == '>':
    242                 self.pos += 2
    243                 return GREATER_EQ
    244             elif s[pos] == '!':
    245                 self.pos += 2
    246                 return NOT_EQ
    247             elif s[pos] == '=':
    248                 self.pos += 2
    249                 return '='
    250                
    251         elif s[pos] == '*' and s[pos+1] == '*':
    252             self.pos += 2
    253             return '^'
    254            
    255         # simple tokens
    256         if strchr("+-*/^()=<>,[]{}", s[pos]):
    257             type = s[pos]
    258             self.pos += 1
    259             return type
    260                            
    261         # numeric literals
    262         if '0' <= s[pos] <= '9' or s[pos] == '.':
    263             type = INT
    264             seen_exp = False
    265             seen_decimal = False
    266             while True:
    267                 if '0' <= s[pos] <= '9':
    268                     pass
    269                 elif s[pos] == '.':
    270                     if seen_decimal or seen_exp:
    271                         self.pos = pos
    272                         return type
    273                     else:
    274                         type = FLOAT
    275                         seen_decimal = True
    276                 elif s[pos] == 'e' or s[pos] == 'E':
    277                     if seen_exp:
    278                         self.pos = pos
    279                         return type
    280                     else:
    281                         type = FLOAT
    282                         seen_exp = True
    283                 elif s[pos] == '+' or s[pos] == '-':
    284                     if not (seen_exp and (s[pos-1] == 'e' or s[pos-1] == 'E')):
    285                         self.pos = pos
    286                         return type
    287                 else:
    288                     self.pos = pos
    289                     return type
    290                 pos += 1
    291                
    292         # name literals
    293         if is_alphanumeric(s[pos]):
    294             while is_alphanumeric(s[pos]):
    295                 pos += 1
    296             self.pos = pos
    297             return NAME
    298            
    299         pos += 1
    300         self.pos = pos
    301         return ERROR
    302        
    303     cpdef int next(self):
    304         """
    305         Returns the next token in the string.
    306        
    307         EXAMPLES:
    308             sage: from sage.calculus.parser import Tokenizer, token_to_str
    309             sage: t = Tokenizer("a+3")
    310             sage: token_to_str(t.next())
    311             'NAME'
    312             sage: token_to_str(t.next())
    313             '+'
    314             sage: token_to_str(t.next())
    315             'INT'
    316             sage: token_to_str(t.next())
    317             'EOS'
    318         """
    319         while is_whitespace(self.s[self.pos]):
    320             self.pos += 1
    321         self.last_pos = self.pos
    322         self.token = self.find()
    323         return self.token
    324        
    325     cpdef int last(self):
    326         """
    327         Returns the last token seen.
    328        
    329         EXAMPLES:
    330             sage: from sage.calculus.parser import Tokenizer, token_to_str
    331             sage: t = Tokenizer("3a")
    332             sage: token_to_str(t.next())
    333             'INT'
    334             sage: token_to_str(t.last())
    335             'INT'
    336             sage: token_to_str(t.next())
    337             'NAME'
    338             sage: token_to_str(t.last())
    339             'NAME'
    340         """
    341         return self.token
    342        
    343     cpdef int peek(self):
    344         """
    345         Returns the next token that will be encountered, without changing
    346         the state of self.
    347        
    348         EXAMPLES:
    349             sage: from sage.calculus.parser import Tokenizer, token_to_str
    350             sage: t = Tokenizer("a+b")
    351             sage: token_to_str(t.peek())
    352             'NAME'
    353             sage: token_to_str(t.next())
    354             'NAME'
    355             sage: token_to_str(t.peek())
    356             '+'
    357             sage: token_to_str(t.peek())
    358             '+'
    359             sage: token_to_str(t.next())
    360             '+'
    361         """
    362         cdef int save_pos = self.pos
    363         cdef int token = self.find()
    364         self.pos = save_pos
    365         return token
    366        
    367     cpdef bint backtrack(self) except -2:
    368         """
    369         Put self in such a state that the subsequent call to next() will
    370         return the same as if next() had not been called.
    371        
    372         Currently, one can only backtrack once.
    373        
    374         EXAMPLES:
    375             sage: from sage.calculus.parser import Tokenizer, token_to_str
    376             sage: t = Tokenizer("a+b")
    377             sage: token_to_str(t.next())
    378             'NAME'
    379             sage: token_to_str(t.next())
    380             '+'
    381             sage: t.backtrack()   # the return type is bint for performance reasons
    382             False
    383             sage: token_to_str(t.next())
    384             '+'
    385         """
    386         if self.pos == self.last_pos and self.token != EOS:
    387             raise NotImplementedError, "Can only backtrack once."
    388         else:
    389             self.pos = self.last_pos
    390             self.token = 0
    391        
    392     cpdef last_token_string(self):
    393         """
    394         Return the actual contents of the last token.
    395        
    396         EXAMPLES:
    397             sage: from sage.calculus.parser import Tokenizer, token_to_str
    398             sage: t = Tokenizer("a - 1e5")
    399             sage: token_to_str(t.next())
    400             'NAME'
    401             sage: t.last_token_string()
    402             'a'
    403             sage: token_to_str(t.next())
    404             '-'
    405             sage: token_to_str(t.next())
    406             'FLOAT'
    407             sage: t.last_token_string()
    408             '1e5'
    409         """
    410         return PyString_FromStringAndSize(&self.s[self.last_pos], self.pos-self.last_pos)
    411 
    412        
    413 cdef class Parser:
    414 
    415     cdef integer_constructor
    416     cdef float_constructor
    417     cdef variable_constructor
    418     cdef callable_constructor
    419     cdef bint implicit_multiplication
    420    
    421     def __init__(self, make_int=int, make_float=float, make_var=str, make_function={}, bint implicit_multiplication=True):
    422         """
    423         Create a symbolic expression parser.
    424        
    425         INPUT:
    426             make_int      -- callable object to construct integers from strings (default int)
    427             make_float    -- callable object to construct real numbers from strings (default float)
    428             make_var      -- callable object to construct variables from strings (default str)
    429                              this may also be a dictionary of variable names
    430             make_function -- callable object to construct callable functions from strings
    431                              this may also be a dictionary
    432             implicit_multiplication -- whether or not to accept implicit multiplication
    433            
    434         OUTPUT:
    435             The evaluated expression tree given by the string, where the above
    436             functions are used to create the leaves of this tree.
    437            
    438         EXAMPLES:
    439             sage: from sage.calculus.parser import Parser
    440             sage: p = Parser()
    441             sage: p.parse("1+2")
    442             3
    443             sage: p.parse("1+2 == 3")
    444             True
    445 
    446             sage: p = Parser(make_var=var)
    447             sage: p.parse("a*b^c - 3a")
    448             a*b^c - 3*a
    449            
    450             sage: R.<x> = QQ[]
    451             sage: p = Parser(make_var = {'x': x })
    452             sage: p.parse("(x+1)^5-x")
    453             x^5 + 5*x^4 + 10*x^3 + 10*x^2 + 4*x + 1
    454             sage: p.parse("(x+1)^5-x").parent() is R
    455             True
    456 
    457             sage: p = Parser(make_float=RR, make_var=var, make_function={'foo': (lambda x: x*x+x)})
    458             sage: p.parse("1.5 + foo(b)")
    459             b^2 + b + 1.50000000000000
    460             sage: p.parse("1.9").parent()
    461             Real Field with 53 bits of precision
    462         """
    463         self.integer_constructor = make_int
    464         self.float_constructor = make_float
    465         if not callable(make_var):
    466             make_var = LookupNameMaker(make_var)
    467         if not callable(make_function):
    468             make_function = LookupNameMaker(make_function)
    469         self.variable_constructor = make_var
    470         self.callable_constructor = make_function
    471         self.implicit_multiplication = implicit_multiplication
    472        
    473     cpdef parse(self, s, bint accept_eqn=True):
    474         """
    475         Parse the given string.
    476        
    477         EXAMPLES:
    478             sage: from sage.calculus.parser import Parser
    479             sage: p = Parser(make_var=var)
    480             sage: p.parse("E = m c^2")
    481             E == c^2*m
    482         """
    483         cdef Tokenizer tokens = Tokenizer(s)
    484         expr = self.p_eqn(tokens) if accept_eqn else self.p_expr(tokens)
    485         if tokens.next() != EOS:
    486             self.parse_error(tokens)
    487         return expr
    488        
    489     cpdef parse_expression(self, s):
    490         """
    491         Parse an expression.
    492        
    493         EXAMPLES:
    494             sage: from sage.calculus.parser import Parser
    495             sage: p = Parser(make_var=var)
    496             sage: p.parse_expression('a-3b^2')
    497             a - 3*b^2
    498         """
    499         cdef Tokenizer tokens = Tokenizer(s)
    500         expr = self.p_expr(tokens)
    501         if tokens.next() != EOS:
    502             self.parse_error(tokens)
    503         return expr
    504        
    505     cpdef parse_sequence(self, s):
    506         """
    507         Parse a (possibly nested) set of lists and tuples.
    508        
    509         EXAMPLES:
    510             sage: from sage.calculus.parser import Parser
    511             sage: p = Parser(make_var=var)
    512             sage: p.parse_sequence("1,2,3")
    513             [1, 2, 3]
    514             sage: p.parse_sequence("[1,2,(a,b,c+d)]")
    515             [1, 2, (a, b, d + c)]
    516             sage: p.parse_sequence("13")
    517             13
    518         """
    519         cdef Tokenizer tokens = Tokenizer(s)
    520         all = self.p_sequence(tokens)
    521         if tokens.next() != EOS:
    522             self.parse_error(tokens)
    523         if len(all) == 1 and type(all) is list:
    524             all = all[0]
    525         return all
    526        
    527        
    528     cpdef p_sequence(self, Tokenizer tokens):
    529         """
    530         Parse a (possibly nested) set of lists and tuples.
    531        
    532         EXAMPLES:
    533             sage: from sage.calculus.parser import Parser, Tokenizer
    534             sage: p = Parser(make_var=var)
    535             sage: p.p_sequence(Tokenizer("[1+2,0]"))
    536             [[3, 0]]
    537             sage: p.p_sequence(Tokenizer("(1,2,3) , [1+a, 2+b, (3+c), (4+d,)]"))
    538             [(1, 2, 3), [a + 1, b + 2, c + 3, (d + 4,)]]
    539         """
    540         all = []
    541         cdef int token = ','
    542         while token == ',':
    543             token = tokens.peek()
    544             if token == INT:
    545                 # we optimize for this rather than going all the way to atom
    546                 tokens.next()
    547                 if tokens.peek() == c',':
    548                     obj = self.integer_constructor(tokens.last_token_string())
    549                 else:
    550                     tokens.backtrack()
    551                     obj = self.p_expr(tokens)
    552             elif token == '[':
    553                 obj = self.p_list(tokens)
    554             elif token == '(':
    555                 obj = self.p_tuple(tokens)
    556             elif token == EOS:
    557                 return all
    558             elif token == ']' or token == ')':
    559                 tokens.token = ','
    560                 return all
    561             else:
    562                 obj = self.p_expr(tokens)
    563             PyList_Append(all, obj)
    564             token = tokens.next()
    565            
    566         tokens.backtrack()
    567         return all
    568        
    569     cpdef p_list(self, tokens):
    570         """
    571         Parse a list of items.
    572        
    573         EXAMPLES:
    574             sage: from sage.calculus.parser import Parser, Tokenizer
    575             sage: p = Parser(make_var=var)
    576             sage: p.p_list(Tokenizer("[1+2, 1e3]"))
    577             [3, 1000.0]
    578             sage: p.p_list(Tokenizer("[]"))
    579             []
    580         """
    581         cdef int token = tokens.next()
    582         if token != '[':
    583             self.parse_error(tokens, "Malformed list")
    584         all = self.p_sequence(tokens)
    585         token = tokens.next()
    586         if token != ']':
    587             self.parse_error(tokens, "Malformed list")
    588         return all
    589        
    590     cpdef p_tuple(self, tokens):
    591         """
    592         Parse a tuple of items.
    593        
    594         EXAMPLES:
    595             sage: from sage.calculus.parser import Parser, Tokenizer
    596             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))
    599         """
    600         cdef int token = tokens.next()
    601         cdef bint real_tuple = True
    602         if token != '(':
    603             self.parse_error(tokens, "Malformed tuple")
    604         all = self.p_sequence(tokens)
    605         if len(all) == 1:
    606             if tokens.last() != c',':
    607                 real_tuple = False
    608         token = tokens.next()
    609         if token != ')':
    610             self.parse_error(tokens, "Malformed tuple")
    611         if real_tuple:
    612             return tuple(all)
    613         else:
    614             return all[0]
    615        
    616 # eqn ::= expr op expr | expr
    617     cpdef p_eqn(self, Tokenizer tokens):
    618         """
    619         Parse an equation or expression.
    620        
    621         This is the top-level node called by the \code{parse} function.
    622        
    623         EXAMPLES:
    624             sage: from sage.calculus.parser import Parser, Tokenizer
    625             sage: p = Parser(make_var=var)
    626             sage: p.p_eqn(Tokenizer("1+a"))
    627             a + 1
    628            
    629             sage: p.p_eqn(Tokenizer("a == b"))
    630             a == b
    631             sage: p.p_eqn(Tokenizer("a < b"))
    632             a < b
    633             sage: p.p_eqn(Tokenizer("a > b"))
    634             a > b
    635             sage: p.p_eqn(Tokenizer("a <= b"))
    636             a <= b
    637             sage: p.p_eqn(Tokenizer("a >= b"))
    638             a >= b
    639             sage: p.p_eqn(Tokenizer("a != b"))
    640             a != b
    641         """
    642         lhs = self.p_expr(tokens)
    643         cdef int op = tokens.next()
    644         if op == EOS:
    645             return lhs
    646         elif op == '=':
    647             return lhs == self.p_expr(tokens)
    648         elif op == NOT_EQ:
    649             return lhs != self.p_expr(tokens)
    650         elif op == '<':
    651             return lhs < self.p_expr(tokens)
    652         elif op == LESS_EQ:
    653             return lhs <= self.p_expr(tokens)
    654         elif op == '>':
    655             return lhs > self.p_expr(tokens)
    656         elif op == GREATER_EQ:
    657             return lhs >= self.p_expr(tokens)
    658         else:
    659             self.parse_error(tokens, "Malformed equation")
    660        
    661 # expr ::=  term | expr '+' term | expr '-' term
    662     cpdef p_expr(self, Tokenizer tokens):
    663         """
    664         Parse a list of one or more terms.
    665        
    666         EXAMPLES:
    667             sage: from sage.calculus.parser import Parser, Tokenizer
    668             sage: p = Parser(make_var=var)
    669             sage: p.p_expr(Tokenizer("a+b"))
    670             b + a
    671             sage: p.p_expr(Tokenizer("a"))
    672             a
    673             sage: p.p_expr(Tokenizer("a - b + 4*c - d^2"))
    674             -d^2 + 4*c - b + a
    675             sage: p.p_expr(Tokenizer("a - -3"))
    676             a + 3
    677             sage: p.p_expr(Tokenizer("a + 1 == b"))
    678             a + 1
    679         """
    680         # Note: this is left-recursive, so we can't just recurse
    681         cdef int op
    682         operand1 = self.p_term(tokens)
    683         op = tokens.next()
    684         while op == '+' or op == '-':
    685             operand2 = self.p_term(tokens)
    686             if op == '+':
    687                 operand1 = operand1 + operand2
    688             else:
    689                 operand1 = operand1 - operand2
    690             op = tokens.next()
    691         tokens.backtrack()
    692         return operand1
    693            
    694 # term ::=  factor | term '*' factor | term '/' factor
    695     cpdef p_term(self, Tokenizer tokens):
    696         """
    697         Parse a single term (consisting of one or more factors).
    698        
    699         EXAMPLES:
    700             sage: from sage.calculus.parser import Parser, Tokenizer
    701             sage: p = Parser(make_var=var)
    702             sage: p.p_term(Tokenizer("a*b"))
    703             a*b
    704             sage: p.p_term(Tokenizer("a * b / c * d"))
    705             a*b*d/c
    706             sage: p.p_term(Tokenizer("-a * b + c"))
    707             -a*b
    708             sage: p.p_term(Tokenizer("a*(b-c)^2"))
    709             a*(b - c)^2
    710             sage: p.p_term(Tokenizer("-3a"))
    711             -3*a
    712         """
    713         # Note: this is left-recursive, so we can't just recurse
    714         cdef int op
    715         operand1 = self.p_factor(tokens)
    716         op = tokens.next()
    717         if op == NAME and self.implicit_multiplication:
    718             op = '*'
    719             tokens.backtrack()
    720         while op == '*' or op == '/':
    721             operand2 = self.p_factor(tokens)
    722             if op == '*':
    723                 operand1 = operand1 * operand2
    724             else:
    725                 operand1 = operand1 / operand2
    726             op = tokens.next()
    727             if op == NAME and self.implicit_multiplication:
    728                 op = '*'
    729                 tokens.backtrack()
    730         tokens.backtrack()
    731         return operand1
    732        
    733 # factor ::=  '+' factor | '-' factor | power
    734     cpdef p_factor(self, Tokenizer tokens):
    735         """
    736         Parse a single factor, which consists of any number of unary +/-
    737         and a power.
    738        
    739         EXAMPLES:
    740             sage: from sage.calculus.parser import Parser, Tokenizer
    741             sage: R.<t> = ZZ[['t']]
    742             sage: p = Parser(make_var={'t': t})
    743             sage: p.p_factor(Tokenizer("- -t"))
    744             t
    745             sage: p.p_factor(Tokenizer("- + - -t^2"))
    746             -t^2
    747             sage: p.p_factor(Tokenizer("t^11 * x"))
    748             t^11
    749         """
    750         cdef int token = tokens.next()
    751         if token == '+':
    752             return self.p_factor(tokens)
    753         elif token == '-':
    754             return -self.p_factor(tokens)
    755         else:
    756             tokens.backtrack()
    757             return self.p_power(tokens)
    758            
    759 # power ::=  atom ^ factor | atom
    760     cpdef p_power(self, Tokenizer tokens):
    761         """
    762         Parses a power. Note that exponentiation groups right to left. 
    763        
    764         EXAMPLES:
    765             sage: from sage.calculus.parser import Parser, Tokenizer
    766             sage: R.<t> = ZZ[['t']]
    767             sage: p = Parser(make_var={'t': t})
    768             sage: p.p_factor(Tokenizer("-(1+t)^-1"))
    769             -1 + t - t^2 + t^3 - t^4 + t^5 - t^6 + t^7 - t^8 + t^9 - t^10 + t^11 - t^12 + t^13 - t^14 + t^15 - t^16 + t^17 - t^18 + t^19 + O(t^20)
    770             sage: p.p_factor(Tokenizer("t**2"))
    771             t^2
    772             sage: p.p_power(Tokenizer("2^3^2")) == 2^9
    773             True
    774         """
    775         operand1 = self.p_atom(tokens)
    776         cdef int token = tokens.next()
    777         if token == '^':
    778             operand2 = self.p_factor(tokens)
    779             return operand1 ** operand2
    780         else:
    781             tokens.backtrack()
    782             return operand1
    783 
    784 # atom ::= int | float | name | '(' expr ')' | name '(' args ')'
    785     cpdef p_atom(self, Tokenizer tokens):
    786         """
    787         Parse an atom. This is either a parenthesized expression, a function call, or a literal name/int/float.
    788        
    789         EXAMPLES:
    790             sage: from sage.calculus.parser import Parser, Tokenizer
    791             sage: p = Parser(make_var=var, make_function={'sin': sin})
    792             sage: p.p_atom(Tokenizer("1"))
    793             1
    794             sage: p.p_atom(Tokenizer("12"))
    795             12
    796             sage: p.p_atom(Tokenizer("12.5"))
    797             12.5
    798             sage: p.p_atom(Tokenizer("(1+a)"))
    799             a + 1
    800             sage: p.p_atom(Tokenizer("(1+a)^2"))
    801             a + 1
    802             sage: p.p_atom(Tokenizer("sin(1+a)"))
    803             sin(a + 1)
    804             sage: p = Parser(make_var=var, make_function={'foo': sage.calculus.parser.foo})
    805             sage: p.p_atom(Tokenizer("foo(a, b, key=value)"))
    806             ((a, b), {'key': value})
    807             sage: p.p_atom(Tokenizer("foo()"))
    808             ((), {})
    809         """
    810         cdef int token = tokens.next()
    811         if token == INT:
    812             return self.integer_constructor(tokens.last_token_string())
    813         elif token == FLOAT:
    814             return self.float_constructor(tokens.last_token_string())
    815         elif token == NAME:
    816             name = tokens.last_token_string()
    817             token = tokens.next()
    818             if token == '(':
    819                 func = self.callable_constructor(name)
    820                 args, kwds = self.p_args(tokens)
    821                 token = tokens.next()
    822                 if token != ')':
    823                     self.parse_error(tokens, "Bad function call")
    824                 return func(*args, **kwds)
    825             else:
    826                 tokens.backtrack()
    827                 return self.variable_constructor(name)
    828         elif token == '(':
    829             expr = self.p_expr(tokens)
    830             token = tokens.next()
    831             if token != ')':
    832                 self.parse_error(tokens, "Mismatched parentheses")
    833             return expr
    834         else:
    835             self.parse_error(tokens)
    836        
    837 # args = arg (',' arg)* | EMPTY
    838     cpdef p_args(self, Tokenizer tokens):
    839         """
    840         Returns a list, dict pair.
    841        
    842         EXAMPLES:
    843             sage: from sage.calculus.parser import Parser, Tokenizer
    844             sage: p = Parser()
    845             sage: p.p_args(Tokenizer("1,2,a=3"))
    846             ([1, 2], {'a': 3})
    847             sage: p.p_args(Tokenizer("1, 2, a = 1+5^2"))
    848             ([1, 2], {'a': 26})
    849         """
    850         args = []
    851         kwds = {}
    852         if tokens.peek() == ')':
    853             return args, kwds
    854         cdef int token = ','
    855         while token == ',':
    856             arg = self.p_arg(tokens)
    857             if isinstance(arg, tuple):
    858                 name, value = arg
    859                 kwds[name] = value
    860             else:
    861                 args.append(arg)
    862             token = tokens.next()
    863         tokens.backtrack()
    864         return args, kwds
    865 
    866 # arg = expr | name '=' expr
    867     cpdef p_arg(self, Tokenizer tokens):
    868         """
    869         Returns an expr, or a (name, expr) tuple corresponding to a single
    870         function call argument.
    871        
    872         EXAMPLES:
    873             sage: from sage.calculus.parser import Parser, Tokenizer
    874             sage: p = Parser(make_var=var)
    875             sage: p.p_arg(Tokenizer("a+b"))
    876             b + a
    877             sage: p.p_arg(Tokenizer("val=a+b"))
    878             ('val', b + a)
    879         """
    880         cdef int token = tokens.next()
    881         if token == NAME and tokens.peek() == '=':
    882             name = tokens.last_token_string()
    883             tokens.next()
    884             return name, self.p_expr(tokens)
    885         else:
    886             tokens.backtrack()
    887             return self.p_expr(tokens)
    888            
    889     cdef parse_error(self, Tokenizer tokens, msg="Malformed expression"):
    890         raise ValueError, (msg, tokens.s, tokens.pos)
    891 
    892 
    893 cdef class LookupNameMaker:
    894     cdef object names
    895     cdef object fallback
    896     def __init__(self, names, fallback=None):
    897         """
    898         This class wraps a dictionary as a callable for use in creating names.
    899         It takes a dictionary of names, and an (optional) callable to use
    900         when the given name is not found in the dictionary.
    901        
    902         EXAMPLES:
    903             sage: from sage.calculus.parser import LookupNameMaker
    904             sage: maker = LookupNameMaker({'pi': pi}, var)
    905             sage: maker('pi')
    906             pi
    907             sage: maker('pi') is pi
    908             True
    909             sage: maker('a')
    910             a
    911         """
    912         self.names = names
    913         self.fallback = fallback
    914     def __call__(self, name):
    915         """
    916         TESTS:
    917             sage: from sage.calculus.parser import LookupNameMaker
    918             sage: maker = LookupNameMaker({'a': x}, str)
    919             sage: maker('a')
    920             x
    921             sage: maker('a') is x
    922             True
    923             sage: maker('b')
    924             'b'
    925         """
    926         try:
    927             return self.names[name]
    928         except KeyError:
    929             if self.fallback is not None:
    930                 return self.fallback(name)
    931             raise NameError, "Unknown variable: '%s'" % name
    932 
    933 
  • new file sage/misc/parser.pyx

    diff -r e5b5b9eeff71 -r 5c7590ae6837 sage/misc/parser.pyx
    - +  
     1"""
     2This module provides a parser for symbolic equations and expressions.
     3
     4It is both safer and more powerful than using Python's eval, as one has
     5complete control over what names are used (including dynamically creating
     6variables) and how integer and floating point literals are created.
     7
     8AUTHOR:
     9    -- Robert Bradshaw 2008-04 (initial version)
     10"""
     11
     12#*****************************************************************************
     13#     Copyright (C) 2008 Robert Bradshaw <robertwb@math.washington.edu>
     14#
     15#  Distributed under the terms of the GNU General Public License (GPL)
     16#
     17#    This code is distributed in the hope that it will be useful,
     18#    but WITHOUT ANY WARRANTY; without even the implied warranty of
     19#    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
     20#    General Public License for more details.
     21#
     22#  The full text of the GPL is available at:
     23#
     24#                  http://www.gnu.org/licenses/
     25#*****************************************************************************
     26
     27cdef extern from "string.h":
     28    char *strchr(char *str, int ch)
     29   
     30cdef extern from "Python.h":
     31    object PyString_FromStringAndSize(char *v, Py_ssize_t len)
     32    int PyList_Append(object list, object item) except -1
     33
     34import math
     35
     36def foo(*args, **kwds):
     37    """
     38    This is a function for testing that simply returns the arguments and
     39    keywords passed into it.
     40   
     41    EXAMPLES:
     42        sage: from sage.misc.parser import foo
     43        sage: foo(1, 2, a=3)
     44        ((1, 2), {'a': 3})
     45    """
     46    return args, kwds
     47
     48fuction_map = {
     49  'foo': foo,
     50  'sqrt': math.sqrt,
     51  'sin': math.sin,
     52  'cos': math.cos,
     53  'tan': math.tan,
     54}
     55
     56cdef enum token_types:
     57    # leave room for ASCII character tokens such as '+'
     58    INT = 128
     59    FLOAT
     60    NAME
     61    EOS
     62    ERROR
     63   
     64    LESS_EQ
     65    GREATER_EQ
     66    NOT_EQ
     67
     68enum_map = {
     69  INT:        'INT',
     70  FLOAT:      'FLOAT',
     71  NAME:       'NAME',
     72  EOS:        'EOS',
     73  ERROR:      'ERROR',
     74  LESS_EQ:    'LESS_EQ',
     75  GREATER_EQ: 'GREATER_EQ',
     76  NOT_EQ:     'NOT_EQ',
     77}
     78
     79def token_to_str(int token):
     80    """
     81    For speed reasons, tokens are integers. This function returns a string
     82    representation of a given token.
     83   
     84    EXAMPLES:
     85        sage: from sage.misc.parser import Tokenizer, token_to_str
     86        sage: t = Tokenizer("+ 2")
     87        sage: token_to_str(t.next())
     88        '+'
     89        sage: token_to_str(t.next())
     90        'INT'
     91    """
     92    try:
     93        return enum_map[token]
     94    except KeyError:
     95        return chr(token)
     96
     97
     98cdef inline bint is_alphanumeric(char c):
     99    return 'a' <= c < 'z' or 'A' <= c <= 'Z' or '0' <= c <= '9' or c == '_'
     100       
     101cdef inline bint is_whitespace(char c):
     102    return (c != 0) & (strchr(" \t\n\r", c) != NULL)
     103
     104
     105cdef class Tokenizer:
     106    cdef char *s
     107    cdef string_obj
     108    cdef int token
     109    cdef int pos
     110    cdef int last_pos
     111   
     112    def __init__(self, s):
     113        """
     114        This class takes a string and turns it into a list of tokens for use
     115        by the parser.
     116       
     117        The tokenizer wraps a string object, to tokenize a different string
     118        create a new tokenizer.
     119       
     120        EXAMPLES:
     121            sage: from sage.misc.parser import Tokenizer
     122            sage: Tokenizer("1.5+2*3^4-sin(x)").test()
     123            ['FLOAT(1.5)', '+', 'INT(2)', '*', 'INT(3)', '^', 'INT(4)', '-', 'NAME(sin)', '(', 'NAME(x)', ')']
     124           
     125        The single character tokens are given by:
     126            sage: Tokenizer("+-*/^(),=<>[]{}").test()
     127            ['+', '-', '*', '/', '^', '(', ')', ',', '=', '<', '>', '[', ']', '{', '}']
     128           
     129        Two-character comparisons accepted are:
     130            sage: Tokenizer("<= >= != == **").test()
     131            ['LESS_EQ', 'GREATER_EQ', 'NOT_EQ', '=', '^']
     132           
     133        Integers are strings of 0-9:
     134            sage: Tokenizer("1 123 9879834759873452908375013").test()
     135            ['INT(1)', 'INT(123)', 'INT(9879834759873452908375013)']
     136           
     137        Floating point numbers can contain a single decimal point and possibly exponential notation:
     138            sage: Tokenizer("1. .01 1e3 1.e-3").test()
     139            ['FLOAT(1.)', 'FLOAT(.01)', 'FLOAT(1e3)', 'FLOAT(1.e-3)']
     140           
     141        Note that negative signes are not attached to the token:
     142            sage: Tokenizer("-1 -1.2").test()
     143            ['-', 'INT(1)', '-', 'FLOAT(1.2)']
     144
     145        Names are alphanumeric sequences not starting with a digit:
     146            sage: Tokenizer("a a1 _a_24").test()
     147            ['NAME(a)', 'NAME(a1)', 'NAME(_a_24)']
     148       
     149        Anything else is an error:
     150            sage: Tokenizer("&@!~").test()
     151            ['ERROR', 'ERROR', 'ERROR', 'ERROR']
     152
     153        No attempt for correctness is made at this stage:
     154            sage: Tokenizer(") )( 5e5e5").test()
     155            [')', ')', '(', 'FLOAT(5e5)', 'NAME(e5)']
     156            sage: Tokenizer("?$%").test()
     157            ['ERROR', 'ERROR', 'ERROR']
     158        """
     159        self.pos = 0
     160        self.last_pos = 0
     161        self.s = s
     162        self.string_obj = s # so it doesn't get deallocated before self
     163       
     164    def test(self):
     165        """
     166        This is a utility function for easy testing of the tokenizer.
     167       
     168        Distructively read off the tokens in self, returning a list of string
     169        representations of the tokens.
     170
     171        EXAMPLES:
     172            sage: from sage.misc.parser import Tokenizer
     173            sage: t = Tokenizer("a b 3")
     174            sage: t.test()
     175            ['NAME(a)', 'NAME(b)', 'INT(3)']
     176            sage: t.test()
     177            []
     178        """
     179        all = []
     180        cdef int token = self.next()
     181        while token != EOS:
     182            if token in [INT, FLOAT, NAME]:
     183                all.append("%s(%s)" % (token_to_str(token), self.last_token_string()))
     184            else:
     185                all.append(token_to_str(token))
     186            token = self.next()
     187        return all
     188       
     189    def reset(self, int pos = 0):
     190        """
     191        Reset the tokenizer to a given position.
     192       
     193        EXAMPLES:
     194            sage: from sage.misc.parser import Tokenizer
     195            sage: t = Tokenizer("a+b*c")
     196            sage: t.test()
     197            ['NAME(a)', '+', 'NAME(b)', '*', 'NAME(c)']
     198            sage: t.test()
     199            []
     200            sage: t.reset()
     201            sage: t.test()
     202            ['NAME(a)', '+', 'NAME(b)', '*', 'NAME(c)']
     203            sage: t.reset(3)
     204            sage: t.test()
     205            ['*', 'NAME(c)']
     206           
     207        No care is taken to make sure we don't jump in the middle of a token:
     208            sage: t = Tokenizer("12345+a")
     209            sage: t.test()
     210            ['INT(12345)', '+', 'NAME(a)']
     211            sage: t.reset(2)
     212            sage: t.test()
     213            ['INT(345)', '+', 'NAME(a)']
     214        """
     215        self.pos = self.last_pos = pos
     216       
     217    cdef int find(self) except -1:
     218        """
     219        This function actually does all the work, and extensively is tested above.
     220        """
     221        cdef bint seen_exp, seen_decimal
     222        cdef int type
     223        cdef char* s = self.s
     224        cdef int pos = self.pos
     225       
     226        # skip whitespace
     227        if is_whitespace(s[pos]):
     228            while is_whitespace(s[pos]):
     229                pos += 1
     230            self.pos = pos
     231
     232        # end of string
     233        if s[pos] == 0:
     234            return EOS
     235           
     236        # dipthongs
     237        if s[pos+1] == '=':
     238            if s[pos] == '<':
     239                self.pos += 2
     240                return LESS_EQ
     241            elif s[pos] == '>':
     242                self.pos += 2
     243                return GREATER_EQ
     244            elif s[pos] == '!':
     245                self.pos += 2
     246                return NOT_EQ
     247            elif s[pos] == '=':
     248                self.pos += 2
     249                return '='
     250               
     251        elif s[pos] == '*' and s[pos+1] == '*':
     252            self.pos += 2
     253            return '^'
     254           
     255        # simple tokens
     256        if strchr("+-*/^()=<>,[]{}", s[pos]):
     257            type = s[pos]
     258            self.pos += 1
     259            return type
     260                           
     261        # numeric literals
     262        if '0' <= s[pos] <= '9' or s[pos] == '.':
     263            type = INT
     264            seen_exp = False
     265            seen_decimal = False
     266            while True:
     267                if '0' <= s[pos] <= '9':
     268                    pass
     269                elif s[pos] == '.':
     270                    if seen_decimal or seen_exp:
     271                        self.pos = pos
     272                        return type
     273                    else:
     274                        type = FLOAT
     275                        seen_decimal = True
     276                elif s[pos] == 'e' or s[pos] == 'E':
     277                    if seen_exp:
     278                        self.pos = pos
     279                        return type
     280                    else:
     281                        type = FLOAT
     282                        seen_exp = True
     283                elif s[pos] == '+' or s[pos] == '-':
     284                    if not (seen_exp and (s[pos-1] == 'e' or s[pos-1] == 'E')):
     285                        self.pos = pos
     286                        return type
     287                else:
     288                    self.pos = pos
     289                    return type
     290                pos += 1
     291               
     292        # name literals
     293        if is_alphanumeric(s[pos]):
     294            while is_alphanumeric(s[pos]):
     295                pos += 1
     296            self.pos = pos
     297            return NAME
     298           
     299        pos += 1
     300        self.pos = pos
     301        return ERROR
     302       
     303    cpdef int next(self):
     304        """
     305        Returns the next token in the string.
     306       
     307        EXAMPLES:
     308            sage: from sage.misc.parser import Tokenizer, token_to_str
     309            sage: t = Tokenizer("a+3")
     310            sage: token_to_str(t.next())
     311            'NAME'
     312            sage: token_to_str(t.next())
     313            '+'
     314            sage: token_to_str(t.next())
     315            'INT'
     316            sage: token_to_str(t.next())
     317            'EOS'
     318        """
     319        while is_whitespace(self.s[self.pos]):
     320            self.pos += 1
     321        self.last_pos = self.pos
     322        self.token = self.find()
     323        return self.token
     324       
     325    cpdef int last(self):
     326        """
     327        Returns the last token seen.
     328       
     329        EXAMPLES:
     330            sage: from sage.misc.parser import Tokenizer, token_to_str
     331            sage: t = Tokenizer("3a")
     332            sage: token_to_str(t.next())
     333            'INT'
     334            sage: token_to_str(t.last())
     335            'INT'
     336            sage: token_to_str(t.next())
     337            'NAME'
     338            sage: token_to_str(t.last())
     339            'NAME'
     340        """
     341        return self.token
     342       
     343    cpdef int peek(self):
     344        """
     345        Returns the next token that will be encountered, without changing
     346        the state of self.
     347       
     348        EXAMPLES:
     349            sage: from sage.misc.parser import Tokenizer, token_to_str
     350            sage: t = Tokenizer("a+b")
     351            sage: token_to_str(t.peek())
     352            'NAME'
     353            sage: token_to_str(t.next())
     354            'NAME'
     355            sage: token_to_str(t.peek())
     356            '+'
     357            sage: token_to_str(t.peek())
     358            '+'
     359            sage: token_to_str(t.next())
     360            '+'
     361        """
     362        cdef int save_pos = self.pos
     363        cdef int token = self.find()
     364        self.pos = save_pos
     365        return token
     366       
     367    cpdef bint backtrack(self) except -2:
     368        """
     369        Put self in such a state that the subsequent call to next() will
     370        return the same as if next() had not been called.
     371       
     372        Currently, one can only backtrack once.
     373       
     374        EXAMPLES:
     375            sage: from sage.misc.parser import Tokenizer, token_to_str
     376            sage: t = Tokenizer("a+b")
     377            sage: token_to_str(t.next())
     378            'NAME'
     379            sage: token_to_str(t.next())
     380            '+'
     381            sage: t.backtrack()   # the return type is bint for performance reasons
     382            False
     383            sage: token_to_str(t.next())
     384            '+'
     385        """
     386        if self.pos == self.last_pos and self.token != EOS:
     387            raise NotImplementedError, "Can only backtrack once."
     388        else:
     389            self.pos = self.last_pos
     390            self.token = 0
     391       
     392    cpdef last_token_string(self):
     393        """
     394        Return the actual contents of the last token.
     395       
     396        EXAMPLES:
     397            sage: from sage.misc.parser import Tokenizer, token_to_str
     398            sage: t = Tokenizer("a - 1e5")
     399            sage: token_to_str(t.next())
     400            'NAME'
     401            sage: t.last_token_string()
     402            'a'
     403            sage: token_to_str(t.next())
     404            '-'
     405            sage: token_to_str(t.next())
     406            'FLOAT'
     407            sage: t.last_token_string()
     408            '1e5'
     409        """
     410        return PyString_FromStringAndSize(&self.s[self.last_pos], self.pos-self.last_pos)
     411
     412       
     413cdef class Parser:
     414
     415    cdef integer_constructor
     416    cdef float_constructor
     417    cdef variable_constructor
     418    cdef callable_constructor
     419    cdef bint implicit_multiplication
     420   
     421    def __init__(self, make_int=int, make_float=float, make_var=str, make_function={}, bint implicit_multiplication=True):
     422        """
     423        Create a symbolic expression parser.
     424       
     425        INPUT:
     426            make_int      -- callable object to construct integers from strings (default int)
     427            make_float    -- callable object to construct real numbers from strings (default float)
     428            make_var      -- callable object to construct variables from strings (default str)
     429                             this may also be a dictionary of variable names
     430            make_function -- callable object to construct callable functions from strings
     431                             this may also be a dictionary
     432            implicit_multiplication -- whether or not to accept implicit multiplication
     433           
     434        OUTPUT:
     435            The evaluated expression tree given by the string, where the above
     436            functions are used to create the leaves of this tree.
     437           
     438        EXAMPLES:
     439            sage: from sage.misc.parser import Parser
     440            sage: p = Parser()
     441            sage: p.parse("1+2")
     442            3
     443            sage: p.parse("1+2 == 3")
     444            True
     445
     446            sage: p = Parser(make_var=var)
     447            sage: p.parse("a*b^c - 3a")
     448            a*b^c - 3*a
     449           
     450            sage: R.<x> = QQ[]
     451            sage: p = Parser(make_var = {'x': x })
     452            sage: p.parse("(x+1)^5-x")
     453            x^5 + 5*x^4 + 10*x^3 + 10*x^2 + 4*x + 1
     454            sage: p.parse("(x+1)^5-x").parent() is R
     455            True
     456
     457            sage: p = Parser(make_float=RR, make_var=var, make_function={'foo': (lambda x: x*x+x)})
     458            sage: p.parse("1.5 + foo(b)")
     459            b^2 + b + 1.50000000000000
     460            sage: p.parse("1.9").parent()
     461            Real Field with 53 bits of precision
     462        """
     463        self.integer_constructor = make_int
     464        self.float_constructor = make_float
     465        if not callable(make_var):
     466            make_var = LookupNameMaker(make_var)
     467        if not callable(make_function):
     468            make_function = LookupNameMaker(make_function)
     469        self.variable_constructor = make_var
     470        self.callable_constructor = make_function
     471        self.implicit_multiplication = implicit_multiplication
     472       
     473    cpdef parse(self, s, bint accept_eqn=True):
     474        """
     475        Parse the given string.
     476       
     477        EXAMPLES:
     478            sage: from sage.misc.parser import Parser
     479            sage: p = Parser(make_var=var)
     480            sage: p.parse("E = m c^2")
     481            E == c^2*m
     482        """
     483        cdef Tokenizer tokens = Tokenizer(s)
     484        expr = self.p_eqn(tokens) if accept_eqn else self.p_expr(tokens)
     485        if tokens.next() != EOS:
     486            self.parse_error(tokens)
     487        return expr
     488       
     489    cpdef parse_expression(self, s):
     490        """
     491        Parse an expression.
     492       
     493        EXAMPLES:
     494            sage: from sage.misc.parser import Parser
     495            sage: p = Parser(make_var=var)
     496            sage: p.parse_expression('a-3b^2')
     497            a - 3*b^2
     498        """
     499        cdef Tokenizer tokens = Tokenizer(s)
     500        expr = self.p_expr(tokens)
     501        if tokens.next() != EOS:
     502            self.parse_error(tokens)
     503        return expr
     504       
     505    cpdef parse_sequence(self, s):
     506        """
     507        Parse a (possibly nested) set of lists and tuples.
     508       
     509        EXAMPLES:
     510            sage: from sage.misc.parser import Parser
     511            sage: p = Parser(make_var=var)
     512            sage: p.parse_sequence("1,2,3")
     513            [1, 2, 3]
     514            sage: p.parse_sequence("[1,2,(a,b,c+d)]")
     515            [1, 2, (a, b, d + c)]
     516            sage: p.parse_sequence("13")
     517            13
     518        """
     519        cdef Tokenizer tokens = Tokenizer(s)
     520        all = self.p_sequence(tokens)
     521        if tokens.next() != EOS:
     522            self.parse_error(tokens)
     523        if len(all) == 1 and type(all) is list:
     524            all = all[0]
     525        return all
     526       
     527       
     528    cpdef p_sequence(self, Tokenizer tokens):
     529        """
     530        Parse a (possibly nested) set of lists and tuples.
     531       
     532        EXAMPLES:
     533            sage: from sage.misc.parser import Parser, Tokenizer
     534            sage: p = Parser(make_var=var)
     535            sage: p.p_sequence(Tokenizer("[1+2,0]"))
     536            [[3, 0]]
     537            sage: p.p_sequence(Tokenizer("(1,2,3) , [1+a, 2+b, (3+c), (4+d,)]"))
     538            [(1, 2, 3), [a + 1, b + 2, c + 3, (d + 4,)]]
     539        """
     540        all = []
     541        cdef int token = ','
     542        while token == ',':
     543            token = tokens.peek()
     544            if token == INT:
     545                # we optimize for this rather than going all the way to atom
     546                tokens.next()
     547                if tokens.peek() == c',':
     548                    obj = self.integer_constructor(tokens.last_token_string())
     549                else:
     550                    tokens.backtrack()
     551                    obj = self.p_expr(tokens)
     552            elif token == '[':
     553                obj = self.p_list(tokens)
     554            elif token == '(':
     555                obj = self.p_tuple(tokens)
     556            elif token == EOS:
     557                return all
     558            elif token == ']' or token == ')':
     559                tokens.token = ','
     560                return all
     561            else:
     562                obj = self.p_expr(tokens)
     563            PyList_Append(all, obj)
     564            token = tokens.next()
     565           
     566        tokens.backtrack()
     567        return all
     568       
     569    cpdef p_list(self, tokens):
     570        """
     571        Parse a list of items.
     572       
     573        EXAMPLES:
     574            sage: from sage.misc.parser import Parser, Tokenizer
     575            sage: p = Parser(make_var=var)
     576            sage: p.p_list(Tokenizer("[1+2, 1e3]"))
     577            [3, 1000.0]
     578            sage: p.p_list(Tokenizer("[]"))
     579            []
     580        """
     581        cdef int token = tokens.next()
     582        if token != '[':
     583            self.parse_error(tokens, "Malformed list")
     584        all = self.p_sequence(tokens)
     585        token = tokens.next()
     586        if token != ']':
     587            self.parse_error(tokens, "Malformed list")
     588        return all
     589       
     590    cpdef p_tuple(self, tokens):
     591        """
     592        Parse a tuple of items.
     593       
     594        EXAMPLES:
     595            sage: from sage.misc.parser import Parser, Tokenizer
     596            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))
     599        """
     600        cdef int token = tokens.next()
     601        cdef bint real_tuple = True
     602        if token != '(':
     603            self.parse_error(tokens, "Malformed tuple")
     604        all = self.p_sequence(tokens)
     605        if len(all) == 1:
     606            if tokens.last() != c',':
     607                real_tuple = False
     608        token = tokens.next()
     609        if token != ')':
     610            self.parse_error(tokens, "Malformed tuple")
     611        if real_tuple:
     612            return tuple(all)
     613        else:
     614            return all[0]
     615       
     616# eqn ::= expr op expr | expr
     617    cpdef p_eqn(self, Tokenizer tokens):
     618        """
     619        Parse an equation or expression.
     620       
     621        This is the top-level node called by the \code{parse} function.
     622       
     623        EXAMPLES:
     624            sage: from sage.misc.parser import Parser, Tokenizer
     625            sage: p = Parser(make_var=var)
     626            sage: p.p_eqn(Tokenizer("1+a"))
     627            a + 1
     628           
     629            sage: p.p_eqn(Tokenizer("a == b"))
     630            a == b
     631            sage: p.p_eqn(Tokenizer("a < b"))
     632            a < b
     633            sage: p.p_eqn(Tokenizer("a > b"))
     634            a > b
     635            sage: p.p_eqn(Tokenizer("a <= b"))
     636            a <= b
     637            sage: p.p_eqn(Tokenizer("a >= b"))
     638            a >= b
     639            sage: p.p_eqn(Tokenizer("a != b"))
     640            a != b
     641        """
     642        lhs = self.p_expr(tokens)
     643        cdef int op = tokens.next()
     644        if op == EOS:
     645            return lhs
     646        elif op == '=':
     647            return lhs == self.p_expr(tokens)
     648        elif op == NOT_EQ:
     649            return lhs != self.p_expr(tokens)
     650        elif op == '<':
     651            return lhs < self.p_expr(tokens)
     652        elif op == LESS_EQ:
     653            return lhs <= self.p_expr(tokens)
     654        elif op == '>':
     655            return lhs > self.p_expr(tokens)
     656        elif op == GREATER_EQ:
     657            return lhs >= self.p_expr(tokens)
     658        else:
     659            self.parse_error(tokens, "Malformed equation")
     660       
     661# expr ::=  term | expr '+' term | expr '-' term
     662    cpdef p_expr(self, Tokenizer tokens):
     663        """
     664        Parse a list of one or more terms.
     665       
     666        EXAMPLES:
     667            sage: from sage.misc.parser import Parser, Tokenizer
     668            sage: p = Parser(make_var=var)
     669            sage: p.p_expr(Tokenizer("a+b"))
     670            b + a
     671            sage: p.p_expr(Tokenizer("a"))
     672            a
     673            sage: p.p_expr(Tokenizer("a - b + 4*c - d^2"))
     674            -d^2 + 4*c - b + a
     675            sage: p.p_expr(Tokenizer("a - -3"))
     676            a + 3
     677            sage: p.p_expr(Tokenizer("a + 1 == b"))
     678            a + 1
     679        """
     680        # Note: this is left-recursive, so we can't just recurse
     681        cdef int op
     682        operand1 = self.p_term(tokens)
     683        op = tokens.next()
     684        while op == '+' or op == '-':
     685            operand2 = self.p_term(tokens)
     686            if op == '+':
     687                operand1 = operand1 + operand2
     688            else:
     689                operand1 = operand1 - operand2
     690            op = tokens.next()
     691        tokens.backtrack()
     692        return operand1
     693           
     694# term ::=  factor | term '*' factor | term '/' factor
     695    cpdef p_term(self, Tokenizer tokens):
     696        """
     697        Parse a single term (consisting of one or more factors).
     698       
     699        EXAMPLES:
     700            sage: from sage.misc.parser import Parser, Tokenizer
     701            sage: p = Parser(make_var=var)
     702            sage: p.p_term(Tokenizer("a*b"))
     703            a*b
     704            sage: p.p_term(Tokenizer("a * b / c * d"))
     705            a*b*d/c
     706            sage: p.p_term(Tokenizer("-a * b + c"))
     707            -a*b
     708            sage: p.p_term(Tokenizer("a*(b-c)^2"))
     709            a*(b - c)^2
     710            sage: p.p_term(Tokenizer("-3a"))
     711            -3*a
     712        """
     713        # Note: this is left-recursive, so we can't just recurse
     714        cdef int op
     715        operand1 = self.p_factor(tokens)
     716        op = tokens.next()
     717        if op == NAME and self.implicit_multiplication:
     718            op = '*'
     719            tokens.backtrack()
     720        while op == '*' or op == '/':
     721            operand2 = self.p_factor(tokens)
     722            if op == '*':
     723                operand1 = operand1 * operand2
     724            else:
     725                operand1 = operand1 / operand2
     726            op = tokens.next()
     727            if op == NAME and self.implicit_multiplication:
     728                op = '*'
     729                tokens.backtrack()
     730        tokens.backtrack()
     731        return operand1
     732       
     733# factor ::=  '+' factor | '-' factor | power
     734    cpdef p_factor(self, Tokenizer tokens):
     735        """
     736        Parse a single factor, which consists of any number of unary +/-
     737        and a power.
     738       
     739        EXAMPLES:
     740            sage: from sage.misc.parser import Parser, Tokenizer
     741            sage: R.<t> = ZZ[['t']]
     742            sage: p = Parser(make_var={'t': t})
     743            sage: p.p_factor(Tokenizer("- -t"))
     744            t
     745            sage: p.p_factor(Tokenizer("- + - -t^2"))
     746            -t^2
     747            sage: p.p_factor(Tokenizer("t^11 * x"))
     748            t^11
     749        """
     750        cdef int token = tokens.next()
     751        if token == '+':
     752            return self.p_factor(tokens)
     753        elif token == '-':
     754            return -self.p_factor(tokens)
     755        else:
     756            tokens.backtrack()
     757            return self.p_power(tokens)
     758           
     759# power ::=  atom ^ factor | atom
     760    cpdef p_power(self, Tokenizer tokens):
     761        """
     762        Parses a power. Note that exponentiation groups right to left. 
     763       
     764        EXAMPLES:
     765            sage: from sage.misc.parser import Parser, Tokenizer
     766            sage: R.<t> = ZZ[['t']]
     767            sage: p = Parser(make_var={'t': t})
     768            sage: p.p_factor(Tokenizer("-(1+t)^-1"))
     769            -1 + t - t^2 + t^3 - t^4 + t^5 - t^6 + t^7 - t^8 + t^9 - t^10 + t^11 - t^12 + t^13 - t^14 + t^15 - t^16 + t^17 - t^18 + t^19 + O(t^20)
     770            sage: p.p_factor(Tokenizer("t**2"))
     771            t^2
     772            sage: p.p_power(Tokenizer("2^3^2")) == 2^9
     773            True
     774        """
     775        operand1 = self.p_atom(tokens)
     776        cdef int token = tokens.next()
     777        if token == '^':
     778            operand2 = self.p_factor(tokens)
     779            return operand1 ** operand2
     780        else:
     781            tokens.backtrack()
     782            return operand1
     783
     784# atom ::= int | float | name | '(' expr ')' | name '(' args ')'
     785    cpdef p_atom(self, Tokenizer tokens):
     786        """
     787        Parse an atom. This is either a parenthesized expression, a function call, or a literal name/int/float.
     788       
     789        EXAMPLES:
     790            sage: from sage.misc.parser import Parser, Tokenizer
     791            sage: p = Parser(make_var=var, make_function={'sin': sin})
     792            sage: p.p_atom(Tokenizer("1"))
     793            1
     794            sage: p.p_atom(Tokenizer("12"))
     795            12
     796            sage: p.p_atom(Tokenizer("12.5"))
     797            12.5
     798            sage: p.p_atom(Tokenizer("(1+a)"))
     799            a + 1
     800            sage: p.p_atom(Tokenizer("(1+a)^2"))
     801            a + 1
     802            sage: p.p_atom(Tokenizer("sin(1+a)"))
     803            sin(a + 1)
     804            sage: p = Parser(make_var=var, make_function={'foo': sage.misc.parser.foo})
     805            sage: p.p_atom(Tokenizer("foo(a, b, key=value)"))
     806            ((a, b), {'key': value})
     807            sage: p.p_atom(Tokenizer("foo()"))
     808            ((), {})
     809        """
     810        cdef int token = tokens.next()
     811        if token == INT:
     812            return self.integer_constructor(tokens.last_token_string())
     813        elif token == FLOAT:
     814            return self.float_constructor(tokens.last_token_string())
     815        elif token == NAME:
     816            name = tokens.last_token_string()
     817            token = tokens.next()
     818            if token == '(':
     819                func = self.callable_constructor(name)
     820                args, kwds = self.p_args(tokens)
     821                token = tokens.next()
     822                if token != ')':
     823                    self.parse_error(tokens, "Bad function call")
     824                return func(*args, **kwds)
     825            else:
     826                tokens.backtrack()
     827                return self.variable_constructor(name)
     828        elif token == '(':
     829            expr = self.p_expr(tokens)
     830            token = tokens.next()
     831            if token != ')':
     832                self.parse_error(tokens, "Mismatched parentheses")
     833            return expr
     834        else:
     835            self.parse_error(tokens)
     836       
     837# args = arg (',' arg)* | EMPTY
     838    cpdef p_args(self, Tokenizer tokens):
     839        """
     840        Returns a list, dict pair.
     841       
     842        EXAMPLES:
     843            sage: from sage.misc.parser import Parser, Tokenizer
     844            sage: p = Parser()
     845            sage: p.p_args(Tokenizer("1,2,a=3"))
     846            ([1, 2], {'a': 3})
     847            sage: p.p_args(Tokenizer("1, 2, a = 1+5^2"))
     848            ([1, 2], {'a': 26})
     849        """
     850        args = []
     851        kwds = {}
     852        if tokens.peek() == ')':
     853            return args, kwds
     854        cdef int token = ','
     855        while token == ',':
     856            arg = self.p_arg(tokens)
     857            if isinstance(arg, tuple):
     858                name, value = arg
     859                kwds[name] = value
     860            else:
     861                args.append(arg)
     862            token = tokens.next()
     863        tokens.backtrack()
     864        return args, kwds
     865
     866# arg = expr | name '=' expr
     867    cpdef p_arg(self, Tokenizer tokens):
     868        """
     869        Returns an expr, or a (name, expr) tuple corresponding to a single
     870        function call argument.
     871       
     872        EXAMPLES:
     873            sage: from sage.misc.parser import Parser, Tokenizer
     874            sage: p = Parser(make_var=var)
     875            sage: p.p_arg(Tokenizer("a+b"))
     876            b + a
     877            sage: p.p_arg(Tokenizer("val=a+b"))
     878            ('val', b + a)
     879        """
     880        cdef int token = tokens.next()
     881        if token == NAME and tokens.peek() == '=':
     882            name = tokens.last_token_string()
     883            tokens.next()
     884            return name, self.p_expr(tokens)
     885        else:
     886            tokens.backtrack()
     887            return self.p_expr(tokens)
     888           
     889    cdef parse_error(self, Tokenizer tokens, msg="Malformed expression"):
     890        raise ValueError, (msg, tokens.s, tokens.pos)
     891
     892
     893cdef class LookupNameMaker:
     894    cdef object names
     895    cdef object fallback
     896    def __init__(self, names, fallback=None):
     897        """
     898        This class wraps a dictionary as a callable for use in creating names.
     899        It takes a dictionary of names, and an (optional) callable to use
     900        when the given name is not found in the dictionary.
     901       
     902        EXAMPLES:
     903            sage: from sage.misc.parser import LookupNameMaker
     904            sage: maker = LookupNameMaker({'pi': pi}, var)
     905            sage: maker('pi')
     906            pi
     907            sage: maker('pi') is pi
     908            True
     909            sage: maker('a')
     910            a
     911        """
     912        self.names = names
     913        self.fallback = fallback
     914    def __call__(self, name):
     915        """
     916        TESTS:
     917            sage: from sage.misc.parser import LookupNameMaker
     918            sage: maker = LookupNameMaker({'a': x}, str)
     919            sage: maker('a')
     920            x
     921            sage: maker('a') is x
     922            True
     923            sage: maker('b')
     924            'b'
     925        """
     926        try:
     927            return self.names[name]
     928        except KeyError:
     929            if self.fallback is not None:
     930                return self.fallback(name)
     931            raise NameError, "Unknown variable: '%s'" % name
     932
     933
  • sage/rings/complex_double.pyx

    diff -r e5b5b9eeff71 -r 5c7590ae6837 sage/rings/complex_double.pyx
    a b def ComplexDoubleField(): 
    17611761    """
    17621762    return _CDF   
    17631763
    1764 from sage.calculus.parser import Parser
     1764from sage.misc.parser import Parser
    17651765cdef cdf_parser = Parser(float, float,  {"I" : _CDF.gen(), "i" : _CDF.gen()})
    17661766
    17671767
  • sage/rings/polynomial/polynomial_ring.py

    diff -r e5b5b9eeff71 -r 5c7590ae6837 sage/rings/polynomial/polynomial_ring.py
    a b class PolynomialRing_general(sage.algebr 
    224224                raise TypeError, "Unable to coerce singular object"
    225225        elif isinstance(x , str):
    226226            try:
    227                 from sage.calculus.parser import Parser, LookupNameMaker
     227                from sage.misc.parser import Parser, LookupNameMaker
    228228                R = self.base_ring()
    229229                p = Parser(integer.Integer, R, LookupNameMaker({self.variable_name(): self.gen()}, R))
    230230                return self(p.parse(x))
  • setup.py

    diff -r e5b5b9eeff71 -r 5c7590ae6837 setup.py
    a b ext_modules = [ \ 
    683683    Extension('sage.misc.misc_c',
    684684              sources = ['sage/misc/misc_c.pyx']), \
    685685
     686    Extension('sage.misc.parser',
     687              ['sage/misc/parser.pyx']), \
     688
    686689    Extension('sage.misc.refcount',
    687690              sources = ['sage/misc/refcount.pyx']), \
    688691
    ext_modules = [ \ 
    873876
    874877    Extension('sage.calculus.var',
    875878              ['sage/calculus/var.pyx']), \
    876 
    877     Extension('sage.calculus.parser',
    878               ['sage/calculus/parser.pyx']), \
    879879
    880880    Extension('sage.modular.modsym.heilbronn',
    881881              ['sage/modular/modsym/heilbronn.pyx',