BlockIt
grammars.py
Go to the documentation of this file.
00001 #............................................................................
00002 # Copyright (c) 2009,2010,2011 David Car, david.car7@gmail.com
00003 # Copyright (c) 2009,2010,2011 Michael List, michael.list@gmail.com
00004 #
00005 # This program is free software; you can redistribute it and/or modify it under
00006 # the terms of the GNU General Public License as published by the Free Software
00007 # Foundation; either version 2 of the License, or (at your option) any later
00008 # version.
00009 #
00010 # This program is distributed in the hope that it will be useful, but WITHOUT
00011 # ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
00012 # FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
00013 #
00014 # You should have received a copy of the GNU General Public License along with
00015 # this program; if not, write to the Free Software Foundation, Inc., 59 Temple
00016 # Place, Suite 330, Boston, MA 02111-1307 USA
00017 #..........................................................................
00018 from blockit.parsers import pyp as pp
00019 # pp.ParserElement.enablePackrat()
00020 
00021 #................................................................................
00022 # pyparsing Global ParserElement objects used by all blocks
00023 #................................................................................
00024 TEMPLATE_TOKENS = ('<','>')
00025 RESERVED_KEYWORDS = ['template']
00026 LEFT_TEMPTOK, RIGHT_TEMPTOK = map(pp.Literal, TEMPLATE_TOKENS)
00027 COMMENT = pp.Literal( "!" ) + pp.restOfLine
00028 #COMMENT = \
00029 #    pp.originalTextFor( pp.Literal( '!' ) + \
00030 #                        pp.Optional( 
00031 #        pp.OneOrMore( pp.Word( pp.alphanums + '_' ))) +\
00032 #                            pp.lineEnd )
00033 CONTINUATION = pp.Literal( "&" )
00034 INLINE_CONTINUATION = \
00035     CONTINUATION + \
00036     pp.Optional( COMMENT )+ \
00037     CONTINUATION
00038 # VALIDNAME = pp.Word(pp.alphas+'_', pp.alphanums+'_%').ignore( INLINE_CONTINUATION )
00039 VALIDNAME = pp.Word(pp.alphas+'_', pp.alphanums+'_%')
00040 VALIDNAME_WITH_PAREN = VALIDNAME+pp.Optional('('+VALIDNAME+')')
00041 VAR_ASSIGNMENT = VALIDNAME + pp.Literal('=')
00042 NUM = pp.Word(pp.nums)
00043 INTEGER = pp.Combine(pp.Optional('-')+pp.Word(pp.nums))
00044 DECIMAL_NUM = pp.Combine(INTEGER+'.'+pp.Optional(NUM))
00045 ENGINEERING_NUM = pp.Combine((DECIMAL_NUM|INTEGER) +\
00046                               pp.oneOf('e d', caseless=True) +\
00047                               INTEGER)
00048 
00049 PRIVATE = pp.CaselessKeyword('private')
00050 PUBLIC = pp.CaselessKeyword('public')
00051 PROTECTED = pp.CaselessKeyword('protected')
00052 ACCESS_SPEC = PRIVATE ^ PUBLIC ^ PROTECTED
00053 
00054 OUT = pp.CaselessKeyword('out')
00055 IN = pp.CaselessKeyword('in')
00056 INOUT = pp.CaselessKeyword('inout')
00057 INTENT = \
00058     pp.CaselessKeyword('intent') +\
00059     pp.Suppress('(') +\
00060     (IN|OUT|INOUT)('intent') +\
00061     pp.Suppress(')')
00062 
00063 NUM.setParseAction(lambda s,l,t: int(t[0]))
00064 INTEGER.setParseAction(lambda s,l,t: int(t[0]))
00065 DECIMAL_NUM.setParseAction(lambda s,l,t: float(t[0]))
00066 
00067 TEMPLATE_DECL = pp.Forward()
00068 VALID_TEMPLATE_PARAM = (pp.Literal('*')|TEMPLATE_DECL|VALIDNAME_WITH_PAREN|INTEGER)
00069 TEMPLATE_PARAM_LIST =  (VALID_TEMPLATE_PARAM + pp.ZeroOrMore(','+VALID_TEMPLATE_PARAM))
00070 TEMPLATE_DECL << \
00071     VALIDNAME +\
00072     pp.Combine((LEFT_TEMPTOK + TEMPLATE_PARAM_LIST + RIGHT_TEMPTOK), 
00073                adjacent=False)
00074 
00075 ARGLIST = \
00076     pp.Group(
00077     pp.Suppress('(')+\
00078         pp.Optional(pp.delimitedList(VALIDNAME |
00079                                      pp.quotedString |
00080                                      (ENGINEERING_NUM | DECIMAL_NUM | INTEGER)))+\
00081         pp.Suppress(')'))
00082 
00083 # Arglist2 is for things with 2 arg lists. we should probably have a dictionary of
00084 # these to allow multiple arg names.
00085 ARGLIST2 = \
00086     pp.Group(
00087     pp.Suppress('(')+\
00088         pp.Optional(pp.delimitedList(VALIDNAME |
00089                                      pp.quotedString |
00090                                      (ENGINEERING_NUM | DECIMAL_NUM | INTEGER)))+\
00091         pp.Suppress(')'))('args2')
00092 
00093 #........................................
00094 # Attribute Spec
00095 #........................................
00096 TARGET = pp.CaselessKeyword('target')
00097 OPTIONAL = pp.CaselessKeyword('optional')
00098 SAVE = pp.CaselessKeyword('save')
00099 POINTER = pp.CaselessKeyword('pointer')
00100 ALLOCATABLE = pp.CaselessKeyword('allocatable')
00101 EXTERNAL = pp.CaselessKeyword('external')
00102 INTRINSIC = pp.CaselessKeyword('intrinsic')
00103 ASYNCHRONOUS = pp.CaselessKeyword('asynchronous')
00104 VALUE = pp.CaselessKeyword('value')
00105 DIMENSION = \
00106     pp.CaselessKeyword('dimension') +\
00107     pp.Literal('(') +\
00108     pp.delimitedList(pp.Literal(':')|pp.Word(pp.nums)) +\
00109     pp.Literal(')')
00110 ATTR_SPEC = ACCESS_SPEC | INTENT | TARGET | SAVE | POINTER | ALLOCATABLE | \
00111     EXTERNAL | INTRINSIC | VALUE | DIMENSION | ASYNCHRONOUS
00112 
00113 ONLY_CLAUSE = \
00114     pp.Literal(',')+pp.CaselessKeyword('only')+pp.Literal(':')
00115 
00116 #............................................................
00117 # Types
00118 #............................................................
00119 INTEGER = \
00120     pp.originalTextFor( \
00121         pp.CaselessKeyword( 'integer' ) +\
00122         pp.Optional( '(' +\
00123                      pp.originalTextFor(VALIDNAME) +\
00124                      ')' \
00125                    ) \
00126                       )
00127 LOGICAL = \
00128     pp.originalTextFor( \
00129         pp.CaselessKeyword('logical') )
00130 REAL = \
00131     pp.originalTextFor( \
00132         pp.CaselessKeyword('real') +\
00133         pp.Optional( '(' +\
00134                      pp.originalTextFor(VALIDNAME) +\
00135                      ')' \
00136                    ) \
00137                       )
00138 CHARACTER = pp.CaselessKeyword('character')
00139 INTRINSIC_TYPE = \
00140     pp.StringStart() +\
00141     ( INTEGER | REAL | LOGICAL )('name') +\
00142     pp.Optional(pp.Suppress(',') + pp.delimitedList(ATTR_SPEC)) +\
00143     pp.Optional(pp.Suppress('::')) +\
00144     pp.delimitedList(VALIDNAME)('symbolList')
00145 USE_DECL = \
00146     pp.StringStart() +\
00147     pp.CaselessKeyword('use')('use') +\
00148     ( pp.originalTextFor( TEMPLATE_DECL )| VALIDNAME )('name') +\
00149     pp.Optional(ONLY_CLAUSE) +\
00150     pp.Optional(pp.delimitedList( pp.originalTextFor( TEMPLATE_DECL ) ^ 
00151                                   VALIDNAME ) )('onlyList')
00152 TEMPLATE_MOD_PROCEDURE_DECL = \
00153     pp.StringStart() +\
00154     pp.Suppress( pp.CaselessKeyword('module procedure') ) + \
00155     ( pp.originalTextFor( TEMPLATE_DECL ) )('name')
00156 IMPORT_DECL = \
00157     pp.StringStart() +\
00158     pp.Suppress( pp.CaselessKeyword('import') ) + \
00159     ( pp.originalTextFor( TEMPLATE_DECL )| VALIDNAME )('name')
00160 TEMPLATE_TYPE = \
00161     pp.StringStart() +\
00162     pp.CaselessKeyword('type') +\
00163     pp.Suppress('(') +\
00164     pp.originalTextFor(TEMPLATE_DECL ^ VALIDNAME)('name') +\
00165     pp.Suppress(')') +\
00166     pp.Optional(pp.Suppress(',') + pp.delimitedList(ATTR_SPEC)) +\
00167     pp.Suppress('::') +\
00168     pp.delimitedList(VALIDNAME)('symbolList')
00169 TEMPLATE_SUB = \
00170     pp.StringStart() +\
00171     pp.CaselessKeyword('call') +\
00172     pp.originalTextFor(TEMPLATE_DECL)('name') +\
00173     pp.Optional(ARGLIST('args'))
00174 TEMPLATE_FUNC = \
00175     pp.Suppress( pp.oneOf('= ( ,') ) +\
00176     pp.originalTextFor(TEMPLATE_DECL)('name') +\
00177     pp.Optional(ARGLIST('args'))
00178 TEMPLATE_SYMBOL = pp.originalTextFor(TEMPLATE_DECL)('name')
00179 
00180 # TEMPLATE_FUNC.ignore( INLINE_CONTINUATION )
00181 
00182 #........................................
00183 # PyF95 directives grammars
00184 #........................................
00185 PYF95_KEYWORD = pp.LineStart() + pp.Literal('#PyF95')
00186 PYF95_START = \
00187     PYF95_KEYWORD + \
00188     pp.Literal( '.start' )
00189 PYF95_END = \
00190     PYF95_KEYWORD + \
00191     pp.Literal( '.end' )
00192 PYF95_EXTERNAL = \
00193     pp.Suppress( 
00194       PYF95_KEYWORD + \
00195       pp.Literal( '.external' ) + \
00196       pp.Literal( ':' ) ) + \
00197     ( pp.Combine( TEMPLATE_DECL, adjacent=False ) | VALIDNAME )( 'symbol' )
00198 PYF95_ALWAYSWRITE = \
00199     pp.Suppress(
00200       PYF95_KEYWORD + \
00201       pp.Literal( '.alwayswrite' ) + \
00202       pp.Literal( ':' ) ) + \
00203     pp.Word( pp.alphas )('bool')
00204 PYF95_BLOCK = pp.Suppress( PYF95_START ) + pp.SkipTo( PYF95_END )
00205  
00206 ########################################################################
00207 ##
00208 ## Unit-Tests
00209 ##
00210 ########################################################################
00211 
00212 if __name__ == "__main__":
00213 
00214     import unittest
00215 
00216 #=======================================================================
00217     class TestFGrammar(unittest.TestCase):
00218 #=======================================================================
00219     
00220         def setUp(self): pass
00221 
00222         def tearDown(self): pass
00223 
00224     #-------------------------------------------------------------------
00225         def testUseDecl(self):
00226     #-------------------------------------------------------------------
00227             """Checks parsing of use statements"""
00228 
00229             bnf = USE_DECL
00230 
00231             # The USE_DECL should only parse 1 occurrance of the match because
00232             # it uses StringStart method.
00233             ctr = 0
00234             for match,s,e in bnf.scanString( """
00235                     use common_area, only: var1,var2, var3
00236                     use DLList<String>
00237                 """ ):
00238                 ctr += 1
00239             self.failUnlessEqual(ctr,1,"Parsed more than one use statement!")
00240 
00241             # Make sure it peels out the use and name correctly for a template
00242             try:
00243                 output = bnf.parseString("use DLList<String>")
00244             except:
00245                 self.fail("Failed to parse string")
00246             self.failUnlessEqual(output['use'], 'use', "Match use not correct")
00247             self.failUnlessEqual(output['name'], 'DLList<String>', "Match name not correct")
00248 
00249             # Make sure the only statement works
00250             try:
00251                 output = bnf.parseString(" use common_area, only: var1,var2, var3")
00252             except:
00253                 self.fail("Failed to parse string")
00254             self.failUnlessEqual(output['use'], 'use',"Match use not correct")
00255             self.failUnlessEqual(output['name'], 'common_area', "Match name not correct")
00256             self.failUnlessEqual(len(output['onlyList']), 3, "Match list len not correct")
00257             for i in xrange(1,4):
00258                 self.failUnlessEqual(output['onlyList'][i-1], 'var'+str(i), "Match list varname not correct")
00259 
00260         def testIntent(self):
00261             """Checks intent parsing"""
00262 
00263             bnf = INTENT
00264 
00265             try:
00266                 output = bnf.parseString("intent (inout)")
00267             except:
00268                 self.fail("Failed to parse string")
00269             self.failUnlessEqual(output['intent'], 'inout', 'Improper parse of intent')
00270 
00271             try:
00272                 output = bnf.parseString("intent (in)")
00273             except:
00274                 self.fail("Failed to parse string")
00275             self.failUnlessEqual(output['intent'], 'in', 'Improper parse of intent')
00276 
00277             try:
00278                 output = bnf.parseString("intent (out)")
00279             except:
00280                 self.fail("Failed to parse string")
00281             self.failUnlessEqual(output['intent'], 'out', 'Improper parse of intent')
00282 
00283 
00284     suite = unittest.TestLoader().loadTestsFromTestCase(TestFGrammar)
00285     unittest.TextTestRunner(verbosity=2).run(suite)
 All Classes Namespaces Files Functions Variables Properties