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__))
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 usesenterandexit, 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 tofluent.syntax, but to make things more tangible, here's howtransforms_fromcould look like. Compare with https://hg.mozilla.org/l10n/fluent-migration/file/797c19359d4b/fluent/migrate/helpers.py#l43 through line 123.