The Wayback Machine - https://web.archive.org/web/20201017104039/https://github.com/pyparsing/pyparsing/pull/236
Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Some proposed changes to pp_diagram output, plus monkeypatch create_diagram method onto ParserElement #236

Open
wants to merge 2 commits into
base: master
from

Conversation

@ptmcg
Copy link
Member

@ptmcg ptmcg commented Aug 17, 2020

@TMiguelT - I've had these changes for a while, but wasn't sure about the Git incantations to push them properly. I think this is... oh, crap, I should have done this in a branch. Well, I can just nuke this forked repo when we are done, rather than try to patch it up.

These changes are not complete, there are still some issues with some of the labeling I think, and proper referencing to a named element if it is used multiple times in a high expression.

I also would like to add the results name to the dashed border now, since the expression name is getting written on the block element itself. If you can point me to where this is set, I'll take a run at it.

What do you think of monkeypatching .create_diagram onto ParserElement?

ptmcg added 2 commits Aug 17, 2020
Pull latest from pyparsing/pyparsing
…gram method, demo in delta_time.py
def _already_extracted(
element: pyparsing.ParserElement, lookup: ConverterState
) -> bool:
return any(element.name == diag.kwargs["name"] for diag in lookup.diagrams.values())

This comment has been minimized.

@TMiguelT

TMiguelT Aug 17, 2020
Contributor

The ConverterState object indexes both of its dictionaries using the pyparsing element ID. So you can just check return id(element) in lookup.diagrams here.

This comment has been minimized.

@ptmcg

ptmcg Aug 17, 2020
Author Member

Ok, much simpler!

@@ -341,9 +354,9 @@ def _to_diagram_element(
ret = EditablePartial.from_call(railroad.Sequence, items=[])
elif isinstance(element, (pyparsing.Or, pyparsing.MatchFirst)):
if _should_vertical(vertical, len(exprs)):
ret = EditablePartial.from_call(railroad.HorizontalChoice, items=[])

This comment has been minimized.

@TMiguelT

TMiguelT Aug 17, 2020
Contributor

Good catch, I evidently had this wrong. Let me know if it actually looks better this way, though.

railroad.Terminal, title=element.name, text=element.defaultName
)
else:
ret = EditablePartial.from_call(

This comment has been minimized.

@TMiguelT

TMiguelT Aug 17, 2020
Contributor

I think this check (if not _already_extracted) could be simplified just by adding a new condition to _worth_extracting. As it stands, we already check if it's _worth_extracting, and if it has already been extracted, then I already have code for creating a NonTerminal here:

if _worth_extracting(element):
if el_id in lookup.first:
# If we've seen this element exactly once before, we are only just now finding out that it's a duplicate,
# so we have to extract it into a new diagram.
looked_up = lookup.first[el_id]
looked_up.mark_for_extraction(el_id, lookup, name=name_hint)
return EditablePartial.from_call(railroad.NonTerminal, text=looked_up.name)
elif el_id in lookup.diagrams:
# If we have seen the element at least twice before, and have already extracted it into a subdiagram, we
# just put in a marker element that refers to the sub-diagram
return EditablePartial.from_call(
railroad.NonTerminal, text=lookup.diagrams[el_id].kwargs["name"]
)
.

So if you simply add if _is_named_terminal(element): return True to _already_extracted, I think you get this behaviour for free, with less and simpler code.

)
else:
ret = EditablePartial.from_call(
railroad.NonTerminal, title=element.name, text=element.name

This comment has been minimized.

@TMiguelT

TMiguelT Aug 17, 2020
Contributor

title will only set the hover text, I assume you know that? This will make it harder to see both the customName and the defaultName compared to my current solution of using a group to do that:

terminal = EditablePartial.from_call(railroad.Terminal, element.defaultName)
if element.customName is not None:
ret = EditablePartial.from_call(
railroad.Group, item=terminal, label=element.customName
)
else:
ret = terminal

This comment has been minimized.

@ptmcg

ptmcg Aug 17, 2020
Author Member

The default name gets displayed in the element's subdiagram, so we shouldn't need to show both defaultName and customName at the same time.

This comment has been minimized.

@TMiguelT

TMiguelT Aug 17, 2020
Contributor

Oh, okay. I think I need to see an example of this.

@@ -379,6 +401,12 @@ def _to_diagram_element(
number=lookup.generate_index(),
)

if _is_named_terminal(element):

This comment has been minimized.

@TMiguelT

TMiguelT Aug 17, 2020
Contributor

So here you're bypassing the normal mechanism of "only extract if we've seen it twice". Is this intentional? I forget what we discussed. If so, there's probably an easier way to do this than using the extraction mechanism at all, but I can't think of exactly what it might be.

This comment has been minimized.

@ptmcg

ptmcg Aug 17, 2020
Author Member

Yeah, I think this is intentional. Even if an object is only seen once, if it is sufficiently complex (and has a name), it should be a separate subdiagram. This makes the diagrams more likely to map to a BNF.



# monkeypatch .create_diagram method onto ParserElement
def _create_diagram(expr: pyparsing.ParserElement, output_html: Union[str, TextIO]):

This comment has been minimized.

@TMiguelT

TMiguelT Aug 17, 2020
Contributor

I think this might be better as a method on the original ParserElement class, which checks for an ImportError as you have done at the top. I prefer this to monkey patching, anyway.

This comment has been minimized.

@ptmcg

ptmcg Aug 17, 2020
Author Member

My intention behind monkeypatching is so that the core library can be easily vendored without having to include dependencies on other packages beside the stdlib.

This comment has been minimized.

@TMiguelT

TMiguelT Aug 17, 2020
Contributor

But if you do this, is that not sufficient? Then the dependencies are still optional.

try:
    import pyparsing.diagram
except ImportError:
    raise Exception('You need to run `pip install pyparsing[diagrams]`')

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Linked issues

Successfully merging this pull request may close these issues.

None yet

2 participants
You can’t perform that action at this time.