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

lunarmodules / Penlight / 474

01 Feb 2024 02:24PM UTC coverage: 89.675% (+0.8%) from 88.873%
474

push

appveyor

web-flow
docs: Fix typos (#462)

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

140 existing lines in 18 files now uncovered.

5489 of 6121 relevant lines covered (89.67%)

345.29 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 = {}
8✔
21

22
local assert = assert
8✔
23

24
-- map opening brace <-> closing brace.
25
local ends = { ['('] = ')', ['{'] = '}', ['['] = ']' }
8✔
26
local begins = {}; for k,v in pairs(ends) do begins[v] = k end
32✔
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
72✔
36
  local posa = pos
72✔
37
  local c = s:sub(pos,pos)
72✔
38
  if c == '"' or c == "'" then
72✔
39
    pos = pos + 1
40✔
UNCOV
40
    while 1 do
20✔
41
      pos = assert(s:find("[" .. c .. "\\]", pos), 'syntax error')
40✔
42
      if s:sub(pos,pos) == c then
60✔
43
        local part = s:sub(posa, pos)
40✔
44
        return part, pos + 1
40✔
45
      else
46
        pos = pos + 2
×
47
      end
48
    end
49
  else
50
    local sc = s:match("^%[(=*)%[", pos)
32✔
51
    if sc then
32✔
52
      local _; _, pos = s:find("%]" .. sc .. "%]", pos)
32✔
53
      assert(pos)
32✔
54
      local part = s:sub(posa, pos)
32✔
55
      return part, pos + 1
32✔
56
    else
57
      return nil, pos
×
58
    end
59
  end
60
end
61
M.match_string = match_string
8✔
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
208✔
69
  local posa = pos
208✔
70
  local ca = s:sub(pos,pos)
208✔
71
  if not ends[ca] then
208✔
72
    return nil, pos
×
73
  end
74
  local stack = {}
208✔
UNCOV
75
  while 1 do
252✔
76
    pos = s:find('[%(%{%[%)%}%]\"\']', pos)
504✔
77
    assert(pos, 'syntax error: unbalanced')
504✔
78
    local c = s:sub(pos,pos)
504✔
79
    if c == '"' or c == "'" then
504✔
80
      local part; part, pos = match_string(s, pos)
24✔
81
      assert(part)
16✔
82
    elseif ends[c] then -- open
488✔
83
      local mid, posb
84
      if c == '[' then mid, posb = s:match('^%[(=*)%[()', pos) end
248✔
85
      if mid then
248✔
86
        pos = s:match('%]' .. mid .. '%]()', posb)
8✔
87
        assert(pos, 'syntax error: long string not terminated')
8✔
88
        if #stack == 0 then
8✔
89
          local part = s:sub(posa, pos-1)
×
90
          return part, pos
×
91
        end
92
      else
93
        stack[#stack+1] = c
240✔
94
        pos = pos + 1
240✔
95
      end
96
    else -- close
97
      assert(stack[#stack] == assert(begins[c]), 'syntax error: unbalanced')
240✔
98
      stack[#stack] = nil
240✔
99
      if #stack == 0 then
240✔
100
        local part = s:sub(posa, pos)
208✔
101
        return part, pos+1
208✔
102
      end
103
      pos = pos + 1
32✔
104
    end
105
  end
106
end
107
M.match_bracketed = match_bracketed
8✔
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
32✔
114
  if s:sub(pos, pos+1) ~= '--' then
48✔
115
    return nil, pos
×
116
  end
117
  pos = pos + 2
32✔
118
  local partt, post = match_string(s, pos)
32✔
119
  if partt then
32✔
120
    return '--' .. partt, post
32✔
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}
8✔
130
local is_compare = {['>']=true, ['<']=true, ['~']=true}
8✔
131
local function match_expression(s, pos)
132
  pos = pos or 1
456✔
133
  local _
134
  local posa = pos
456✔
135
  local lastident
136
  local poscs, posce
137
  while pos do
1,200✔
138
    local c = s:sub(pos,pos)
1,184✔
139
    if c == '"' or c == "'" or c == '[' and s:find('^[=%[]', pos+1) then
1,184✔
140
      local part; part, pos = match_string(s, pos)
12✔
141
      assert(part, 'syntax error')
8✔
142
    elseif c == '-' and s:sub(pos+1,pos+1) == '-' then
1,184✔
143
      -- note: handle adjacent comments in loop to properly support
144
      -- backtracing (poscs/posce).
145
      poscs = pos
16✔
146
      while s:sub(pos,pos+1) == '--' do
48✔
147
        local part; part, pos = match_comment(s, pos)
24✔
148
        assert(part)
16✔
149
        pos = s:match('^%s*()', pos)
16✔
150
        posce = pos
16✔
151
      end
152
    elseif c == '(' or c == '{' or c == '[' then
1,160✔
153
      _, pos = match_bracketed(s, pos)
72✔
154
    elseif c == '=' and s:sub(pos+1,pos+1) == '=' then
1,124✔
155
      pos = pos + 2  -- skip over two-char op containing '='
16✔
156
    elseif c == '=' and is_compare[s:sub(pos-1,pos-1)] then
1,100✔
157
      pos = pos + 1  -- skip over two-char op containing '='
8✔
158
    elseif c:match'^[%)%}%];,=]' then
1,088✔
159
      local part = s:sub(posa, pos-1)
168✔
160
      return part, pos
168✔
161
    elseif c:match'^[%w_]' then
920✔
162
      local newident,newpos = s:match('^([%w_]+)()', pos)
920✔
163
      if pos ~= posa and not wordop[newident] then -- non-first ident
920✔
164
        local pose = ((posce == pos) and poscs or pos) - 1
496✔
165
        while s:match('^%s', pose) do pose = pose - 1 end
816✔
166
        local ce = s:sub(pose,pose)
496✔
167
        if ce:match'[%)%}\'\"%]]' or
496✔
168
           ce:match'[%w_]' and not wordop[lastident]
472✔
169
        then
170
          local part = s:sub(posa, pos-1)
272✔
171
          return part, pos
272✔
172
        end
173
      end
174
      lastident, pos = newident, newpos
648✔
175
    else
176
      pos = pos + 1
×
177
    end
178
    pos = s:find('[%(%{%[%)%}%]\"\';,=%w_%-]', pos)
744✔
179
  end
180
  local part = s:sub(posa, #s)
16✔
181
  return part, #s+1
16✔
182
end
183
M.match_expression = match_expression
8✔
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
296✔
191
  local list = {}
296✔
UNCOV
192
  while 1 do
308✔
193
    local c = #list == 0 and '^' or '^%s*,%s*'
616✔
194
    local item, post = s:match(c .. '([%a_][%w_]*)%s*()', pos)
616✔
195
    if item then pos = post else break end
616✔
196
    list[#list+1] = item
320✔
197
  end
198
  return list, pos
296✔
199
end
200
M.match_namelist = match_namelist
8✔
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
304✔
208
  local list = {}
304✔
UNCOV
209
  while 1 do
352✔
210
    if #list ~= 0 then
704✔
211
      local post = s:match('^%s*,%s*()', pos)
400✔
212
      if post then pos = post else break end
400✔
213
    end
214
    local item; item, pos = match_expression(s, pos)
600✔
215
    assert(item, 'syntax error')
400✔
216
    list[#list+1] = item
400✔
217
  end
218
  return list, pos
304✔
219
end
220
M.match_explist = match_explist
8✔
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
224✔
232
  local posa = 1
224✔
233
  local sret = ''
224✔
UNCOV
234
  while 1 do
128✔
235
    pos = s:find('[%-\'\"%[]', pos)
256✔
236
    if not pos then break end
256✔
237
    if s:match('^%-%-', pos) then
32✔
238
      local exp = s:sub(posa, pos-1)
16✔
239
      if #exp > 0 then sret = sret .. (f('e', exp) or exp) end
24✔
240
      local comment; comment, pos = match_comment(s, pos)
24✔
241
      sret = sret .. (f('c', assert(comment)) or comment)
24✔
242
      posa = pos
16✔
243
    else
244
      local posb = s:find('^[\'\"%[]', pos)
16✔
245
      local str
246
      if posb then str, pos = match_string(s, posb) end
24✔
247
      if str then
16✔
248
        local exp = s:sub(posa, posb-1)
16✔
249
        if #exp > 0 then sret = sret .. (f('e', exp) or exp) end
20✔
250
        sret = sret .. (f('s', str) or str)
24✔
251
        posa = pos
16✔
252
      else
253
        pos = pos + 1
×
254
      end
255
    end
256
  end
257
  local exp = s:sub(posa)
224✔
258
  if #exp > 0 then sret = sret .. (f('e', exp) or exp) end
336✔
259
  return sret
224✔
260
end
261
M.gsub = gsub
8✔
262

263

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