source: specdomain/src/specdomain/test/parser.py @ 956

Last change on this file since 956 was 956, checked in by jemian, 10 years ago

refs #8, basics of the parser are established

  • Property svn:eol-style set to native
  • Property svn:executable set to *
  • Property svn:keywords set to Author Date Id Revision URL Header
File size: 6.4 KB
Line 
1#!/usr/bin/env python
2
3########### SVN repository information ###################
4# $Date: 2012-06-21 03:57:40 +0000 (Thu, 21 Jun 2012) $
5# $Author: jemian $
6# $Revision: 956 $
7# $HeadURL: specdomain/src/specdomain/test/parser.py $
8# $Id: parser.py 956 2012-06-21 03:57:40Z jemian $
9########### SVN repository information ###################
10
11
12"""
13Construct a SPEC macro source code file parser for
14use by the specdomain for Sphinx.
15"""
16
17import os
18import re
19
20
21string_start                = r'^'
22string_end                  = r'$'
23match_all                   = r'.*'
24non_greedy_filler           = match_all + r'?'
25non_greedy_whitespace       = r'\w*?'
26double_quote_string_match   = r'("' + non_greedy_filler + r'")'
27word_match                  = r'((?:[a-z_]\w*))'
28cdef_match                  = r'(cdef)'
29extended_comment_marker     = r'\"{3}'
30extended_comment_match      = r'(' + extended_comment_marker + r')'
31
32spec_macro_sig_re = re.compile(
33                               r'''^ ([a-zA-Z_]\w*)         # macro name
34                               ''', re.VERBOSE)
35
36spec_func_sig_re = re.compile(word_match + r'\('
37                      + r'(' + match_all + r')' 
38                      + r'\)', 
39                      re.IGNORECASE|re.DOTALL)
40
41spec_cdef_name_sig_re = re.compile(double_quote_string_match, 
42                                   re.IGNORECASE|re.DOTALL)
43
44
45spec_extended_comment_flag_sig_re = re.compile(extended_comment_marker, 
46                                               re.IGNORECASE|re.DOTALL)
47spec_extended_comment_start_sig_re = re.compile(string_start
48                                                + non_greedy_whitespace
49                                                + extended_comment_match, 
50                                                re.IGNORECASE|re.VERBOSE)
51spec_extended_comment_end_sig_re = re.compile(non_greedy_whitespace
52                                                + extended_comment_match
53                                                + non_greedy_whitespace
54                                                + r'#' + non_greedy_filler
55                                                + r'$',
56                                                re.IGNORECASE|re.VERBOSE)
57spec_extended_comment_block_sig_re = re.compile(string_start
58                                                + non_greedy_whitespace
59                                                + extended_comment_marker
60                                                + r'(' + non_greedy_filler + r')'
61                                                + extended_comment_marker
62                                                + non_greedy_filler
63                                                + string_end, 
64                                                re.IGNORECASE|re.DOTALL|re.MULTILINE)
65
66
67class SpecMacrofileParser:
68    '''
69    Parse a SPEC macro file for macro definitions,
70    variable declarations, and extended comments.
71    '''
72
73    states = (
74        'command level', 
75        'extended comment', 
76        'def macro', 
77        'rdef macro', 
78        'cdef macro'
79             
80    )
81
82    def __init__(self, macrofile):
83        '''
84        Constructor
85        '''
86        self.buf = None
87        self.findings = []
88        self.filename = None
89        self.read(macrofile)
90        self.parse_macro_file()
91   
92    def read(self, filename):
93        """
94        load the SPEC macro source code file into an internal buffer
95       
96        :param str filename: name (with optional path) of SPEC macro file
97            (The path is relative to the ``.rst`` document.)
98        """
99        if not os.path.exists(filename):
100            raise RuntimeError, "file not found: " + filename
101        self.filename = filename
102        self.buf = open(filename, 'r').read()
103   
104    def parse_macro_file(self):
105        """
106        parse the internal buffer
107        """
108        line_number = 0
109        state = 'command level'
110        state_stack = []
111        for line in self.buf.split('\n'):
112            if state not in self.states:
113                raise RuntimeError, "unexpected parser state: " + state
114            line_number += 1
115            if state == 'command level':
116                # test if one-line extended comment
117                m = self._match(spec_extended_comment_block_sig_re, line)
118                if m is not None:
119                    del m['start'], m['end'], m['line']
120                    m['objtype'] = 'extended comment'
121                    m['start_line'] = line_number
122                    m['end_line'] = line_number
123                    self.findings.append(m)
124                    continue
125               
126                # test if start of multiline extended comment
127                m = self._match(spec_extended_comment_start_sig_re, line)
128                if m is not None:
129                    text = m['line'][m['end']:]
130                    del m['start'], m['end'], m['line']
131                    m['objtype'] = 'extended comment'
132                    m['start_line'] = line_number
133                    ec = dict(m)    # container for extended comment data
134                    ec['text'] = [text]
135                    state_stack.append(state)
136                    state = 'extended comment'
137                    continue
138
139            elif state == 'extended comment':
140                # test if end of multiline extended comment
141                if line_number > 250:
142                    pass
143                m = self._match(spec_extended_comment_end_sig_re, line)
144                if m is not None:
145                    text = m['line'][:m['start']]
146                    ec['text'].append(text)
147                    ec['text'] = '\n'.join(ec['text'])
148                    ec['end_line'] = line_number
149                    self.findings.append(ec)
150                    state = state_stack.pop()
151                    del ec
152                else:
153                    # multiline extended comment continues
154                    ec['text'].append(line)
155                continue
156   
157    def _match(self, regexp, line):
158        m = regexp.search(line)
159        if m is None:
160            return None
161        d = {
162            'start': m.start(1),
163            'end':   m.end(1),
164            'text':  m.group(1),
165            'line':  line,
166        }
167        return d
168
169    def __str__(self):
170        s = []
171        for r in self.findings:
172            s.append( '' )
173            t = '%s %s %d %d %s' % ('*'*20, r['objtype'], r['start_line'], r['end_line'], '*'*20)
174            s.append( t )
175            s.append( r['text'] )
176        return '\n'.join(s)
177
178
179if __name__ == '__main__':
180    print SpecMacrofileParser('test-battery.mac')
181    print SpecMacrofileParser('cdef-examples.mac')
Note: See TracBrowser for help on using the repository browser.