• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In
Build has been canceled!

lunarmodules / Penlight / 16815657215

07 Aug 2025 08:56PM UTC coverage: 88.858% (-0.01%) from 88.871%
16815657215

push

github

Tieske
fix(*): some Lua 5.5 fixes for constant loop variables

see #497

12 of 13 new or added lines in 4 files covered. (92.31%)

9 existing lines in 3 files now uncovered.

5455 of 6139 relevant lines covered (88.86%)

256.08 hits per line

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

93.24
/lua/pl/luabalanced.lua
1
--- Extract delimited Lua sequences from strings.
2
-- Inspired by Damian Conway's Text::Balanced in Perl. <br/>
3
-- <ul>
4
--   <li>[1] <a href="http://lua-users.org/wiki/LuaBalanced">Lua Wiki Page</a></li>
5
--   <li>[2] http://search.cpan.org/dist/Text-Balanced/lib/Text/Balanced.pm</li>
6
-- </ul> <br/>
7
-- <pre class=example>
8
-- local lb = require "pl.luabalanced"
9
-- --Extract Lua expression starting at position 4.
10
--  print(lb.match_expression("if x^2 + x > 5 then print(x) end", 4))
11
--  --> x^2 + x > 5     16
12
-- --Extract Lua string starting at (default) position 1.
13
-- print(lb.match_string([["test\"123" .. "more"]]))
14
-- --> "test\"123"     12
15
-- </pre>
16
-- (c) 2008, David Manura, Licensed under the same terms as Lua (MIT license).
17
-- @class module
18
-- @name pl.luabalanced
19

20
local M = {}
6✔
21

22
local assert = assert
6✔
23

24
-- map opening brace <-> closing brace.
25
local ends = { ['('] = ')', ['{'] = '}', ['['] = ']' }
6✔
26
local begins = {}; for k,v in pairs(ends) do begins[v] = k end
24✔
27

28

29
-- Match Lua string in string <s> starting at position <pos>.
30
-- Returns <string>, <posnew>, where <string> is the matched
31
-- string (or nil on no match) and <posnew> is the character
32
-- following the match (or <pos> on no match).
33
-- Supports all Lua string syntax: "...", '...', [[...]], [=[...]=], etc.
34
local function match_string(s, pos)
35
  pos = pos or 1
54✔
36
  local posa = pos
54✔
37
  local c = s:sub(pos,pos)
54✔
38
  if c == '"' or c == "'" then
54✔
39
    pos = pos + 1
30✔
UNCOV
40
    while 1 do
10✔
41
      pos = assert(s:find("[" .. c .. "\\]", pos), 'syntax error')
30✔
42
      if s:sub(pos,pos) == c then
40✔
43
        local part = s:sub(posa, pos)
30✔
44
        return part, pos + 1
30✔
45
      else
46
        pos = pos + 2
×
47
      end
48
    end
49
  else
50
    local sc = s:match("^%[(=*)%[", pos)
24✔
51
    if sc then
24✔
52
      local _; _, pos = s:find("%]" .. sc .. "%]", pos)
24✔
53
      assert(pos)
24✔
54
      local part = s:sub(posa, pos)
24✔
55
      return part, pos + 1
24✔
56
    else
57
      return nil, pos
×
58
    end
59
  end
60
end
61
M.match_string = match_string
6✔
62

63

64
-- Match bracketed Lua expression, e.g. "(...)", "{...}", "[...]", "[[...]]",
65
-- [=[...]=], etc.
66
-- Function interface is similar to match_string.
67
local function match_bracketed(s, pos)
68
  pos = pos or 1
156✔
69
  local posa = pos
156✔
70
  local ca = s:sub(pos,pos)
156✔
71
  if not ends[ca] then
156✔
72
    return nil, pos
×
73
  end
74
  local stack = {}
156✔
UNCOV
75
  while 1 do
126✔
76
    pos = s:find('[%(%{%[%)%}%]\"\']', pos)
378✔
77
    assert(pos, 'syntax error: unbalanced')
378✔
78
    local c = s:sub(pos,pos)
378✔
79
    if c == '"' or c == "'" then
378✔
80
      local part; part, pos = match_string(s, pos)
16✔
81
      assert(part)
12✔
82
    elseif ends[c] then -- open
366✔
83
      local mid, posb
84
      if c == '[' then mid, posb = s:match('^%[(=*)%[()', pos) end
186✔
85
      if mid then
186✔
86
        pos = s:match('%]' .. mid .. '%]()', posb)
6✔
87
        assert(pos, 'syntax error: long string not terminated')
6✔
88
        if #stack == 0 then
6✔
89
          local part = s:sub(posa, pos-1)
×
90
          return part, pos
×
91
        end
92
      else
93
        stack[#stack+1] = c
180✔
94
        pos = pos + 1
180✔
95
      end
96
    else -- close
97
      assert(stack[#stack] == assert(begins[c]), 'syntax error: unbalanced')
180✔
98
      stack[#stack] = nil
180✔
99
      if #stack == 0 then
180✔
100
        local part = s:sub(posa, pos)
156✔
101
        return part, pos+1
156✔
102
      end
103
      pos = pos + 1
24✔
104
    end
105
  end
106
end
107
M.match_bracketed = match_bracketed
6✔
108

109

110
-- Match Lua comment, e.g. "--...\n", "--[[...]]", "--[=[...]=]", etc.
111
-- Function interface is similar to match_string.
112
local function match_comment(s, pos)
113
  pos = pos or 1
24✔
114
  if s:sub(pos, pos+1) ~= '--' then
32✔
115
    return nil, pos
×
116
  end
117
  pos = pos + 2
24✔
118
  local partt, post = match_string(s, pos)
24✔
119
  if partt then
24✔
120
    return '--' .. partt, post
24✔
121
  end
122
  local part; part, pos = s:match('^([^\n]*\n?)()', pos)
×
123
  return '--' .. part, pos
×
124
end
125

126

127
-- Match Lua expression, e.g. "a + b * c[e]".
128
-- Function interface is similar to match_string.
129
local wordop = {['and']=true, ['or']=true, ['not']=true}
6✔
130
local is_compare = {['>']=true, ['<']=true, ['~']=true}
6✔
131
local function match_expression(s, pos)
132
  pos = pos or 1
342✔
133
  local _
134
  local posa = pos
342✔
135
  local lastident
136
  local poscs, posce
137
  while pos do
900✔
138
    local c = s:sub(pos,pos)
888✔
139
    if c == '"' or c == "'" or c == '[' and s:find('^[=%[]', pos+1) then
888✔
140
      local part; part, pos = match_string(s, pos)
8✔
141
      assert(part, 'syntax error')
6✔
142
    elseif c == '-' and s:sub(pos+1,pos+1) == '-' then
886✔
143
      -- note: handle adjacent comments in loop to properly support
144
      -- backtracing (poscs/posce).
145
      poscs = pos
12✔
146
      while s:sub(pos,pos+1) == '--' do
32✔
147
        local part; part, pos = match_comment(s, pos)
16✔
148
        assert(part)
12✔
149
        pos = s:match('^%s*()', pos)
12✔
150
        posce = pos
12✔
151
      end
152
    elseif c == '(' or c == '{' or c == '[' then
870✔
153
      _, pos = match_bracketed(s, pos)
48✔
154
    elseif c == '=' and s:sub(pos+1,pos+1) == '=' then
840✔
155
      pos = pos + 2  -- skip over two-char op containing '='
12✔
156
    elseif c == '=' and is_compare[s:sub(pos-1,pos-1)] then
824✔
157
      pos = pos + 1  -- skip over two-char op containing '='
6✔
158
    elseif c:match'^[%)%}%];,=]' then
816✔
159
      local part = s:sub(posa, pos-1)
126✔
160
      return part, pos
126✔
161
    elseif c:match'^[%w_]' then
690✔
162
      local newident,newpos = s:match('^([%w_]+)()', pos)
690✔
163
      if pos ~= posa and not wordop[newident] then -- non-first ident
690✔
164
        local pose = ((posce == pos) and poscs or pos) - 1
372✔
165
        while s:match('^%s', pose) do pose = pose - 1 end
612✔
166
        local ce = s:sub(pose,pose)
372✔
167
        if ce:match'[%)%}\'\"%]]' or
372✔
168
           ce:match'[%w_]' and not wordop[lastident]
354✔
169
        then
170
          local part = s:sub(posa, pos-1)
204✔
171
          return part, pos
204✔
172
        end
173
      end
174
      lastident, pos = newident, newpos
486✔
175
    else
176
      pos = pos + 1
×
177
    end
178
    pos = s:find('[%(%{%[%)%}%]\"\';,=%w_%-]', pos)
558✔
179
  end
180
  local part = s:sub(posa, #s)
12✔
181
  return part, #s+1
12✔
182
end
183
M.match_expression = match_expression
6✔
184

185

186
-- Match name list (zero or more names).  E.g. "a,b,c"
187
-- Function interface is similar to match_string,
188
-- but returns array as match.
189
local function match_namelist(s, pos)
190
  pos = pos or 1
222✔
191
  local list = {}
222✔
UNCOV
192
  while 1 do
154✔
193
    local c = #list == 0 and '^' or '^%s*,%s*'
462✔
194
    local item, post = s:match(c .. '([%a_][%w_]*)%s*()', pos)
462✔
195
    if item then pos = post else break end
462✔
196
    list[#list+1] = item
240✔
197
  end
198
  return list, pos
222✔
199
end
200
M.match_namelist = match_namelist
6✔
201

202

203
-- Match expression list (zero or more expressions).  E.g. "a+b,b*c".
204
-- Function interface is similar to match_string,
205
-- but returns array as match.
206
local function match_explist(s, pos)
207
  pos = pos or 1
228✔
208
  local list = {}
228✔
UNCOV
209
  while 1 do
176✔
210
    if #list ~= 0 then
528✔
211
      local post = s:match('^%s*,%s*()', pos)
300✔
212
      if post then pos = post else break end
300✔
213
    end
214
    local item; item, pos = match_expression(s, pos)
400✔
215
    assert(item, 'syntax error')
300✔
216
    list[#list+1] = item
300✔
217
  end
218
  return list, pos
228✔
219
end
220
M.match_explist = match_explist
6✔
221

222

223
-- Replace snippets of code in Lua code string <s>
224
-- using replacement function f(u,sin) --> sout.
225
-- <u> is the type of snippet ('c' = comment, 's' = string,
226
-- 'e' = any other code).
227
-- Snippet is replaced with <sout> (unless <sout> is nil or false, in
228
-- which case the original snippet is kept)
229
-- This is somewhat analogous to string.gsub .
230
local function gsub(s, f)
231
  local pos = 1
168✔
232
  local posa = 1
168✔
233
  local sret = ''
168✔
UNCOV
234
  while 1 do
64✔
235
    pos = s:find('[%-\'\"%[]', pos)
192✔
236
    if not pos then break end
192✔
237
    if s:match('^%-%-', pos) then
24✔
238
      local exp = s:sub(posa, pos-1)
12✔
239
      if #exp > 0 then sret = sret .. (f('e', exp) or exp) end
16✔
240
      local comment; comment, pos = match_comment(s, pos)
16✔
241
      sret = sret .. (f('c', assert(comment)) or comment)
16✔
242
      posa = pos
12✔
243
    else
244
      local posb = s:find('^[\'\"%[]', pos)
12✔
245
      local str
246
      if posb then str, pos = match_string(s, posb) end
16✔
247
      if str then
12✔
248
        local exp = s:sub(posa, posb-1)
12✔
249
        if #exp > 0 then sret = sret .. (f('e', exp) or exp) end
14✔
250
        sret = sret .. (f('s', str) or str)
16✔
251
        posa = pos
12✔
252
      else
253
        pos = pos + 1
×
254
      end
255
    end
256
  end
257
  local exp = s:sub(posa)
168✔
258
  if #exp > 0 then sret = sret .. (f('e', exp) or exp) end
224✔
259
  return sret
168✔
260
end
261
M.gsub = gsub
6✔
262

263

264
return M
6✔
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

© 2025 Coveralls, Inc