From 64aa669c47fd6eb98c9b1e0a74cb8ecbd1f5d3e6 Mon Sep 17 00:00:00 2001 From: Dekel Date: Wed, 18 Mar 2026 12:24:01 +0100 Subject: [PATCH 01/12] testitems + remove issame --- src/HerbCore.jl | 3 +-- src/indexing.jl | 9 ------- src/rulenode.jl | 22 ++++++----------- test/Project.toml | 1 + test/aqua_test.jl | 4 +++ test/{test_grammar.jl => grammar_test.jl} | 2 +- test/{test_rulenode.jl => rulenode_test.jl} | 27 +++++++++++---------- test/runtests.jl | 12 ++------- 8 files changed, 31 insertions(+), 49 deletions(-) create mode 100644 test/aqua_test.jl rename test/{test_grammar.jl => grammar_test.jl} (95%) rename test/{test_rulenode.jl => rulenode_test.jl} (97%) diff --git a/src/HerbCore.jl b/src/HerbCore.jl index d60d774..8822e0c 100644 --- a/src/HerbCore.jl +++ b/src/HerbCore.jl @@ -36,7 +36,6 @@ export AbstractGrammar, print_tree, update_rule_indices!, - is_domain_valid, - issame + is_domain_valid end # module HerbCore diff --git a/src/indexing.jl b/src/indexing.jl index 39a9484..f430317 100644 --- a/src/indexing.jl +++ b/src/indexing.jl @@ -17,12 +17,3 @@ If [`isfilled`](@ref)`(x)` and `x` has children, it checks if all children are v """ function is_domain_valid end -""" - issame(a, b) - -Returns whether the two given objects `a` and `b` (ex: [`RuleNode`](@ref), -[`Hole`](@ref) or [`AbstractConstraint`](@ref)) are the same. -""" -function issame(a, b) - false -end diff --git a/src/rulenode.jl b/src/rulenode.jl index 3b89cd9..6f98a08 100644 --- a/src/rulenode.jl +++ b/src/rulenode.jl @@ -329,8 +329,14 @@ function Base.:(==)(A::RuleNode, B::RuleNode) length(A.children) == length(B.children) && #required because zip doesn't check lengths all(isequal(a, b) for (a, b) in zip(A.children, B.children)) end -# We do not know how the holes will be expanded yet, so we cannot assume equality even if the domains are equal. -Base.:(==)(A::AbstractHole, B::AbstractHole) = false +function Base.:(==)(a::UniformHole, b::UniformHole) + a.domain == b.domain && length(a.children) == length(b.children) && + all(isequal(a, b) for (a, b) in zip(a.children, b.children)) +end +function Base.:(==)(a::AbstractHole, b::AbstractHole) + a.domain == b.domain +end + Base.copy(r::RuleNode) = RuleNode(r.ind, r._val, r.children) Base.copy(h::Hole) = Hole(copy(h.domain)) @@ -762,15 +768,3 @@ function have_same_shape(node1, node2) end return true end - -function issame(a::RuleNode, b::RuleNode) - (a.ind == b.ind) && length(a.children) == length(b.children) && - all(issame(a, b) for (a, b) in zip(a.children, b.children)) -end - -function issame(a::UniformHole, b::UniformHole) - a.domain == b.domain && length(a.children) == length(b.children) && - all(issame(a, b) for (a, b) in zip(a.children, b.children)) -end - -issame(a::Hole, b::Hole) = a.domain == b.domain diff --git a/test/Project.toml b/test/Project.toml index cd6e197..676f0a5 100644 --- a/test/Project.toml +++ b/test/Project.toml @@ -2,3 +2,4 @@ AbstractTrees = "1520ce14-60c1-5f80-bbc7-55ef81b5835c" Aqua = "4c88cf16-eb10-579e-8560-4a9242c79595" Test = "8dfed614-e22c-5e08-85e1-65c5234f0b40" +TestItemRunner = "f8b46487-2199-4994-9208-9a1283c18c0a" diff --git a/test/aqua_test.jl b/test/aqua_test.jl new file mode 100644 index 0000000..eafa582 --- /dev/null +++ b/test/aqua_test.jl @@ -0,0 +1,4 @@ +@testitem "Aqua.jl" begin + using Aqua + Aqua.test_all(HerbCore) +end \ No newline at end of file diff --git a/test/test_grammar.jl b/test/grammar_test.jl similarity index 95% rename from test/test_grammar.jl rename to test/grammar_test.jl index f8844ab..a1b0360 100644 --- a/test/test_grammar.jl +++ b/test/grammar_test.jl @@ -1,4 +1,4 @@ -@testset "Grammar" begin +@testitem "Grammar" begin struct ExGrammar <: AbstractGrammar rules::Vector{Any} types::Vector{Symbol} diff --git a/test/test_rulenode.jl b/test/rulenode_test.jl similarity index 97% rename from test/test_rulenode.jl rename to test/rulenode_test.jl index 027a30c..2e8920d 100644 --- a/test/test_rulenode.jl +++ b/test/rulenode_test.jl @@ -1,4 +1,5 @@ -@testset verbose=true "T <: AbstractRuleNode" begin +@testitem "T <: AbstractRuleNode" begin + using AbstractTrees: children, nodevalue, treeheight @testset "AbstractTrees Interface" begin @test nodevalue(RuleNode(1)) == 1 @test isempty(children(RuleNode(1))) @@ -360,8 +361,8 @@ hole1 = Hole([1, 1, 0, 1]) hole2 = Hole([1, 1, 0, 1]) hole3 = hole1 - @test hole1 != hole2 - @test hole3 != hole1 + @test hole1 == hole2 + @test hole3 == hole1 end end @@ -514,19 +515,19 @@ @test is_domain_valid(hole, 4) == true end - @testset "issame" begin + @testset "isequal" begin # RuleNode node1 = @rulenode 1{4{5, 6}, 1{2, 3}} node2 = @rulenode 1{4{5, 6}, 1{2, 3}} node3 = @rulenode 1{4{5, 5}, 1{2, 3}} - @test issame(node1, node2) == true - @test issame(node1, node3) == false + @test node1 == node2 + @test node1 != node3 # Hole hole1 = Hole([1, 1, 0, 1]) hole2 = Hole([1, 1, 0, 1]) hole3 = Hole([1, 0, 0, 1]) - @test issame(hole1, hole2) == true - @test issame(hole2, hole3) == false + @test hole1 == hole2 + @test hole2 != hole3 # UniformHole uhole1 = UniformHole([0, 0, 1], @@ -561,11 +562,11 @@ ) uhole4 = UniformHole([0, 0, 1], [RuleNode(14)]) uhole5 = UniformHole([1, 0, 1], [RuleNode(14)]) - @test issame(uhole1, uhole2) == true - @test issame(uhole1, uhole3) == false - @test issame(uhole4, uhole5) == false + @test uhole1 == uhole2 + @test uhole1 != uhole3 + @test uhole4 != uhole5 # compare different types - @test issame(node1, uhole2) == false - @test issame(hole3, uhole5) == false + @test node1 != uhole2 + @test hole3 != uhole5 end end diff --git a/test/runtests.jl b/test/runtests.jl index 02934e2..0b66d7c 100644 --- a/test/runtests.jl +++ b/test/runtests.jl @@ -1,11 +1,3 @@ -using AbstractTrees: children, nodevalue, treeheight -using Aqua +using ReTestItems using HerbCore -using Test - -@testset "HerbCore.jl" verbose=true begin - @testset "Aqua Tests" Aqua.test_all(HerbCore) - - include("test_rulenode.jl") - include("test_grammar.jl") -end +runtests(HerbCore) From c7eb5dc310b2d9b5246895a2fe3b56bac19bee8a Mon Sep 17 00:00:00 2001 From: Dekel Date: Thu, 19 Mar 2026 16:38:43 +0100 Subject: [PATCH 02/12] add ReTestItems --- test/Project.toml | 1 + 1 file changed, 1 insertion(+) diff --git a/test/Project.toml b/test/Project.toml index 676f0a5..62e5ea9 100644 --- a/test/Project.toml +++ b/test/Project.toml @@ -1,5 +1,6 @@ [deps] AbstractTrees = "1520ce14-60c1-5f80-bbc7-55ef81b5835c" Aqua = "4c88cf16-eb10-579e-8560-4a9242c79595" +ReTestItems = "817f1d60-ba6b-4fd5-9520-3cf149f6a823" Test = "8dfed614-e22c-5e08-85e1-65c5234f0b40" TestItemRunner = "f8b46487-2199-4994-9208-9a1283c18c0a" From 448667f3af99fc580a4f420df68ff0c24741d99f Mon Sep 17 00:00:00 2001 From: Dekel Date: Thu, 19 Mar 2026 17:08:15 +0100 Subject: [PATCH 03/12] version bump --- Project.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Project.toml b/Project.toml index b45feb1..f59bbeb 100644 --- a/Project.toml +++ b/Project.toml @@ -1,7 +1,7 @@ name = "HerbCore" uuid = "2b23ba43-8213-43cb-b5ea-38c12b45bd45" authors = ["Jaap de Jong ", "Nicolae Filat ", "Tilman Hinnerichs ", "Sebastijan Dumancic "] -version = "1.0.0" +version = "1.1.0" [deps] AbstractTrees = "1520ce14-60c1-5f80-bbc7-55ef81b5835c" From aefe037d366a604c3ee9ef12d9b2f46dd620b8c3 Mon Sep 17 00:00:00 2001 From: Dekel Date: Fri, 20 Mar 2026 10:33:54 +0100 Subject: [PATCH 04/12] Base.:(==)(a::H, b::H) where H <: AbstractHole --- src/rulenode.jl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/rulenode.jl b/src/rulenode.jl index 6f98a08..57db5c1 100644 --- a/src/rulenode.jl +++ b/src/rulenode.jl @@ -333,7 +333,7 @@ function Base.:(==)(a::UniformHole, b::UniformHole) a.domain == b.domain && length(a.children) == length(b.children) && all(isequal(a, b) for (a, b) in zip(a.children, b.children)) end -function Base.:(==)(a::AbstractHole, b::AbstractHole) +function Base.:(==)(a::H, b::H) where H <: AbstractHole a.domain == b.domain end From c059e966ec3b1646050a5c50c396a4f2b4121992 Mon Sep 17 00:00:00 2001 From: Dekel Date: Fri, 20 Mar 2026 10:47:08 +0100 Subject: [PATCH 05/12] define issame to redirect to ==, but add @deprecate --- src/indexing.jl | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/src/indexing.jl b/src/indexing.jl index f430317..18b5cb1 100644 --- a/src/indexing.jl +++ b/src/indexing.jl @@ -17,3 +17,16 @@ If [`isfilled`](@ref)`(x)` and `x` has children, it checks if all children are v """ function is_domain_valid end +""" + issame(a, b) + +!!! warning + + This function is deprecated and should not be used. Use `==` instead. + +""" +function issame(a, b) + a == b +end + + Base.@deprecate issame(a, b) Base.:(==)(a, b) From ed50d5c2921c1beb58944c2d9a92a5b82a534766 Mon Sep 17 00:00:00 2001 From: Dekel Date: Fri, 20 Mar 2026 10:47:42 +0100 Subject: [PATCH 06/12] space --- src/indexing.jl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/indexing.jl b/src/indexing.jl index 18b5cb1..3662fca 100644 --- a/src/indexing.jl +++ b/src/indexing.jl @@ -29,4 +29,4 @@ function issame(a, b) a == b end - Base.@deprecate issame(a, b) Base.:(==)(a, b) +Base.@deprecate issame(a, b) Base.:(==)(a, b) From b705f99e200f9c9ba385c79fc153e2fa7100e677 Mon Sep 17 00:00:00 2001 From: Dekel Date: Fri, 20 Mar 2026 11:05:42 +0100 Subject: [PATCH 07/12] export issame --- src/HerbCore.jl | 3 ++- src/indexing.jl | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/src/HerbCore.jl b/src/HerbCore.jl index 8822e0c..d60d774 100644 --- a/src/HerbCore.jl +++ b/src/HerbCore.jl @@ -36,6 +36,7 @@ export AbstractGrammar, print_tree, update_rule_indices!, - is_domain_valid + is_domain_valid, + issame end # module HerbCore diff --git a/src/indexing.jl b/src/indexing.jl index 3662fca..5a15cb5 100644 --- a/src/indexing.jl +++ b/src/indexing.jl @@ -29,4 +29,4 @@ function issame(a, b) a == b end -Base.@deprecate issame(a, b) Base.:(==)(a, b) +Base.@deprecate issame(a, b) Base.:(==)(a, b) false From d6184a972522327bd55a496f47e002d09b91e6b9 Mon Sep 17 00:00:00 2001 From: Dekel Date: Fri, 20 Mar 2026 11:09:23 +0100 Subject: [PATCH 08/12] remove types from deprication --- src/indexing.jl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/indexing.jl b/src/indexing.jl index 5a15cb5..f7b3891 100644 --- a/src/indexing.jl +++ b/src/indexing.jl @@ -29,4 +29,4 @@ function issame(a, b) a == b end -Base.@deprecate issame(a, b) Base.:(==)(a, b) false +Base.@deprecate issame Base.:(==) false From ca7a41f19574c48b6f6da3ccef01722746a94aa5 Mon Sep 17 00:00:00 2001 From: Reuben Gardos Reid <5456207+ReubenJ@users.noreply.github.com> Date: Fri, 20 Mar 2026 11:35:39 +0100 Subject: [PATCH 09/12] chore: switch to pre-commit + runic for formatting --- .JuliaFormatter.toml | 1 - .dev/Project.toml | 3 -- .dev/herb_format.jl | 75 ---------------------------- .dev/setup.jl | 21 -------- .githooks/pre-commit | 19 ------- .github/workflows/JuliaFormatter.yml | 36 +------------ .pre-commit-config.yaml | 5 ++ 7 files changed, 7 insertions(+), 153 deletions(-) delete mode 100644 .JuliaFormatter.toml delete mode 100644 .dev/Project.toml delete mode 100755 .dev/herb_format.jl delete mode 100644 .dev/setup.jl delete mode 100755 .githooks/pre-commit create mode 100644 .pre-commit-config.yaml diff --git a/.JuliaFormatter.toml b/.JuliaFormatter.toml deleted file mode 100644 index 453925c..0000000 --- a/.JuliaFormatter.toml +++ /dev/null @@ -1 +0,0 @@ -style = "sciml" \ No newline at end of file diff --git a/.dev/Project.toml b/.dev/Project.toml deleted file mode 100644 index 2922960..0000000 --- a/.dev/Project.toml +++ /dev/null @@ -1,3 +0,0 @@ -[deps] -Git = "d7ba0133-e1db-5d97-8f8c-041e4b3a1eb2" -JuliaFormatter = "98e50ef6-434e-11e9-1051-2b60c6c9e899" diff --git a/.dev/herb_format.jl b/.dev/herb_format.jl deleted file mode 100755 index 5987189..0000000 --- a/.dev/herb_format.jl +++ /dev/null @@ -1,75 +0,0 @@ -""" -Formatting script inspired by that of the `Flux.jl` package, which -can be found at: - -https://github.com/FluxML/Flux.jl/blob/caa1ceef9cf59bd817b7bf5c94d0ffbec5a0f32c/dev/flux_format.jl -""" - -using Pkg -Pkg.activate(@__DIR__) -Pkg.instantiate() - -using JuliaFormatter - -help = """ -Usage: herb_format [flags] [FILE/PATH]... - -Formats the given julia files using the Herb formatting options. -If paths are given instead, it will format all *.jl files under -the paths. If nothing is given, all changed julia files are formatted. - - -v, --verbose - Print the name of the files being formatted with relevant details. - - -h, --help - Print this help message. - - --check - Check if the files are formatted without changing them. -""" - -options = Dict{Symbol, Bool}() -indices_to_remove = [] # used to delete options once processed - -for (index, arg) in enumerate(ARGS) - if arg[1] != '-' - continue - end - val = true - if arg in ["-v", "--verbose"] - opt = :verbose - push!(indices_to_remove, index) - elseif arg in ["-h", "--help"] - opt = :help - push!(indices_to_remove, index) - elseif arg == "--check" - opt = :overwrite - val = false - write(stdout, "Checking files.\n") - push!(indices_to_remove, index) - else - error("Option $arg is not supported.") - end - options[opt] = val -end - -# remove options from args -deleteat!(ARGS, indices_to_remove) - -# print help message if asked -if haskey(options, :help) - write(stdout, help) - exit(0) -end - -# otherwise format files -if isempty(ARGS) - filenames = readlines(`git ls-files "*.jl"`) -else - filenames = ARGS -end - -write(stdout, "Formatting in progress.\n") -# format returns true if the files were already formatted -# and false if they were not (had to be formatted) -exit(format(filenames; options...) ? 0 : 1) diff --git a/.dev/setup.jl b/.dev/setup.jl deleted file mode 100644 index 4f4fe0e..0000000 --- a/.dev/setup.jl +++ /dev/null @@ -1,21 +0,0 @@ -""" -Dev environment setup script inspired by that of the `Flux.jl` package, which -can be found at: - -https://github.com/FluxML/Flux.jl/blob/caa1ceef9cf59bd817b7bf5c94d0ffbec5a0f32c/dev/setup.jl -""" - -# instantiate the environment -using Pkg -Pkg.activate(@__DIR__) -Pkg.instantiate() - -# setup the custom git hook -using Git - -# set the local hooks path -const git = Git.git() -run(`$git config --local core.hooksPath .githooks/`) - -# set file permission for hook -Base.Filesystem.chmod(".githooks", 0o777; recursive = true) diff --git a/.githooks/pre-commit b/.githooks/pre-commit deleted file mode 100755 index 4b9ff02..0000000 --- a/.githooks/pre-commit +++ /dev/null @@ -1,19 +0,0 @@ -#!/usr/bin/env bash - -# Get the list of files that are about to be committed and filter out only the .jl files -files=$(git diff --cached --name-only --diff-filter=ACM | grep "\.jl$") - -# If no files are found, exit -if [ -z "$files" ]; then - exit 0 -fi - -# Run the herb formatter on the list of files -julia --startup-file=no -O1 --color=yes .dev/herb_format.jl --check $files - -# If the formatter exited with an error, abort the commit -if [ $? -ne 0 ]; then - echo "Error: formatter must be run on the files before committing." - echo "Please run julia .dev/herb_format.jl YOUR_CHANGED_FILES.jl" - exit 1 -fi diff --git a/.github/workflows/JuliaFormatter.yml b/.github/workflows/JuliaFormatter.yml index 59cdca9..17d15b6 100644 --- a/.github/workflows/JuliaFormatter.yml +++ b/.github/workflows/JuliaFormatter.yml @@ -15,40 +15,8 @@ concurrency: jobs: format: name: Julia ${{ matrix.version }} - ${{ matrix.os }} - ${{ matrix.arch }} - ${{ github.event_name }} - runs-on: ${{ matrix.os }} - strategy: - fail-fast: false - matrix: - version: - - '1' - os: - - ubuntu-latest - arch: - - x64 steps: - uses: actions/checkout@v4 - - - uses: dorny/paths-filter@v3 - id: filter - with: - filters: | - julia_file_change: - - added|modified: '**.jl' - list-files: 'shell' - - - uses: julia-actions/setup-julia@latest - if: steps.filter.outputs.julia_file_change == 'true' + - uses: fredrikekre/runic-action@v1 with: - version: ${{ matrix.version }} - - - uses: julia-actions/cache@v1 - - - name: Apply JuliaFormatter - if: steps.filter.outputs.julia_file_change == 'true' - run: | - julia --color=yes .dev/herb_format.jl --verbose ${{ steps.filter.outputs.julia_file_change_files }} - - - name: Check formatting diff - if: steps.filter.outputs.julia_file_change == 'true' - run: | - git diff --color=always --exit-code \ No newline at end of file + version: '1' diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml new file mode 100644 index 0000000..3e2823c --- /dev/null +++ b/.pre-commit-config.yaml @@ -0,0 +1,5 @@ +repos: + - repo: https://github.com/fredrikekre/runic-pre-commit + rev: v2.0.1 + hooks: + - id: runic From 05a1fec6e6809b7354ea851f6907312fe843aef3 Mon Sep 17 00:00:00 2001 From: Reuben Gardos Reid <5456207+ReubenJ@users.noreply.github.com> Date: Fri, 20 Mar 2026 11:36:16 +0100 Subject: [PATCH 10/12] style: apply runic --- src/HerbCore.jl | 60 ++++++------- src/grammar.jl | 1 + src/indexing.jl | 2 +- src/rulenode.jl | 115 +++++++++++++++---------- test/aqua_test.jl | 2 +- test/rulenode_test.jl | 190 ++++++++++++++++++++++++++---------------- 6 files changed, 221 insertions(+), 149 deletions(-) diff --git a/src/HerbCore.jl b/src/HerbCore.jl index d60d774..3f6178d 100644 --- a/src/HerbCore.jl +++ b/src/HerbCore.jl @@ -8,35 +8,35 @@ include("constraint.jl") include("indexing.jl") export - AbstractRuleNode, - RuleNode, - @rulenode, - AbstractHole, - AbstractUniformHole, - UniformHole, - Hole, - HoleReference, depth, - node_depth, - rulesoftype, - contains_index, - swap_node, - get_rulesequence, - rulesonleft, - get_node_at_location, - get_path, - number_of_holes, - contains_hole, - contains_nonuniform_hole, - get_children, - get_rule, - isuniform, - isfilled, - hasdynamicvalue, - have_same_shape, AbstractConstraint, - AbstractGrammar, - print_tree, - update_rule_indices!, - is_domain_valid, - issame + AbstractRuleNode, + RuleNode, + @rulenode, + AbstractHole, + AbstractUniformHole, + UniformHole, + Hole, + HoleReference, depth, + node_depth, + rulesoftype, + contains_index, + swap_node, + get_rulesequence, + rulesonleft, + get_node_at_location, + get_path, + number_of_holes, + contains_hole, + contains_nonuniform_hole, + get_children, + get_rule, + isuniform, + isfilled, + hasdynamicvalue, + have_same_shape, AbstractConstraint, + AbstractGrammar, + print_tree, + update_rule_indices!, + is_domain_valid, + issame end # module HerbCore diff --git a/src/grammar.jl b/src/grammar.jl index bf1794b..de34cf8 100644 --- a/src/grammar.jl +++ b/src/grammar.jl @@ -24,6 +24,7 @@ function Base.show(io::IO, grammar::AbstractGrammar) for i in eachindex(grammar.rules) println(io, i, ": ", grammar.types[i], " = ", grammar.rules[i]) end + return end Base.getindex(grammar::AbstractGrammar, typ::Symbol) = grammar.bytype[typ] diff --git a/src/indexing.jl b/src/indexing.jl index f7b3891..25458d3 100644 --- a/src/indexing.jl +++ b/src/indexing.jl @@ -26,7 +26,7 @@ function is_domain_valid end """ function issame(a, b) - a == b + return a == b end Base.@deprecate issame Base.:(==) false diff --git a/src/rulenode.jl b/src/rulenode.jl index 57db5c1..068e8ec 100644 --- a/src/rulenode.jl +++ b/src/rulenode.jl @@ -60,6 +60,7 @@ function update_rule_indices!(node::RuleNode, n_rules::Integer) for child in children update_rule_indices!(child, n_rules) end + return end """ @@ -76,7 +77,7 @@ function update_rule_indices!( node::RuleNode, n_rules::Integer, mapping::AbstractDict{<:Integer, <:Integer} -) + ) rule_ind = get_rule(node) if rule_ind > n_rules error("Rule index $(rule_ind) exceeds the number of grammar rules ($n_rules).") @@ -88,6 +89,7 @@ function update_rule_indices!( for child in children update_rule_indices!(child, n_rules, mapping) end + return end # Check whether the `node`'s rule index exceeds the number of rules `n_rules.` @@ -95,7 +97,7 @@ function is_domain_valid(node::RuleNode, n_rules::Integer) if get_rule(node) > n_rules return false end - all(child -> is_domain_valid(child, n_rules), get_children(node)) + return all(child -> is_domain_valid(child, n_rules), get_children(node)) end """ @@ -133,7 +135,7 @@ function is_domain_valid(hole::AbstractHole, n_rules::Integer) if length(hole.domain) != n_rules return false end - all(child -> is_domain_valid(child, n_rules), get_children(hole)) + return all(child -> is_domain_valid(child, n_rules), get_children(hole)) end """ @@ -154,6 +156,7 @@ function update_rule_indices!(hole::AbstractHole, n_rules::Integer) for child in children update_rule_indices!(child, n_rules) end + return end """ @@ -168,10 +171,11 @@ Errors if the length of the domain vector exceeds new `n_rules`. - `n_rules`: The new number of rules in the grammar - `mapping`: A dictionary mapping the old rule indices to new ones """ -function update_rule_indices!(hole::AbstractHole, +function update_rule_indices!( + hole::AbstractHole, n_rules::Integer, mapping::AbstractDict{<:Integer, <:Integer} -) + ) update_rule_indices!(hole, n_rules) # resize (also checks n_rules) # update domain BV according to mapping rule_indices = findall(hole.domain) @@ -185,6 +189,7 @@ function update_rule_indices!(hole::AbstractHole, for child in children update_rule_indices!(child, n_rules, mapping) end + return end """ @@ -221,16 +226,16 @@ function _get_hole_name(holetype) else throw( ArgumentError( - styled""" - Input to the {code:@rulenode} macro appears to be a hole, but the macro does not support the type: {code:{red:$holetype}}. The macro currently supports concrete subtypes of {code:AbstractHole}s {bold:that are defined in {code:HerbCore.jl}}. - """, - ), + styled""" + Input to the {code:@rulenode} macro appears to be a hole, but the macro does not support the type: {code:{red:$holetype}}. The macro currently supports concrete subtypes of {code:AbstractHole}s {bold:that are defined in {code:HerbCore.jl}}. + """, + ), ) end end function _shorthand2rulenode(i::Integer) - :(RuleNode($i)) + return :(RuleNode($i)) end function _shorthand2rulenode(ex::Expr) @@ -261,8 +266,10 @@ function _shorthand2rulenode(ex::Expr) # rulenodes without children ex = postwalk(ex) do x @capture(x, {children__}) || return x - children = [isexpr(child, Int, Integer) ? :(RuleNode($child)) : child - for child in children] + children = [ + isexpr(child, Int, Integer) ? :(RuleNode($child)) : child + for child in children + ] return :([$(children...)]) end @@ -303,7 +310,7 @@ Hole[Bool[1, 1, 0, 0]] ``` """ macro rulenode(ex::Union{Integer, Expr}) - _shorthand2rulenode(ex) + return _shorthand2rulenode(ex) end """ @@ -325,16 +332,16 @@ RuleNode(ind::Int, _val::Any) = RuleNode(ind, _val, AbstractRuleNode[]) Base.:(==)(::RuleNode, ::AbstractHole) = false Base.:(==)(::AbstractHole, ::RuleNode) = false function Base.:(==)(A::RuleNode, B::RuleNode) - (A.ind == B.ind) && + return (A.ind == B.ind) && length(A.children) == length(B.children) && #required because zip doesn't check lengths all(isequal(a, b) for (a, b) in zip(A.children, B.children)) end function Base.:(==)(a::UniformHole, b::UniformHole) - a.domain == b.domain && length(a.children) == length(b.children) && + return a.domain == b.domain && length(a.children) == length(b.children) && all(isequal(a, b) for (a, b) in zip(a.children, b.children)) end -function Base.:(==)(a::H, b::H) where H <: AbstractHole - a.domain == b.domain +function Base.:(==)(a::H, b::H) where {H <: AbstractHole} + return a.domain == b.domain end @@ -356,18 +363,18 @@ end function Base.show(io::IO, node::RuleNode; separator = ",") print(io, node.ind) - if !isempty(children(node)) + return if !isempty(children(node)) Base.show_enclosed_list(io, "{", children(node), separator, "}", 0) end end function Base.show(io::IO, node::AbstractHole; _...) - print(io, "Hole[$(node.domain)]") + return print(io, "Hole[$(node.domain)]") end function Base.show(io::IO, node::UniformHole; separator = ",") print(io, "UniformHole[$(node.domain)]") - if !isempty(node.children) + return if !isempty(node.children) Base.show_enclosed_list(io, "{", children(node), separator, "}", 0) end end @@ -396,8 +403,10 @@ performed in both [`RuleNode`](@ref)s until nodes with a different index are found. """ Base.isless( - rn₁::AbstractRuleNode, rn₂::AbstractRuleNode)::Bool = _rulenode_compare( - rn₁, rn₂) == -1 + rn₁::AbstractRuleNode, rn₂::AbstractRuleNode +)::Bool = _rulenode_compare( + rn₁, rn₂ +) == -1 function _rulenode_compare(rn₁::AbstractRuleNode, rn₂::AbstractRuleNode)::Int # Helper function for Base.isless @@ -461,8 +470,10 @@ Returns every rule in the ruleset that is also used in the [`AbstractRuleNode`]( !!! warning The `ignoreNode` must be a subtree of `node` for it to have an effect. """ -function rulesoftype(node::RuleNode, ruleset::Set{Int}, - ignoreNode::Union{Nothing, AbstractRuleNode} = nothing)::Set{Int} +function rulesoftype( + node::RuleNode, ruleset::Set{Int}, + ignoreNode::Union{Nothing, AbstractRuleNode} = nothing + )::Set{Int} retval = Set{Int}() if !isnothing(ignoreNode) && node == ignoreNode @@ -483,9 +494,11 @@ function rulesoftype(node::RuleNode, ruleset::Set{Int}, return retval end end -function rulesoftype(node::RuleNode, rule_index::Int, - ignoreNode::Union{Nothing, AbstractRuleNode} = nothing) - rulesoftype(node, Set{Int}(rule_index), ignoreNode) +function rulesoftype( + node::RuleNode, rule_index::Int, + ignoreNode::Union{Nothing, AbstractRuleNode} = nothing + ) + return rulesoftype(node, Set{Int}(rule_index), ignoreNode) end rulesoftype(::Hole, ::Vararg{Any}) = Set{Int}() @@ -503,9 +516,10 @@ function rulesoftype( grammar::AbstractGrammar, ruletype::Symbol, ignoreNode::Union{Nothing, RuleNode} = nothing -) - rulesoftype( - node, Set(grammar[ruletype]), ignoreNode) + ) + return rulesoftype( + node, Set(grammar[ruletype]), ignoreNode + ) end """ @@ -514,8 +528,11 @@ end Returns true if the rulenode contains the index. """ function contains_index(rulenode::AbstractRuleNode, index::Int) - !isempty(rulesoftype( - rulenode, index)) + return !isempty( + rulesoftype( + rulenode, index + ) + ) end """ @@ -525,7 +542,7 @@ Replace a node in `expr`, specified by `path`, with `new_expr`. Path is a sequence of child indices, starting from the root node. """ function swap_node(expr::AbstractRuleNode, new_expr::AbstractRuleNode, path::Vector{Int}) - if length(path) == 1 + return if length(path) == 1 expr.children[path[begin]] = new_expr else swap_node(expr.children[path[begin]], new_expr, path[2:end]) @@ -538,7 +555,7 @@ end Replace child `i` of a node, a part of larger `expr`, with `new_expr`. """ function swap_node(expr::RuleNode, node::RuleNode, child_index::Int, new_expr::RuleNode) - if expr == node + return if expr == node node.children[child_index] = new_expr else for child in expr.children @@ -554,7 +571,7 @@ Extract the derivation sequence from a path (sequence of child indices) and an [ If the path is deeper than the deepest node, it returns what it has. """ function get_rulesequence(node::RuleNode, path::Vector{Int}) - if node.ind == 0 # sign for empty node + if node.ind == 0 # sign for empty node return Vector{Int}() elseif isempty(node.children) # no childnen, nowehere to follow the path; still return the index return [node.ind] @@ -563,13 +580,17 @@ function get_rulesequence(node::RuleNode, path::Vector{Int}) elseif isassigned(path, 2) # at least two items are left in the path # need to access the child with get because it can happen that the child is not yet built - return append!([node.ind], - get_rulesequence(get(node.children, path[begin], RuleNode(0)), path[2:end])) + return append!( + [node.ind], + get_rulesequence(get(node.children, path[begin], RuleNode(0)), path[2:end]) + ) else # if only one item left in the path # need to access the child with get because it can happen that the child is not yet built - return append!([node.ind], - get_rulesequence(get(node.children, path[begin], RuleNode(0)), Vector{Int}())) + return append!( + [node.ind], + get_rulesequence(get(node.children, path[begin], RuleNode(0)), Vector{Int}()) + ) end end @@ -642,7 +663,8 @@ end Returns the path from the `root` to the `targetnode`. Returns nothing if no path exists. """ function get_path( - root::AbstractRuleNode, targetnode::AbstractRuleNode)::Union{Vector{Int}, Nothing} + root::AbstractRuleNode, targetnode::AbstractRuleNode + )::Union{Vector{Int}, Nothing} if root === targetnode return Vector{Int}() end @@ -661,11 +683,12 @@ end Recursively counts the number of holes in an [`AbstractRuleNode`](@ref) """ function number_of_holes(rn::RuleNode) - reduce( - +, [number_of_holes(c) for c in rn.children], init = 0) + return reduce( + +, [number_of_holes(c) for c in rn.children], init = 0 + ) end function number_of_holes(rn::UniformHole) - 1 + reduce(+, [number_of_holes(c) for c in rn.children], init = 0) + return 1 + reduce(+, [number_of_holes(c) for c in rn.children], init = 0) end number_of_holes(rn::Hole) = 1 @@ -683,8 +706,10 @@ contains_hole(hole::AbstractHole) = true Checks if an [`AbstractRuleNode`](@ref) tree contains a [`Hole`](@ref). """ function contains_nonuniform_hole(rn::AbstractRuleNode) - any(contains_nonuniform_hole(c) - for c in rn.children) + return any( + contains_nonuniform_hole(c) + for c in rn.children + ) end contains_nonuniform_hole(hole::Hole) = true diff --git a/test/aqua_test.jl b/test/aqua_test.jl index eafa582..a6df0f4 100644 --- a/test/aqua_test.jl +++ b/test/aqua_test.jl @@ -1,4 +1,4 @@ @testitem "Aqua.jl" begin using Aqua Aqua.test_all(HerbCore) -end \ No newline at end of file +end diff --git a/test/rulenode_test.jl b/test/rulenode_test.jl index 2e8920d..ec06f70 100644 --- a/test/rulenode_test.jl +++ b/test/rulenode_test.jl @@ -15,12 +15,12 @@ node = RuleNode(1, [RuleNode(2), RuleNode(3)]) @test node == node @test RuleNode(1, [RuleNode(2), RuleNode(3)]) == - RuleNode(1, [RuleNode(2), RuleNode(3)]) + RuleNode(1, [RuleNode(2), RuleNode(3)]) @test RuleNode(1, [RuleNode(2), node]) == RuleNode(1, [RuleNode(2), node]) @test RuleNode(1) !== RuleNode(2) @test RuleNode(1, [RuleNode(2), RuleNode(3)]) !== - RuleNode(2, [RuleNode(2), RuleNode(3)]) + RuleNode(2, [RuleNode(2), RuleNode(3)]) end @testset "Hash tests" begin @@ -46,8 +46,8 @@ @test RuleNode(2) > RuleNode(1) @test RuleNode(1, [RuleNode(2)]) < RuleNode(1, [RuleNode(3)]) @test RuleNode(1, [RuleNode(2)]) < RuleNode(2, [RuleNode(1)]) - @test_throws ArgumentError RuleNode(1) 5, 2 => 6, 3 => 1) @testset "UniformHole" begin n_rules = 6 @@ -484,7 +514,8 @@ n_rules = 2 @test_throws ErrorException update_rule_indices!(uniform_hole, n_rules) @test_throws ErrorException update_rule_indices!( - uniform_hole, n_rules, mapping) + uniform_hole, n_rules, mapping + ) end @testset "Hole" begin n_rules = 6 @@ -530,34 +561,49 @@ @test hole2 != hole3 # UniformHole - uhole1 = UniformHole([0, 0, 1], + uhole1 = UniformHole( + [0, 0, 1], [ RuleNode(14), - RuleNode(2, [ - RuleNode(4, [ - RuleNode(6) - ]) - ]) + RuleNode( + 2, [ + RuleNode( + 4, [ + RuleNode(6), + ] + ), + ] + ), ] ) - uhole2 = UniformHole([0, 0, 1], + uhole2 = UniformHole( + [0, 0, 1], [ RuleNode(14), - RuleNode(2, [ - RuleNode(4, [ - RuleNode(6) - ]) - ]) + RuleNode( + 2, [ + RuleNode( + 4, [ + RuleNode(6), + ] + ), + ] + ), ] ) - uhole3 = UniformHole([0, 0, 1], + uhole3 = UniformHole( + [0, 0, 1], [ RuleNode(14), - RuleNode(2, [ - RuleNode(4, [ - RuleNode(66) - ]) - ]) + RuleNode( + 2, [ + RuleNode( + 4, [ + RuleNode(66), + ] + ), + ] + ), ] ) uhole4 = UniformHole([0, 0, 1], [RuleNode(14)]) From 141a1d848bcc4c95c0534fcf9e2789473d4e8b95 Mon Sep 17 00:00:00 2001 From: Reuben Gardos Reid <5456207+ReubenJ@users.noreply.github.com> Date: Fri, 20 Mar 2026 11:37:01 +0100 Subject: [PATCH 11/12] chore: ignore style change pr --- .git-blame-ignore-revs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/.git-blame-ignore-revs b/.git-blame-ignore-revs index ec0bb15..a859b5b 100644 --- a/.git-blame-ignore-revs +++ b/.git-blame-ignore-revs @@ -1,2 +1,4 @@ # Migrate code style to SciML -02be3454903ae3d03e1b8062794d96adabbdd814 \ No newline at end of file +02be3454903ae3d03e1b8062794d96adabbdd814 +# Migrate code style to Runic +05a1fec6e6809b7354ea851f6907312fe843aef3 From f8f9f0cc11cb6d4fe0bbd0981b49e5a7acb5264f Mon Sep 17 00:00:00 2001 From: Reuben Gardos Reid <5456207+ReubenJ@users.noreply.github.com> Date: Fri, 20 Mar 2026 11:41:17 +0100 Subject: [PATCH 12/12] ci: suggest style changes on PRs --- .github/workflows/{JuliaFormatter.yml => format.yaml} | 6 ++++++ 1 file changed, 6 insertions(+) rename .github/workflows/{JuliaFormatter.yml => format.yaml} (71%) diff --git a/.github/workflows/JuliaFormatter.yml b/.github/workflows/format.yaml similarity index 71% rename from .github/workflows/JuliaFormatter.yml rename to .github/workflows/format.yaml index 17d15b6..15569fb 100644 --- a/.github/workflows/JuliaFormatter.yml +++ b/.github/workflows/format.yaml @@ -20,3 +20,9 @@ jobs: - uses: fredrikekre/runic-action@v1 with: version: '1' + continue-on-error: ${{ github.event_name == 'pull_request' }} + - uses: reviewdog/action-suggester@v1 + if: github.event_name == 'pull_request' + with: + tool_name: Runic + fail_level: warning