Created
January 21, 2020 07:17
-
-
Save castleberrysam/87a64997799499cd4c98e7962988661f to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
--[[ | |
Generates a diagram for bitfields in a register, data type, or data structure. | |
The single arg is a comma separated list. | |
A normal entry is two elements, the first is the field name and the second is the bit width. | |
There are also directives, starting with !, to change the table formatting. | |
!SEP: | |
Inserts a visual break in the row, for grouping fields together into larger objects. | |
!WIDTH <N>: | |
Overrides the visual width to use in the diagram for the field immediately after the directive. | |
Bit numbering will still use the original width. | |
!ROW: | |
Starts a new row in the diagram. The bit numbering for each row is done independently. | |
All rows in the diagram must have the same visual width. | |
The visual width of a field is the same as its bit width, except when the !WIDTH directive is used. | |
!LBL <label>: | |
Prints a label to the left of the diagram, aligned with the current row. | |
Can be used either before or after the !ROW directive, but has to occur at the beginning of a row. | |
--]] | |
function insntbl(args) | |
-- Parse comma separated arguments and directives | |
local argt = {} | |
local arg = {} | |
local numcols = nil | |
local rowlabel = nil | |
local state = 0 | |
for tok in string.gmatch(args, "%s*([^,]*)%s*,") do | |
if string.sub(tok, 1, 1) == "!" then | |
if state ~= 0 then | |
tex.error("Malformed arguments to insntbl") | |
end | |
if tok == "!SEP" then | |
argt[#argt].separate = true | |
elseif tok == "!ROW" then | |
argt[#argt].endrow = true | |
else | |
m = string.match(tok, "!WIDTH%s+(%d+)") | |
if m then | |
numcols = m | |
else | |
m = string.match(tok, "!LBL%s+(.+)") | |
if m then | |
rowlabel = m | |
else | |
tex.error("Unknown or malformed directive to insntbl") | |
end | |
end | |
end | |
elseif state == 0 then | |
arg.label = tok | |
state = 1 | |
else | |
arg.numbits = tok | |
arg.numcols = numcols or arg.numbits | |
arg.rowlabel = rowlabel | |
table.insert(argt, arg) | |
arg = {} | |
numcols = nil | |
rowlabel = nil | |
state = 0 | |
end | |
end | |
argt[#argt].endrow = true | |
-- Calculate indices | |
local base = 0 | |
local width = 0 | |
local index = 0 | |
for i,arg in ipairs(argt) do | |
if index == 0 then | |
base = base + width | |
width = _getwidth(argt, i, false) | |
index = width | |
end | |
index = index - arg.numbits | |
arg.index = base + index | |
if arg.endrow then | |
base = 0 | |
width = 0 | |
end | |
end | |
-- Calculate number of columns | |
local newrow = false | |
local haverowlabels = false | |
local numcols = _getwidth(argt, 1, true) | |
for i,arg in ipairs(argt) do | |
if arg.rowlabel then | |
haverowlabels = true | |
elseif arg.endrow then | |
newrow = true | |
elseif newrow then | |
if _getwidth(argt, i, true) ~= numcols then | |
tex.error("Row length mismatch in insntbl") | |
end | |
newrow = false | |
end | |
end | |
-- Write table start | |
-- Column spec doesn't really matter since multicols will override it | |
local ret = {} | |
table.insert(ret, "\\gap{\\setlength{\\tabcolsep}{0pt}\\begin{tabular}{") | |
if haverowlabels then | |
table.insert(ret, "C") | |
end | |
table.insert(ret, string.rep("B{1}", numcols)) | |
table.insert(ret, "}") | |
-- Precompute string to use for hlines | |
if haverowlabels then | |
hline = string.format("\\cline{2-%d}", numcols+1) | |
else | |
hline = "\\hline " | |
end | |
-- Write rows | |
local rowstart = 1 | |
local state = 0 | |
local i = 1 | |
repeat | |
arg = argt[i] | |
-- Row start and delimiters | |
if i == rowstart then | |
table.insert(ret, hline) | |
if haverowlabels then | |
if arg.rowlabel and state == 0 then | |
table.insert(ret, arg.rowlabel) | |
table.insert(ret, "\\hspace*{6pt}") | |
end | |
table.insert(ret, "&") | |
end | |
else | |
table.insert(ret, "&") | |
end | |
-- Row contents | |
if state == 0 then | |
str = string.format("\\multicolumn{%s}{%sB{%s}|%s}{%s}", | |
arg.numcols, | |
((i == rowstart) and "|") or "", | |
arg.numcols, | |
(arg.separate and "|") or "", | |
arg.label) | |
else | |
str = string.format("\\multicolumn{%s}{R}{%s}", arg.numcols, arg.index) | |
end | |
table.insert(ret, str) | |
-- Row terminator | |
i = i + 1 | |
if arg.endrow then | |
table.insert(ret, "\\\\") | |
if state == 0 then | |
i = rowstart | |
state = 1 | |
else | |
rowstart = i | |
state = 0 | |
end | |
end | |
until i > #argt | |
-- Terminate table and commit | |
table.insert(ret, "\\end{tabular}}%") | |
texsend(table.concat(ret)) | |
end | |
-- Utility function used by insntbl | |
function _getwidth(argt, start, columns) | |
local acc = 0 | |
for i = start, #argt do | |
arg = argt[i] | |
acc = acc + ((columns and arg.numcols) or arg.numbits) | |
if (not columns and arg.separate) or arg.endrow then | |
break | |
end | |
end | |
return acc | |
end | |
-- Utility function for debugging | |
function texsend(str) | |
--texio.write(str) | |
tex.print(str) | |
end |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment