source: coopr.pyomo/trunk/coopr/pyomo/data/parse_datacmds.py @ 2307

Last change on this file since 2307 was 2307, checked in by wehart, 11 years ago

Adding a test that is used to verify that data commands can include indexed variables with astrices
in their arguments.

File size: 9.3 KB
Line 
1
2#  _________________________________________________________________________
3#
4#  Coopr: A COmmon Optimization Python Repository
5#  Copyright (c) 2008 Sandia Corporation.
6#  This software is distributed under the BSD License.
7#  Under the terms of Contract DE-AC04-94AL85000 with Sandia Corporation,
8#  the U.S. Government retains certain rights in this software.
9#  For more information, see the Coopr README.txt file.
10#  _________________________________________________________________________
11
12__all__ = ['parse_data_commands']
13
14import re
15import os
16import ply.lex as lex
17import ply.yacc as yacc
18from pyutilib.misc import flatten
19from pyutilib.ply import t_newline, t_ignore, _find_column, p_error, ply_init
20
21
22## -----------------------------------------------------------
23##
24## Lexer definitions for tokenizing the input
25##
26## -----------------------------------------------------------
27
28_parse_info = None
29debugging = False
30
31reserved = {
32    'data' : 'DATA',
33    'set' : 'SET',
34    'param' : 'PARAM',
35    'end' : 'END',
36    'import' : 'IMPORT',
37    'include' : 'INCLUDE',
38}
39
40# Token names
41tokens = [
42    "COMMA",
43#    "LBRACE",
44#    "RBRACE",
45#    "NUMBER",
46    "SEMICOLON",
47    "COLON",
48    "COLONEQ",
49    "LBRACKET",
50    "RBRACKET",
51    "LPAREN",
52    "RPAREN",
53#    "RANGE",
54    "WORD",
55    "STRING",
56    "QUOTEDSTRING",
57    "FILENAME",
58    "EQ",
59    "TR",
60    "ASTERISK",
61    "NONWORD",
62] + reserved.values()
63
64# Regular expression rules
65t_COMMA     = r","
66t_LBRACKET  = r"\["
67t_RBRACKET  = r"\]"
68#t_NUMBER    = r"[0-9]+(\.[0-9]+){0,1}"
69t_SEMICOLON = r";"
70t_COLON     = r":"
71t_COLONEQ   = r":="
72t_EQ        = r"="
73t_TR        = r"\(tr\)"
74#t_LT        = r"<"
75#t_GT        = r">"
76#t_LBRACE    = r"{"
77#t_RBRACE    = r"}"
78t_LPAREN    = r"\("
79t_RPAREN    = r"\)"
80t_ASTERISK  = r"\*"
81
82# Discard comments
83def t_COMMENT(t):
84    r'\#[^\n]*'
85    #global _comment_list
86    #_comment_list.append(t.value)
87
88def t_WORD(t):
89    r'[a-zA-Z_0-9][a-zA-Z_0-9\.]*'
90    t.type = reserved.get(t.value,'WORD')    # Check for reserved words
91    return t
92
93def t_STRING(t):
94    r'[a-zA-Z_0-9\.+\-]+'
95    t.type = reserved.get(t.value,'STRING')    # Check for reserved words
96    return t
97
98def t_QUOTEDSTRING(t):
99    r'"([^"]|\"\")*"|\'([^\']|\'\')*\''
100    t.type = reserved.get(t.value,'QUOTEDSTRING')    # Check for reserved words
101    return t
102
103def t_FILENAME(t):
104    r'[a-zA-Z_0-9\./\\]*(/|\\)[a-zA-Z_0-9\./\\]*'
105    t.type = reserved.get(t.value,'FILENAME')    # Check for reserved words
106    return t
107
108t_NONWORD   = r"[^\.A-Za-z0-9,;:=<>\*\(\)\#{}\[\] \n\t\r]+"
109
110# Error handling rule
111def t_error(t):             #pragma:nocover
112    raise IOError, "Illegal character '%s'" % t.value[0]
113    t.lexer.skip(1)
114
115
116## -----------------------------------------------------------
117##
118## Yacc grammar for data commands
119##
120## -----------------------------------------------------------
121
122def p_expr(p):
123    '''expr : statements
124            | '''
125
126def p_statements(p):
127    '''statements : statement statements
128                  | statement '''
129
130def p_statement(p):
131    '''statement : SET WORD COLONEQ setdecl SEMICOLON
132                 | SET WORD COLONEQ SEMICOLON
133                 | SET WORD COLON items COLONEQ setdecl SEMICOLON
134                 | SET WORD COLON items COLONEQ SEMICOLON
135                 | SET indexed_word COLONEQ setdecl SEMICOLON
136                 | SET indexed_word COLONEQ SEMICOLON
137                 | PARAM items COLONEQ paramdecl SEMICOLON
138                 | IMPORT importdecl SEMICOLON
139                 | INCLUDE WORD SEMICOLON
140                 | INCLUDE QUOTEDSTRING SEMICOLON
141                 | DATA SEMICOLON
142                 | END SEMICOLON
143    '''
144    global _parse_info
145    #print "STATEMENT",len(p), p[1:]
146    if p[1] in ['set','param']:
147        _parse_info.append( flatten(p[1:-1]) )
148    elif p[1] in ['include']:
149        _parse_info.append( p[1:-1] )
150    elif p[1] in ['import']:
151        _parse_info.append( [p[1]]+ p[2] )
152        #_parse_info.append( [p[1], p[2][0], p[1:-1] )
153
154def p_setdecl(p):
155    '''setdecl : items'''
156    p[0] = p[1]
157
158def p_paramdecl(p):
159    '''paramdecl : items'''
160    p[0] = p[1]
161
162def p_importdecl(p):
163    '''importdecl : filename import_options
164                  | filename
165                  | filename import_options COLON WORD EQ indices variable_options
166                  | filename COLON WORD EQ indices variable_options
167                  | filename import_options COLON indices variable_options
168                  | filename COLON indices variable_options
169                  | filename import_options COLON variable_options
170                  | filename COLON variable_options
171    '''
172    tmp = {'filename':p[1]}
173    if len(p) == 2:
174        p[0] = [tmp, (None,[]), {}]
175    elif len(p) == 3:
176        tmp.update(p[2])
177        p[0] = [tmp, (None,[]), {}]
178    elif len(p) == 4:
179        p[0] = [tmp, (None,[]), p[3]]
180    elif len(p) == 5:
181        if p[2] == ':':
182            p[0] = [tmp, (None,p[3]), p[4]]
183        else:
184            tmp.update(p[2])
185            p[0] = [tmp, (None,[]), p[4]]
186    elif len(p) == 6:
187        tmp.update(p[2])
188        p[0] = [tmp, (None,p[4]), p[5]]
189    elif len(p) == 7:
190        p[0] = [tmp, (p[3],p[5]), p[6]]
191    elif len(p) == 8:
192        tmp.update(p[2])
193        p[0] = [tmp, (p[4],p[6]), p[7]]
194    else:
195        raise IOError, "Unexpected condition"
196
197def p_import_options(p):
198    '''import_options : WORD EQ STRING import_options
199                      | WORD EQ STRING
200                      | WORD EQ QUOTEDSTRING import_options
201                      | WORD EQ QUOTEDSTRING
202                      | WORD EQ WORD import_options
203                      | WORD EQ WORD
204                      | WORD EQ PARAM import_options
205                      | WORD EQ PARAM
206                      | WORD EQ SET import_options
207                      | WORD EQ SET
208    '''
209    tmp = {p[1]:p[3]}
210    if len(p) == 4:
211        p[0] = tmp
212    else:
213        tmp.update(p[4])
214        p[0] = tmp
215
216def p_variable_options(p):
217    '''variable_options : variable variable_options
218                        | variable
219    '''
220    if len(p) == 2:
221        p[0] = p[1]
222    else:
223        p[1].update(p[2])
224        p[0] = p[1]
225
226def p_variable(p):
227    '''variable : WORD
228                | WORD EQ WORD
229    '''
230    if len(p) == 2:
231        p[0] = {p[1]:p[1]}
232    else:
233        p[0] = {p[3]:p[1]}
234
235def p_indices(p):
236    '''indices : LBRACKET WORD index_list RBRACKET
237               | LBRACKET WORD RBRACKET
238    '''
239    if len(p) == 5:
240        p[0] = [p[2]] + p[3]
241    else:
242        p[0] = [p[2]]
243
244def p_index_list(p):
245    '''index_list : COMMA WORD index_list
246                  | COMMA ASTERISK index_list
247                  | COMMA WORD
248                  | COMMA ASTERISK
249    '''
250    if len(p) == 4:
251        p[0] = [p[2]]+p[3]
252    else:
253        p[0] = [p[2]]
254
255def p_indexed_word(p):
256    '''indexed_word : WORD LBRACKET WORD index_list RBRACKET
257                    | WORD LBRACKET ASTERISK index_list RBRACKET
258                    | WORD LBRACKET WORD RBRACKET
259                    | WORD LBRACKET ASTERISK RBRACKET
260    '''
261    if len(p) == 6:
262        p[0] = p[1]+p[2]+",".join([p[3]]+p[4])+p[5]
263    else:
264        p[0] = p[1]+p[2]+p[3]+p[4]
265
266def p_template(p):
267    '''template : LPAREN WORD index_list RPAREN
268                | LPAREN ASTERISK index_list RPAREN
269                | LPAREN WORD RPAREN
270                | LPAREN ASTERISK RPAREN
271    '''
272    if len(p) == 5:
273        p[0] = p[1]+",".join([p[2]]+p[3])+p[4]
274    else:
275        p[0] = p[1]+p[2]+p[3]
276
277def p_items(p):
278    '''items : item items
279             | item'''
280    if len(p) == 2:
281        p[0] = [p[1]]
282    else:
283        p[0] = [p[1]] + p[2]
284
285def p_item(p):
286    '''item : WORD
287            | NONWORD
288            | STRING
289            | QUOTEDSTRING
290            | COMMA
291            | COLON
292            | LBRACKET
293            | RBRACKET
294            | TR
295            | LPAREN
296            | RPAREN
297            | indexed_word
298            | template
299    '''
300    p[0] = p[1]
301
302def p_filename(p):
303    '''filename : WORD
304                | STRING
305                | QUOTEDSTRING
306                | FILENAME
307                | WORD COLON FILENAME
308    '''
309    if len(p) == 2:
310        p[0] = p[1]
311    else:
312        p[0] = p[1]+p[2]+p[3]
313
314#
315# The function that performs the parsing
316#
317def parse_data_commands(data=None, filename=None, debug=0):
318    global debugging
319    #
320    # Always remove the parser.out file, which is generated to create debugging
321    #
322    if os.path.exists("parser.out"):        #pragma:nocover
323       os.remove("parser.out")
324    if debug > 0:                           #pragma:nocover
325        #
326        # Remove the parsetab.py* files.  These apparently need to be removed
327        # to ensure the creation of a parser.out file.
328        #
329        if os.path.exists("parsetab.py"):
330           os.remove("parsetab.py")
331        if os.path.exists("parsetab.pyc"):
332           os.remove("parsetab.pyc")
333        debugging=True
334    #
335    # Build lexer
336    #
337    lex.lex()
338    #
339    # Initialize parse object
340    #
341    global _parse_info
342    _parse_info = []
343    #
344    # Build yaccer
345    #
346    yacc.yacc(debug=debug)
347    #
348    # Parse the file
349    #
350    global _parsedata
351    if not data is None:
352        _parsedata=data
353        ply_init(_parsedata)
354        yacc.parse(data,debug=debug)
355    elif not filename is None:
356        f = open(filename)
357        data = f.read()
358        f.close()
359        _parsedata=data
360        ply_init(_parsedata)
361        yacc.parse(data, debug=debug)
362    else:
363        _parse_info = None
364    #
365    # Disable parsing I/O
366    #
367    debugging=False
368    return _parse_info
369
Note: See TracBrowser for help on using the repository browser.