text: import unittest
  
  from xlcalculator.model import parser
  
  
  class FormulaParserTest(unittest.TestCase):
  
      def parse(self, formula, named_ranges=None):
          if named_ranges is None:
              named_ranges = {
                  'first': 'A1',
                  'two_by_two': 'A1:B2'
              }
          ast = parser.FormulaParser().parse(formula, named_ranges)
          return ast
  
      def test_parse(self):
          self.assertEqual(str(self.parse('=A1+1')), '(A1) + (1)')
  
      def test_parse_without_equal(self):
          self.assertEqual(str(self.parse('A1+1')), '(A1) + (1)')
  
      def test_parse_with_named_range(self):
          # Named ranges get immediately resolved.
          self.assertEqual(str(self.parse('first+1')), '(A1) + (1)')
  
      def test_parse_function(self):
          self.assertEqual(str(self.parse('SUM(A1:B2)')), 'SUM(A1:B2)')
  
      def test_parse_function_with_arg_expr(self):
          self.assertEqual(
              str(self.parse('SUM(1, 2, (3+4))')), 'SUM(1, 2, (3) + (4))')
  
      def test_parse_nested_function(self):
          self.assertEqual(
              str(self.parse('MOD(SUM(A1:B2), 2)')),
              'MOD(SUM(A1:B2), 2)')
  
      def test_parse_with_parens(self):
          self.assertEqual(str(self.parse('2*(A1+1)')), '(2) * ((A1) + (1))')
  
      def test_parse_prefix_op(self):
          self.assertEqual(str(self.parse('-(A1+1)')), '- ((A1) + (1))')
  
      def test_parse_consecutive_unary_negation(self):
          self.assertEqual(str(self.parse('--A1')), '- (- (A1))')
  
      def test_parse_infix_and_prefix_op(self):
          self.assertEqual(str(self.parse('B1--A1')), '(B1) - (- (A1))')
  
      def test_parse_percent(self):
          # Should be a proper postfix op, but tokenizer takes care of it right
          # away.
          self.assertEqual(str(self.parse('(A1+1)%')), '((A1) + (1)) * (0.01)')
  
      def test_parse_open_range_start(self):
          self.assertEqual(str(self.parse(':B2')), ':B2')
  
      def test_parse_open_range_end(self):
          self.assertEqual(str(self.parse('A1:')), 'A1:')
  
      @unittest.skip(
          'Not properly supported during shunting. Does not remove func node.')
      def test_parse_with_offser(self):
          self.assertEqual(str(self.parse(':OFFSET(A1, 1, 1)')), ':B2')
  
      def test_parse_with_parens_mismatched(self):
          with self.assertRaises(SyntaxError):
              self.parse('2*(A1+1')
          with self.assertRaises(IndexError):
              self.parse('2*)')
  