[Libre-soc-bugs] [Bug 1094] insndb instruction database visitor-walker is needed

bugzilla-daemon at libre-soc.org bugzilla-daemon at libre-soc.org
Sat Jun 10 10:04:46 BST 2023


https://bugs.libre-soc.org/show_bug.cgi?id=1094

--- Comment #85 from Luke Kenneth Casson Leighton <lkcl at lkcl.net> ---
(In reply to Dmitry Selyutin from comment #82)
> Long story short. I share your opinion almost on everything, but I had to at
> least probe this route to understand its pros and cons.
> The only exception is generic __call__. This actually comes from your
> isinstance hints (about another section?),

this is where the misunderstanding is: i was talking at the time
about ast.py iter_child_nodes use of isinstance:

def iter_child_nodes(node):
    for name, field in iter_fields(node):
        if isinstance(field, AST): yield field
        elif isinstance(field, list):
            for item in field:
                if isinstance(item, AST):  yield item

* you solved the first use there by doing contextmanager.
* you solved the second use by doing Node.subnodes (now Nodes.walk)
* the third instance could again use another contextmanager

whereas i was talking about the use in the *visitor*, which in
ast.py is this:
https://github.com/python/cpython/blob/2.7/Lib/ast.py#L240

  def visit(self, node):
        method = 'visit_' + node.__class__.__name__
        visitor = getattr(self, method, self.generic_visit)
        return visitor(node)

which for us would be:

  def visit(self, node):
        method = 'visit_' + node.__class__.__name__
        visitor = getattr(self, method, self.__call__)
        return visitor(node)


> but I realized that relying on
> class name is too fragile.

it's not "fragile", it's the best aspect of the entire visitor pattern
when applied to python.

plus it breaks the "Principle of Least Surprise".  *everyone* who has ever
used Visitors in python expects to have function-names in the Visitor named
after the "data type of the database object being walked".

you are proposing to break that expectation in effect.

i don't mind self.__call__ being the default for 

> isinstance refers to an object (yes, types are
> objects too in Python); name just refers to some string. Whilst it looks
> simpler, it's indeed less intuitive than explicit checks. If somebody wants
> this functionality (per-class context managers), they can do it directly in
> concrete visitors:
> 
> class WeirdVisitor(Visitor):
>     @contextlib.contextmanager
>     def __call__(self, node):
>         method = getattr(self, node.__class__.__name__, self.Node)
>         return method(node)

the *very* first thing that i will do on creating *any* visitors is
precisely and exactly that, copying that exactly and *only* using
that!  and in all documentation i will strongly recommend using
that and not the Visitor!


>     @contextlib.contextmanager
>     def Node(self, node):
>         raise 666
> 
>     @contextlib.contextmanager
>     def Record(self, node):
>         raise 42
> 
> TL;DR: I'm removing paths branch. However, there's still one issue with the
> approach you suggest.
> 
>     def __call__(self, node):
>         print("/".join(self.stack_of_stuff), node)
>         self.stack_of_stuff.append(node.name)
>         yield node
>         self.stack_of_stuff.pop(-1) # i think
> 
> I'f we're going to drop the Node-inherited classes (Path, String and similar
> stuff), there's no way we could call node.name (or, again, some other
> generic field, because "name" is occupied). `str` or `bool` don't have such
> interface.

then the *Visitor* should do "if isinstance(node, str) self.stack.append(node)"

again, like the "depth" parameter, the walker-function should not be
compromised by what the *Visitor* does.

> As for bool inheritance, that's not a problem: whilst bool isn't
> inheritable, int is.

you see how going down that path is a danger?  using bool is clear about
the intent.

additionally i would really like this code to be useable elsewhere in the
project, and even a separate git repo created for it (and put onto pypi).

in particular at some point i would like to replace the existing nmigen
visitor-pattern (which is the usual "faulty" design) with this one.


(In reply to Dmitry Selyutin from comment #84)
> Ah, and one note about a single __call__. Even though it's an unlikely
> scenario, two classes might share the same name (e.g. one is on global level
> and other is class-nested).

that would be completely confusing and seriously inadviseable from a project
maintainability perspective!

as an outside possibility that will immediately get picked up by the user
when they find that they can't call a Visitor on that nested-class, they'll
soon change the name.

with Visitors being such a huge integral part of a good AST-walking /
Language-translator / Compiler API, "users" (us at the moment but
other people in future if we do drop this onto pypi) *will* keep a clean
global namespace.

think about HTML/DOM: imagine the total chaos if HTML's DOM
(Domain Object Model) had an object-type that was locally different
from the global type namespace, somewhere deep inside the structure.

if DOM.Window was different from DOM.Document.Window for example.

or if c had a different meaning for "struct" just because it was
inside a "switch" statement?

part of a clean AST / language / compiler is verrrry much about picking
a clean global namespace - *definitely* don't do nested classes in
insndb!!

again (just on nested-classes): "Principle of Least Surprise".

(i aim to propose to use insndb for work inside the ISA Working Group,
for walking the specification document which is now in latex)

-- 
You are receiving this mail because:
You are on the CC list for the bug.


More information about the libre-soc-bugs mailing list