From bfed22e3b7b6de27359d41f1a51ea345f7284ce2 Mon Sep 17 00:00:00 2001 From: Alexandru Croitor Date: Thu, 23 May 2019 15:57:59 +0200 Subject: [PATCH] Remove all line continuations when processing qmake syntax We constantly had to adjust the qmake grammar to handle line continuations (\\\n) in weird places. Instead of doing that, just do a preprocess step to remove all the LCs like we do with comments, and simplify the grammar not to take into account the LCs. From some manual testing it doesn't look like we get any regressions. Change-Id: I2017d59396004cf67b6cb54977583db65c65e7d3 Reviewed-by: Tobias Hunger --- util/cmake/pro2cmake.py | 59 +++++++++++++++---------------- util/cmake/tests/test_lc_fixup.py | 8 ++--- 2 files changed, 32 insertions(+), 35 deletions(-) diff --git a/util/cmake/pro2cmake.py b/util/cmake/pro2cmake.py index 0f3f796227c..5a2db68e670 100755 --- a/util/cmake/pro2cmake.py +++ b/util/cmake/pro2cmake.py @@ -149,9 +149,12 @@ def process_qrc_file(target: str, filepath: str, base_dir: str = '') -> str: def fixup_linecontinuation(contents: str) -> str: - contents = re.sub(r'([^\t ])\\[ \t]*\n', '\\1 \\\n', contents) - contents = re.sub(r'\\[ \t]*\n', '\\\n', contents) - + # Remove all line continuations, aka a backslash followed by + # a newline character with an arbitrary amount of whitespace + # between the backslash and the newline. + # This greatly simplifies the qmake parsing grammar. + contents = re.sub(r'([^\t ])\\[ \t]*\n', '\\1 ', contents) + contents = re.sub(r'\\[ \t]*\n', '', contents) return contents @@ -721,8 +724,6 @@ class QmakeParser: value.setDebug() return value - LC = add_element('LC', pp.Suppress(pp.Literal('\\\n'))) - EOL = add_element('EOL', pp.Suppress(pp.LineEnd())) Else = add_element('Else', pp.Keyword('else')) Identifier = add_element('Identifier', pp.Word(pp.alphas + '_', @@ -763,7 +764,7 @@ class QmakeParser: | SubstitutionValue | BracedValue)) - Values = add_element('Values', pp.ZeroOrMore(Value + pp.Optional(LC))('value')) + Values = add_element('Values', pp.ZeroOrMore(Value)('value')) Op = add_element('OP', pp.Literal('=') | pp.Literal('-=') | pp.Literal('+=') \ @@ -771,11 +772,8 @@ class QmakeParser: Key = add_element('Key', Identifier) - Operation = add_element('Operation', - Key('key') + pp.Optional(LC) \ - + Op('operation') + pp.Optional(LC) \ - + Values('value')) - CallArgs = add_element('CallArgs', pp.Optional(LC) + pp.nestedExpr()) + Operation = add_element('Operation', Key('key') + Op('operation') + Values('value')) + CallArgs = add_element('CallArgs', pp.nestedExpr()) def parse_call_args(results): out = '' @@ -807,8 +805,7 @@ class QmakeParser: # ignore the whole thing... ForLoopSingleLine = add_element( 'ForLoopSingleLine', - pp.Suppress(pp.Keyword('for') + CallArgs - + pp.Literal(':') + pp.SkipTo(EOL, ignore=LC))) + pp.Suppress(pp.Keyword('for') + CallArgs + pp.Literal(':') + pp.SkipTo(EOL))) # ignore the whole thing... FunctionCall = add_element('FunctionCall', pp.Suppress(Identifier + pp.nestedExpr())) @@ -823,29 +820,30 @@ class QmakeParser: pp.ZeroOrMore(StatementLine | Scope | pp.Suppress(EOL))) Block = add_element('Block', - pp.Suppress('{') + pp.Optional(LC | EOL) - + StatementGroup + pp.Optional(LC | EOL) - + pp.Suppress('}') + pp.Optional(LC | EOL)) + pp.Suppress('{') + pp.Optional(EOL) + + StatementGroup + pp.Optional(EOL) + + pp.Suppress('}') + pp.Optional(EOL)) ConditionEnd = add_element('ConditionEnd', pp.FollowedBy((pp.Optional(pp.White()) - + pp.Optional(LC) + (pp.Literal(':') - | pp.Literal('{') - | pp.Literal('|'))))) + + (pp.Literal(':') + | pp.Literal('{') + | pp.Literal('|'))))) ConditionPart1 = add_element('ConditionPart1', (pp.Optional('!') + Identifier + pp.Optional(BracedValue))) ConditionPart2 = add_element('ConditionPart2', pp.CharsNotIn('#{}|:=\\\n')) ConditionPart = add_element( 'ConditionPart', - (ConditionPart1 ^ ConditionPart2) + pp.Optional(LC) + ConditionEnd) + (ConditionPart1 ^ ConditionPart2) + ConditionEnd) ConditionOp = add_element('ConditionOp', pp.Literal('|') ^ pp.Literal(':')) - ConditionLC = add_element('ConditionLC', - pp.Suppress(pp.Optional(pp.White(' ') + LC + pp.White(' ')))) + ConditionWhiteSpace = add_element('ConditionWhiteSpace', + pp.Suppress(pp.Optional(pp.White(' ')))) ConditionRepeated = add_element('ConditionRepeated', - pp.ZeroOrMore((ConditionOp) + ConditionLC + ConditionPart)) + pp.ZeroOrMore(ConditionOp + + ConditionWhiteSpace + ConditionPart)) Condition = add_element('Condition', pp.Combine(ConditionPart + ConditionRepeated)) Condition.setParseAction(lambda x: ' '.join(x).strip().replace(':', ' && ').strip(' && ')) @@ -859,22 +857,21 @@ class QmakeParser: .setResultsName('statements')) SingleLineScope = add_element('SingleLineScope', - pp.Suppress(pp.Literal(':')) + pp.Optional(LC) + pp.Suppress(pp.Literal(':')) + pp.Group(Block | (Statement + EOL))('statements')) - MultiLineScope = add_element('MultiLineScope', - pp.Optional(LC) + Block('statements')) + MultiLineScope = add_element('MultiLineScope', Block('statements')) SingleLineElse = add_element('SingleLineElse', - pp.Suppress(pp.Literal(':')) + pp.Optional(LC) + pp.Suppress(pp.Literal(':')) + (Scope | Block | (Statement + pp.Optional(EOL)))) MultiLineElse = add_element('MultiLineElse', Block) ElseBranch = add_element('ElseBranch', pp.Suppress(Else) + (SingleLineElse | MultiLineElse)) # Scope is already add_element'ed in the forward declaration above. - Scope <<= pp.Optional(LC) \ - + pp.Group(Condition('condition') - + (SingleLineScope | MultiLineScope | ConditionEndingInFunctionCall) - + pp.Optional(ElseBranch)('else_statements')) + Scope <<= \ + pp.Group(Condition('condition') + + (SingleLineScope | MultiLineScope | ConditionEndingInFunctionCall) + + pp.Optional(ElseBranch)('else_statements')) Grammar = StatementGroup('statements') Grammar.ignore(pp.pythonStyleComment()) diff --git a/util/cmake/tests/test_lc_fixup.py b/util/cmake/tests/test_lc_fixup.py index d3680e895d5..841e11615ea 100755 --- a/util/cmake/tests/test_lc_fixup.py +++ b/util/cmake/tests/test_lc_fixup.py @@ -29,18 +29,18 @@ from pro2cmake import fixup_linecontinuation -from textwrap import dedent - def test_no_change(): input = "test \\\nline2\n line3" + output = "test line2\n line3" result = fixup_linecontinuation(input) - assert input == result + assert output == result def test_fix(): input = "test \\\t\nline2\\\n line3\\ \nline4 \\ \t\nline5\\\n\n\n" + output = "test line2 line3 line4 line5 \n\n" result = fixup_linecontinuation(input) - assert 'test \\\nline2 \\\n line3 \\\nline4 \\\nline5 \\\n\n\n' == result + assert output == result