1e5c31af7Sopenharmony_ci# Copyright 2023-2024 The Khronos Group Inc. 2e5c31af7Sopenharmony_ci# 3e5c31af7Sopenharmony_ci# SPDX-License-Identifier: Apache-2.0 4e5c31af7Sopenharmony_ci 5e5c31af7Sopenharmony_ci"""Utilities for automatic transformation of spec sources. Most of the logic 6e5c31af7Sopenharmony_cihas to do with detecting asciidoc markup or block types that should not be 7e5c31af7Sopenharmony_citransformed (tables, code) and ignoring them. It is very likely there are many 8e5c31af7Sopenharmony_ciasciidoc constructs not yet accounted for in the script, our usage of asciidoc 9e5c31af7Sopenharmony_cimarkup is intentionally somewhat limited. 10e5c31af7Sopenharmony_ci""" 11e5c31af7Sopenharmony_ci 12e5c31af7Sopenharmony_ciimport re 13e5c31af7Sopenharmony_ciimport sys 14e5c31af7Sopenharmony_cifrom reflib import logDiag, logWarn 15e5c31af7Sopenharmony_ci 16e5c31af7Sopenharmony_ci# Vulkan-specific - will consolidate into scripts/ like OpenXR soon 17e5c31af7Sopenharmony_cisys.path.insert(0, 'xml') 18e5c31af7Sopenharmony_ci 19e5c31af7Sopenharmony_cifrom apiconventions import APIConventions 20e5c31af7Sopenharmony_ciconventions = APIConventions() 21e5c31af7Sopenharmony_ci 22e5c31af7Sopenharmony_ci# Start of an asciidoctor conditional 23e5c31af7Sopenharmony_ci# ifdef:: 24e5c31af7Sopenharmony_ci# ifndef:: 25e5c31af7Sopenharmony_ciconditionalStart = re.compile(r'^(ifdef|ifndef)::') 26e5c31af7Sopenharmony_ci 27e5c31af7Sopenharmony_ci# Markup that always ends a paragraph 28e5c31af7Sopenharmony_ci# empty line or whitespace 29e5c31af7Sopenharmony_ci# [block options] 30e5c31af7Sopenharmony_ci# [[anchor]] 31e5c31af7Sopenharmony_ci# // comment 32e5c31af7Sopenharmony_ci# <<<< page break 33e5c31af7Sopenharmony_ci# :attribute-setting 34e5c31af7Sopenharmony_ci# macro-directive::terms 35e5c31af7Sopenharmony_ci# + standalone list item continuation 36e5c31af7Sopenharmony_ci# label:: labelled list - label must be standalone 37e5c31af7Sopenharmony_ciendPara = re.compile(r'^( *|\[.*\]|//.*|<<<<|:.*|[a-z]+::.*|\+|.*::)$') 38e5c31af7Sopenharmony_ci 39e5c31af7Sopenharmony_ci# Special case of markup ending a paragraph, used to track the current 40e5c31af7Sopenharmony_ci# command/structure. This allows for either OpenXR or Vulkan API path 41e5c31af7Sopenharmony_ci# conventions. Nominally it should use the file suffix defined by the API 42e5c31af7Sopenharmony_ci# conventions (conventions.file_suffix), except that XR uses '.txt' for 43e5c31af7Sopenharmony_ci# generated API include files, not '.adoc' like its other includes. 44e5c31af7Sopenharmony_ciincludePat = re.compile( 45e5c31af7Sopenharmony_ci r'include::(?P<directory_traverse>((../){1,4}|\{generated\}/)(generated/)?)(?P<generated_type>[\w]+)/(?P<category>\w+)/(?P<entity_name>[^./]+).adoc[\[][\]]') 46e5c31af7Sopenharmony_ci 47e5c31af7Sopenharmony_ci# Markup that is OK in a contiguous paragraph but otherwise passed through 48e5c31af7Sopenharmony_ci# .anything (except .., which indicates a literal block) 49e5c31af7Sopenharmony_ci# === Section Titles 50e5c31af7Sopenharmony_ci# image::path_to_image[attributes] (apparently a single colon is OK but less idiomatic) 51e5c31af7Sopenharmony_ciendParaContinue = re.compile(r'^(\.[^.].*|=+ .*|image:.*\[.*\])$') 52e5c31af7Sopenharmony_ci 53e5c31af7Sopenharmony_ci# Markup for block delimiters whose contents *should* be reformatted 54e5c31af7Sopenharmony_ci# -- (exactly two) (open block) 55e5c31af7Sopenharmony_ci# **** (4 or more) (sidebar block) 56e5c31af7Sopenharmony_ci# ==== (4 or more) (example block) 57e5c31af7Sopenharmony_ci# ____ (4 or more) (quote block) 58e5c31af7Sopenharmony_ciblockTransform = re.compile(r'^(--|[*=_]{4,})$') 59e5c31af7Sopenharmony_ci 60e5c31af7Sopenharmony_ci# Fake block delimiters for "common" VU statements 61e5c31af7Sopenharmony_ciblockCommonTransform = '// Common Valid Usage\n' 62e5c31af7Sopenharmony_ci 63e5c31af7Sopenharmony_ci# Markup for block delimiters whose contents should *not* be transformed 64e5c31af7Sopenharmony_ci# |=== (3 or more) (table) 65e5c31af7Sopenharmony_ci# ``` (3 or more) (listing block) 66e5c31af7Sopenharmony_ci# //// (4 or more) (comment block) 67e5c31af7Sopenharmony_ci# ---- (4 or more) (listing block) 68e5c31af7Sopenharmony_ci# .... (4 or more) (literal block) 69e5c31af7Sopenharmony_ci# ++++ (4 or more) (passthrough block) 70e5c31af7Sopenharmony_ciblockPassthrough = re.compile(r'^(\|={3,}|[`]{3}|[\-+./]{4,})$') 71e5c31af7Sopenharmony_ci 72e5c31af7Sopenharmony_ci# Markup for introducing lists (hanging paragraphs) 73e5c31af7Sopenharmony_ci# * bullet 74e5c31af7Sopenharmony_ci# ** bullet 75e5c31af7Sopenharmony_ci# -- bullet 76e5c31af7Sopenharmony_ci# . bullet 77e5c31af7Sopenharmony_ci# :: bullet (no longer supported by asciidoctor 2) 78e5c31af7Sopenharmony_ci# {empty}:: bullet 79e5c31af7Sopenharmony_ci# 1. list item 80e5c31af7Sopenharmony_ci# <1> source listing callout 81e5c31af7Sopenharmony_cibeginBullet = re.compile(r'^ *([-*.]+|\{empty\}::|::|[0-9]+[.]|<([0-9]+)>) ') 82e5c31af7Sopenharmony_ci 83e5c31af7Sopenharmony_ciclass TransformState: 84e5c31af7Sopenharmony_ci """State machine for transforming documents. 85e5c31af7Sopenharmony_ci 86e5c31af7Sopenharmony_ci Represents the state of the transform operation""" 87e5c31af7Sopenharmony_ci def __init__(self): 88e5c31af7Sopenharmony_ci self.blockStack = [ None ] 89e5c31af7Sopenharmony_ci """The last element is a line with the asciidoc block delimiter that is 90e5c31af7Sopenharmony_ci currently in effect, such as '--', '----', '****', '====', or '++++'. 91e5c31af7Sopenharmony_ci This affects whether or not the block contents should be transformed.""" 92e5c31af7Sopenharmony_ci self.transformStack = [ True ] 93e5c31af7Sopenharmony_ci """The last element is True or False if the current blockStack contents 94e5c31af7Sopenharmony_ci should be transformed.""" 95e5c31af7Sopenharmony_ci self.vuStack = [ False ] 96e5c31af7Sopenharmony_ci """the last element is True or False if the current blockStack contents 97e5c31af7Sopenharmony_ci are an explicit Valid Usage block.""" 98e5c31af7Sopenharmony_ci 99e5c31af7Sopenharmony_ci self.para = [] 100e5c31af7Sopenharmony_ci """list of lines in the paragraph being accumulated. 101e5c31af7Sopenharmony_ci When this is non-empty, there is a current paragraph.""" 102e5c31af7Sopenharmony_ci 103e5c31af7Sopenharmony_ci self.lastTitle = False 104e5c31af7Sopenharmony_ci """true if the previous line was a document title line 105e5c31af7Sopenharmony_ci (e.g. :leveloffset: 0 - no attempt to track changes to this is made).""" 106e5c31af7Sopenharmony_ci 107e5c31af7Sopenharmony_ci self.leadIndent = 0 108e5c31af7Sopenharmony_ci """indent level (in spaces) of the first line of a paragraph.""" 109e5c31af7Sopenharmony_ci 110e5c31af7Sopenharmony_ci self.hangIndent = 0 111e5c31af7Sopenharmony_ci """indent level of the remaining lines of a paragraph.""" 112e5c31af7Sopenharmony_ci 113e5c31af7Sopenharmony_ci self.lineNumber = 0 114e5c31af7Sopenharmony_ci """line number being read from the input file.""" 115e5c31af7Sopenharmony_ci 116e5c31af7Sopenharmony_ci self.defaultApiName = '{refpage}' 117e5c31af7Sopenharmony_ci self.apiName = self.defaultApiName 118e5c31af7Sopenharmony_ci """String name of an API structure or command for VUID tag generation, 119e5c31af7Sopenharmony_ci or {refpage} if one has not been included in this file yet.""" 120e5c31af7Sopenharmony_ci 121e5c31af7Sopenharmony_ci def incrLineNumber(self): 122e5c31af7Sopenharmony_ci self.lineNumber = self.lineNumber + 1 123e5c31af7Sopenharmony_ci 124e5c31af7Sopenharmony_ci def isOpenBlockDelimiter(self, line): 125e5c31af7Sopenharmony_ci """Returns True if line is an open block delimiter. 126e5c31af7Sopenharmony_ci This does not and should not match the listing block delimiter, 127e5c31af7Sopenharmony_ci which is used inside refpage blocks both as a listing block and, 128e5c31af7Sopenharmony_ci via an extension, as a nested open block.""" 129e5c31af7Sopenharmony_ci return line.rstrip() == '--' 130e5c31af7Sopenharmony_ci 131e5c31af7Sopenharmony_ci def resetPara(self): 132e5c31af7Sopenharmony_ci """Reset the paragraph, including its indentation level""" 133e5c31af7Sopenharmony_ci self.para = [] 134e5c31af7Sopenharmony_ci self.leadIndent = 0 135e5c31af7Sopenharmony_ci self.hangIndent = 0 136e5c31af7Sopenharmony_ci 137e5c31af7Sopenharmony_ci def endBlock(self, line, transform, vuBlock): 138e5c31af7Sopenharmony_ci """If beginning a block, tag whether or not to transform the contents. 139e5c31af7Sopenharmony_ci 140e5c31af7Sopenharmony_ci vuBlock is True if the previous line indicates this is a Valid Usage 141e5c31af7Sopenharmony_ci block.""" 142e5c31af7Sopenharmony_ci if self.blockStack[-1] == line: 143e5c31af7Sopenharmony_ci logDiag('endBlock line', self.lineNumber, 144e5c31af7Sopenharmony_ci ': popping block end depth:', len(self.blockStack), 145e5c31af7Sopenharmony_ci ':', line, end='') 146e5c31af7Sopenharmony_ci 147e5c31af7Sopenharmony_ci # Reset apiName at the end of an open block. 148e5c31af7Sopenharmony_ci # Open blocks cannot be nested (at present), so this is safe. 149e5c31af7Sopenharmony_ci if self.isOpenBlockDelimiter(line): 150e5c31af7Sopenharmony_ci logDiag('reset apiName to empty at line', self.lineNumber) 151e5c31af7Sopenharmony_ci self.apiName = self.defaultApiName 152e5c31af7Sopenharmony_ci else: 153e5c31af7Sopenharmony_ci logDiag('NOT resetting apiName to default at line', 154e5c31af7Sopenharmony_ci self.lineNumber) 155e5c31af7Sopenharmony_ci 156e5c31af7Sopenharmony_ci self.blockStack.pop() 157e5c31af7Sopenharmony_ci self.transformStack.pop() 158e5c31af7Sopenharmony_ci self.vuStack.pop() 159e5c31af7Sopenharmony_ci else: 160e5c31af7Sopenharmony_ci # Start a block 161e5c31af7Sopenharmony_ci self.blockStack.append(line) 162e5c31af7Sopenharmony_ci self.transformStack.append(transform) 163e5c31af7Sopenharmony_ci self.vuStack.append(vuBlock) 164e5c31af7Sopenharmony_ci 165e5c31af7Sopenharmony_ci logDiag('endBlock transform =', transform, ' line', self.lineNumber, 166e5c31af7Sopenharmony_ci ': pushing block start depth', len(self.blockStack), 167e5c31af7Sopenharmony_ci ':', line, end='') 168e5c31af7Sopenharmony_ci 169e5c31af7Sopenharmony_ci def addLine(self, line, indent): 170e5c31af7Sopenharmony_ci """Add a line to the current paragraph""" 171e5c31af7Sopenharmony_ci if self.para == []: 172e5c31af7Sopenharmony_ci # Begin a new paragraph 173e5c31af7Sopenharmony_ci self.para = [line] 174e5c31af7Sopenharmony_ci self.leadIndent = indent 175e5c31af7Sopenharmony_ci self.hangIndent = indent 176e5c31af7Sopenharmony_ci else: 177e5c31af7Sopenharmony_ci # Add a line to a paragraph. Increase the hanging indentation 178e5c31af7Sopenharmony_ci # level - once. 179e5c31af7Sopenharmony_ci if self.hangIndent == self.leadIndent: 180e5c31af7Sopenharmony_ci self.hangIndent = indent 181e5c31af7Sopenharmony_ci self.para.append(line) 182e5c31af7Sopenharmony_ci 183e5c31af7Sopenharmony_ci 184e5c31af7Sopenharmony_ciclass TransformCallbackState: 185e5c31af7Sopenharmony_ci """State given to the transformer callback object, derived from 186e5c31af7Sopenharmony_ci TransformState.""" 187e5c31af7Sopenharmony_ci def __init__(self, state): 188e5c31af7Sopenharmony_ci self.isVU = state.vuStack[-1] if len(state.vuStack) > 0 else False 189e5c31af7Sopenharmony_ci """Whether this paragraph is a VU.""" 190e5c31af7Sopenharmony_ci 191e5c31af7Sopenharmony_ci self.apiName = state.apiName 192e5c31af7Sopenharmony_ci """String name of an API structure or command this paragraph belongs 193e5c31af7Sopenharmony_ci to.""" 194e5c31af7Sopenharmony_ci 195e5c31af7Sopenharmony_ci self.leadIndent = state.leadIndent 196e5c31af7Sopenharmony_ci """indent level (in spaces) of the first line of a paragraph.""" 197e5c31af7Sopenharmony_ci 198e5c31af7Sopenharmony_ci self.hangIndent = state.hangIndent 199e5c31af7Sopenharmony_ci """indent level of the remaining lines of a paragraph.""" 200e5c31af7Sopenharmony_ci 201e5c31af7Sopenharmony_ci self.lineNumber = state.lineNumber 202e5c31af7Sopenharmony_ci """line number being read from the input file.""" 203e5c31af7Sopenharmony_ci 204e5c31af7Sopenharmony_ci 205e5c31af7Sopenharmony_ciclass DocTransformer: 206e5c31af7Sopenharmony_ci """A transformer that recursively goes over all spec files under a path. 207e5c31af7Sopenharmony_ci 208e5c31af7Sopenharmony_ci The transformer goes over all spec files under a path and does some basic 209e5c31af7Sopenharmony_ci parsing. In particular, it tracks which section the current text belongs 210e5c31af7Sopenharmony_ci to, whether it references a VU, etc and processes them in 'paragraph' 211e5c31af7Sopenharmony_ci granularity. 212e5c31af7Sopenharmony_ci The transformer takes a callback object with the following methods: 213e5c31af7Sopenharmony_ci 214e5c31af7Sopenharmony_ci - transformParagraph: Called when a paragraph is parsed. The paragraph 215e5c31af7Sopenharmony_ci along with some information (such as whether it is a VU) is passed. The 216e5c31af7Sopenharmony_ci function may transform the paragraph as necessary. 217e5c31af7Sopenharmony_ci - onEmbeddedVUConditional: Called when an embedded VU conditional is 218e5c31af7Sopenharmony_ci encountered. 219e5c31af7Sopenharmony_ci """ 220e5c31af7Sopenharmony_ci def __init__(self, 221e5c31af7Sopenharmony_ci filename, 222e5c31af7Sopenharmony_ci outfile, 223e5c31af7Sopenharmony_ci callback): 224e5c31af7Sopenharmony_ci self.filename = filename 225e5c31af7Sopenharmony_ci """base name of file being read from.""" 226e5c31af7Sopenharmony_ci 227e5c31af7Sopenharmony_ci self.outfile = outfile 228e5c31af7Sopenharmony_ci """file handle to write to.""" 229e5c31af7Sopenharmony_ci 230e5c31af7Sopenharmony_ci self.state = TransformState() 231e5c31af7Sopenharmony_ci """State of transformation""" 232e5c31af7Sopenharmony_ci 233e5c31af7Sopenharmony_ci self.callback = callback 234e5c31af7Sopenharmony_ci """The transformation callback object""" 235e5c31af7Sopenharmony_ci 236e5c31af7Sopenharmony_ci def printLines(self, lines): 237e5c31af7Sopenharmony_ci """Print an array of lines with newlines already present""" 238e5c31af7Sopenharmony_ci if len(lines) > 0: 239e5c31af7Sopenharmony_ci logDiag(':: printLines:', len(lines), 'lines: ', lines[0], end='') 240e5c31af7Sopenharmony_ci 241e5c31af7Sopenharmony_ci if self.outfile is not None: 242e5c31af7Sopenharmony_ci for line in lines: 243e5c31af7Sopenharmony_ci print(line, file=self.outfile, end='') 244e5c31af7Sopenharmony_ci 245e5c31af7Sopenharmony_ci def emitPara(self): 246e5c31af7Sopenharmony_ci """Emit a paragraph, possibly transforming it depending on the block 247e5c31af7Sopenharmony_ci context. 248e5c31af7Sopenharmony_ci 249e5c31af7Sopenharmony_ci Resets the paragraph accumulator.""" 250e5c31af7Sopenharmony_ci if self.state.para != []: 251e5c31af7Sopenharmony_ci transformedPara = self.state.para 252e5c31af7Sopenharmony_ci 253e5c31af7Sopenharmony_ci if self.state.transformStack[-1]: 254e5c31af7Sopenharmony_ci callbackState = TransformCallbackState(self.state) 255e5c31af7Sopenharmony_ci 256e5c31af7Sopenharmony_ci transformedPara = self.callback.transformParagraph( 257e5c31af7Sopenharmony_ci self.state.para, 258e5c31af7Sopenharmony_ci callbackState) 259e5c31af7Sopenharmony_ci 260e5c31af7Sopenharmony_ci self.printLines(transformedPara) 261e5c31af7Sopenharmony_ci 262e5c31af7Sopenharmony_ci self.state.resetPara() 263e5c31af7Sopenharmony_ci 264e5c31af7Sopenharmony_ci def endPara(self, line): 265e5c31af7Sopenharmony_ci """'line' ends a paragraph and should itself be emitted. 266e5c31af7Sopenharmony_ci line may be None to indicate EOF or other exception.""" 267e5c31af7Sopenharmony_ci logDiag('endPara line', self.state.lineNumber, ': emitting paragraph') 268e5c31af7Sopenharmony_ci 269e5c31af7Sopenharmony_ci # Emit current paragraph, this line, and reset tracker 270e5c31af7Sopenharmony_ci self.emitPara() 271e5c31af7Sopenharmony_ci 272e5c31af7Sopenharmony_ci if line: 273e5c31af7Sopenharmony_ci self.printLines([line]) 274e5c31af7Sopenharmony_ci 275e5c31af7Sopenharmony_ci def endParaContinue(self, line): 276e5c31af7Sopenharmony_ci """'line' ends a paragraph (unless there is already a paragraph being 277e5c31af7Sopenharmony_ci accumulated, e.g. len(para) > 0 - currently not implemented)""" 278e5c31af7Sopenharmony_ci self.endPara(line) 279e5c31af7Sopenharmony_ci 280e5c31af7Sopenharmony_ci def endBlock(self, line, transform = False, vuBlock = False): 281e5c31af7Sopenharmony_ci """'line' begins or ends a block. 282e5c31af7Sopenharmony_ci 283e5c31af7Sopenharmony_ci If beginning a block, tag whether or not to transform the contents. 284e5c31af7Sopenharmony_ci 285e5c31af7Sopenharmony_ci vuBlock is True if the previous line indicates this is a Valid Usage 286e5c31af7Sopenharmony_ci block.""" 287e5c31af7Sopenharmony_ci self.endPara(line) 288e5c31af7Sopenharmony_ci self.state.endBlock(line, transform, vuBlock) 289e5c31af7Sopenharmony_ci 290e5c31af7Sopenharmony_ci def endParaBlockTransform(self, line, vuBlock): 291e5c31af7Sopenharmony_ci """'line' begins or ends a block. The paragraphs in the block *should* be 292e5c31af7Sopenharmony_ci reformatted (e.g. a NOTE).""" 293e5c31af7Sopenharmony_ci self.endBlock(line, transform = True, vuBlock = vuBlock) 294e5c31af7Sopenharmony_ci 295e5c31af7Sopenharmony_ci def endParaBlockPassthrough(self, line): 296e5c31af7Sopenharmony_ci """'line' begins or ends a block. The paragraphs in the block should 297e5c31af7Sopenharmony_ci *not* be reformatted (e.g. a code listing).""" 298e5c31af7Sopenharmony_ci self.endBlock(line, transform = False) 299e5c31af7Sopenharmony_ci 300e5c31af7Sopenharmony_ci def addLine(self, line): 301e5c31af7Sopenharmony_ci """'line' starts or continues a paragraph. 302e5c31af7Sopenharmony_ci 303e5c31af7Sopenharmony_ci Paragraphs may have "hanging indent", e.g. 304e5c31af7Sopenharmony_ci 305e5c31af7Sopenharmony_ci ``` 306e5c31af7Sopenharmony_ci * Bullet point... 307e5c31af7Sopenharmony_ci ... continued 308e5c31af7Sopenharmony_ci ``` 309e5c31af7Sopenharmony_ci 310e5c31af7Sopenharmony_ci In this case, when the higher indentation level ends, so does the 311e5c31af7Sopenharmony_ci paragraph.""" 312e5c31af7Sopenharmony_ci logDiag('addLine line', self.state.lineNumber, ':', line, end='') 313e5c31af7Sopenharmony_ci 314e5c31af7Sopenharmony_ci # See https://stackoverflow.com/questions/13648813/what-is-the-pythonic-way-to-count-the-leading-spaces-in-a-string 315e5c31af7Sopenharmony_ci indent = len(line) - len(line.lstrip()) 316e5c31af7Sopenharmony_ci 317e5c31af7Sopenharmony_ci # A hanging paragraph ends due to a less-indented line. 318e5c31af7Sopenharmony_ci if self.state.para != [] and indent < self.state.hangIndent: 319e5c31af7Sopenharmony_ci logDiag('addLine: line reduces indentation, emit paragraph') 320e5c31af7Sopenharmony_ci self.emitPara() 321e5c31af7Sopenharmony_ci 322e5c31af7Sopenharmony_ci # A bullet point (or something that looks like one) always ends the 323e5c31af7Sopenharmony_ci # current paragraph. 324e5c31af7Sopenharmony_ci if beginBullet.match(line): 325e5c31af7Sopenharmony_ci logDiag('addLine: line matches beginBullet, emit paragraph') 326e5c31af7Sopenharmony_ci self.emitPara() 327e5c31af7Sopenharmony_ci 328e5c31af7Sopenharmony_ci self.state.addLine(line, indent) 329e5c31af7Sopenharmony_ci 330e5c31af7Sopenharmony_ci def apiMatch(self, oldname, newname): 331e5c31af7Sopenharmony_ci """Returns whether oldname and newname match, up to an API suffix. 332e5c31af7Sopenharmony_ci This should use the API map instead of this heuristic, since aliases 333e5c31af7Sopenharmony_ci like VkPhysicalDeviceVariablePointerFeaturesKHR -> 334e5c31af7Sopenharmony_ci VkPhysicalDeviceVariablePointersFeatures are not recognized.""" 335e5c31af7Sopenharmony_ci upper = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' 336e5c31af7Sopenharmony_ci return oldname.rstrip(upper) == newname.rstrip(upper) 337e5c31af7Sopenharmony_ci 338e5c31af7Sopenharmony_ci def transformFile(self, lines): 339e5c31af7Sopenharmony_ci """Transform lines, and possibly output to to the given file.""" 340e5c31af7Sopenharmony_ci 341e5c31af7Sopenharmony_ci for line in lines: 342e5c31af7Sopenharmony_ci self.state.incrLineNumber() 343e5c31af7Sopenharmony_ci 344e5c31af7Sopenharmony_ci # Is this a title line (leading '= ' followed by text)? 345e5c31af7Sopenharmony_ci thisTitle = False 346e5c31af7Sopenharmony_ci 347e5c31af7Sopenharmony_ci # The logic here is broken. If we are in a non-transformable block and 348e5c31af7Sopenharmony_ci # this line *does not* end the block, it should always be 349e5c31af7Sopenharmony_ci # accumulated. 350e5c31af7Sopenharmony_ci 351e5c31af7Sopenharmony_ci # Test for a blockCommonTransform delimiter comment first, to avoid 352e5c31af7Sopenharmony_ci # treating it solely as a end-Paragraph marker comment. 353e5c31af7Sopenharmony_ci if line == blockCommonTransform: 354e5c31af7Sopenharmony_ci # Starting or ending a pseudo-block for "common" VU statements. 355e5c31af7Sopenharmony_ci self.endParaBlockTransform(line, vuBlock = True) 356e5c31af7Sopenharmony_ci 357e5c31af7Sopenharmony_ci elif blockTransform.match(line): 358e5c31af7Sopenharmony_ci # Starting or ending a block whose contents may be transformed. 359e5c31af7Sopenharmony_ci # Blocks cannot be nested. 360e5c31af7Sopenharmony_ci 361e5c31af7Sopenharmony_ci # Is this is an explicit Valid Usage block? 362e5c31af7Sopenharmony_ci vuBlock = (self.state.lineNumber > 1 and 363e5c31af7Sopenharmony_ci lines[self.state.lineNumber-2] == '.Valid Usage\n') 364e5c31af7Sopenharmony_ci 365e5c31af7Sopenharmony_ci self.endParaBlockTransform(line, vuBlock) 366e5c31af7Sopenharmony_ci 367e5c31af7Sopenharmony_ci elif endPara.match(line): 368e5c31af7Sopenharmony_ci # Ending a paragraph. Emit the current paragraph, if any, and 369e5c31af7Sopenharmony_ci # prepare to begin a new paragraph. 370e5c31af7Sopenharmony_ci 371e5c31af7Sopenharmony_ci self.endPara(line) 372e5c31af7Sopenharmony_ci 373e5c31af7Sopenharmony_ci # If this is an include:: line starting the definition of a 374e5c31af7Sopenharmony_ci # structure or command, track that for use in VUID generation. 375e5c31af7Sopenharmony_ci 376e5c31af7Sopenharmony_ci matches = includePat.search(line) 377e5c31af7Sopenharmony_ci if matches is not None: 378e5c31af7Sopenharmony_ci generated_type = matches.group('generated_type') 379e5c31af7Sopenharmony_ci include_type = matches.group('category') 380e5c31af7Sopenharmony_ci if generated_type == 'api' and include_type in ('protos', 'structs', 'funcpointers'): 381e5c31af7Sopenharmony_ci apiName = matches.group('entity_name') 382e5c31af7Sopenharmony_ci if self.state.apiName != self.state.defaultApiName: 383e5c31af7Sopenharmony_ci # This happens when there are multiple API include 384e5c31af7Sopenharmony_ci # lines in a single block. The style guideline is to 385e5c31af7Sopenharmony_ci # always place the API which others are promoted to 386e5c31af7Sopenharmony_ci # first. In virtually all cases, the promoted API 387e5c31af7Sopenharmony_ci # will differ solely in the vendor suffix (or 388e5c31af7Sopenharmony_ci # absence of it), which is benign. 389e5c31af7Sopenharmony_ci if not self.apiMatch(self.state.apiName, apiName): 390e5c31af7Sopenharmony_ci logDiag(f'Promoted API name mismatch at line {self.state.lineNumber}: {apiName} does not match self.state.apiName (this is OK if it is just a spelling alias)') 391e5c31af7Sopenharmony_ci else: 392e5c31af7Sopenharmony_ci self.state.apiName = apiName 393e5c31af7Sopenharmony_ci 394e5c31af7Sopenharmony_ci elif endParaContinue.match(line): 395e5c31af7Sopenharmony_ci # For now, always just end the paragraph. 396e5c31af7Sopenharmony_ci # Could check see if len(para) > 0 to accumulate. 397e5c31af7Sopenharmony_ci 398e5c31af7Sopenharmony_ci self.endParaContinue(line) 399e5c31af7Sopenharmony_ci 400e5c31af7Sopenharmony_ci # If it is a title line, track that 401e5c31af7Sopenharmony_ci if line[0:2] == '= ': 402e5c31af7Sopenharmony_ci thisTitle = True 403e5c31af7Sopenharmony_ci 404e5c31af7Sopenharmony_ci elif blockPassthrough.match(line): 405e5c31af7Sopenharmony_ci # Starting or ending a block whose contents must not be 406e5c31af7Sopenharmony_ci # transformed. These are tables, etc. Blocks cannot be nested. 407e5c31af7Sopenharmony_ci # Note that the use of a listing block masquerading as an 408e5c31af7Sopenharmony_ci # open block, via an extension, will not be formatted even 409e5c31af7Sopenharmony_ci # though it should be. 410e5c31af7Sopenharmony_ci # Fixing this would require looking at the previous line 411e5c31af7Sopenharmony_ci # state for the '[open]' tag, and there are so few cases of 412e5c31af7Sopenharmony_ci # this in the spec markup that it is not worth the trouble. 413e5c31af7Sopenharmony_ci 414e5c31af7Sopenharmony_ci self.endParaBlockPassthrough(line) 415e5c31af7Sopenharmony_ci elif self.state.lastTitle: 416e5c31af7Sopenharmony_ci # The previous line was a document title line. This line 417e5c31af7Sopenharmony_ci # is the author / credits line and must not be transformed. 418e5c31af7Sopenharmony_ci 419e5c31af7Sopenharmony_ci self.endPara(line) 420e5c31af7Sopenharmony_ci else: 421e5c31af7Sopenharmony_ci # Just accumulate a line to the current paragraph. Watch out for 422e5c31af7Sopenharmony_ci # hanging indents / bullet-points and track that indent level. 423e5c31af7Sopenharmony_ci 424e5c31af7Sopenharmony_ci self.addLine(line) 425e5c31af7Sopenharmony_ci 426e5c31af7Sopenharmony_ci # Commented out now that VU extractor supports this, but may 427e5c31af7Sopenharmony_ci # need to refactor through a conventions object enable if 428e5c31af7Sopenharmony_ci # OpenXR still needs this. 429e5c31af7Sopenharmony_ci 430e5c31af7Sopenharmony_ci # This test looks for disallowed conditionals inside Valid Usage 431e5c31af7Sopenharmony_ci # blocks, by checking if (a) this line does not start a new VU 432e5c31af7Sopenharmony_ci # (bullet point) and (b) the previous line starts an asciidoctor 433e5c31af7Sopenharmony_ci # conditional (ifdef:: or ifndef::). 434e5c31af7Sopenharmony_ci # if (self.state.vuStack[-1] 435e5c31af7Sopenharmony_ci # and not beginBullet.match(line) 436e5c31af7Sopenharmony_ci # and conditionalStart.match(lines[self.state.lineNumber-2])): 437e5c31af7Sopenharmony_ci # self.callback.onEmbeddedVUConditional(self.state) 438e5c31af7Sopenharmony_ci 439e5c31af7Sopenharmony_ci self.state.lastTitle = thisTitle 440e5c31af7Sopenharmony_ci 441e5c31af7Sopenharmony_ci # Cleanup at end of file 442e5c31af7Sopenharmony_ci self.endPara(None) 443e5c31af7Sopenharmony_ci 444e5c31af7Sopenharmony_ci # Check for sensible block nesting 445e5c31af7Sopenharmony_ci if len(self.state.blockStack) > 1: 446e5c31af7Sopenharmony_ci logWarn('file', self.filename, 447e5c31af7Sopenharmony_ci 'mismatched asciidoc block delimiters at EOF:', 448e5c31af7Sopenharmony_ci self.state.blockStack[-1]) 449e5c31af7Sopenharmony_ci 450