Ticket #2347: 2347-3.patch

File 2347-3.patch, 6.2 KB (added by robertwb, 14 years ago)
  • sage/calculus/parser.pyx

    # HG changeset patch
    # User Robert Bradshaw <robertwb@math.washington.edu>
    # Date 1207878275 25200
    # Node ID e5b5b9eeff71c8dc42cdfdf077c1690dc35719c4
    # Parent  4b6c28d7c99deb91a099b94165b1c60140cb6c62
    Parse lists and tuples
    
    diff -r 4b6c28d7c99d -r e5b5b9eeff71 sage/calculus/parser.pyx
    a b cdef extern from "string.h": 
    2929   
    3030cdef extern from "Python.h":
    3131    object PyString_FromStringAndSize(char *v, Py_ssize_t len)
     32    int PyList_Append(object list, object item) except -1
    3233
    3334import math
    3435
    cdef class Tokenizer: 
    122123            ['FLOAT(1.5)', '+', 'INT(2)', '*', 'INT(3)', '^', 'INT(4)', '-', 'NAME(sin)', '(', 'NAME(x)', ')']
    123124           
    124125        The single character tokens are given by:
    125             sage: Tokenizer("+-*/^(),=<>").test()
    126             ['+', '-', '*', '/', '^', '(', ')', ',', '=', '<', '>']
     126            sage: Tokenizer("+-*/^(),=<>[]{}").test()
     127            ['+', '-', '*', '/', '^', '(', ')', ',', '=', '<', '>', '[', ']', '{', '}']
    127128           
    128129        Two-character comparisons accepted are:
    129130            sage: Tokenizer("<= >= != == **").test()
    cdef class Tokenizer: 
    152153        No attempt for correctness is made at this stage:
    153154            sage: Tokenizer(") )( 5e5e5").test()
    154155            [')', ')', '(', 'FLOAT(5e5)', 'NAME(e5)']
    155             sage: Tokenizer("[$%").test()
     156            sage: Tokenizer("?$%").test()
    156157            ['ERROR', 'ERROR', 'ERROR']
    157158        """
    158159        self.pos = 0
    cdef class Tokenizer: 
    252253            return '^'
    253254           
    254255        # simple tokens
    255         if strchr("+-*/^()=<>,", s[pos]):
     256        if strchr("+-*/^()=<>,[]{}", s[pos]):
    256257            type = s[pos]
    257258            self.pos += 1
    258259            return type
    cdef class Parser: 
    484485        if tokens.next() != EOS:
    485486            self.parse_error(tokens)
    486487        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]
    487615       
    488616# eqn ::= expr op expr | expr
    489617    cpdef p_eqn(self, Tokenizer tokens):