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

sile-typesetter / sile / 7246678005

18 Dec 2023 10:19AM UTC coverage: 67.096% (-7.5%) from 74.62%
7246678005

push

github

web-flow
chore(deps): Bump actions/upload-artifact from 3 to 4 (#1940)

Bumps [actions/upload-artifact](https://github.com/actions/upload-artifact) from 3 to 4.
- [Release notes](https://github.com/actions/upload-artifact/releases)
- [Commits](https://github.com/actions/upload-artifact/compare/v3...v4)

---
updated-dependencies:
- dependency-name: actions/upload-artifact
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>

10583 of 15773 relevant lines covered (67.1%)

3150.6 hits per line

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

95.14
/inputters/sil.lua
1
local base = require("inputters.base")
64✔
2

3
local epnf = require("epnf")
64✔
4

5
local inputter = pl.class(base)
64✔
6
inputter._name = "sil"
64✔
7

8
inputter.order = 50
64✔
9

10
inputter.appropriate = function (round, filename, doc)
11
  if round == 1 then
60✔
12
    return filename:match(".sil$")
57✔
13
  elseif round == 2 then
3✔
14
    local sniff = doc:sub(1, 100)
2✔
15
    local promising = sniff:match("\\begin") or sniff:match("\\document") or sniff:match("\\sile")
2✔
16
    return promising and inputter.appropriate(3, filename, doc) or false
3✔
17
  elseif round == 3 then
1✔
18
    local _parser = epnf.define(inputter._grammar)
1✔
19
    local status, _ = pcall(epnf.parsestring, _parser, doc)
1✔
20
    return status
1✔
21
  end
22
end
23

24
local bits = SILE.parserBits
64✔
25

26

27
inputter.passthroughCommands = {
64✔
28
  ftl = true,
29
  lua = true,
30
  math = true,
31
  raw = true,
32
  script = true,
33
  sil = true,
34
  use = true,
35
  xml = true
×
36
}
64✔
37

38
function inputter:_init ()
64✔
39
  -- Save time when parsing strings by only setting up the grammar once per
40
  -- instantiation then re-using it on every use.
41
  self._parser = self:rebuildParser()
110✔
42
  base._init(self)
55✔
43
end
44

45
-- luacheck: push ignore
46
---@diagnostic disable: undefined-global, unused-local, lowercase-global
47
function inputter._grammar (_ENV)
64✔
48
  local isPassthrough = function (_, _, command)
49
    return inputter.passthroughCommands[command] or false
1,344✔
50
  end
51
  local isNotPassthrough = function (...)
52
    return not isPassthrough(...)
1,296✔
53
  end
54
  local isMatchingEndEnv = function (a, b, thisCommand, lastCommand)
55
    return thisCommand == lastCommand
108✔
56
  end
57
  local _ = WS^0
56✔
58
  local eol = S"\r\n"
56✔
59
  local specials = S"{}%\\"
56✔
60
  local escaped_specials = P"\\" * specials
56✔
61
  local unescapeSpecials = function (str)
62
    return str:gsub('\\([{}%%\\])', '%1')
583✔
63
  end
64
  local myID = C(bits.silidentifier) / 1
56✔
65
  local cmdID = myID - P"beign" - P"end"
56✔
66
  local wrapper = function (a) return type(a)=="table" and a or {} end
618✔
67
  local parameters = (P"[" * bits.parameters * P"]")^-1 / wrapper
56✔
68
  local comment = (
69
      P"%" *
56✔
70
      P(1-eol)^0 *
56✔
71
      eol^-1
56✔
72
    ) / ""
56✔
73

74
  START "document"
56✔
75
  document = V"texlike_stuff" * EOF"Unexpected character at end of input"
112✔
76
  texlike_stuff = Cg(
112✔
77
      V"environment" +
56✔
78
      comment +
56✔
79
      V"texlike_text" +
56✔
80
      V"texlike_braced_stuff" +
56✔
81
      V"texlike_command"
56✔
82
    )^0
56✔
83
  passthrough_stuff = C(Cg(
168✔
84
      V"passthrough_text" +
56✔
85
      V"passthrough_debraced_stuff"
56✔
86
    )^0)
112✔
87
  passthrough_env_stuff = Cg(
112✔
88
      V"passthrough_env_text"
56✔
89
    )^0
56✔
90
  texlike_text = C((1 - specials + escaped_specials)^1) / unescapeSpecials
56✔
91
  passthrough_text = C((1-S("{}"))^1)
56✔
92
  passthrough_env_text = C((1 - (P"\\end{" * Cmt(cmdID * Cb"command", isMatchingEndEnv) * P"}"))^1)
56✔
93
  texlike_braced_stuff = P"{" * V"texlike_stuff" * ( P"}" + E("} expected") )
112✔
94
  passthrough_braced_stuff = P"{" * V"passthrough_stuff" * ( P"}" + E("} expected") )
112✔
95
  passthrough_debraced_stuff = C(V"passthrough_braced_stuff")
56✔
96
  texlike_command = (
×
97
      P"\\" *
56✔
98
      Cg(cmdID, "command") *
56✔
99
      Cg(parameters, "options") *
56✔
100
      (
×
101
        (Cmt(Cb"command", isPassthrough) * V"passthrough_braced_stuff") +
56✔
102
        (Cmt(Cb"command", isNotPassthrough) * V"texlike_braced_stuff")
56✔
103
      )^0
56✔
104
    )
56✔
105
  local notpass_end =
106
      P"\\end{" *
56✔
107
      ( Cmt(cmdID * Cb"command", isMatchingEndEnv) + E"Environment mismatch") *
112✔
108
      ( P"}" * _ ) + E"Environment begun but never ended"
112✔
109
  local pass_end =
110
      P"\\end{" *
56✔
111
      ( cmdID * Cb"command" ) *
56✔
112
      ( P"}" * _ ) + E"Environment begun but never ended"
112✔
113
  environment =
×
114
    P"\\begin" *
56✔
115
    Cg(parameters, "options") *
56✔
116
    P"{" *
56✔
117
    Cg(cmdID, "command") *
56✔
118
    P"}" *
56✔
119
    (
×
120
      (Cmt(Cb"command", isPassthrough) * V"passthrough_env_stuff" * pass_end) +
56✔
121
      (Cmt(Cb"command", isNotPassthrough) * V"texlike_stuff" * notpass_end)
56✔
122
    )
56✔
123
end
124
-- luacheck: pop
125
---@diagnostic enable: undefined-global, unused-local, lowercase-global
126

127
local linecache = {}
64✔
128
local lno, col, lastpos
129
local function resetCache ()
130
  lno = 1
55✔
131
  col = 1
55✔
132
  lastpos = 0
55✔
133
  linecache = { { lno = 1, pos = 1} }
55✔
134
end
135

136
local function getline (str, pos)
137
  local start = 1
2,249✔
138
  lno = 1
2,249✔
139
  if pos > lastpos then
2,249✔
140
    lno = linecache[#linecache].lno
1,520✔
141
    start = linecache[#linecache].pos + 1
1,520✔
142
    col = 1
1,520✔
143
  else
144
    for j = 1, #linecache-1 do
10,626✔
145
      if linecache[j+1].pos >= pos then
10,626✔
146
        lno = linecache[j].lno
729✔
147
        col = pos - linecache[j].pos
729✔
148
        return lno, col
729✔
149
      end
150
    end
151
  end
152
  for i = start, pos do
43,368✔
153
    if string.sub( str, i, i ) == "\n" then
83,696✔
154
      lno = lno + 1
905✔
155
      col = 1
905✔
156
      linecache[#linecache+1] = { pos = i, lno = lno }
905✔
157
      lastpos = i
905✔
158
    end
159
    col = col + 1
41,848✔
160
  end
161
  return lno, col
1,520✔
162
end
163

164
local function massage_ast (tree, doc)
165
  -- Sort out pos
166
  if type(tree) == "string" then return tree end
3,096✔
167
  if tree.pos then
2,249✔
168
    tree.lno, tree.col = getline(doc, tree.pos)
4,498✔
169
  end
170
  if tree.id == "document"
2,249✔
171
      or tree.id == "texlike_braced_stuff"
2,249✔
172
      or tree.id == "passthrough_stuff"
2,136✔
173
      or tree.id == "passthrough_braced_stuff"
2,110✔
174
      or tree.id == "passthrough_env_stuff"
2,084✔
175
    then
176
      return massage_ast(tree[1], doc)
187✔
177
  end
178
  if tree.id == "texlike_text"
2,062✔
179
    or tree.id == "passthrough_text"
1,480✔
180
    or tree.id == "passthrough_env_text"
1,480✔
181
    then
182
      return tree[1]
604✔
183
  end
184
  for key, val in ipairs(tree) do
4,312✔
185
    if val.id == "texlike_stuff" then
2,854✔
186
      SU.splice(tree, key, key, massage_ast(val, doc))
528✔
187
    else
188
      tree[key] = massage_ast(val, doc)
5,356✔
189
    end
190
  end
191
  return tree
1,458✔
192
end
193

194
function inputter:rebuildParser ()
64✔
195
  return epnf.define(self._grammar)
55✔
196
end
197

198
function inputter:parse (doc)
64✔
199
  local parsed = epnf.parsestring(self._parser, doc)[1]
110✔
200
  if not parsed then
55✔
201
    return SU.error("Unable to parse input document to an AST tree")
×
202
  end
203
  resetCache()
55✔
204
  local top = massage_ast(parsed, doc)
55✔
205
  local tree
206
  -- Content not part of a tagged command could either be part of a document
207
  -- fragment or junk (e.g. comments, whitespace) outside of a document tag. We
208
  -- need to either capture the document tag only or decide this is a fragment
209
  -- and wrap it in a document tag.
210
  for _, leaf in ipairs(top) do
55✔
211
    if leaf.command and (leaf.command == "document" or leaf.command == "sile") then
55✔
212
        tree = leaf
55✔
213
        break
55✔
214
    end
215
  end
216
  -- In the event we didn't isolate a top level document tag above, assume this
217
  -- is a fragment and wrap it in one.
218
  if not tree then
55✔
219
    tree = { top, command = "document" }
×
220
  end
221
  return { tree }
55✔
222
end
223

224
return inputter
64✔
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