Changes related to logs and error handling.
[confparser-old] / basicparser.py
1 # -*- coding: iso-8859-1 -*-
2
3 #
4 # A quick made parser for mail.filter configuration file.
5 #
6 # The parser could be interesting for some other uses though.
7 #
8
9 import re
10 import types
11
12 #-----------------------------------------------------------------------------
13
14 class ParserError( Exception ) : pass
15
16 class Parser :
17
18         def __init__( self , tokens , text ) :
19
20                 self.tokenMatches = {}
21                 for k , v in tokens.items() :
22                         if type( v ) in types.StringTypes :
23                                 v = re.compile( v )
24                         self.tokenMatches[ k ] = v
25                 self.text = text
26                 self.pos = 0
27                 self.tokensToIgnore = ()
28                 self.x = 1
29                 self.y = 1
30
31         def ignore( self , *tokens ) :
32
33                 self.tokensToIgnore = tokens
34
35         def peek( self , *set ) :
36
37                 tokens = []
38                 if not set :
39                         set = self.tokenMatches.keys()
40                 for tokenName in set :
41                         tokenMatch = self.tokenMatches.get( tokenName )
42                         if not tokenMatch : continue
43                         m = tokenMatch.match( self.text , self.pos )
44                         if m :
45                                 tk = m.group( 0 )
46                                 tokens.append( ( tokenName , tk ) )
47                 tokens.sort( lambda a , b : cmp( len( a[ 1 ] ) , len( b[ 1 ] ) ) )
48                 return tokens
49
50         def advance( self , n ) :
51
52                 if type( n ) in types.StringTypes :
53                         n = len( n )
54                 p = self.text.rfind( '\n' , self.pos , self.pos + n )
55                 if p == -1 :
56                         self.x += n
57                 else :
58                         self.x = self.pos + n - p
59                         self.y += self.text.count( '\n' , self.pos , self.pos + n )
60                 self.pos += n
61
62         def snext( self , *set ) :
63
64                 set += self.tokensToIgnore
65                 r = None
66                 while 1 :
67                         r = self.peek( *set )
68                         if not r :
69                                 break
70                         r = r[ 0 ]
71                         self.advance( r[ 1 ] )
72                         if r[ 0 ] not in self.tokensToIgnore :
73                                 break
74                 return r
75
76         def next( self , *set ) :
77
78                 r = self.snext( *set )
79                 if not r :
80                         r = self.peek()
81                         if r :
82                                 misc = 'found %r but ' % r[ 0 ][ 1 ]
83                         else :
84                                 misc = ''
85                         raise ParserError( '%s, %sexpected one of the following tokens: %r'
86                                 % ( self.getPos() , misc , list( set ) ) )
87                 return r
88
89         def getPos( self ) :
90
91                 return 'at line %d, column %d' % ( self.y , self.x )
92
93         def point( self ) :
94
95                 lineStart = self.text.rfind( '\n' , 0 , self.pos + 1 ) + 1
96                 lineEnd = self.text.find( '\n' , self.pos + 1 )
97                 if lineEnd == -1 :
98                         lineEnd = len( self.text )
99                 prefix = 'line %s: ' % self.y
100                 r = ''
101                 r += prefix + self.text[ lineStart : lineEnd ].replace( '\t' , ' ' ) + '\n'
102                 r += prefix + ' ' * ( self.pos - lineStart ) + '^' + '\n'
103                 return r