1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111
|
from distutils.version import StrictVersion
from pyparsing import (
ParserElement, Forward, Combine, Optional, Word, Literal, CaselessKeyword,
CaselessLiteral, Group, FollowedBy, LineEnd, OneOrMore, ZeroOrMore,
nums, alphas, alphanums, printables, delimitedList, quotedString,
__version__
)
ParserElement.enablePackrat()
grammar = Forward()
expression = Forward()
# Literals
intNumber = Combine(
Optional('-') + Word(nums)
)('integer')
floatNumber = Combine(
Optional('-') + Word(nums) + Literal('.') + Word(nums)
)('float')
sciNumber = Combine(
(floatNumber | intNumber) + CaselessLiteral('e') + intNumber
)('scientific')
aString = quotedString('string')
# Use lookahead to match only numbers in a list (can't remember why this
# is necessary)
afterNumber = FollowedBy(",") ^ FollowedBy(")") ^ FollowedBy(LineEnd())
number = Group(
(sciNumber + afterNumber) |
(floatNumber + afterNumber) |
(intNumber + afterNumber)
)('number')
boolean = Group(
CaselessKeyword("true") |
CaselessKeyword("false")
)('boolean')
argname = Word(alphas + '_', alphanums + '_')('argname')
funcname = Word(alphas + '_', alphanums + '_')('funcname')
# Symbols
leftParen = Literal('(').suppress()
rightParen = Literal(')').suppress()
comma = Literal(',').suppress()
equal = Literal('=').suppress()
# Function calls
# Symbols
leftBrace = Literal('{')
rightBrace = Literal('}')
leftParen = Literal('(').suppress()
rightParen = Literal(')').suppress()
comma = Literal(',').suppress()
equal = Literal('=').suppress()
backslash = Literal('\\').suppress()
symbols = '''(){},=.'"\\'''
arg = Group(
boolean |
number |
aString |
expression
)('args*')
kwarg = Group(argname + equal + arg)('kwargs*')
args = delimitedList(~kwarg + arg) # lookahead to prevent failing on equals
kwargs = delimitedList(kwarg)
call = Group(
funcname + leftParen +
Optional(
args + Optional(
comma + kwargs
)
) + rightParen
)('call')
# Metric pattern (aka. pathExpression)
validMetricChars = ''.join((set(printables) - set(symbols)))
escapedChar = backslash + Word(symbols, exact=1)
partialPathElem = Combine(
OneOrMore(
escapedChar | Word(validMetricChars)
)
)
matchEnum = Combine(
leftBrace +
delimitedList(partialPathElem, combine=True) +
rightBrace
)
pathElement = Combine(
Group(partialPathElem | matchEnum) +
ZeroOrMore(matchEnum | partialPathElem)
)
pathExpression = delimitedList(pathElement,
delim='.', combine=True)('pathExpression')
if StrictVersion(__version__) >= StrictVersion('2.0.0'):
expression <<= Group(call | pathExpression)('expression')
grammar <<= expression
else:
expression << (Group(call | pathExpression)('expression'))
grammar << expression
|