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

sile-typesetter / sile / 6915746301

18 Nov 2023 07:02PM UTC coverage: 56.433% (-12.3%) from 68.751%
6915746301

push

github

web-flow
Merge 8b3fdc301 into f64e235fa

8729 of 15468 relevant lines covered (56.43%)

932.75 hits per line

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

66.95
/core/tracestack.lua
1
-- Represents a stack of stack frame objects,
2
-- which describe the call-stack stack of the currently processed document.
3
-- Stack frames are stored contiguously, treating this object as an array.
4
-- Most recent and relevant stack frames are in higher indices, up to #traceStack.
5
-- Do not manipulate the stack directly, use provided push<Type> and pop methods.
6
-- There are different types of stack frames, see pushFrame for more details.
7
local traceStack = pl.class({
68✔
8
    -- Stores the frame which was last popped. Reset after a push.
9
    -- Helps to further specify current location in the processed document.
10
    afterFrame = nil,
11
  })
12

13
traceStack.defaultFrame = pl.class({
68✔
14

15
    location = function (self, relative)
16
      local str = ""
4✔
17
      if self.file and not relative then
4✔
18
        str = str .. self.file .. ":"
3✔
19
      end
20
      if self.lno then
4✔
21
        str = str .. self.lno .. ":"
1✔
22
        if self.col then
1✔
23
          str = str .. self.col .. ":"
1✔
24
        end
25
      end
26
      str = str .. (str:len() > 0 and " " or "") .. "in "
8✔
27
      str = str .. tostring(self)
8✔
28
      return str
4✔
29
    end,
30

31
    __tostring = function (self)
32
      self.file = nil
×
33
      self.lno = nil
×
34
      self.col = nil
×
35
      return #self > 0 and tostring(self) or ""
×
36
    end
37
  })
34✔
38

39
traceStack.documentFrame = pl.class(traceStack.defaultFrame)
68✔
40

41
local function oneline (str)
42
  return str:gsub("\n", "␤"):gsub("\r", "␍"):gsub("\f", "␊"):gsub("\a", "␇"):gsub("\b", "␈"):gsub("\t", "␉"):gsub("\v", "␋")
2✔
43
end
44

45
function traceStack.documentFrame:_init (file, snippet)
68✔
46
  self.command = "document"
40✔
47
  self.file = file
40✔
48
  self.snippet = snippet
40✔
49
end
50

51
function traceStack.documentFrame:__tostring ()
68✔
52
  return "<snippet>:\n\t\t[[" .. oneline(self.snippet) .. "]]"
4✔
53
end
54

55
traceStack.commandFrame = pl.class(traceStack.defaultFrame)
68✔
56

57
function traceStack.commandFrame:_init (command, content, options)
68✔
58
  self.command = command
429✔
59
  self.file = content.file or SILE.currentlyProcessingFile
429✔
60
  self.lno = content.lno
429✔
61
  self.col = content.col
429✔
62
  self.options = options or {}
429✔
63
end
64

65
function traceStack.commandFrame:__tostring ()
68✔
66
  local opts = pl.tablex.size(self.options) == 0 and "" or
4✔
67
    pl.pretty.write(self.options, ""):gsub("^{", "["):gsub("}$", "]")
×
68
  return "\\" .. tostring(self.command) .. opts
2✔
69
end
70

71
traceStack.contentFrame = pl.class(traceStack.commandFrame)
68✔
72

73
function traceStack.contentFrame:_init (command, content)
68✔
74
  self:super(command, content, content.options)
×
75
end
76

77
traceStack.textFrame = pl.class(traceStack.defaultFrame)
68✔
78

79
function traceStack.textFrame:_init (text)
68✔
80
  self.text = text
148✔
81
end
82

83
function traceStack.textFrame:__tostring ()
68✔
84
  if self.text:len() > 20 then
×
85
    self.text = luautf8.sub(self.text, 1, 18) .. "…"
×
86
  end
87
  self.text = oneline(self.text)
×
88
  return '"' .. self.text .. '"'
×
89
end
90

91
local function formatTraceLine (string)
92
  local prefix = "\t"
×
93
  return prefix .. string .. "\n"
×
94
end
95

96
-- Push a document processing run (input method) onto the stack
97
function traceStack:pushDocument(file, doc)
34✔
98
  if type(doc) == "table" then doc = tostring(doc) end
40✔
99
  local snippet = doc:sub(1, 100)
40✔
100
  local frame = traceStack.documentFrame(file, snippet)
40✔
101
  return self:pushFrame(frame)
40✔
102
end
103

104
-- Push a command frame on to the stack to record the execution trace for debugging.
105
-- Carries information about the command call, not the command itself.
106
-- Must be popped with `pop(returnOfPush)`.
107
function traceStack:pushCommand(command, content, options)
34✔
108
  if not command then
429✔
109
    SU.warn("Command should be specified for SILE.traceStack:pushCommand", true)
×
110
  end
111
  if type(content) == "function" then content = {} end
429✔
112
  local frame = traceStack.commandFrame(command, content, options)
429✔
113
  return self:pushFrame(frame)
429✔
114
end
115

116
-- Push a command frame on to the stack to record the execution trace for debugging.
117
-- Command arguments are inferred from AST content, any item may be overridden.
118
-- Must be popped with `pop(returnOfPush)`.
119
function traceStack:pushContent(content, command)
34✔
120
  if type(content) ~= "table" then
×
121
    SU.warn("Content parameter of SILE.traceStack:pushContent must be a table", true)
×
122
  end
123
  command = command or content.command
×
124
  if not command then
×
125
    SU.warn("Command should be specified or inferable for SILE.traceStack:pushContent", true)
×
126
  end
127
  local frame = traceStack.contentFrame(command, content)
×
128
  return self:pushFrame(frame)
×
129
end
130

131
-- Push a text that is going to get typeset on to the stack to record the execution trace for debugging.
132
-- Must be popped with `pop(returnOfPush)`.
133
function traceStack:pushText(text)
34✔
134
  local frame = traceStack.textFrame(text)
148✔
135
  return self:pushFrame(frame)
148✔
136
end
137

138
-- Internal: Push-pop balance checking ID
139
local lastPushId = 0
34✔
140

141
-- Push complete frame onto the stack.
142
-- Frame is a table with following optional fields:
143
-- .file = string - name of the file from which this originates
144
-- .lno = number - line in the file
145
-- .col = number - column on the line
146
-- .toStringHelper = function() that serializes extended information about the frame BESIDES location
147
function traceStack:pushFrame(frame)
34✔
148
  SU.debug("traceStack", function ()
1,234✔
149
    return string.rep(".", #self) .. "PUSH(" .. frame:location() .. ")"
×
150
  end)
151
  self[#self + 1] = frame
617✔
152
  self.afterFrame = nil
617✔
153
  lastPushId = lastPushId + 1
617✔
154
  frame._pushId = lastPushId
617✔
155
  return lastPushId
617✔
156
end
157

158
-- Pop previously pushed command from the stack.
159
-- Return value of `push` function must be provided as argument to check for balanced usage.
160
function traceStack:pop(pushId)
34✔
161
  if type(pushId) ~= "number" then
617✔
162
    SU.error("SILE.traceStack:pop's argument must be the result value of the corresponding push", true)
×
163
  end
164
  -- First verify that push/pop is balanced
165
  local popped = self[#self]
617✔
166
  if popped._pushId ~= pushId then
617✔
167
    local message = "Unbalanced content push/pop"
×
168
    if SILE.traceback or SU.debugging("traceStack") then
×
169
      message = message .. ". Expected " .. popped.pushId .. " - (" .. popped:location() .. "), got " .. pushId
×
170
    end
171
    SU.warn(message, true)
×
172
  else
173
    -- Correctly balanced: pop the frame
174
    self.afterFrame = popped
617✔
175
    self[#self] = nil
617✔
176
    SU.debug("traceStack", function ()
1,234✔
177
      return string.rep(".", #self) .. "POP(" .. popped:location() .. ")"
×
178
    end)
179
  end
180
end
181

182
-- Returns single line string with location of top most trace frame
183
function traceStack:locationHead()
34✔
184
  local afterFrame = self.afterFrame
4✔
185
  local top = self[#self]
4✔
186
  if not top then
4✔
187
    -- Stack is empty, there is not much we can do
188
    return formatTraceLine(afterFrame and "after " .. afterFrame:location() or SILE.currentlyProcessingFile or "<nowhere>")
×
189
  end
190
  local trace = top:location()
4✔
191
  local locationFrame = top
4✔
192
  -- Not all stack traces have to carry location information.
193
  -- If the first stack trace does not carry it, find a frame which does.
194
  -- Then append it, because its information may be useful.
195
  if not locationFrame.lno then
4✔
196
    for i = #self - 1, 1, -1 do
5✔
197
      if self[i].lno then
2✔
198
        locationFrame = self[i]
×
199
        trace = trace .. " near " .. locationFrame:location(locationFrame.file == top.file)
×
200
        break
201
      end
202
    end
203
  end
204
  -- Print after, if it is in a relevant file
205
  if afterFrame and (not locationFrame or afterFrame.file == locationFrame.file) then
4✔
206
    trace = trace .. " after " .. afterFrame:location(true)
×
207
  end
208
  return trace
4✔
209
end
210

211
-- Returns multiline trace string with locations of each frame up to maxdepth
212
function traceStack:locationTrace(maxdepth)
34✔
213
  local depth = maxdepth or #self
×
214
  local trace = formatTraceLine(self:locationHead())
×
215
  depth = depth - 1
×
216
  if depth > 1 then
×
217
    repeat
218
      trace = trace .. formatTraceLine(self[depth]:location())
×
219
      depth = depth - 1
×
220
    until depth == 1 -- stop at 1 (document) as not useful in trace
×
221
  end
222
  return trace
×
223
end
224

225
return traceStack
34✔
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