diff --git a/.github/workflows/main.yaml b/.github/workflows/main.yaml index df50f3420..f90d12441 100644 --- a/.github/workflows/main.yaml +++ b/.github/workflows/main.yaml @@ -155,7 +155,7 @@ jobs: alr toolchain --install gnat_native=12.2.1 gprbuild=22.0.1 - name: Setup Python - run: python3 -m pip install pytest epycs pandocfilters + run: python3 -m pip install pytest epycs pandocfilters adacut - name: Run PyTest run: pytest --ignore=cached_gnat --ignore=courses/fundamentals_of_ada/labs/radar/test_all.py diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 2d5209a12..c51a9a8d3 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -6,6 +6,8 @@ lint-rst: services: - image:ada-trainings stage: build + before_script: + - python3 -m pip install adacut script: - python3 contrib/fix_broken_titles.py --check - python3 contrib/ci/fix_prelude.py diff --git a/contrib/adacut.py b/contrib/adacut.py deleted file mode 100755 index cc47a9461..000000000 --- a/contrib/adacut.py +++ /dev/null @@ -1,214 +0,0 @@ -#!/usr/bin/env python3 -import argparse -import re -import sys -import enum - - -class CutState(enum.Enum): - NONE = 0 - IN_BLOCK = 1 - IN_LINE = 2 - - -class AdaCut: - RE_DIRECTIVE = re.compile(r"^\s*\-\-\$\s+(\S+)\s+(\S+)(\s+(\S+))?\s*$") - RE_DIRECTIVE_PARTIAL = re.compile(r"^\s*\-\-\$.*$") - - DIRECTIVES = ["begin", "end", "line"] - TYPES = ["answer", "question", "cut"] - TARGETS = ["code", "comment", "all"] - - RE_PURE_COMMENT = re.compile(r"^\s*\-\-.*$") - - def __init__(self, cut, mode, default_keeping): - self.lines = 0 - self.directives = 0 - self.comments = 0 - self.kept = 0 - - self.default_keeping = default_keeping - self.cut = cut - self.cut_state = CutState.NONE - self.current_cut = 0 - if self.cut and not self.default_keeping: - # Pure cut mode: throw everything but the given cut(s) - self.mode = None - else: - self.mode = mode - self.line = None - self.block = [] - - def match_mode_or_cut(self, typ_name): - keep = False - if self.cut and self.cut_state != CutState.NONE: - keep = self.current_cut in self.cut - if (not self.cut) or self.default_keeping: - keep = keep or self.mode == typ_name or self.mode == "keep_all" - return keep - - def keeping_code_comments_with(self, typ): - if self.match_mode_or_cut(typ[0]): - return True, True - elif typ[1] == "comment": - return True, False - elif typ[1] == "code": - return False, True - else: - return False, False - - def keeping_code_comments(self): - if self.line: - return self.keeping_code_comments_with(self.line) - elif self.block: - return self.keeping_code_comments_with(self.block[-1]) - else: - return self.default_keeping, self.default_keeping - - def new_line(self, l): - keeping_code, keeping_comments = self.keeping_code_comments() - self.line = None - if self.cut_state == CutState.IN_LINE: - self.cut_state = CutState.NONE - self.lines += 1 - - is_code = not self.RE_PURE_COMMENT.match(l) - if is_code: - if keeping_code: - self.kept += 1 - return l - elif keeping_comments: - self.kept += 1 - # Special case: Keep empty lines to fill - return l[-1] - else: - None - - m = self.RE_DIRECTIVE.match(l) - if not m: - assert not self.RE_DIRECTIVE_PARTIAL.match( - l - ), f"malformed --$ comment: {l[:-1]}" - - self.comments += 1 - if keeping_comments: - self.kept += 1 - return l - else: - return None - - self.directives += 1 - directive = m.group(1).lower() - typ = m.group(2).lower(), m.group(4).lower() if m.group(4) else "all" - if directive not in self.DIRECTIVES: - # Warning - print("unknown directive:", directive, file=sys.stderr) - return - if typ[0] not in self.TYPES: - # warning - print("unknown type:", typ[0], file=sys.stderr) - return - if typ[1] not in self.TARGETS: - # warning - print("unknown target:", typ[1], file=sys.stderr) - return - - if directive == "begin": - self.block.append(typ) - if typ[0] == "cut": - self.cut_state = CutState.IN_BLOCK - self.current_cut += 1 - elif directive == "end": - assert typ == self.block[-1], f"{typ} != {self.block[-1]}" - self.block = self.block[:-1] - if typ[0] == "cut": - self.cut_state = CutState.NONE - elif directive == "line": - self.line = typ - if typ[0] == "cut": - self.cut_state = CutState.IN_LINE - self.current_cut += 1 - else: - assert False, "Bug!" - - -if __name__ == "__main__": - ap = argparse.ArgumentParser() - ap.add_argument("input_file") - ap.add_argument("-o", "--output-file") - ap.add_argument("-c", "--cut", nargs="*", type=int) - ap.add_argument( - "-k", - "--default-keeping", - action="store_true", - help="Set for answer, question, keep_all modes by default", - ) - ap.add_argument( - "-K", - "--no-default-keeping", - action="store_true", - help="Set for cut mode by default", - ) - ap.add_argument( - "-d", "--dedent", action="store_true", help="Dedent by the first-line indent" - ) - ap.add_argument( - "-C", - "--cut-counting", - action="store_true", - help="Return the number of cuts in the file", - ) - ap.add_argument( - "-m", "--mode", default="question", choices=["answer", "question", "keep_all"] - ) - args = ap.parse_args() - - if args.output_file: - out = open(ap, "w") - else: - out = sys.stdout - - output_cut = not args.cut_counting - assert not (args.default_keeping and args.no_default_keeping) - if args.default_keeping: - default_keeping = True - elif args.no_default_keeping: - default_keeping = False - else: - default_keeping = not args.cut - - cut = AdaCut(args.cut, args.mode, default_keeping=default_keeping) - dedent_cols = None - prev_cut = None - prev_ln = 0 - prev_indent = None - with open(args.input_file) as fin: - for l in fin: - lp = cut.new_line(l) - if lp != None: - cur_ln = cut.lines - cut.directives - cur_indent = len(lp) - len(lp.lstrip()) - if args.dedent: - if dedent_cols is None: - dedent_cols = cur_indent - - if lp.strip() != "": - assert lp[:dedent_cols] == " " * dedent_cols, repr(lp) - lp = lp[dedent_cols:] - - if ( - args.cut - and not default_keeping - and prev_cut - and prev_cut != cut.current_cut - and prev_ln != cur_ln - 1 - ): - print((" " * max(cur_indent, prev_indent)) + "...", file=out) - prev_ln = cur_ln - prev_cut = cut.current_cut - prev_indent = cur_indent - if output_cut: - print(lp, file=out, end="") - - if args.cut_counting: - print(cut.current_cut, file=out) diff --git a/contrib/example_extract.py b/contrib/example_extract.py index a283073f6..441729726 100644 --- a/contrib/example_extract.py +++ b/contrib/example_extract.py @@ -5,7 +5,7 @@ import difflib scripts_dir = Path(__file__).parent -adacut = esubp.cmd.python.arg(scripts_dir / "adacut.py") +adacut = esubp.cmd.adacut esubp.verbose = True diff --git a/contrib/quiz.py b/contrib/quiz.py index b41bcc0d3..a1d81df1f 100644 --- a/contrib/quiz.py +++ b/contrib/quiz.py @@ -12,12 +12,10 @@ import shutil SCRIPTS = Path(__file__).parent -ADACUT_PY = SCRIPTS / "adacut.py" -assert ADACUT_PY.is_file() debug = False -adacut = cmd.python.arg(ADACUT_PY) +adacut = cmd.adacut gprbuild = cmd.gprbuild.arg("-p") diff --git a/contrib/testsuite/adacut/unconstrained_arrays_declaration/src_-C/main.adb b/contrib/testsuite/adacut/unconstrained_arrays_declaration/src_-C/main.adb deleted file mode 100644 index b8626c4cf..000000000 --- a/contrib/testsuite/adacut/unconstrained_arrays_declaration/src_-C/main.adb +++ /dev/null @@ -1 +0,0 @@ -4 diff --git a/contrib/testsuite/adacut/unconstrained_arrays_declaration/src_-K_-m_question/main.adb b/contrib/testsuite/adacut/unconstrained_arrays_declaration/src_-K_-m_question/main.adb deleted file mode 100644 index 28ebf55b5..000000000 --- a/contrib/testsuite/adacut/unconstrained_arrays_declaration/src_-K_-m_question/main.adb +++ /dev/null @@ -1 +0,0 @@ - type My_Array is array (Integer range <>) of Boolean; diff --git a/contrib/testsuite/adacut/unconstrained_arrays_declaration/src_-c_1/main.adb b/contrib/testsuite/adacut/unconstrained_arrays_declaration/src_-c_1/main.adb deleted file mode 100644 index bd8b65f2b..000000000 --- a/contrib/testsuite/adacut/unconstrained_arrays_declaration/src_-c_1/main.adb +++ /dev/null @@ -1 +0,0 @@ - O : My_Array (2); diff --git a/contrib/testsuite/adacut/unconstrained_arrays_declaration/src_-c_1_2/main.adb b/contrib/testsuite/adacut/unconstrained_arrays_declaration/src_-c_1_2/main.adb deleted file mode 100644 index 0fd97c944..000000000 --- a/contrib/testsuite/adacut/unconstrained_arrays_declaration/src_-c_1_2/main.adb +++ /dev/null @@ -1,3 +0,0 @@ - O : My_Array (2); - -- Multiline cut - O : My_Array (1 .. 2); diff --git a/contrib/testsuite/adacut/unconstrained_arrays_declaration/src_-c_1_3/main.adb b/contrib/testsuite/adacut/unconstrained_arrays_declaration/src_-c_1_3/main.adb deleted file mode 100644 index 1afc9a85c..000000000 --- a/contrib/testsuite/adacut/unconstrained_arrays_declaration/src_-c_1_3/main.adb +++ /dev/null @@ -1,3 +0,0 @@ - O : My_Array (2); - ... - O : My_Array (1 .. 3); diff --git a/contrib/testsuite/adacut/unconstrained_arrays_declaration/src_-c_2/main.adb b/contrib/testsuite/adacut/unconstrained_arrays_declaration/src_-c_2/main.adb deleted file mode 100644 index 078802cea..000000000 --- a/contrib/testsuite/adacut/unconstrained_arrays_declaration/src_-c_2/main.adb +++ /dev/null @@ -1,2 +0,0 @@ - -- Multiline cut - O : My_Array (1 .. 2); diff --git a/contrib/testsuite/adacut/unconstrained_arrays_declaration/src_-dc_1/main.adb b/contrib/testsuite/adacut/unconstrained_arrays_declaration/src_-dc_1/main.adb deleted file mode 100644 index ed4d14303..000000000 --- a/contrib/testsuite/adacut/unconstrained_arrays_declaration/src_-dc_1/main.adb +++ /dev/null @@ -1 +0,0 @@ -O : My_Array (2); diff --git a/contrib/testsuite/adacut/unconstrained_arrays_declaration/src_-kc_1/main.adb b/contrib/testsuite/adacut/unconstrained_arrays_declaration/src_-kc_1/main.adb deleted file mode 100644 index de1bf8153..000000000 --- a/contrib/testsuite/adacut/unconstrained_arrays_declaration/src_-kc_1/main.adb +++ /dev/null @@ -1,6 +0,0 @@ -procedure Main is - type My_Array is array (Integer range <>) of Boolean; - O : My_Array (2); -begin - pragma Assert (O'Length = 2); -end Main; diff --git a/contrib/testsuite/adacut/unconstrained_arrays_declaration/src_-kc_4/main.adb b/contrib/testsuite/adacut/unconstrained_arrays_declaration/src_-kc_4/main.adb deleted file mode 100644 index be6082731..000000000 --- a/contrib/testsuite/adacut/unconstrained_arrays_declaration/src_-kc_4/main.adb +++ /dev/null @@ -1,6 +0,0 @@ -procedure Main is - type My_Array is array (Integer range <>) of Boolean; - O : My_Array (1, 3); -begin - pragma Assert (O'Length = 2); -end Main; diff --git a/contrib/testsuite/adacut/unconstrained_arrays_declaration/template/main.adb b/contrib/testsuite/adacut/unconstrained_arrays_declaration/template/main.adb deleted file mode 100644 index 6428ae12c..000000000 --- a/contrib/testsuite/adacut/unconstrained_arrays_declaration/template/main.adb +++ /dev/null @@ -1,19 +0,0 @@ -procedure Main is - --$ line question - type My_Array is array (Integer range <>) of Boolean; - --$ line cut - O : My_Array (2); - --$ begin cut - -- Multiline cut - O : My_Array (1 .. 2); - --$ end cut - --$ line cut - O : My_Array (1 .. 3); - --$ line cut - O : My_Array (1, 3); - --$ begin answer - -- You must declare the :ada:`range` using the :ada:`".."` operator - --$ end answer -begin - pragma Assert (O'Length = 2); -end Main; diff --git a/contrib/testsuite/conftest.py b/contrib/testsuite/conftest.py deleted file mode 100644 index 85c68e958..000000000 --- a/contrib/testsuite/conftest.py +++ /dev/null @@ -1,2 +0,0 @@ -def pytest_addoption(parser): - parser.addoption("--update-baseline", action="store_true") diff --git a/contrib/testsuite/test_adacut.py b/contrib/testsuite/test_adacut.py deleted file mode 100644 index c0c3cdd5e..000000000 --- a/contrib/testsuite/test_adacut.py +++ /dev/null @@ -1,68 +0,0 @@ -from pathlib import Path -import epycs.subprocess -from epycs.subprocess import cmd, ShellProgramFilters - -epycs.subprocess.exit_on_error = False - - -TEST_DIR = Path(__file__).parent -TEST_DATA_DIR = TEST_DIR / "adacut" -CONTRIB = TEST_DIR.parent -ADACUT_PY = CONTRIB / "adacut.py" - - -adacut = cmd.python.arg(ADACUT_PY) - - -class TestAdaCut: - @classmethod - def init_tests(cls): - for d in ( - d_tpl.parent - for d_tpl in TEST_DATA_DIR.glob("**/template") - if d_tpl.is_dir() - ): - tests = {} - - for dd in (d_src for d_src in d.glob("src_*") if d_src.is_dir()): - cls.add_src_tests(tests, d, dd) - - for name, test in tests.items(): - setattr(cls, f"test_{name}", test) - - def maybe_update_baseline(self, pytestconfig, expected_file, actual): - if pytestconfig.getoption("--update-baseline"): - with open(expected_file, "wt") as f: - f.write(actual) - - def assert_file_content_equal(self, pytestconfig, expected_file, actual): - with open(expected_file) as f: - expected = f.read() - - try: - assert actual == expected - except AssertionError: - self.maybe_update_baseline(pytestconfig, expected_file, actual) - raise - - @classmethod - def parse_options(cls, name): - return name.split("_") - - @classmethod - def add_src_tests(cls, tests, d, d_src): - options = cls.parse_options(d_src.name[len("src_") :]) - for f in (d / "template").glob("*.ad?"): - - def test_file_content_is_expected(self, pytestconfig): - actual = adacut( - *options, "--", f, check=True, out_filter=ShellProgramFilters.text - ) - self.assert_file_content_equal(pytestconfig, d_src / f.name, actual) - - tests[f"{d_src.parent.name}_{d_src.name}_{f.name}"] = ( - test_file_content_is_expected - ) - - -TestAdaCut.init_tests() diff --git a/courses/fundamentals_of_ada/examples/protected_objects_2/cut_extracts.sh b/courses/fundamentals_of_ada/examples/protected_objects_2/cut_extracts.sh index 15b9b6e4a..09f629d7b 100644 --- a/courses/fundamentals_of_ada/examples/protected_objects_2/cut_extracts.sh +++ b/courses/fundamentals_of_ada/examples/protected_objects_2/cut_extracts.sh @@ -1,7 +1,7 @@ #! /usr/bin/env bash set -e FWD=$(dirname "$0") -ADACUT=$FWD/../../../../contrib/adacut.py +ADACUT=$(which adacut) ( set -e diff --git a/courses/fundamentals_of_ada/examples/protected_objects_lock_free/cut_extracts.sh b/courses/fundamentals_of_ada/examples/protected_objects_lock_free/cut_extracts.sh index d9761e887..89448ebaa 100644 --- a/courses/fundamentals_of_ada/examples/protected_objects_lock_free/cut_extracts.sh +++ b/courses/fundamentals_of_ada/examples/protected_objects_lock_free/cut_extracts.sh @@ -1,7 +1,7 @@ #! /usr/bin/env bash set -e FWD=$(dirname "$0") -ADACUT=$FWD/../../../../contrib/adacut.py +ADACUT=$(which adacut) ( set -e diff --git a/courses/fundamentals_of_ada/examples/select_non_blocking_entry_and_call/cut_extracts.sh b/courses/fundamentals_of_ada/examples/select_non_blocking_entry_and_call/cut_extracts.sh index 60fbb23f2..8bb22f6ff 100644 --- a/courses/fundamentals_of_ada/examples/select_non_blocking_entry_and_call/cut_extracts.sh +++ b/courses/fundamentals_of_ada/examples/select_non_blocking_entry_and_call/cut_extracts.sh @@ -1,7 +1,7 @@ #! /usr/bin/env bash set -e FWD=$(dirname "$0") -ADACUT=$FWD/../../../../contrib/adacut.py +ADACUT=$(which adacut) ( set -e diff --git a/courses/fundamentals_of_ada/examples/select_requeue_issue/cut_extracts.sh b/courses/fundamentals_of_ada/examples/select_requeue_issue/cut_extracts.sh index e1988f2f8..b5b66f35c 100644 --- a/courses/fundamentals_of_ada/examples/select_requeue_issue/cut_extracts.sh +++ b/courses/fundamentals_of_ada/examples/select_requeue_issue/cut_extracts.sh @@ -1,7 +1,7 @@ #! /usr/bin/env bash set -e FWD=$(dirname "$0") -ADACUT=$FWD/../../../../contrib/adacut.py +ADACUT=$(which adacut) ( set -e diff --git a/courses/fundamentals_of_ada/examples/task_select_multiple_or/cut_extracts.sh b/courses/fundamentals_of_ada/examples/task_select_multiple_or/cut_extracts.sh index bc577fb5f..4d160ccab 100644 --- a/courses/fundamentals_of_ada/examples/task_select_multiple_or/cut_extracts.sh +++ b/courses/fundamentals_of_ada/examples/task_select_multiple_or/cut_extracts.sh @@ -1,7 +1,7 @@ #! /usr/bin/env bash set -e FWD=$(dirname "$0") -ADACUT=$FWD/../../../../contrib/adacut.py +ADACUT=$(which adacut) ( set -e diff --git a/courses/fundamentals_of_ada/mini_projects/cinema/README.md b/courses/fundamentals_of_ada/mini_projects/cinema/README.md index 32626e482..11422b231 100644 --- a/courses/fundamentals_of_ada/mini_projects/cinema/README.md +++ b/courses/fundamentals_of_ada/mini_projects/cinema/README.md @@ -165,7 +165,7 @@ The commands available for the ref implementation are: # Updating the question / answer code -Since, the question code is extracted from the answers code using adacut.py (present in this repository). +The question code is extracted from the answers code using adacut (installed from pip) You will need to update the code from the `template/` directory then use the `build.sh` script to generate the `src/` and `answers/` directories.