@@ -80,49 +80,96 @@ local function getline (str, pos)
8080 return lno , col
8181end
8282
83- local function massage_ast (tree , doc )
83+ local function ast_from_parse_tree (tree , doc , depth )
8484 if type (tree ) == " string" then
8585 return tree
8686 end
87+
8788 if tree .pos then
8889 tree .lno , tree .col = getline (doc , tree .pos )
8990 tree .pos = nil
9091 end
91- SU .debug (" inputter" , " Processing ID:" , tree .id )
92- if false or tree .id == " comment" then
93- SU .debug (" inputter" , " Discarding comment:" , pl .stringx .strip (tree [1 ]))
94- return {}
92+
93+ local sep -- luacheck: ignore 211
94+ if SU .debugging (" inputter" ) then
95+ depth = depth + 1
96+ sep = (" " ):rep (depth )
97+ end
98+ SU .debug (" inputter" , sep and (sep .. " Processing ID:" ), tree .id )
99+
100+ local res
101+ if tree .id == " comment" then
102+ -- Drop comments
103+ SU .debug (" inputter" , sep and (sep .. " Discarding comment" ))
104+ res = {}
95105 elseif
96106 false
97107 or tree .id == " document"
98108 or tree .id == " braced_content"
99109 or tree .id == " passthrough_content"
100110 or tree .id == " braced_passthrough_content"
101111 or tree .id == " env_passthrough_content"
102- then
103- SU .debug (" inputter" , " Re-massage subtree" , tree .id )
104- return massage_ast (tree [1 ], doc )
105- elseif
106- false
107112 or tree .id == " text"
108113 or tree .id == " passthrough_text"
109114 or tree .id == " braced_passthrough_text"
110115 or tree .id == " env_passthrough_text"
111116 then
112- SU .debug (" inputter" , " - Collapse subtree" )
113- return tree [1 ]
114- elseif false or tree .id == " content" or tree .id == " environment" or tree .id == " command" then
115- SU .debug (" inputter" , " - Massage in place" , tree .id )
116- for key , val in ipairs (tree ) do
117- SU .debug (" inputter" , " -" , val .id )
118- if val .id == " content" then
119- SU .splice (tree , key , key , massage_ast (val , doc ))
120- elseif val .id then -- requiring an id discards nodes with no content such as comments
121- tree [key ] = massage_ast (val , doc )
117+ -- These nodes have only one child, which needs recursion.
118+ SU .debug (" inputter" , sep and (sep .. " Massaging a node" ))
119+ res = ast_from_parse_tree (tree [1 ], doc , depth )
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
122+ -- These nodes have multiple children, which need recursion.
123+ SU .debug (" inputter" , sep and (sep .. " Processing command" ), tree .command , # tree , " subtrees" )
124+ local newtree = { -- I don't think we can avoid a shallow copy here
125+ command = tree .command ,
126+ options = tree .options ,
127+ id = tree .id ,
128+ lno = tree .lno ,
129+ col = tree .col ,
130+ }
131+ for _ , node in ipairs (tree ) do
132+ if type (node ) == " table" then
133+ SU .debug (" inputter" , sep and (sep .. " -" ), node .id or " table" )
134+ local ast_node = ast_from_parse_tree (node , doc , depth )
135+ if type (ast_node ) == " table" and not ast_node .id then
136+ SU .debug (" inputter" , sep and (sep .. " -" ), " Collapsing subtree" )
137+ -- Comments can an empty table, skip them
138+ if # ast_node > 0 then
139+ -- Simplify the tree if it's just a plain list
140+ for _ , child in ipairs (ast_node ) do
141+ if type (child ) ~= " table" or child .id or # child > 0 then
142+ table.insert (newtree , child )
143+ end
144+ end
145+ end
146+ else
147+ table.insert (newtree , ast_node )
148+ end
122149 end
150+ -- Non table nodes are skipped (e.g. extraneous text from 'raw' commands)
123151 end
124- return tree
152+ res = newtree
153+ elseif tree .id == " content" then
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" )
157+ local newtree = {} -- I don't think we can avoid a shallow copy here
158+ for i , node in ipairs (tree ) do
159+ SU .debug (" inputter" , sep and (sep .. " -" ), node .id )
160+ newtree [i ] = ast_from_parse_tree (node , doc , depth )
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
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
125170 end
171+ SU .debug (" inputter" , sep and (sep .. " Returning a" ), type (res ) == " table" and res .id or " string" )
172+ return res
126173end
127174
128175function inputter :parse (doc )
@@ -133,24 +180,27 @@ function inputter:parse (doc)
133180%s thrown from document beginning]] ):format (pl .stringx .indent (result , 6 )))
134181 end
135182 resetCache ()
136- local top = massage_ast (result [1 ], doc )
183+ local top = ast_from_parse_tree (result [1 ], doc , 0 )
137184 local tree
138185 -- Content not part of a tagged command could either be part of a document
139186 -- fragment or junk (e.g. comments, whitespace) outside of a document tag. We
140187 -- need to either capture the document tag only or decide this is a fragment
141188 -- and wrap it in a document tag.
142- for _ , leaf in ipairs (top ) do
143- if leaf .command and (leaf .command == " document" or leaf .command == " sile" ) then
144- tree = leaf
145- break
189+ if top .command == " document" or top .command == " sile" then
190+ tree = top
191+ elseif type (top ) == " table" then
192+ for _ , leaf in ipairs (top ) do
193+ if leaf .command and (leaf .command == " document" or leaf .command == " sile" ) then
194+ tree = leaf
195+ break
196+ end
146197 end
147198 end
148199 -- In the event we didn't isolate a top level document tag above, assume this
149200 -- is a fragment and wrap it in one.
150201 if not tree then
151202 tree = { top , command = " document" }
152203 end
153- -- SU.dump(tree)
154204 return { tree }
155205end
156206
0 commit comments