from __future__ import annotations
from typing import List
from fluent.syntax import FluentParser, ast as FTL
from fluent.syntax.visitor import Transformer
from .transforms import Transform, CONCAT, COPY, COPY_PATTERN
from .errors import NotSupportedError, InvalidTransformError
def VARIABLE_REFERENCE(name):
return FTL.VariableReference(id=FTL.Identifier(name))
def MESSAGE_REFERENCE(name):
if "." in name:
name, attribute = name.split(".")
attribute = FTL.Identifier(attribute)
else:
attribute = None
return FTL.MessageReference(
id=FTL.Identifier(name),
attribute=attribute,
)
def TERM_REFERENCE(name):
return FTL.TermReference(id=FTL.Identifier(name))
class IntoTranforms(Transformer):
IMPLICIT_TRANSFORMS = ("CONCAT",)
FORBIDDEN_TRANSFORMS = ("PLURALS", "REPLACE", "REPLACE_IN_TEXT")
def __init__(self, substitutions):
self.substitutions = substitutions
def visit_Junk(self, node):
anno = node.annotations[0]
raise InvalidTransformError(
"Transform contains parse error: {}, at {}".format(
anno.message, anno.span.start
)
)
def visit_FunctionReference(self, node):
name = node.id.name
if name in self.IMPLICIT_TRANSFORMS:
raise NotSupportedError(
"{} may not be used with transforms_from(). It runs "
"implicitly on all Patterns anyways.".format(name)
)
if name in self.FORBIDDEN_TRANSFORMS:
raise NotSupportedError(
"{} may not be used with transforms_from(). It requires "
"additional logic in Python code.".format(name)
)
if name in ("COPY", "COPY_PATTERN"):
args = (self.into_argument(arg) for arg in node.arguments.positional)
kwargs = {
arg.name.name: self.into_argument(arg.value)
for arg in node.arguments.named
}
if name == "COPY":
return COPY(*args, **kwargs)
return COPY_PATTERN(*args, **kwargs)
return self.generic_visit(node)
def visit_Placeable(self, node):
node = self.generic_visit(node)
if isinstance(node.expression, Transform):
return node.expression
return node
def visit_Pattern(self, node):
node = self.generic_visit(node)
return CONCAT(*node.elements)
def into_argument(self, node):
if isinstance(node, FTL.StringLiteral):
if node.value == "True":
return True
if node.value == "False":
return False
return node.value
if isinstance(node, FTL.MessageReference):
try:
return self.substitutions[node.id.name]
except KeyError:
raise InvalidTransformError(
"Unknown substitution in COPY: {}".format(node.id.name)
)
else:
raise InvalidTransformError(
"Invalid argument passed to COPY: {}".format(type(node).__name__)
)
def transforms_from(ftl, **substitutions) -> List[FTL.Message | FTL.Term]:
parser = FluentParser(with_spans=False)
resource = parser.parse(ftl)
return IntoTranforms(substitutions).visit(resource).body