|
| 1 | +local json = require 'pandoc.json' |
| 2 | + |
| 3 | +local FieldType = { |
| 4 | + COMMAND = 1, |
| 5 | + HANDLE = 2, |
| 6 | + PARAMETER = 3, |
| 7 | +} |
| 8 | + |
| 9 | +-- Extracts the field type from a cell of the form `========== [TYPE] ==========` |
| 10 | +function getFieldTypeFromSpan(text) |
| 11 | + -- ^ : Start of string |
| 12 | + -- %=+ : One or more "=" (the % escapes the = character) |
| 13 | + -- %s* : Zero or more spaces |
| 14 | + -- (.-) : Capture the shortest possible sequence of any characters (the target text) |
| 15 | + -- %s* : Zero or more spaces |
| 16 | + -- %=+ : One or more "=" |
| 17 | + -- $ : End of string |
| 18 | + local extracted = text:match("^%=+%s*(.-)%s*%=+$") |
| 19 | + |
| 20 | + if extracted == "Handles" then |
| 21 | + return FieldType.HANDLE |
| 22 | + elseif extracted == "Parameters" then |
| 23 | + return FieldType.PARAMETER |
| 24 | + else |
| 25 | + return nil |
| 26 | + end |
| 27 | +end |
| 28 | + |
| 29 | +-- Extracts a JSON representation of a command table from Part 3. |
| 30 | +-- |
| 31 | +-- Must have three columns: Type, Name, and Description |
| 32 | +-- Colspan separators can either be 'Handles' or 'Parameters' |
| 33 | +-- Any fields that appear before the first colspan separator are assumed to be command-code fields. |
| 34 | +function extractCommandFields(tbl) |
| 35 | + local caption = pandoc.utils.stringify(tbl.caption.long) |
| 36 | + if #tbl.head.rows ~= 1 then |
| 37 | + print(string.format("Table '%s' has %d rows, expected 1", caption, #tbl.head.rows)) |
| 38 | + return nil |
| 39 | + end |
| 40 | + |
| 41 | + local header = tbl.head.rows[1] |
| 42 | + |
| 43 | + if #header.cells ~= 3 then |
| 44 | + print(string.format("Table '%s' has %d header cells, expected 3", caption, #header.cells)) |
| 45 | + return nil |
| 46 | + end |
| 47 | + |
| 48 | + local header_1 = pandoc.utils.stringify(header.cells[1].contents) |
| 49 | + local header_2 = pandoc.utils.stringify(header.cells[2].contents) |
| 50 | + local header_3 = pandoc.utils.stringify(header.cells[3].contents) |
| 51 | + |
| 52 | + if header_1 ~= "Type" or header_2 ~= "Name" or header_3 ~= "Description" then |
| 53 | + print(string.format("Table '%s' has malformed header cells: '%s', '%s', '%s'. Expected 'Type', 'Name' and 'Description'", |
| 54 | + caption, header_1, header_2, header_3)) |
| 55 | + return nil |
| 56 | + end |
| 57 | + |
| 58 | + if #tbl.bodies ~= 1 then |
| 59 | + print(string.format("Table '%s' has %d bodies, expected 1", caption, #tbl.bodies)) |
| 60 | + return nil |
| 61 | + end |
| 62 | + |
| 63 | + local fields = {command_fields = {}, handle_fields = {}, parameter_fields = {}} |
| 64 | + local current_field_type = FieldType.COMMAND |
| 65 | + |
| 66 | + for i, row in ipairs(tbl.bodies[1].body) do |
| 67 | + if #row.cells == 1 then |
| 68 | + -- We're in a colspan which indicates the type of fields that follow. |
| 69 | + |
| 70 | + local span_contents = pandoc.utils.stringify(row.cells[1].contents) |
| 71 | + current_field_type = getFieldTypeFromSpan(span_contents) |
| 72 | + |
| 73 | + if current_field_type == nil then |
| 74 | + print(string.format("Table '%s' has malformed span: '%s', expected a valid field type ('Handles' or 'Parameters')", |
| 75 | + caption, span_contents)) |
| 76 | + return nil |
| 77 | + end |
| 78 | + elseif #row.cells == 3 then |
| 79 | + type_cell = pandoc.utils.stringify(row.cells[1].contents) |
| 80 | + name_cell = pandoc.utils.stringify(row.cells[2].contents) |
| 81 | + desc_cell = pandoc.utils.stringify(row.cells[3].contents) |
| 82 | + |
| 83 | + local field = {type = type_cell, name = name_cell, description = desc_cell} |
| 84 | + |
| 85 | + if current_field_type == FieldType.COMMAND then |
| 86 | + table.insert(fields.command_fields, field) |
| 87 | + elseif current_field_type == FieldType.HANDLE then |
| 88 | + table.insert(fields.handle_fields, field) |
| 89 | + elseif current_field_type == FieldType.PARAMETER then |
| 90 | + table.insert(fields.parameter_fields, field) |
| 91 | + end |
| 92 | + else |
| 93 | + print(string.format("Table '%s' row %d has %d columns, expected 1", caption, i, #row.cells)) |
| 94 | + return nil |
| 95 | + end |
| 96 | + end |
| 97 | + |
| 98 | + return fields |
| 99 | +end |
| 100 | + |
| 101 | +-- Takes a table of entries whose captions are of the form "[Command name] [Command/Response]" |
| 102 | +-- and collates the command and response fields. |
| 103 | +function collateCommandsAndResponses(data_entries) |
| 104 | + local collated = {} |
| 105 | + |
| 106 | + for _, table_data in ipairs(data_entries) do |
| 107 | + local command_name, type = string.match(table_data["caption"], "(%w+) (%w+)") |
| 108 | + if collated[command_name] == nil then |
| 109 | + collated[command_name] = {} |
| 110 | + end |
| 111 | + |
| 112 | + if type == "Command" then |
| 113 | + collated[command_name]["command"] = table_data["fields"] |
| 114 | + elseif type == "Response" then |
| 115 | + collated[command_name]["response"] = table_data["fields"] |
| 116 | + else |
| 117 | + print(string.format("Table '%s' has malformed type, expected 'Command' or 'Response'", table_data["caption"])) |
| 118 | + end |
| 119 | + end |
| 120 | + |
| 121 | + return collated |
| 122 | +end |
| 123 | + |
| 124 | +function Pandoc(doc) |
| 125 | + local table_fields = {} |
| 126 | + |
| 127 | + for _, block in ipairs(doc.blocks) do |
| 128 | + if block.t == "Table" then |
| 129 | + local fields = extractCommandFields(block) |
| 130 | + if fields ~= nil then |
| 131 | + table.insert(table_fields, {caption = pandoc.utils.stringify(block.caption.long), fields = fields}) |
| 132 | + end |
| 133 | + end |
| 134 | + end |
| 135 | + |
| 136 | + print(string.format("Extracted data from %d tables", #table_fields)) |
| 137 | + print("Collating tables") |
| 138 | + |
| 139 | + local collated = collateCommandsAndResponses(table_fields) |
| 140 | + |
| 141 | + -- Overwrite the entire document with a single code block containing the JSON |
| 142 | + local json_string = json.encode(collated) |
| 143 | + return pandoc.Pandoc({pandoc.CodeBlock(json_string)}) |
| 144 | +end |
0 commit comments