Skip to content

Generic enter/exit visitor to replace BaseNode.traverse and friends #97

Description

@Pike

The simple read-only visitor we landed in #96 is great for performance, but doesn't cut it for actual transformations. I'm having a local branch, that I hope is generic enough to replace traverse.

Not happy with the name of it, I'm using ContextVisitor, because it uses enter and exit, but I can't really use python context managers, because you can't pass arguments to __exit__.

I'm implementing .traverse() as part of the patch to fluent.syntax, but to make things more tangible, here's how transforms_from could look like. Compare with https://hg.mozilla.org/l10n/fluent-migration/file/797c19359d4b/fluent/migrate/helpers.py#l43 through line 123.

classIntoTranforms(FTL.ContextVisitor):
IMPLICIT_TRANSFORMS= ("CONCAT",)
FORBIDDEN_TRANSFORMS= ("PLURALS", "REPLACE", "REPLACE_IN_TEXT")
def__init__(self, substitutions):
self.substitutions=substitutionsdefgeneric_exit(self, node, props):
returnnode.__class__(**props)
defenter_Junk(self, node):
anno=node.annotations[0]
raiseInvalidTransformError(
"Transform contains parse error: {}, at {}".format(
anno.message, anno.span.start))
defenter_CallExpression(self, node):
name=node.callee.id.nameifnameinself.IMPLICIT_TRANSFORMS:
raiseNotSupportedError(
"{} may not be used with transforms_from(). It runs ""implicitly on all Patterns anyways.".format(name))
ifnameinself.FORBIDDEN_TRANSFORMS:
raiseNotSupportedError(
"{} may not be used with transforms_from(). It requires ""additional logic in Python code.".format(name))
returnTruedefexit_CallExpression(self, node, props):
ifnode.callee.id.name!='COPY':
returnself.generic_exit(node, props)
args= (self.into_argument(arg) forarginnode.positional)
kwargs= {
arg.name.name: self.into_argument(arg.value)
forarginnode.named}
returnCOPY(*args, **kwargs)
defexit_Placeable(self, node, props):
ifisinstance(props['expression'], Transform):
returnprops['expression']
returnself.generic_exit(node, props)
defexit_Pattern(self, node, props):
# Replace the Pattern with CONCAT which is more accepting of its# elements. CONCAT takes PatternElements, Expressions and other# Patterns (e.g. returned from evaluating transforms).returnCONCAT(*props['elements'])
definto_argument(self, node):
"""Convert AST node into an argument to migration transforms."""ifisinstance(node, FTL.StringLiteral):
# Special cases for booleans which don't exist in Fluent.ifnode.value=="True":
returnTrueifnode.value=="False":
returnFalsereturnnode.valueifisinstance(node, FTL.MessageReference):
try:
returnself.substitutions[node.id.name]
exceptKeyError:
raiseInvalidTransformError(
"Unknown substitution in COPY: {}".format(
node.id.name))
else:
raiseInvalidTransformError(
"Invalid argument passed to COPY: {}".format(
type(node).__name__))

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions