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

sile-typesetter / sile / 11645631468

02 Nov 2024 08:31PM UTC coverage: 66.687% (-2.7%) from 69.34%
11645631468

Pull #2072

github

alerque
refactor(cli): Redo option parsing enabled by mlua v0.10 scopes
Pull Request #2072: Setup Lua module for exporting Rust functions to Lua

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

476 existing lines in 28 files now uncovered.

12087 of 18125 relevant lines covered (66.69%)

5249.73 hits per line

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

89.6
/inputters/sil.lua
1
local base = require("inputters.base")
148✔
2

3
local _variant = "epnf"
148✔
4
local parser
5
local function load_parser ()
6
   parser = require("inputters.sil-" .. _variant)
141✔
7
end
8

9
local inputter = pl.class(base)
148✔
10
inputter._name = "sil"
148✔
11

12
inputter.order = 50
148✔
13

14
inputter.appropriate = function (round, filename, doc)
15
   if not parser then
145✔
16
      load_parser()
141✔
17
   end
18
   if round == 1 then
145✔
19
      return filename:match(".sil$")
142✔
20
   elseif round == 2 then
3✔
21
      local sniff = doc:sub(1, 100)
2✔
22
      local promising = sniff:match("\\begin") or sniff:match("\\document") or sniff:match("\\sile")
2✔
23
      return promising and inputter.appropriate(3, filename, doc) or false
3✔
24
   elseif round == 3 then
1✔
25
      local status, _ = pcall(parser, doc)
1✔
26
      return status
1✔
27
   end
28
end
29

30
function inputter:_init (options)
148✔
31
   options = options or {}
140✔
32
   if options.variant then
140✔
33
      _variant = options.variant
×
34
      load_parser()
×
35
   else
36
      if not parser then
140✔
37
         load_parser()
×
38
      end
39
   end
40
   -- Save time when parsing strings by only setting up the grammar once per
41
   -- instantiation then re-using it on every use.
42
   self._parser = parser
140✔
43
   base._init(self)
140✔
44
end
45

46
local linecache = {}
148✔
47
local lno, col, lastpos
48
local function resetCache ()
49
   lno = 1
140✔
50
   col = 1
140✔
51
   lastpos = 0
140✔
52
   linecache = { { lno = 1, pos = 1 } }
140✔
53
end
54

55
local function getline (str, pos)
56
   local start = 1
4,634✔
57
   lno = 1
4,634✔
58
   if pos > lastpos then
4,634✔
59
      lno = linecache[#linecache].lno
4,415✔
60
      start = linecache[#linecache].pos + 1
4,415✔
61
      col = 1
4,415✔
62
   else
63
      for j = 1, #linecache - 1 do
1,691✔
64
         if linecache[j + 1].pos >= pos then
1,691✔
65
            lno = linecache[j].lno
219✔
66
            col = pos - linecache[j].pos
219✔
67
            return lno, col
219✔
68
         end
69
      end
70
   end
71
   for i = start, pos do
128,644✔
72
      if string.sub(str, i, i) == "\n" then
248,458✔
73
         lno = lno + 1
2,608✔
74
         col = 1
2,608✔
75
         linecache[#linecache + 1] = { pos = i, lno = lno }
2,608✔
76
         lastpos = i
2,608✔
77
      end
78
      col = col + 1
124,229✔
79
   end
80
   return lno, col
4,415✔
81
end
82

83
local function ast_from_parse_tree (tree, doc, depth)
84
   if type(tree) == "string" then
6,546✔
85
      return tree
1,912✔
86
   end
87

88
   if tree.pos then
4,634✔
89
      tree.lno, tree.col = getline(doc, tree.pos)
9,268✔
90
      tree.pos = nil
4,634✔
91
   end
92

93
   local sep -- luacheck: ignore 211
94
   if SU.debugging("inputter") then
9,268✔
95
      depth = depth + 1
×
96
      sep = ("   "):rep(depth)
×
97
   end
98
   SU.debug("inputter", sep and (sep .. "Processing ID:"), tree.id)
4,634✔
99

100
   local res
101
   if tree.id == "comment" then
4,634✔
102
      -- Drop comments
103
      SU.debug("inputter", sep and (sep .. "Discarding comment"))
160✔
104
      res = {}
160✔
105
   elseif
×
106
      false
107
      or tree.id == "document"
4,474✔
108
      or tree.id == "braced_content"
4,474✔
109
      or tree.id == "passthrough_content"
4,138✔
110
      or tree.id == "braced_passthrough_content"
4,103✔
111
      or tree.id == "env_passthrough_content"
4,103✔
112
      or tree.id == "text"
4,070✔
113
      or tree.id == "passthrough_text"
2,226✔
114
      or tree.id == "braced_passthrough_text"
2,226✔
115
      or tree.id == "env_passthrough_text"
2,191✔
116
   then
117
      -- These nodes have only one child, which needs recursion.
118
      SU.debug("inputter", sep and (sep .. "Massaging a node"))
2,316✔
119
      res = ast_from_parse_tree(tree[1], doc, depth)
4,632✔
120
      --res = #res > 1 and not res.id and res or res[1]
121
   elseif false or tree.id == "environment" or tree.id == "command" then
2,158✔
122
      -- These nodes have multiple children, which need recursion.
123
      SU.debug("inputter", sep and (sep .. "Processing command"), tree.command, #tree, "subtrees")
1,502✔
124
      local newtree = { -- I don't think we can avoid a shallow copy here
1,502✔
125
         command = tree.command,
1,502✔
126
         options = tree.options,
1,502✔
127
         id = tree.id,
1,502✔
128
         lno = tree.lno,
1,502✔
129
         col = tree.col,
1,502✔
130
      }
131
      for _, node in ipairs(tree) do
2,145✔
132
         if type(node) == "table" then
643✔
133
            SU.debug("inputter", sep and (sep .. " -"), node.id or "table")
577✔
134
            local ast_node = ast_from_parse_tree(node, doc, depth)
577✔
135
            if type(ast_node) == "table" and not ast_node.id then
577✔
136
               SU.debug("inputter", sep and (sep .. " -"), "Collapsing subtree")
267✔
137
               -- Comments can an empty table, skip them
138
               if #ast_node > 0 then
267✔
139
                  -- Simplify the tree if it's just a plain list
140
                  for _, child in ipairs(ast_node) do
3,200✔
141
                     if type(child) ~= "table" or child.id or #child > 0 then
2,982✔
142
                        table.insert(newtree, child)
2,822✔
143
                     end
144
                  end
145
               end
146
            else
147
               table.insert(newtree, ast_node)
310✔
148
            end
149
         end
150
         -- Non table nodes are skipped (e.g. extraneous text from 'raw' commands)
151
      end
152
      res = newtree
1,502✔
153
   elseif tree.id == "content" then
656✔
154
      -- This node has multiple children, which need recursion
155
      -- And the node itself needs to be replaced with its children
156
      SU.debug("inputter", sep and (sep .. "Massage content node"), #tree, "subtrees")
656✔
157
      local newtree = {} -- I don't think we can avoid a shallow copy here
656✔
158
      for i, node in ipairs(tree) do
4,169✔
159
         SU.debug("inputter", sep and (sep .. " -"), node.id)
3,513✔
160
         newtree[i] = ast_from_parse_tree(node, doc, depth)
7,026✔
161
      end
162
      -- Simplify the tree if it has only one child
163
      res = #newtree == 1 and not newtree.id and newtree[1] or newtree
656✔
164
   elseif tree.id then
×
165
      -- Shouldn't happen, or we missed something
166
      SU.error("Unknown node type: " .. tree.id)
×
167
   else
168
      SU.debug("inputter", sep and (sep .. "Table node"), #tree, "subtrees")
×
169
      res = #tree == 1 and tree[1] or tree
×
170
   end
171
   SU.debug("inputter", sep and (sep .. "Returning a"), type(res) == "table" and res.id or "string")
4,634✔
172
   return res
4,634✔
173
end
174

175
function inputter:parse (doc)
148✔
176
   local status, result = pcall(self._parser, doc)
140✔
177
   if not status then
140✔
178
      return SU.error(([[
×
179
         Unable to parse input document to an AST tree
180

181
         Parser error:
182

183
           %s
184

185
         thrown from document beginning.]]):format(pl.stringx.indent(result, 6)))
×
186
   end
187
   resetCache()
140✔
188
   local top = ast_from_parse_tree(result[1], doc, 0)
140✔
189
   local tree
190
   -- Content not part of a tagged command could either be part of a document
191
   -- fragment or junk (e.g. comments, whitespace) outside of a document tag. We
192
   -- need to either capture the document tag only or decide this is a fragment
193
   -- and wrap it in a document tag.
194
   if top.command == "document" or top.command == "sile" then
140✔
195
      tree = top
3✔
196
   elseif type(top) == "table" then
137✔
197
      for _, leaf in ipairs(top) do
140✔
198
         if leaf.command and (leaf.command == "document" or leaf.command == "sile") then
140✔
199
            tree = leaf
137✔
200
            break
137✔
201
         end
202
      end
203
   end
204
   -- In the event we didn't isolate a top level document tag above, assume this
205
   -- is a fragment and wrap it in one.
206
   if not tree then
140✔
UNCOV
207
      tree = { top, command = "document" }
×
208
   end
209
   return { tree }
140✔
210
end
211

212
return inputter
148✔
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