diff options
author | Paul McGuire <ptmcg@austin.rr.com> | 2019-04-07 07:42:20 -0500 |
---|---|---|
committer | Paul McGuire <ptmcg@austin.rr.com> | 2019-04-07 07:42:20 -0500 |
commit | 76b3af7f25d0f25d4eb7f0849a23e22b5b2a0297 (patch) | |
tree | 53ae14557c3bbbe7545983ee7414f442f2fe4d07 | |
parent | a2439508ba5c94546db98593cfa676de9b59babe (diff) | |
download | pyparsing-git-76b3af7f25d0f25d4eb7f0849a23e22b5b2a0297.tar.gz |
Improved support for "python setup.py test"
-rw-r--r-- | CHANGES | 22 | ||||
-rw-r--r-- | examples/antlr_grammar.py | 442 | ||||
-rw-r--r-- | pyparsing.py | 6 | ||||
-rw-r--r-- | setup.py | 77 | ||||
-rw-r--r-- | simple_unit_tests.py | 30 | ||||
-rw-r--r-- | unitTests.py | 6 |
6 files changed, 308 insertions, 275 deletions
@@ -13,7 +13,12 @@ Version 2.4.0 - April, 2019 future breaking changes. . Conditionalizes the API-breaking behavior, based on the value pyparsing.__compat__.collect_all_And_tokens. By default, this value - will be set to True, reflecting the new bugfixed behavior. + will be set to True, reflecting the new bugfixed behavior. To set this + value to False, add to your code: + + import pyparsing + pyparsing.__compat__.collect_all_And_tokens = False + . User code that is dependent on the pre-bugfix behavior can restore it by setting this value to False. @@ -21,6 +26,15 @@ Version 2.4.0 - April, 2019 setting the flag to True or False in these later versions will have no effect. +- Updated unitTests.py and simple_unit_tests.py to be compatible with + "python setup.py test". To run tests using setup, do: + + python setup.py test + python setup.py test -s unitTests.suite + python setup.py test -s simple_unit_tests.suite + + Prompted by issue #83 and PR submitted by bdragon28, thanks. + - Fixed bug in runTests handling '\n' literals in quoted strings. - Added tag_body attribute to the start tag expressions generated by @@ -41,7 +55,6 @@ Version 2.4.0 - April, 2019 - Fixed docstring with embedded '\w', which creates SyntaxWarnings in Py3.8, issue #80. - - Examples: - Added example parser for rosettacode.org tutorial compiler. @@ -57,6 +70,11 @@ Version 2.4.0 - April, 2019 - Examples using makeHTMLTags use new tag_body expression when retrieving a tag's body text. + - Updated examples that are runnable as unit tests: + + python setup.py test -s examples.antlr_grammar_tests + python setup.py test -s examples.test_bibparse + Version 2.3.1 - January, 2019 ----------------------------- diff --git a/examples/antlr_grammar.py b/examples/antlr_grammar.py index e25ff7f..09ef264 100644 --- a/examples/antlr_grammar.py +++ b/examples/antlr_grammar.py @@ -1,219 +1,223 @@ -'''
-antlr_grammar.py
-
-Created on 4 sept. 2010
-
-@author: luca
-
-Submitted by Luca DallOlio, September, 2010
-(Minor updates by Paul McGuire, June, 2012)
-'''
-from pyparsing import Word, ZeroOrMore, printables, Suppress, OneOrMore, Group, \
- LineEnd, Optional, White, originalTextFor, hexnums, nums, Combine, Literal, Keyword, \
- cStyleComment, Regex, Forward, MatchFirst, And, oneOf, alphas, alphanums, \
- delimitedList
-
-# http://www.antlr.org/grammar/ANTLR/ANTLRv3.g
-
-# Tokens
-EOL = Suppress(LineEnd()) # $
-singleTextString = originalTextFor(ZeroOrMore(~EOL + (White(" \t") | Word(printables)))).leaveWhitespace()
-XDIGIT = hexnums
-INT = Word(nums)
-ESC = Literal('\\') + (oneOf(list(r'nrtbf\">'+"'")) | ('u' + Word(hexnums, exact=4)) | Word(printables, exact=1))
-LITERAL_CHAR = ESC | ~(Literal("'") | Literal('\\')) + Word(printables, exact=1)
-CHAR_LITERAL = Suppress("'") + LITERAL_CHAR + Suppress("'")
-STRING_LITERAL = Suppress("'") + Combine(OneOrMore(LITERAL_CHAR)) + Suppress("'")
-DOUBLE_QUOTE_STRING_LITERAL = '"' + ZeroOrMore(LITERAL_CHAR) + '"'
-DOUBLE_ANGLE_STRING_LITERAL = '<<' + ZeroOrMore(Word(printables, exact=1)) + '>>'
-TOKEN_REF = Word(alphas.upper(), alphanums+'_')
-RULE_REF = Word(alphas.lower(), alphanums+'_')
-ACTION_ESC = (Suppress("\\") + Suppress("'")) | Suppress('\\"') | Suppress('\\') + (~(Literal("'") | Literal('"')) + Word(printables, exact=1))
-ACTION_CHAR_LITERAL = Suppress("'") + (ACTION_ESC | ~(Literal('\\') | Literal("'")) + Word(printables, exact=1)) + Suppress("'")
-ACTION_STRING_LITERAL = Suppress('"') + ZeroOrMore(ACTION_ESC | ~(Literal('\\') | Literal('"')) + Word(printables, exact=1)) + Suppress('"')
-SRC = Suppress('src') + ACTION_STRING_LITERAL("file") + INT("line")
-id = TOKEN_REF | RULE_REF
-SL_COMMENT = Suppress('//') + Suppress('$ANTLR') + SRC | ZeroOrMore(~EOL + Word(printables)) + EOL
-ML_COMMENT = cStyleComment
-WS = OneOrMore(Suppress(' ') | Suppress('\t') | (Optional(Suppress('\r')) + Literal('\n')))
-WS_LOOP = ZeroOrMore(SL_COMMENT | ML_COMMENT)
-NESTED_ARG_ACTION = Forward()
-NESTED_ARG_ACTION << Suppress('[') + ZeroOrMore(NESTED_ARG_ACTION | ACTION_STRING_LITERAL | ACTION_CHAR_LITERAL) + Suppress(']')
-ARG_ACTION = NESTED_ARG_ACTION
-NESTED_ACTION = Forward()
-NESTED_ACTION << Suppress('{') + ZeroOrMore(NESTED_ACTION | SL_COMMENT | ML_COMMENT | ACTION_STRING_LITERAL | ACTION_CHAR_LITERAL) + Suppress('}')
-ACTION = NESTED_ACTION + Optional('?')
-SCOPE = Suppress('scope')
-OPTIONS = Suppress('options') + Suppress('{') # + WS_LOOP + Suppress('{')
-TOKENS = Suppress('tokens') + Suppress('{') # + WS_LOOP + Suppress('{')
-FRAGMENT = 'fragment';
-TREE_BEGIN = Suppress('^(')
-ROOT = Suppress('^')
-BANG = Suppress('!')
-RANGE = Suppress('..')
-REWRITE = Suppress('->')
-
-# General Parser Definitions
-
-# Grammar heading
-optionValue = id | STRING_LITERAL | CHAR_LITERAL | INT | Literal('*').setName("s")
-
-option = Group(id("id") + Suppress('=') + optionValue("value"))("option")
-optionsSpec = OPTIONS + Group(OneOrMore(option + Suppress(';')))("options") + Suppress('}')
-tokenSpec = Group(TOKEN_REF("token_ref") + (Suppress('=') + (STRING_LITERAL | CHAR_LITERAL)("lit")))("token") + Suppress(';')
-tokensSpec = TOKENS + Group(OneOrMore(tokenSpec))("tokens") + Suppress('}')
-attrScope = Suppress('scope') + id + ACTION
-grammarType = Keyword('lexer') + Keyword('parser') + Keyword('tree')
-actionScopeName = id | Keyword('lexer')("l") | Keyword('parser')("p")
-action = Suppress('@') + Optional(actionScopeName + Suppress('::')) + id + ACTION
-
-grammarHeading = Optional(ML_COMMENT("ML_COMMENT")) + Optional(grammarType) + Suppress('grammar') + id("grammarName") + Suppress(';') + Optional(optionsSpec) + Optional(tokensSpec) + ZeroOrMore(attrScope) + ZeroOrMore(action)
-
-modifier = Keyword('protected') | Keyword('public') | Keyword('private') | Keyword('fragment')
-ruleAction = Suppress('@') + id + ACTION
-throwsSpec = Suppress('throws') + delimitedList(id)
-ruleScopeSpec = (Suppress('scope') + ACTION) | (Suppress('scope') + delimitedList(id) + Suppress(';')) | (Suppress('scope') + ACTION + Suppress('scope') + delimitedList(id) + Suppress(';'))
-unary_op = oneOf("^ !")
-notTerminal = CHAR_LITERAL | TOKEN_REF | STRING_LITERAL
-terminal = (CHAR_LITERAL | TOKEN_REF + Optional(ARG_ACTION) | STRING_LITERAL | '.') + Optional(unary_op)
-block = Forward()
-notSet = Suppress('~') + (notTerminal | block)
-rangeNotPython = CHAR_LITERAL("c1") + RANGE + CHAR_LITERAL("c2")
-atom = Group(rangeNotPython + Optional(unary_op)("op")) | terminal | (notSet + Optional(unary_op)("op")) | (RULE_REF + Optional(ARG_ACTION("arg")) + Optional(unary_op)("op"))
-element = Forward()
-treeSpec = Suppress('^(') + element*(2,) + Suppress(')')
-ebnfSuffix = oneOf("? * +")
-ebnf = block + Optional(ebnfSuffix("op") | '=>')
-elementNoOptionSpec = (id("result_name") + oneOf('= +=')("labelOp") + atom("atom") + Optional(ebnfSuffix)) | (id("result_name") + oneOf('= +=')("labelOp") + block + Optional(ebnfSuffix)) | atom("atom") + Optional(ebnfSuffix) | ebnf | ACTION | (treeSpec + Optional(ebnfSuffix)) # | SEMPRED ( '=>' -> GATED_SEMPRED | -> SEMPRED )
-element << Group(elementNoOptionSpec)("element")
-alternative = Group(Group(OneOrMore(element))("elements")) # Do not ask me why group is needed twice... seems like the xml that you see is not always the real structure?
-rewrite = Optional(Literal('TODO REWRITE RULES TODO'))
-block << Suppress('(') + Optional(Optional(optionsSpec("opts")) + Suppress(':')) + Group(alternative('a1') + rewrite + Group(ZeroOrMore(Suppress('|') + alternative('a2') + rewrite))("alternatives"))("block") + Suppress(')')
-altList = alternative('a1') + rewrite + Group(ZeroOrMore(Suppress('|') + alternative('a2') + rewrite))("alternatives")
-exceptionHandler = Suppress('catch') + ARG_ACTION + ACTION
-finallyClause = Suppress('finally') + ACTION
-exceptionGroup = (OneOrMore(exceptionHandler) + Optional(finallyClause)) | finallyClause
-
-ruleHeading = Optional(ML_COMMENT)("ruleComment") + Optional(modifier)("modifier") + id("ruleName") + Optional("!") + Optional(ARG_ACTION("arg")) + Optional(Suppress('returns') + ARG_ACTION("rt")) + Optional(throwsSpec) + Optional(optionsSpec) + Optional(ruleScopeSpec) + ZeroOrMore(ruleAction)
-rule = Group(ruleHeading + Suppress(':') + altList + Suppress(';') + Optional(exceptionGroup))("rule")
-
-grammarDef = grammarHeading + Group(OneOrMore(rule))("rules")
-
-def grammar():
- return grammarDef
-
-def __antlrAlternativesConverter(pyparsingRules, antlrBlock):
- rule = None
- if hasattr(antlrBlock, 'alternatives') and antlrBlock.alternatives != '' and len(antlrBlock.alternatives) > 0:
- alternatives = []
- alternatives.append(__antlrAlternativeConverter(pyparsingRules, antlrBlock.a1))
- for alternative in antlrBlock.alternatives:
- alternatives.append(__antlrAlternativeConverter(pyparsingRules, alternative))
- rule = MatchFirst(alternatives)("anonymous_or")
- elif hasattr(antlrBlock, 'a1') and antlrBlock.a1 != '':
- rule = __antlrAlternativeConverter(pyparsingRules, antlrBlock.a1)
- else:
- raise Exception('Not yet implemented')
- assert rule != None
- return rule
-
-def __antlrAlternativeConverter(pyparsingRules, antlrAlternative):
- elementList = []
- for element in antlrAlternative.elements:
- rule = None
- if hasattr(element.atom, 'c1') and element.atom.c1 != '':
- regex = r'['+str(element.atom.c1[0])+'-'+str(element.atom.c2[0]+']')
- rule = Regex(regex)("anonymous_regex")
- elif hasattr(element, 'block') and element.block != '':
- rule = __antlrAlternativesConverter(pyparsingRules, element.block)
- else:
- ruleRef = element.atom
- assert ruleRef in pyparsingRules
- rule = pyparsingRules[element.atom](element.atom)
- if hasattr(element, 'op') and element.op != '':
- if element.op == '+':
- rule = Group(OneOrMore(rule))("anonymous_one_or_more")
- elif element.op == '*':
- rule = Group(ZeroOrMore(rule))("anonymous_zero_or_more")
- elif element.op == '?':
- rule = Optional(rule)
- else:
- raise Exception('rule operator not yet implemented : ' + element.op)
- rule = rule
- elementList.append(rule)
- if len(elementList) > 1:
- rule = Group(And(elementList))("anonymous_and")
- else:
- rule = elementList[0]
- assert rule != None
- return rule
-
-def __antlrRuleConverter(pyparsingRules, antlrRule):
- rule = None
- rule = __antlrAlternativesConverter(pyparsingRules, antlrRule)
- assert rule != None
- rule(antlrRule.ruleName)
- return rule
-
-def antlrConverter(antlrGrammarTree):
- pyparsingRules = {}
- antlrTokens = {}
- for antlrToken in antlrGrammarTree.tokens:
- antlrTokens[antlrToken.token_ref] = antlrToken.lit
- for antlrTokenName, antlrToken in list(antlrTokens.items()):
- pyparsingRules[antlrTokenName] = Literal(antlrToken)
- antlrRules = {}
- for antlrRule in antlrGrammarTree.rules:
- antlrRules[antlrRule.ruleName] = antlrRule
- pyparsingRules[antlrRule.ruleName] = Forward() # antlr is a top down grammar
- for antlrRuleName, antlrRule in list(antlrRules.items()):
- pyparsingRule = __antlrRuleConverter(pyparsingRules, antlrRule)
- assert pyparsingRule != None
- pyparsingRules[antlrRuleName] << pyparsingRule
- return pyparsingRules
-
-if __name__ == "__main__":
-
- text = """grammar SimpleCalc;
-
-options {
- language = Python;
-}
-
-tokens {
- PLUS = '+' ;
- MINUS = '-' ;
- MULT = '*' ;
- DIV = '/' ;
-}
-
-/*------------------------------------------------------------------
- * PARSER RULES
- *------------------------------------------------------------------*/
-
-expr : term ( ( PLUS | MINUS ) term )* ;
-
-term : factor ( ( MULT | DIV ) factor )* ;
-
-factor : NUMBER ;
-
-
-/*------------------------------------------------------------------
- * LEXER RULES
- *------------------------------------------------------------------*/
-
-NUMBER : (DIGIT)+ ;
-
-/* WHITESPACE : ( '\t' | ' ' | '\r' | '\n'| '\u000C' )+ { $channel = HIDDEN; } ; */
-
-fragment DIGIT : '0'..'9' ;
-
-"""
-
- grammar().validate()
- antlrGrammarTree = grammar().parseString(text)
- print(antlrGrammarTree.asXML("antlrGrammarTree"))
- pyparsingRules = antlrConverter(antlrGrammarTree)
- pyparsingRule = pyparsingRules["expr"]
- pyparsingTree = pyparsingRule.parseString("2 - 5 * 42 + 7 / 25")
- print(pyparsingTree.asXML("pyparsingTree"))
+''' +antlr_grammar.py + +Created on 4 sept. 2010 + +@author: luca + +Submitted by Luca DallOlio, September, 2010 +(Minor updates by Paul McGuire, June, 2012) +''' +from pyparsing import Word, ZeroOrMore, printables, Suppress, OneOrMore, Group, \ + LineEnd, Optional, White, originalTextFor, hexnums, nums, Combine, Literal, Keyword, \ + cStyleComment, Regex, Forward, MatchFirst, And, oneOf, alphas, alphanums, \ + delimitedList, ungroup + +# http://www.antlr.org/grammar/ANTLR/ANTLRv3.g + +# Tokens +EOL = Suppress(LineEnd()) # $ +singleTextString = originalTextFor(ZeroOrMore(~EOL + (White(" \t") | Word(printables)))).leaveWhitespace() +XDIGIT = hexnums +INT = Word(nums) +ESC = Literal('\\') + (oneOf(list(r'nrtbf\">'+"'")) | ('u' + Word(hexnums, exact=4)) | Word(printables, exact=1)) +LITERAL_CHAR = ESC | ~(Literal("'") | Literal('\\')) + Word(printables, exact=1) +CHAR_LITERAL = Suppress("'") + LITERAL_CHAR + Suppress("'") +STRING_LITERAL = Suppress("'") + Combine(OneOrMore(LITERAL_CHAR)) + Suppress("'") +DOUBLE_QUOTE_STRING_LITERAL = '"' + ZeroOrMore(LITERAL_CHAR) + '"' +DOUBLE_ANGLE_STRING_LITERAL = '<<' + ZeroOrMore(Word(printables, exact=1)) + '>>' +TOKEN_REF = Word(alphas.upper(), alphanums+'_') +RULE_REF = Word(alphas.lower(), alphanums+'_') +ACTION_ESC = (Suppress("\\") + Suppress("'")) | Suppress('\\"') | Suppress('\\') + (~(Literal("'") | Literal('"')) + Word(printables, exact=1)) +ACTION_CHAR_LITERAL = Suppress("'") + (ACTION_ESC | ~(Literal('\\') | Literal("'")) + Word(printables, exact=1)) + Suppress("'") +ACTION_STRING_LITERAL = Suppress('"') + ZeroOrMore(ACTION_ESC | ~(Literal('\\') | Literal('"')) + Word(printables, exact=1)) + Suppress('"') +SRC = Suppress('src') + ACTION_STRING_LITERAL("file") + INT("line") +id = TOKEN_REF | RULE_REF +SL_COMMENT = Suppress('//') + Suppress('$ANTLR') + SRC | ZeroOrMore(~EOL + Word(printables)) + EOL +ML_COMMENT = cStyleComment +WS = OneOrMore(Suppress(' ') | Suppress('\t') | (Optional(Suppress('\r')) + Literal('\n'))) +WS_LOOP = ZeroOrMore(SL_COMMENT | ML_COMMENT) +NESTED_ARG_ACTION = Forward() +NESTED_ARG_ACTION << Suppress('[') + ZeroOrMore(NESTED_ARG_ACTION | ACTION_STRING_LITERAL | ACTION_CHAR_LITERAL) + Suppress(']') +ARG_ACTION = NESTED_ARG_ACTION +NESTED_ACTION = Forward() +NESTED_ACTION << Suppress('{') + ZeroOrMore(NESTED_ACTION | SL_COMMENT | ML_COMMENT | ACTION_STRING_LITERAL | ACTION_CHAR_LITERAL) + Suppress('}') +ACTION = NESTED_ACTION + Optional('?') +SCOPE = Suppress('scope') +OPTIONS = Suppress('options') + Suppress('{') # + WS_LOOP + Suppress('{') +TOKENS = Suppress('tokens') + Suppress('{') # + WS_LOOP + Suppress('{') +FRAGMENT = 'fragment'; +TREE_BEGIN = Suppress('^(') +ROOT = Suppress('^') +BANG = Suppress('!') +RANGE = Suppress('..') +REWRITE = Suppress('->') + +# General Parser Definitions + +# Grammar heading +optionValue = id | STRING_LITERAL | CHAR_LITERAL | INT | Literal('*').setName("s") + +option = Group(id("id") + Suppress('=') + optionValue("value"))("option") +optionsSpec = OPTIONS + Group(OneOrMore(option + Suppress(';')))("options") + Suppress('}') +tokenSpec = Group(TOKEN_REF("token_ref") + (Suppress('=') + (STRING_LITERAL | CHAR_LITERAL)("lit")))("token") + Suppress(';') +tokensSpec = TOKENS + Group(OneOrMore(tokenSpec))("tokens") + Suppress('}') +attrScope = Suppress('scope') + id + ACTION +grammarType = Keyword('lexer') + Keyword('parser') + Keyword('tree') +actionScopeName = id | Keyword('lexer')("l") | Keyword('parser')("p") +action = Suppress('@') + Optional(actionScopeName + Suppress('::')) + id + ACTION + +grammarHeading = Optional(ML_COMMENT("ML_COMMENT")) + Optional(grammarType) + Suppress('grammar') + id("grammarName") + Suppress(';') + Optional(optionsSpec) + Optional(tokensSpec) + ZeroOrMore(attrScope) + ZeroOrMore(action) + +modifier = Keyword('protected') | Keyword('public') | Keyword('private') | Keyword('fragment') +ruleAction = Suppress('@') + id + ACTION +throwsSpec = Suppress('throws') + delimitedList(id) +ruleScopeSpec = (Suppress('scope') + ACTION) | (Suppress('scope') + delimitedList(id) + Suppress(';')) | (Suppress('scope') + ACTION + Suppress('scope') + delimitedList(id) + Suppress(';')) +unary_op = oneOf("^ !") +notTerminal = CHAR_LITERAL | TOKEN_REF | STRING_LITERAL +terminal = (CHAR_LITERAL | TOKEN_REF + Optional(ARG_ACTION) | STRING_LITERAL | '.') + Optional(unary_op) +block = Forward() +notSet = Suppress('~') + (notTerminal | block) +rangeNotPython = CHAR_LITERAL("c1") + RANGE + CHAR_LITERAL("c2") +atom = Group((rangeNotPython + Optional(unary_op)("op")) + | terminal + | (notSet + Optional(unary_op)("op")) + | (RULE_REF + Optional(ARG_ACTION("arg")) + Optional(unary_op)("op")) + ) +element = Forward() +treeSpec = Suppress('^(') + element*(2,) + Suppress(')') +ebnfSuffix = oneOf("? * +") +ebnf = block + Optional(ebnfSuffix("op") | '=>') +elementNoOptionSpec = (id("result_name") + oneOf('= +=')("labelOp") + atom("atom") + Optional(ebnfSuffix)) | (id("result_name") + oneOf('= +=')("labelOp") + block + Optional(ebnfSuffix)) | atom("atom") + Optional(ebnfSuffix) | ebnf | ACTION | (treeSpec + Optional(ebnfSuffix)) # | SEMPRED ( '=>' -> GATED_SEMPRED | -> SEMPRED ) +element << Group(elementNoOptionSpec)("element") +alternative = Group(Group(OneOrMore(element))("elements")) # Do not ask me why group is needed twice... seems like the xml that you see is not always the real structure? +rewrite = Optional(Literal('TODO REWRITE RULES TODO')) +block << Suppress('(') + Optional(Optional(optionsSpec("opts")) + Suppress(':')) + Group(alternative('a1') + rewrite + Group(ZeroOrMore(Suppress('|') + alternative('a2') + rewrite))("alternatives"))("block") + Suppress(')') +altList = alternative('a1') + rewrite + Group(ZeroOrMore(Suppress('|') + alternative('a2') + rewrite))("alternatives") +exceptionHandler = Suppress('catch') + ARG_ACTION + ACTION +finallyClause = Suppress('finally') + ACTION +exceptionGroup = (OneOrMore(exceptionHandler) + Optional(finallyClause)) | finallyClause + +ruleHeading = Optional(ML_COMMENT)("ruleComment") + Optional(modifier)("modifier") + id("ruleName") + Optional("!") + Optional(ARG_ACTION("arg")) + Optional(Suppress('returns') + ARG_ACTION("rt")) + Optional(throwsSpec) + Optional(optionsSpec) + Optional(ruleScopeSpec) + ZeroOrMore(ruleAction) +rule = Group(ruleHeading + Suppress(':') + altList + Suppress(';') + Optional(exceptionGroup))("rule") + +grammarDef = grammarHeading + Group(OneOrMore(rule))("rules") + +def grammar(): + return grammarDef + +def __antlrAlternativesConverter(pyparsingRules, antlrBlock): + rule = None + if hasattr(antlrBlock, 'alternatives') and antlrBlock.alternatives != '' and len(antlrBlock.alternatives) > 0: + alternatives = [] + alternatives.append(__antlrAlternativeConverter(pyparsingRules, antlrBlock.a1)) + for alternative in antlrBlock.alternatives: + alternatives.append(__antlrAlternativeConverter(pyparsingRules, alternative)) + rule = MatchFirst(alternatives)("anonymous_or") + elif hasattr(antlrBlock, 'a1') and antlrBlock.a1 != '': + rule = __antlrAlternativeConverter(pyparsingRules, antlrBlock.a1) + else: + raise Exception('Not yet implemented') + assert rule != None + return rule + +def __antlrAlternativeConverter(pyparsingRules, antlrAlternative): + elementList = [] + for element in antlrAlternative.elements: + rule = None + if hasattr(element.atom, 'c1') and element.atom.c1 != '': + regex = r'['+str(element.atom.c1[0])+'-'+str(element.atom.c2[0]+']') + rule = Regex(regex)("anonymous_regex") + elif hasattr(element, 'block') and element.block != '': + rule = __antlrAlternativesConverter(pyparsingRules, element.block) + else: + ruleRef = element.atom[0] + assert ruleRef in pyparsingRules + rule = pyparsingRules[ruleRef](ruleRef) + if hasattr(element, 'op') and element.op != '': + if element.op == '+': + rule = Group(OneOrMore(rule))("anonymous_one_or_more") + elif element.op == '*': + rule = Group(ZeroOrMore(rule))("anonymous_zero_or_more") + elif element.op == '?': + rule = Optional(rule) + else: + raise Exception('rule operator not yet implemented : ' + element.op) + rule = rule + elementList.append(rule) + if len(elementList) > 1: + rule = Group(And(elementList))("anonymous_and") + else: + rule = elementList[0] + assert rule != None + return rule + +def __antlrRuleConverter(pyparsingRules, antlrRule): + rule = None + rule = __antlrAlternativesConverter(pyparsingRules, antlrRule) + assert rule != None + rule(antlrRule.ruleName) + return rule + +def antlrConverter(antlrGrammarTree): + pyparsingRules = {} + antlrTokens = {} + for antlrToken in antlrGrammarTree.tokens: + antlrTokens[antlrToken.token_ref] = antlrToken.lit + for antlrTokenName, antlrToken in list(antlrTokens.items()): + pyparsingRules[antlrTokenName] = Literal(antlrToken) + antlrRules = {} + for antlrRule in antlrGrammarTree.rules: + antlrRules[antlrRule.ruleName] = antlrRule + pyparsingRules[antlrRule.ruleName] = Forward() # antlr is a top down grammar + for antlrRuleName, antlrRule in list(antlrRules.items()): + pyparsingRule = __antlrRuleConverter(pyparsingRules, antlrRule) + assert pyparsingRule != None + pyparsingRules[antlrRuleName] << pyparsingRule + return pyparsingRules + +if __name__ == "__main__": + + text = """grammar SimpleCalc; + +options { + language = Python; +} + +tokens { + PLUS = '+' ; + MINUS = '-' ; + MULT = '*' ; + DIV = '/' ; +} + +/*------------------------------------------------------------------ + * PARSER RULES + *------------------------------------------------------------------*/ + +expr : term ( ( PLUS | MINUS ) term )* ; + +term : factor ( ( MULT | DIV ) factor )* ; + +factor : NUMBER ; + + +/*------------------------------------------------------------------ + * LEXER RULES + *------------------------------------------------------------------*/ + +NUMBER : (DIGIT)+ ; + +/* WHITESPACE : ( '\t' | ' ' | '\r' | '\n'| '\u000C' )+ { $channel = HIDDEN; } ; */ + +fragment DIGIT : '0'..'9' ; + +""" + + grammar().validate() + antlrGrammarTree = grammar().parseString(text) + print(antlrGrammarTree.asXML("antlrGrammarTree")) + pyparsingRules = antlrConverter(antlrGrammarTree) + pyparsingRule = pyparsingRules["expr"] + pyparsingTree = pyparsingRule.parseString("2 - 5 * 42 + 7 / 25") + print(pyparsingTree.asXML("pyparsingTree")) diff --git a/pyparsing.py b/pyparsing.py index 97f3374..093d346 100644 --- a/pyparsing.py +++ b/pyparsing.py @@ -94,7 +94,7 @@ classes inherit from. Use the docstrings for examples of how to: """ __version__ = "2.4.0" -__versionTime__ = "07 Apr 2019 04:08 UTC" +__versionTime__ = "07 Apr 2019 12:40 UTC" __author__ = "Paul McGuire <ptmcg@users.sourceforge.net>" import string @@ -160,7 +160,7 @@ __compat__.collect_all_And_tokens = True #~ sys.stderr.write( "testing pyparsing module, version %s, %s\n" % (__version__,__versionTime__ ) ) -__all__ = [ +__all__ = [ '__version__', '__versionTime__', '__author__', '__compat__', 'And', 'CaselessKeyword', 'CaselessLiteral', 'CharsNotIn', 'Combine', 'Dict', 'Each', 'Empty', 'FollowedBy', 'Forward', 'GoToColumn', 'Group', 'Keyword', 'LineEnd', 'LineStart', 'Literal', 'PrecededBy', 'MatchFirst', 'NoMatch', 'NotAny', 'OneOrMore', 'OnlyOnce', 'Optional', 'Or', @@ -5215,7 +5215,7 @@ def ungroup(expr): """Helper to undo pyparsing's default grouping of And expressions, even if all but one are non-empty. """ - return TokenConverter(expr).setParseAction(lambda t:t[0]) + return TokenConverter(expr).addParseAction(lambda t:t[0]) def locatedExpr(expr): """Helper to decorate a returned token with its starting and ending @@ -1,38 +1,39 @@ -#!/usr/bin/env python
-
-"""Setup script for the pyparsing module distribution."""
-
-from setuptools import setup
-from pyparsing import __version__ as pyparsing_version
-
-modules = ["pyparsing",]
-
-setup(# Distribution meta-data
- name = "pyparsing",
- version = pyparsing_version,
- description = "Python parsing module",
- author = "Paul McGuire",
- author_email = "ptmcg@users.sourceforge.net",
- url = "https://github.com/pyparsing/pyparsing/",
- download_url = "https://pypi.org/project/pyparsing/",
- license = "MIT License",
- py_modules = modules,
- python_requires='>=2.6, !=3.0.*, !=3.1.*, !=3.2.*',
- classifiers=[
- 'Development Status :: 5 - Production/Stable',
- 'Intended Audience :: Developers',
- 'Intended Audience :: Information Technology',
- 'License :: OSI Approved :: MIT License',
- 'Operating System :: OS Independent',
- 'Programming Language :: Python',
- 'Programming Language :: Python :: 2',
- 'Programming Language :: Python :: 2.6',
- 'Programming Language :: Python :: 2.7',
- 'Programming Language :: Python :: 3',
- 'Programming Language :: Python :: 3.3',
- 'Programming Language :: Python :: 3.4',
- 'Programming Language :: Python :: 3.5',
- 'Programming Language :: Python :: 3.6',
- 'Programming Language :: Python :: 3.7',
- ]
- )
+#!/usr/bin/env python + +"""Setup script for the pyparsing module distribution.""" + +from setuptools import setup +from pyparsing import __version__ as pyparsing_version + +modules = ["pyparsing",] + +setup(# Distribution meta-data + name = "pyparsing", + version = pyparsing_version, + description = "Python parsing module", + author = "Paul McGuire", + author_email = "ptmcg@users.sourceforge.net", + url = "https://github.com/pyparsing/pyparsing/", + download_url = "https://pypi.org/project/pyparsing/", + license = "MIT License", + py_modules = modules, + python_requires='>=2.6, !=3.0.*, !=3.1.*, !=3.2.*', + test_suite="unitTests.suite", + classifiers=[ + 'Development Status :: 5 - Production/Stable', + 'Intended Audience :: Developers', + 'Intended Audience :: Information Technology', + 'License :: OSI Approved :: MIT License', + 'Operating System :: OS Independent', + 'Programming Language :: Python', + 'Programming Language :: Python :: 2', + 'Programming Language :: Python :: 2.6', + 'Programming Language :: Python :: 2.7', + 'Programming Language :: Python :: 3', + 'Programming Language :: Python :: 3.3', + 'Programming Language :: Python :: 3.4', + 'Programming Language :: Python :: 3.5', + 'Programming Language :: Python :: 3.6', + 'Programming Language :: Python :: 3.7', + ] + ) diff --git a/simple_unit_tests.py b/simple_unit_tests.py index a40795b..7e42003 100644 --- a/simple_unit_tests.py +++ b/simple_unit_tests.py @@ -94,7 +94,7 @@ class PyparsingExpressionTestCase(unittest.TestCase): self.assertTrue(False, "failed to raise expected exception") -#=========== TEST DEFINITIONS START HERE ============== +# =========== TEST DEFINITIONS START HERE ============== class TestLiteral(PyparsingExpressionTestCase): tests = [ @@ -444,20 +444,26 @@ class TestCommonHelperExpressions(PyparsingExpressionTestCase): ] -#============ MAIN ================ - -if __name__ == '__main__': +def _get_decl_line_no(cls): import inspect - def get_decl_line_no(cls): - return inspect.getsourcelines(cls)[1] + return inspect.getsourcelines(cls)[1] + - # get all test case classes defined in this module and sort them by decl line no - test_case_classes = list(PyparsingExpressionTestCase.__subclasses__()) - test_case_classes.sort(key=get_decl_line_no) +# get all test case classes defined in this module and sort them by decl line no +test_case_classes = list(PyparsingExpressionTestCase.__subclasses__()) +test_case_classes.sort(key=_get_decl_line_no) - # make into a suite and run it - this will run the tests in the same order - # they are declared in this module - suite = unittest.TestSuite(cls() for cls in test_case_classes) +# make into a suite and run it - this will run the tests in the same order +# they are declared in this module +# +# runnable from setup.py using "python setup.py test -s simple_unit_tests.suite" +# +suite = unittest.TestSuite(cls() for cls in test_case_classes) + + +# ============ MAIN ================ + +if __name__ == '__main__': result = unittest.TextTestRunner().run(suite) exit(0 if result.wasSuccessful() else 1) diff --git a/unitTests.py b/unitTests.py index 908266e..5db9ffe 100644 --- a/unitTests.py +++ b/unitTests.py @@ -4327,6 +4327,10 @@ def makeTestSuiteTemp(classes): suite.addTests(cls() for cls in classes) return suite +# runnable from setup.py using "python setup.py test -s unitTests.suite" +suite = makeTestSuite() + + if __name__ == '__main__': testRunner = TextTestRunner() @@ -4337,7 +4341,7 @@ if __name__ == '__main__': ] if not testclasses: - result = testRunner.run(makeTestSuite()) + result = testRunner.run(suite) else: BUFFER_OUTPUT = False result = testRunner.run(makeTestSuiteTemp(testclasses)) |