• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

nhartland / forma / 13819256146

12 Mar 2025 06:51PM UTC coverage: 98.168% (+0.02%) from 98.145%
13819256146

Pull #27

github

web-flow
Merge 3ad96a07c into f35020e35
Pull Request #27: Update to luaunit 3.4

115 of 115 new or added lines in 7 files covered. (100.0%)

8 existing lines in 1 file now uncovered.

1875 of 1910 relevant lines covered (98.17%)

9030.96 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

62.69
/forma/multipattern.lua
1
--- A class contain a collection of `pattern` objects.
2
-- Many pattern operations generate a set of patterns. This
3
-- class aims to provide a convenient collection with some
4
-- common methods for handling them.
5
--
6
-- @module forma.multipattern
7
local multipattern = {}
1✔
8

9

10
-- Multipattern indexing
11
-- For enabling syntax sugar multipattern:method
12
-- This retains the ability to index by number.
13
multipattern.__index = function(mp, key)
14
    if type(key) == "number" then
266✔
15
        return mp.components[key]
108✔
16
    else
17
        return multipattern[key]
158✔
18
    end
19
end
20

21
--- Multipattern length.
22
-- Returns the number of components in the multipattern.
23
multipattern.__len = function(mp)
24
    return mp:n_components()
×
25
end
26

27

28
--- Create a new multipattern from a list of patterns.
29
-- @param components an array of `pattern` objects.
30
-- @return a new multipattern containing those patterns.
31
function multipattern.new(components)
1✔
32
    local mp = {
66✔
33
        components = components or {}
66✔
34
    }
35

36
    mp = setmetatable(mp, multipattern)
66✔
37
    return mp
66✔
38
end
39

40
--- Clone the multipattern.
41
-- @param mp multipattern to clone.
42
-- @return the cloned multipattern.
43
function multipattern.clone(mp)
1✔
44
    local components = {}
×
45
    for i, p in ipairs(mp.components) do
×
UNCOV
46
        components[i] = p:clone()
×
47
    end
48

UNCOV
49
    return multipattern.new(components)
×
50
end
51

52
--- Insert a pattern into the multipattern.
53
-- @param mp multipattern to be operated upon.
54
-- @param ip the new pattern to insert.
55
-- @return the new multipattern.
56
function multipattern.insert(mp, ip)
1✔
57
    assert(getmetatable(mp) == multipattern, "multipattern.insert requires a multipattern as the first argument")
98✔
58
    table.insert(mp.components, ip)
98✔
59
end
60

61
--- Count the number of components in a multipattern.
62
-- @param mp the multipattern to count.
63
-- @return the number of components.
64
function multipattern.n_components(mp)
1✔
65
    assert(getmetatable(mp) == multipattern, "multipattern.n_components requires a multipattern as the first argument")
41✔
66
    return #mp.components
41✔
67
end
68

69
--- Map a function over all patterns in this multipattern.
70
-- Calls `fn(pattern, index)` for each sub-pattern, returning a new multipattern
71
-- of their results.
72
--
73
-- **Example**:
74
--  ```
75
--  local bigger = mp:map(function(p) return p:enlarge(2) end)
76
--  ```
77
--
78
-- @param mp the multipattern upon which to map the function.
79
-- @param fn a function taking `(pattern, index)` and returning a new `pattern`.
80
-- @return a new multipattern of the mapped results.
81
function multipattern.map(mp, fn)
1✔
82
    assert(getmetatable(mp) == multipattern, "multipattern.map requires a multipattern as an argument")
1✔
83
    -- Applies `fn` to each pattern in this multipattern,
84
    -- returning a new multipattern of results.
85
    -- fn is a function(pat, index) -> (some pattern)
86
    local new_components = {}
1✔
87
    for i, pat in ipairs(mp.components) do
3✔
88
        new_components[i] = fn(pat, i)
4✔
89
    end
90
    return multipattern.new(new_components)
1✔
91
end
92

93
--- Filter out sub-patterns according to a predicate.
94
-- Keeps only those patterns for which `predicate(pattern) == true`.
95
--
96
-- **Example**:
97
--  ```
98
--  local bigSegs = mp:filter(function(p) return p:size() >= 10 end)
99
--  ```
100
-- @param mp the multipattern upon which to filter.
101
-- @param fn a function `(pattern) -> boolean`.
102
-- @return a new multipattern containing only the sub-patterns passing the test.
103
function multipattern.filter(mp, fn)
1✔
104
    assert(getmetatable(mp) == multipattern, "multipattern.filter requires a multipattern as an argument")
14✔
105
    -- Keeps only those patterns for which fn(pat) == true.
106
    local new_components = {}
14✔
107
    for _, pat in ipairs(mp.components) do
75✔
108
        if fn(pat) then
122✔
109
            new_components[#new_components + 1] = pat
15✔
110
        end
111
    end
112
    return multipattern.new(new_components)
14✔
113
end
114

115
--- Apply a named method to each pattern, returning a new multipattern.
116
-- This is an alternative to `:map(...)` for calling an *existing* pattern method
117
-- by name on all sub-patterns. You may also supply arguments to that method.
118
--
119
-- **Example**:
120
--   ```
121
--   local translated = mp:apply("translate", 10, 5)
122
--   -- calls p:translate(10,5) on each pattern p
123
--   ```
124
-- @param mp the multipattern upon which to apply the method.
125
-- @param method the name of a function in `pattern`.
126
-- @param ... additional arguments to pass to that method.
127
-- @return a new multipattern of the method's results.
128
function multipattern.apply(mp, method, ...)
1✔
129
    assert(getmetatable(mp) == multipattern, "multipattern.apply requires a multipattern as an argument")
1✔
130
    local new_components = {}
1✔
131
    for i, pat in ipairs(mp.components) do
3✔
132
        local m = pat[method]
2✔
133
        assert(type(m) == "function", "No method named '" .. tostring(method) .. "' on pattern")
2✔
134
        new_components[i] = m(pat, ...)
4✔
135
    end
136
    return multipattern.new(new_components)
1✔
137
end
138

139
--- Union all sub-patterns into a single pattern.
140
-- Folds over the sub-patterns with the union (`+`) operator,
141
-- returning a single `pattern`.
142
--
143
-- **Example**:
144
--   ```
145
--   local combined = mp:union_all()
146
--   ```
147
-- @param mp the multipattern to union over.
148
-- @return a single pattern combining all sub-patterns.
149
function multipattern.union_all(mp)
1✔
150
    -- Require here to avoid circular dependency.
151
    local pattern = require('forma.pattern')
3✔
152
    return pattern.union(mp.components)
3✔
153
end
154

155
--- Utilities
156
-- @section multipattern_utils
157

158
--- Print a multipattern.
159
-- Prints a multipattern to `io.output`. If provided, a table of subpattern labels
160
-- can be used, with one entry per subpattern.
161
-- @param mp the multipattern to be drawn.
162
-- @param chars the characters to be printed for each subpattern (optional).
163
-- @param domain the domain in which to print (optional).
164
function multipattern.print(mp, chars, domain)
1✔
165
    assert(getmetatable(mp) == multipattern, "multipattern.print requires a multipattern as a first argument")
×
166
    domain = domain or mp:union_all()
×
UNCOV
167
    assert(domain:size() > 0, "multipattern.print: domain must have at least one cell")
×
168
    local n = mp:n_components()
×
169
    -- If no dictionary is supplied generate a new one (starting from '0')
170
    if chars == nil then
×
171
        local start_char = 47
×
172
        assert(n < (200 - start_char), "multipattern.print: too many components")
×
173
        chars = {}
×
UNCOV
174
        for i = 1, n, 1 do
×
UNCOV
175
            table.insert(chars, string.char(i + start_char))
×
176
        end
177
    end
UNCOV
178
    assert(n == #chars,
×
179
        "multipattern.print: there must be as many character table entries as components")
×
180
    -- Print out the segments to a map
181
    for i = domain.min.y, domain.max.y, 1 do
×
182
        local string = ''
×
183
        for j = domain.min.x, domain.max.x, 1 do
×
184
            local token = ' '
×
UNCOV
185
            for k, v in ipairs(mp.components) do
×
186
                if v:has_cell(j, i) then token = chars[k] end
×
187
            end
188
            string = string .. token
×
189
        end
UNCOV
190
        io.write(string .. '\n')
×
191
    end
192
end
193

194
return multipattern
1✔
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc