Skip to content

[Flower Field] New practice exercise - #350

Closed
colinleach wants to merge 1 commit into
exercism:mainfrom
colinleach:flower-field
Closed

colinleach wants to merge 1 commit into
exercism:mainfrom
colinleach:flower-field

Conversation

@colinleach

@colinleach colinleach commented Oct 26, 2026 •

Copy link
Copy Markdown
Contributor

This is the first exercise that passes 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.

This exercise will not yet run in Docker, because the extended testlib/TesterMain.f90 needs to be copied to the test runner. I will open a PR on that repo.

The test runner Dockerfile contains this line:

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

Presumably, we need to update the file on this repo, then rebuild the Docker image. Advice on how to proceed would be welcome.

I'll start by splitting out the tooling changes to a separate PR, to be merged first.


This got complicated, so I drafted in Claude to help me. The LLM notes are below the line, providing much more detail for documentation purposes.



Notes for reviewers: [Flower Field] New practice exercise

This PR adds the flower-field practice exercise. It is the first exercise to use a 2-D character array as both input and output, so it also changes some shared code: the test generator and testlib. Those changes are the ones that most need review.

1. bin/generate_tests.py: fortran_matrix filter

This filter came in with #343 and had not been used by any exercise until now. Using it 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, used as fortran_matrix("character(len=1)"), which 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 ([" * ", "***"]).

Things to check:

  • The filter's call signature has changed. It is backwards compatible: all new parameters are optional, and no other exercise uses it 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. This doesn't affect flower-field. It would affect a future boolean-matrix exercise, which 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 blanks are significant cells here. Output is truncated with … like the other helpers.
  • Tested with 0×0, 1×0 and 2×2 matrices, a content mismatch, and two shape mismatches, compiled with gfortran -Wall -std=f2018. It also builds under the track's -std=f2008 -W -Wall -Wextra -pedantic.

Deployment order matters: this PR needs a companion test-runner PR. The exercism/fortran-test-runner image bundles its own copy at /opt/test-runner/testlib/TesterMain.f90, and that copy does not have the new routine. With the current image, every flower-field test fails to compile with There is no specific subroutine for the generic assert_equal (see Verification). fortran-test-runner needs a PR that brings in this TesterMain.f90, merged and deployed before or together with this one. Otherwise every flower-field submission will error on the site.

Separately, when there is no local testlib, the exercise CMakeLists.txt downloads TesterMain.f90 from main on GitHub, so local student builds also depend on this PR being merged.

3. The exercise itself

  • Interface: annotate(garden) takes character(len=1), dimension(:,:) and returns a character(len=1) array of the same shape. Representing the garden as a 2-D character array (rather than an array of strings) seemed the most natural fit for Fortran. Happy to discuss the alternative.
  • .meta/example.f90: counts flowers in each cell's 3×3 neighbourhood (clipped at the edges) with count() on an array section.
  • Stub (flower_field.f90): declares the full signature and returns the input unchanged, so it compiles.
  • Tests: all 13 canonical cases, including no rows (0×0) and no columns (1×0).

Verification

  • Example solution: 0 of 13 tests failed (per-exercise CMake build, with the track's gfortran flags).
  • Stub: compiles without warnings, 9 of 13 tests fail. The other 4 pass because the expected output equals the input.
  • bin/check-cmake-files-up-to-date: passes.
  • bin/verify-exercises-in-docker flower-field with the current exercism/fortran-test-runner:latest image (digest sha256:e96e5c54…): fails. Every test fails to compile with There is no specific subroutine for the generic assert_equal, because the image uses its own bundled TesterMain.f90.
  • The same runner with this branch's testlib/TesterMain.f90 mounted over /opt/test-runner/testlib/TesterMain.f90: passes, 13/13 tests. The only blocker is therefore the runner's copy of TesterMain. If CI runs the Docker verification, this PR will fail that check until the runner is updated.
  • Not run: the root CMakeLists.txt build of all exercises.

@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)

@IsaacG

IsaacG commented Oct 27, 2026

Copy link
Copy Markdown
Member

Is this a dupe of #352 ?

@colinleach

Copy link
Copy Markdown
Contributor Author

Is this a dupe of #352 ?

No, it's the draft precursor which is now split into #351 (tooling) and #352 (draft of the exercise). I will close this #350.

Fixing the test runner is most urgent right now. I'll come back to the exercises once we can run anything.

Also, the CMakeList.txt is going to change again when I PR prime-factors. Currently, tests only handle the default Int32, and several exercises will need Int64. Lots still to do...

@colinleach colinleach closed this Oct 27, 2026
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