From 0cd9ec351b145e0873c12cec5f7ce31eb0996d79 Mon Sep 17 00:00:00 2001 From: Klaas Hoekstra Date: Sun, 14 Jul 2024 22:27:51 +0200 Subject: [PATCH] Add option to only remove comments (#4) * Add option to only remove comments * Remove macos-latest from test run * Fix removing of newlines if comment ends with operator --- .github/workflows/test.yml | 2 +- README.md | 11 ++- src/heishamon_rules_minify/cli.py | 3 +- src/heishamon_rules_minify/minifier.py | 60 +++++++++++---- tests/test_minifier.py | 102 ++++++++++++++++++++++++- 5 files changed, 156 insertions(+), 22 deletions(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index cc9cb41..85f72cb 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -13,7 +13,7 @@ jobs: runs-on: ${{ matrix.os }} strategy: matrix: - os: [ubuntu-latest, macos-latest] + os: [ubuntu-latest] python-version: ["3.7", "3.8", "3.9"] max-parallel: 3 name: Python ${{ matrix.python-version }} diff --git a/README.md b/README.md index 33e08e3..a477555 100644 --- a/README.md +++ b/README.md @@ -28,10 +28,19 @@ heishamon-rules-minify [input_file] [output_file] This will shorten all custom function and variable names and remove all unneeded spaces and newlines. +### Extra options +There are a couple of extra options to pass with the minifier: +* `--print` or `-p` to also print the output on the command line. +* `--comments_only` or `-c` to only remove the comments from the input file but do no further minifying. + ## Release History -* 0.0.1 +* [0.0.1](https://github.com/klaashoekstra94/heishamon_rules_minify/releases/tag/v0.0.1) * Initial version +* [0.0.2](https://github.com/klaashoekstra94/heishamon_rules_minify/releases/tag/v0.0.2) + * Fix incorrect spacing for double operators, minor bugfixes +* [0.0.3](https://github.com/klaashoekstra94/heishamon_rules_minify/releases/tag/v0.0.3) + * Add option to only remove comments ## Contributing diff --git a/src/heishamon_rules_minify/cli.py b/src/heishamon_rules_minify/cli.py index f20ed78..057b1f9 100644 --- a/src/heishamon_rules_minify/cli.py +++ b/src/heishamon_rules_minify/cli.py @@ -9,10 +9,11 @@ def main_cli(): parser.add_argument("input", help="input rules file") parser.add_argument("output", help="output rules file") parser.add_argument("-p", "--print", action="store_true", help="print the output on the commandline") + parser.add_argument("-c", "--comments_only", action="store_true", help="only remove comments from the input") args = parser.parse_args() with open(args.input, "r") as i: - text = Minifier.minify(i.read()) + text = Minifier.minify(i.read(), args.comments_only) with open(args.output, "w") as o: o.write(text) diff --git a/src/heishamon_rules_minify/minifier.py b/src/heishamon_rules_minify/minifier.py index 03bbd0a..23a491b 100644 --- a/src/heishamon_rules_minify/minifier.py +++ b/src/heishamon_rules_minify/minifier.py @@ -5,6 +5,25 @@ class Minifier: _TABLE = str.maketrans("", "", string.ascii_lowercase) + @staticmethod + def _fix_spaces_around_operators(text): + # Remove extra spaces + text = re.sub(r"(?<=[;=&|<>\-+%*\/^()])[\t ]*(?=\S)(?!then)", "", text, flags=re.MULTILINE) + + # Correct spaces around operators and functions + text = re.sub( + r"(?=|<=|\|\||&&|[=+\*\/<>^]) *)(?=-?\d|\b|[(#$@%?].+(;|then|end|$))", + r" \g<2> ", + text, + flags=re.MULTILINE, + ) + text = re.sub( + r"(?(,])(?(,] ) *- *(?=-*\d|[(#$@%?].+(;|then|end|$))", " - ", text, flags=re.MULTILINE + ) + text = re.sub(r" *% *(?=-*\d|[(#$@%?].+(;|then|end|$))", " % ", text, flags=re.MULTILINE) + + return text + @classmethod def _remove_lowercase(cls, input_dict): for key in input_dict.keys(): @@ -22,12 +41,32 @@ def _remove_lowercase(cls, input_dict): return input_dict @classmethod - def minify(cls, input_text): + def minify(cls, input_text, comments_only=False): # Remove comment blocks text = re.sub(r"--\[\[[^\]\]]*]]", "", input_text, flags=re.MULTILINE) - # Remove single-line comments - text = re.sub(r"[ \t]*--(?!\[\[).*$", "", text, flags=re.MULTILINE) + # Remove lines consisting entirely of single-line comments + text = re.sub(r"^[ \t]*--(?!\[\[).*\r?\n", "", text, flags=re.MULTILINE) + + # Remove single line comments at end of lines + text = re.sub(r"[ \t]*--(?!\[\[).*(?=\r?\n)", "", text, flags=re.MULTILINE) + + if comments_only: + # Remove newlines at start of file + text = re.sub(r"^(\r?\n)+", "", text) + + # Replace three or more newlines with two + text = re.sub( + r"(\r?\n){3,}", + lambda match: list(filter(None, re.split(r"(\r?\n)", match.group(0))))[0] * 2, + text, + flags=re.MULTILINE, + ) + + # Remove newline after line ending with all possible operators + text = re.sub(r"(?<=[=&|<>\-+%*\/^()])\r?\n", "", text, flags=re.MULTILINE) + + return Minifier._fix_spaces_around_operators(text) # Find all global/local variable names found_variables = {k: "" for k in re.findall(r"(?<=\B[#$])[_A-za-z0-9]+(?=[= &|<>\-+%*\/^])", text)} @@ -54,24 +93,13 @@ def minify(cls, input_text): # Remove newline after line ending with 'then' or 'else' text = re.sub(r"(?<=then|else)\r?\n", " ", text, flags=re.MULTILINE) - # Remove newline after line ending with all possible operators + # Remove newline after line ending with all possible operators and semicolon text = re.sub(r"(?<=[;=&|<>\-+%*\/^()])\r?\n", "", text, flags=re.MULTILINE) - # Remove extra spaces that are present after removing newlines - text = re.sub(r"(?<=[;=&|<>\-+%*\/^()])[\t ]*(?=\S)(?!then)", "", text, flags=re.MULTILINE) - # Remove newline after line ending with 'end', except for last end of function text = re.sub(r"(?<=end)\s+(?!\s*on |\Z)", " ", text, flags=re.MULTILINE) - # Correct spaces around operators and functions - text = re.sub( - r"(?=|<=|\|\||&&|[=+\*\/<>^]) *)(?=-?\d|\b|[(#$@%?].+(;|then|end))", - r" \g<2> ", - text, - flags=re.MULTILINE, - ) - text = re.sub(r"(?(,])(?(,] ) *- *(?=-*\d|[(#$@%?].+(;|then|end))", " - ", text, flags=re.MULTILINE) - text = re.sub(r" *% *(?=-*\d|[(#$@%?].+(;|then|end))", " % ", text, flags=re.MULTILINE) + text = Minifier._fix_spaces_around_operators(text) # Remove all spaces around comma signs text = re.sub(r"(?<=[)_A-Za-z0-9]) *, *\s*(?=[$#@?_A-Za-z0-9])", ",", text, flags=re.MULTILINE) diff --git a/tests/test_minifier.py b/tests/test_minifier.py index 3d3d6e3..9613213 100644 --- a/tests/test_minifier.py +++ b/tests/test_minifier.py @@ -2,9 +2,7 @@ from heishamon_rules_minify.minifier import Minifier - -def test_minifier(): - input_text = """ +input_text = """ --[[ Multiline block comment ]] @@ -123,6 +121,8 @@ def test_minifier(): end """ + +def test_minifier(): expected_output = """on System#Boot then #HWSTS = 1;#ASQM = 0;#QMH = 1;#QMP = -1;setTimer(3,60);end on CWDC then $WTWW = 32;$OTWW = 14;$WTCW = 41;$OTCW = -4;#HWSTS = $WTWW;if @Outside_Temp >= $OTWW then #HWSTS = $WTWW;else if @Outside_Temp <= $OTCW then #HWSTS = $WTCW;else #HWSTS = $WTWW + (($OTWW - @Outside_Temp) * ($WTCW - $WTWW) / ($OTWW - $OTCW));end end end on SQM then if #ASQM == 1 then if isset(@Outside_Temp) && isset(@Heatpump_State) then if #QMH == 1 then if @Outside_Temp < 13 then #QM = 1;else #QM = 2;end if @Outside_Temp < 8 then #QM = 0;end if @Outside_Temp < 2 then if %hour > 22 || %hour < 7 then #QM = 1;else #QM = 0;end end if #QMP != #QM && @Heatpump_State == 1 then setTimer(2,900);#QMH = 0;#QMP = #QM;@SetQuietMode = #QM;end end end end end @@ -132,3 +132,99 @@ def test_minifier(): """ output = Minifier.minify(input_text) assert output == expected_output + +def test_minifier_comments_only(): + expected_output = """on System#Boot then + #HeatingWaterSupplyTemperatureSetpoint = 1; + #allowSetQuietMode = 0; + #quietModeHelper = 1; + #quietModePrevious = -1; + + setTimer(3, 60); +end + +on CalculateWeatherDependentControl then + $WaterTemperatureWarmWeather = 32; + $OutsideTemperatureWarmWeather = 14; + $WaterTemperatureColdWeather = 41; + $OutsideTemperatureColdWeather = -4; + + #HeatingWaterSupplyTemperatureSetpoint = $WaterTemperatureWarmWeather; + + if @Outside_Temp >= $OutsideTemperatureWarmWeather then + #HeatingWaterSupplyTemperatureSetpoint = $WaterTemperatureWarmWeather; + else + if @Outside_Temp <= $OutsideTemperatureColdWeather then + #HeatingWaterSupplyTemperatureSetpoint = $WaterTemperatureColdWeather; + else + #HeatingWaterSupplyTemperatureSetpoint = $WaterTemperatureWarmWeather + (($OutsideTemperatureWarmWeather - @Outside_Temp) * ($WaterTemperatureColdWeather - $WaterTemperatureWarmWeather) / ($OutsideTemperatureWarmWeather - $OutsideTemperatureColdWeather)); + end + end +end + +on setQuietMode then + if #allowSetQuietMode == 1 then + if isset(@Outside_Temp) && isset(@Heatpump_State) then + if #quietModeHelper == 1 then + if @Outside_Temp < 13 then + #quietMode = 1; + else + #quietMode = 2; + end + if @Outside_Temp < 8 then + #quietMode = 0; + end + if @Outside_Temp < 2 then + if %hour > 22 || %hour < 7 then + #quietMode = 1; + else + #quietMode = 0; + end + end + if #quietModePrevious != #quietMode && @Heatpump_State == 1 then + setTimer(2, 900); + #quietModeHelper = 0; + #quietModePrevious = #quietMode; + @SetQuietMode = #quietMode; + end + end + end + end +end + +on ?roomTemp then + + $margin = 0.25; + $setpoint = ?roomTempSet; + + if ?roomTemp > ($setpoint + $margin) then + if @Heatpump_State == 1 then + @SetHeatpump = 0; + end + else + if ?roomTemp < ($setpoint - $margin) then + if @Heatpump_State == 0 then + @SetHeatpump = 1; + end + else + @SetZ1HeatRequestTemperature = round(#HeatingWaterSupplyTemperatureSetpoint); + end + end +end + +on timer=2 then + #quietModeHelper = 1; + #quietMode = 0; +end + +on timer=3 then + $somevalue = 0; + $someValue = 1; + $SomeValue = 2; + $SoveValue3 = 3; + $SomeValuee = 4; + setTimer(3, 60); +end +""" + output = Minifier.minify(input_text, comments_only=True) + assert output == expected_output