Re: [PATCH 08/20] qapi/parser: differentiate intro and outro paragraphs

2024-05-16 Thread John Snow
On Thu, May 16, 2024 at 11:06 AM John Snow  wrote:

>
>
> On Thu, May 16, 2024, 5:34 AM Markus Armbruster  wrote:
>
>> John Snow  writes:
>>
>> > Add a semantic tag to paragraphs that appear *before* tagged
>> > sections/members/features and those that appear after. This will control
>> > how they are inlined when doc sections are merged and flattened.
>>
>> This future use is not obvious to me now.  I guess the effective way to
>> help me see it is actual patches, which will come in due time.
>>
>
> Head recursion and tail recursion, respectively :)
>
> * intro
> * inherited intro
> * members [ancestor-descendent]
> * features [ancestor-descendent]
> * inherited outro
> * outro
>
> Child gets the first and final words. Inherited stuff goes in the sandwich
> fillings.
>
> It feels like a simple rule that's easy to internalize. As a bonus, you
> can explain it by analogy to Americans as a burger, which is the only
> metaphor we understand.
>
>
>> > Signed-off-by: John Snow 
>> > ---
>> >  scripts/qapi/parser.py | 22 +-
>> >  1 file changed, 17 insertions(+), 5 deletions(-)
>> >
>> > diff --git a/scripts/qapi/parser.py b/scripts/qapi/parser.py
>> > index cf4cbca1c1f..b1794f71e12 100644
>> > --- a/scripts/qapi/parser.py
>> > +++ b/scripts/qapi/parser.py
>> > @@ -503,6 +503,10 @@ def get_doc(self) -> 'QAPIDoc':
>> >  self.accept(False)
>> >  line = self.get_doc_line()
>> >  no_more_args = False
>> > +# Paragraphs before members/features/tagged are "intro"
>> paragraphs.
>> > +# Any appearing subsequently are "outro" paragraphs.
>> > +# This is only semantic metadata for the doc generator.
>>
>> Not sure about the last sentence.  Isn't it true for almost everything
>> around here?
>>
>
> I guess I was trying to say "There's no real difference between the two
> mechanically, it's purely based on where it appears in the doc block, which
> offers only a heuristic for its semantic value- introductory statements or
> additional detail."
>
> In my mind: the other "kind" values have some more mechanical difference
> to them, but intro/outro don't.
>
>
>> Also, long line.
>>
>> > +intro = True
>> >
>> >  while line is not None:
>> >  # Blank lines
>> > @@ -532,6 +536,7 @@ def get_doc(self) -> 'QAPIDoc':
>> >  raise QAPIParseError(
>> >  self, 'feature descriptions expected')
>> >  no_more_args = True
>> > +intro = False
>>
>> After feature descriptions.
>>
>> >  elif match := self._match_at_name_colon(line):
>> >  # description
>> >  if no_more_args:
>> > @@ -547,6 +552,7 @@ def get_doc(self) -> 'QAPIDoc':
>> >  doc.append_line(text)
>> >  line = self.get_doc_indented(doc)
>> >  no_more_args = True
>> > +intro = False
>>
>> Or after member descriptions.
>>
>> >  elif match := re.match(
>> >
>> r'(Returns|Errors|Since|Notes?|Examples?|TODO): *',
>> >  line):
>> > @@ -557,13 +563,14 @@ def get_doc(self) -> 'QAPIDoc':
>> >  doc.append_line(text)
>> >  line = self.get_doc_indented(doc)
>> >  no_more_args = True
>> > +intro = False
>>
>> Or after the first tagged section.
>>
>> Okay, it does what it says on the tin.
>>
>> >  elif line.startswith('='):
>> >  raise QAPIParseError(
>> >  self,
>> >  "unexpected '=' markup in definition
>> documentation")
>> >  else:
>> >  # tag-less paragraph
>> > -doc.ensure_untagged_section(self.info)
>> > +doc.ensure_untagged_section(self.info, intro)
>> >  doc.append_line(line)
>> >  line = self.get_doc_paragraph(doc)
>> >  else:
>> > @@ -617,7 +624,7 @@ def __init__(
>> >  self,
>> >  info: QAPISourceInfo,
>> >  tag: Optional[str] = None,
>> > -kind: str = 'paragraph',
>> > +kind: str = 'intro-paragraph',
>>
>> The question "why is this optional?" crossed my mind when reviewing the
>> previous patch.  I left it unasked, because I felt challenging the
>> overlap between @kind and @tag was more useful.  However, the new
>> default value 'intro-paragraph' feels more arbitrary to me than the old
>> one 'paragraph', and that makes the question pop right back into my
>> mind.
>>
>
> Just "don't break API" habit, nothing more. I can make it mandatory.
>
>
>> Unless I'm mistaken, all calls but one @tag and @kind.  Making that one
>> pass it too feels simpler to me.
>>
>> Moot if we fuse @tag and @kind, of course.
>
>
>> >  ):
>> > 

Re: [PATCH 08/20] qapi/parser: differentiate intro and outro paragraphs

2024-05-16 Thread John Snow
On Thu, May 16, 2024, 5:34 AM Markus Armbruster  wrote:

> John Snow  writes:
>
> > Add a semantic tag to paragraphs that appear *before* tagged
> > sections/members/features and those that appear after. This will control
> > how they are inlined when doc sections are merged and flattened.
>
> This future use is not obvious to me now.  I guess the effective way to
> help me see it is actual patches, which will come in due time.
>

Head recursion and tail recursion, respectively :)

* intro
* inherited intro
* members [ancestor-descendent]
* features [ancestor-descendent]
* inherited outro
* outro

Child gets the first and final words. Inherited stuff goes in the sandwich
fillings.

It feels like a simple rule that's easy to internalize. As a bonus, you can
explain it by analogy to Americans as a burger, which is the only metaphor
we understand.


> > Signed-off-by: John Snow 
> > ---
> >  scripts/qapi/parser.py | 22 +-
> >  1 file changed, 17 insertions(+), 5 deletions(-)
> >
> > diff --git a/scripts/qapi/parser.py b/scripts/qapi/parser.py
> > index cf4cbca1c1f..b1794f71e12 100644
> > --- a/scripts/qapi/parser.py
> > +++ b/scripts/qapi/parser.py
> > @@ -503,6 +503,10 @@ def get_doc(self) -> 'QAPIDoc':
> >  self.accept(False)
> >  line = self.get_doc_line()
> >  no_more_args = False
> > +# Paragraphs before members/features/tagged are "intro"
> paragraphs.
> > +# Any appearing subsequently are "outro" paragraphs.
> > +# This is only semantic metadata for the doc generator.
>
> Not sure about the last sentence.  Isn't it true for almost everything
> around here?
>

I guess I was trying to say "There's no real difference between the two
mechanically, it's purely based on where it appears in the doc block, which
offers only a heuristic for its semantic value- introductory statements or
additional detail."

In my mind: the other "kind" values have some more mechanical difference to
them, but intro/outro don't.


> Also, long line.
>
> > +intro = True
> >
> >  while line is not None:
> >  # Blank lines
> > @@ -532,6 +536,7 @@ def get_doc(self) -> 'QAPIDoc':
> >  raise QAPIParseError(
> >  self, 'feature descriptions expected')
> >  no_more_args = True
> > +intro = False
>
> After feature descriptions.
>
> >  elif match := self._match_at_name_colon(line):
> >  # description
> >  if no_more_args:
> > @@ -547,6 +552,7 @@ def get_doc(self) -> 'QAPIDoc':
> >  doc.append_line(text)
> >  line = self.get_doc_indented(doc)
> >  no_more_args = True
> > +intro = False
>
> Or after member descriptions.
>
> >  elif match := re.match(
> >  r'(Returns|Errors|Since|Notes?|Examples?|TODO):
> *',
> >  line):
> > @@ -557,13 +563,14 @@ def get_doc(self) -> 'QAPIDoc':
> >  doc.append_line(text)
> >  line = self.get_doc_indented(doc)
> >  no_more_args = True
> > +intro = False
>
> Or after the first tagged section.
>
> Okay, it does what it says on the tin.
>
> >  elif line.startswith('='):
> >  raise QAPIParseError(
> >  self,
> >  "unexpected '=' markup in definition
> documentation")
> >  else:
> >  # tag-less paragraph
> > -doc.ensure_untagged_section(self.info)
> > +doc.ensure_untagged_section(self.info, intro)
> >  doc.append_line(line)
> >  line = self.get_doc_paragraph(doc)
> >  else:
> > @@ -617,7 +624,7 @@ def __init__(
> >  self,
> >  info: QAPISourceInfo,
> >  tag: Optional[str] = None,
> > -kind: str = 'paragraph',
> > +kind: str = 'intro-paragraph',
>
> The question "why is this optional?" crossed my mind when reviewing the
> previous patch.  I left it unasked, because I felt challenging the
> overlap between @kind and @tag was more useful.  However, the new
> default value 'intro-paragraph' feels more arbitrary to me than the old
> one 'paragraph', and that makes the question pop right back into my
> mind.
>

Just "don't break API" habit, nothing more. I can make it mandatory.


> Unless I'm mistaken, all calls but one @tag and @kind.  Making that one
> pass it too feels simpler to me.
>
> Moot if we fuse @tag and @kind, of course.


> >  ):
> >  # section source info, i.e. where it begins
> >  self.info = info
> > @@ -625,7 +632,7 @@ def __init__(
> >  self.tag = tag
> >  # section text 

Re: [PATCH 08/20] qapi/parser: differentiate intro and outro paragraphs

2024-05-16 Thread Markus Armbruster
John Snow  writes:

> Add a semantic tag to paragraphs that appear *before* tagged
> sections/members/features and those that appear after. This will control
> how they are inlined when doc sections are merged and flattened.

This future use is not obvious to me now.  I guess the effective way to
help me see it is actual patches, which will come in due time.

> Signed-off-by: John Snow 
> ---
>  scripts/qapi/parser.py | 22 +-
>  1 file changed, 17 insertions(+), 5 deletions(-)
>
> diff --git a/scripts/qapi/parser.py b/scripts/qapi/parser.py
> index cf4cbca1c1f..b1794f71e12 100644
> --- a/scripts/qapi/parser.py
> +++ b/scripts/qapi/parser.py
> @@ -503,6 +503,10 @@ def get_doc(self) -> 'QAPIDoc':
>  self.accept(False)
>  line = self.get_doc_line()
>  no_more_args = False
> +# Paragraphs before members/features/tagged are "intro" 
> paragraphs.
> +# Any appearing subsequently are "outro" paragraphs.
> +# This is only semantic metadata for the doc generator.

Not sure about the last sentence.  Isn't it true for almost everything
around here?

Also, long line.  

> +intro = True
>  
>  while line is not None:
>  # Blank lines
> @@ -532,6 +536,7 @@ def get_doc(self) -> 'QAPIDoc':
>  raise QAPIParseError(
>  self, 'feature descriptions expected')
>  no_more_args = True
> +intro = False

After feature descriptions.

>  elif match := self._match_at_name_colon(line):
>  # description
>  if no_more_args:
> @@ -547,6 +552,7 @@ def get_doc(self) -> 'QAPIDoc':
>  doc.append_line(text)
>  line = self.get_doc_indented(doc)
>  no_more_args = True
> +intro = False

Or after member descriptions.

>  elif match := re.match(
>  r'(Returns|Errors|Since|Notes?|Examples?|TODO): *',
>  line):
> @@ -557,13 +563,14 @@ def get_doc(self) -> 'QAPIDoc':
>  doc.append_line(text)
>  line = self.get_doc_indented(doc)
>  no_more_args = True
> +intro = False

Or after the first tagged section.

Okay, it does what it says on the tin.

>  elif line.startswith('='):
>  raise QAPIParseError(
>  self,
>  "unexpected '=' markup in definition documentation")
>  else:
>  # tag-less paragraph
> -doc.ensure_untagged_section(self.info)
> +doc.ensure_untagged_section(self.info, intro)
>  doc.append_line(line)
>  line = self.get_doc_paragraph(doc)
>  else:
> @@ -617,7 +624,7 @@ def __init__(
>  self,
>  info: QAPISourceInfo,
>  tag: Optional[str] = None,
> -kind: str = 'paragraph',
> +kind: str = 'intro-paragraph',

The question "why is this optional?" crossed my mind when reviewing the
previous patch.  I left it unasked, because I felt challenging the
overlap between @kind and @tag was more useful.  However, the new
default value 'intro-paragraph' feels more arbitrary to me than the old
one 'paragraph', and that makes the question pop right back into my
mind.

Unless I'm mistaken, all calls but one @tag and @kind.  Making that one
pass it too feels simpler to me.

Moot if we fuse @tag and @kind, of course.

>  ):
>  # section source info, i.e. where it begins
>  self.info = info
> @@ -625,7 +632,7 @@ def __init__(
>  self.tag = tag
>  # section text without tag
>  self.text = ''
> -# section type - {paragraph, feature, member, tagged}
> +# section type - {-paragraph, feature, member, 
> tagged}

Long line.

>  self.kind = kind
>  
>  def append_line(self, line: str) -> None:
> @@ -666,7 +673,11 @@ def end(self) -> None:
>  raise QAPISemError(
>  section.info, "text required after '%s:'" % section.tag)
>  
> -def ensure_untagged_section(self, info: QAPISourceInfo) -> None:
> +def ensure_untagged_section(
> +self,
> +info: QAPISourceInfo,
> +intro: bool = True,

Two callers, one passes @info, one doesn't.  Passing it always might be
simpler.

> +) -> None:
>  if self.all_sections and not self.all_sections[-1].tag:
>  section = self.all_sections[-1]
>  # Section is empty so far; update info to start *here*.
> @@ -677,7 +688,8 @@ def ensure_untagged_section(self, info: QAPISourceInfo) 
> -> None:
>  self.all_sections[-1].text += '\n'
>  return

[PATCH 08/20] qapi/parser: differentiate intro and outro paragraphs

2024-05-14 Thread John Snow
Add a semantic tag to paragraphs that appear *before* tagged
sections/members/features and those that appear after. This will control
how they are inlined when doc sections are merged and flattened.

Signed-off-by: John Snow 
---
 scripts/qapi/parser.py | 22 +-
 1 file changed, 17 insertions(+), 5 deletions(-)

diff --git a/scripts/qapi/parser.py b/scripts/qapi/parser.py
index cf4cbca1c1f..b1794f71e12 100644
--- a/scripts/qapi/parser.py
+++ b/scripts/qapi/parser.py
@@ -503,6 +503,10 @@ def get_doc(self) -> 'QAPIDoc':
 self.accept(False)
 line = self.get_doc_line()
 no_more_args = False
+# Paragraphs before members/features/tagged are "intro" paragraphs.
+# Any appearing subsequently are "outro" paragraphs.
+# This is only semantic metadata for the doc generator.
+intro = True
 
 while line is not None:
 # Blank lines
@@ -532,6 +536,7 @@ def get_doc(self) -> 'QAPIDoc':
 raise QAPIParseError(
 self, 'feature descriptions expected')
 no_more_args = True
+intro = False
 elif match := self._match_at_name_colon(line):
 # description
 if no_more_args:
@@ -547,6 +552,7 @@ def get_doc(self) -> 'QAPIDoc':
 doc.append_line(text)
 line = self.get_doc_indented(doc)
 no_more_args = True
+intro = False
 elif match := re.match(
 r'(Returns|Errors|Since|Notes?|Examples?|TODO): *',
 line):
@@ -557,13 +563,14 @@ def get_doc(self) -> 'QAPIDoc':
 doc.append_line(text)
 line = self.get_doc_indented(doc)
 no_more_args = True
+intro = False
 elif line.startswith('='):
 raise QAPIParseError(
 self,
 "unexpected '=' markup in definition documentation")
 else:
 # tag-less paragraph
-doc.ensure_untagged_section(self.info)
+doc.ensure_untagged_section(self.info, intro)
 doc.append_line(line)
 line = self.get_doc_paragraph(doc)
 else:
@@ -617,7 +624,7 @@ def __init__(
 self,
 info: QAPISourceInfo,
 tag: Optional[str] = None,
-kind: str = 'paragraph',
+kind: str = 'intro-paragraph',
 ):
 # section source info, i.e. where it begins
 self.info = info
@@ -625,7 +632,7 @@ def __init__(
 self.tag = tag
 # section text without tag
 self.text = ''
-# section type - {paragraph, feature, member, tagged}
+# section type - {-paragraph, feature, member, tagged}
 self.kind = kind
 
 def append_line(self, line: str) -> None:
@@ -666,7 +673,11 @@ def end(self) -> None:
 raise QAPISemError(
 section.info, "text required after '%s:'" % section.tag)
 
-def ensure_untagged_section(self, info: QAPISourceInfo) -> None:
+def ensure_untagged_section(
+self,
+info: QAPISourceInfo,
+intro: bool = True,
+) -> None:
 if self.all_sections and not self.all_sections[-1].tag:
 section = self.all_sections[-1]
 # Section is empty so far; update info to start *here*.
@@ -677,7 +688,8 @@ def ensure_untagged_section(self, info: QAPISourceInfo) -> 
None:
 self.all_sections[-1].text += '\n'
 return
 # start new section
-section = self.Section(info)
+kind = ("intro" if intro else "outro") + "-paragraph"
+section = self.Section(info, kind=kind)
 self.sections.append(section)
 self.all_sections.append(section)
 
-- 
2.44.0