I'm trying to detect valid Java annotations in a text. Here's my test program (I'm currently ignoring all whitespace for simplicity, I'll add this later):
txts = ['@SomeName2', # match
'@SomeName2(', # no match
'@SomeName2)', # no match
'@SomeName2()', # match
'@SomeName2()()', # no match
'@SomeName2(value)', # no match
'@SomeName2(=)', # no match
'@SomeName2("")', # match
'@SomeName2(".")', # no match
'@SomeName2(",")', # match
'@SomeName2(value=)', # no match
'@SomeName2(value=")', # no match
'@SomeName2(=3)', # no match
'@SomeName2(="")', # no match
'@SomeName2(value=3)', # match
'@SomeName2(value=3L)', # match
'@SomeName2(value="")', # match
'@SomeName2(value=true)', # match
'@SomeName2(value=false)', # match
'@SomeName2(value=".")', # no match
'@SomeName2(value=",")', # match
'@SomeName2(x="o_nbr ASC, a")', # match
# multiple params:
'@SomeName2(,value="ord_nbr ASC, name")', # no match
'@SomeName2(value="ord_nbr ASC, name",)', # no match
'@SomeName2(value="ord_nbr ASC, name"insertable=false)', # no match
'@SomeName2(value="ord_nbr ASC, name",insertable=false)', # match
'@SomeName2(value="ord_nbr ASC, name",insertable=false,length=10L)', # match
'@SomeName2 ( "ord_nbr ASC, name", insertable = false, length = 10L )', # match
]
#regex = '((?:@[a-z][a-z0-9_]*))(\((((?:[a-z][a-z0-9_]*))(=)(\d+l?|"(?:[a-z0-9_, ]*)"|true|false))?\))?$'
#regex = '((?:@[a-z][a-z0-9_]*))(\((((?:[a-z][a-z0-9_]*))(=)(\d+l?|"(?:[a-z0-9_, ]*)"|true|false))?(,((?:[a-z][a-z0-9_]*))(=)(\d+l?|"(?:[a-z0-9_, ]*)"|true|false))*\))?$'
regex = r"""
(?:@[a-z]\w*) # @ + identifier (class name)
(
\( # opening parenthesis
(
(?:[a-z]\w*) # identifier (var name)
= # assigment operator
(\d+l?|"(?:[a-z0-9_, ]*)"|true|false) # either a numeric | a quoted string containing only alphanumeric chars, _, space | true | false
)? # optional assignment group
\) # closing parenthesis
)?$ # optional parentheses group (zero or one)
"""
rg = re.compile(regex, re.VERBOSE + re.IGNORECASE)
for txt in txts:
m = rg.search(txt)
#m = rg.match(txt)
if m:
print "MATCH: ",
output = ''
for i in xrange(2):
output = output + '[' + str(m.group(i+1)) + ']'
print output
else:
print "NO MATCH: " + txt
So basically what I have seems to work for zero or one parameters. Now I'm trying to extend the syntax to zero or more parameters, like in the last example.
I then copied the regex part that represents the assignment and prepend it by a comma for the 2nd to nth group (this group now using * instead of ?):
regex = '((?:@[a-z][a-z0-9_]*))(\((((?:[a-z][a-z0-9_]*))(=)(\d+l?|"(?:[a-z0-9_, ]*)"|true|false))?(,((?:[a-z][a-z0-9_]*))(=)(\d+l?|"(?:[a-z0-9_, ]*)"|true|false))*\))?$'
That cannot work however. The problem seems to be how to handle the first element, because the it must be optional, then strings like the first extension example '@SomeName2(,value="ord_nbr ASC, name")'
would be accepted, which is wrong. I have no idea how to make the 2nd to nth assignment depend only on the presence of the first (optional) element.
Can it be done? Is it done that way? How do you best solve this?
Thanks