Skip to content

Update tooling to handle Fortran matrices as input and output - #351

Merged
colinleach merged 1 commit into
exercism:mainfrom
colinleach:matrix-tooling
Oct 26, 2026
Merged

colinleach merged 1 commit into
exercism:mainfrom
colinleach:matrix-tooling

Conversation

@colinleach

@colinleach colinleach commented Oct 26, 2026 •

Copy link
Copy Markdown
Contributor

Splitting draft #350, this PR replaces the first part.

Some new exercises will pass matrices as input argument and result. This seems desirable, as manipulating matrices and higher-dimensional arrays is probably >95% of what Fortran is used for.

Some extra tooling was needed:

  • More Python in bin/generate_tests.py to convert from JSON nested lists.
  • More Fortran in testlib/TesterMain.f90 to generalize equality testing.

The test runner Dockerfile contains this line:

RUN curl -R -O https://raw.githubusercontent.com/exercism/fortran/main/testlib/TesterMain.f90

Once this PR is merged, how do we get the docker image rebuilt and deployed with this new TesterMain? Exercises which are matrix-based (flower-field, game-of-life, etc) will not run on docker in its current form.

Claude comments are below the line, for documentation purposes.



Notes for reviewers: matrix support in the test generator and testlib

This PR prepares the tooling for exercises whose input or output is a 2-D array. The first such exercise will be flower-field, which comes in a separate PR after this one is merged. Nothing in this PR is used by an existing exercise yet. Regenerating every exercise's tests with the new generator produces no changes.

1. bin/generate_tests.py: fortran_matrix filter

This filter came in with #343 and has not been used by any exercise yet. Writing the flower-field template showed three problems:

  • Quoting. Every element was written with str(x), so string data produced invalid Fortran like [*, , 1]. A new fortran_literal() helper now writes strings as "x" (an embedded " is doubled), bools as .true./.false., and anything else with str() as before.
  • Empty matrices. reshape([], [0, 0]) is invalid because an empty array constructor has no type. The filter now takes an optional type_spec argument. For example, fortran_matrix("character(len=1)") gives reshape([character(len=1) ::], [0, 0], order=[2, 1]). If the data is empty and no type_spec was given, it raises ValueError instead of writing Fortran that won't compile.
  • Line length. Free-form Fortran allows at most 132 characters per line, and larger matrices went past that (gfortran: -Werror=line-truncation). An expression longer than max_width (default 80) is now split with & continuations, one matrix row per line, indented by indent (default 6). Short matrices stay on one line.

Rows can be lists of scalars or strings. A string row gives one element per character, which matches how canonical data stores grids ([" * ", "***"]).

Example output, from flower-field's "large garden" case:

  call assert_equal(reshape([character(len=1) :: &
      "1", "*", "2", "2", "*", "1", &
      "1", "2", "*", "3", "2", "2", &
      " ", "1", "2", "3", "*", "2", &
      "1", "1", "2", "*", "4", "*", &
      "1", "*", "2", "2", "*", "2", &
      "1", "1", "1", "1", "1", "1"], [6, 6], order=[2, 1]),&

Things to check:

  • The filter's call signature has changed. It is backwards compatible: all new parameters are optional, and no exercise uses the filter yet.
  • Known limitation: a single row longer than about 130 characters still isn't wrapped. Rows are never split.
  • generate() runs bool_to_str on expected before the template's filters see it. A boolean matrix in expected would therefore reach fortran_matrix as the strings ".true." and be quoted. No planned exercise hits this. A future boolean-matrix exercise could fix it then.

2. testlib/TesterMain.f90: rank-2 character assert_equal

  • New assert_equal_str_mat(estrmat, istrmat, test_description) added to the assert_equal interface.
  • It checks shape first. On a mismatch it logs Matrices are not the same shape! and reports both shapes (e.g. Expected < 2,2 > but got < 1,4 >), the same way assert_equal_int_arr handles a size mismatch.
  • If the shapes match, it compares every element.
  • New helper sm_to_s formats a failure as one quoted string per row: Expected < "1*","2 " > but got < "1*"," " >. Unlike sa_to_s, it keeps trailing blanks, because blank cells can be significant. Output is truncated with … like the other helpers.
  • Existing routines are unchanged.

Follow-up needed in the test runner. The exercism/fortran-test-runner Dockerfile fetches TesterMain.f90 from this repo's main at build time, and the Docker layer cache keeps the old copy. After this PR merges, the runner's bust_cache needs incrementing to force a rebuild, as in fortran-test-runner#55. The flower-field PR should wait until the rebuilt image is deployed.

Verification

  • bin/generate_tests.py --no-pull over all exercises: no test files changed.
  • bin/check-cmake-files-up-to-date: passes.
  • New assert_equal tested directly with 0×0, 1×0 and 2×2 matrices, a content mismatch and two shape mismatches. It builds cleanly under the track's flags (-std=f2008 -W -Wall -Wextra -pedantic).
  • End-to-end, on the flower-field branch: generated tests compile, and the example passes 13/13, both with the track's CMake build and in the test-runner image with this TesterMain.f90 mounted.

@github-actions

Copy link
Copy Markdown

Hello 👋 Thanks for your PR.

This repo does not currently have dedicated maintainers. Our cross-track maintainers team will attempt to review and merge your PR, but it will likely take longer for your PR to be reviewed.

If you enjoy contributing to Exercism and have a track-record of doing so successfully, you might like to become an Exercism maintainer for this track.

Please feel free to ask any questions, or chat to us about anything to do with this PR or the reviewing process on the Exercism forum.

(cc @exercism/cross-track-maintainers)

@colinleach
colinleach merged commit f809a6f into exercism:main Oct 26, 2026
7 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants