1# coding=utf8 2from __future__ import unicode_literals 3from __future__ import absolute_import 4 5import textwrap 6 7import fluent.syntax.ast as FTL 8from fluent.syntax.parser import FluentParser, FluentParserStream 9 10 11fluent_parser = FluentParser(with_spans=False) 12 13 14def parse(Parser, string): 15 if Parser is FluentParser: 16 return fluent_parser.parse(string) 17 18 # Parsing a legacy resource. 19 20 # Parse the string into the internal Context. 21 parser = Parser() 22 # compare-locales expects ASCII strings. 23 parser.readContents(string.encode('utf8')) 24 # Transform the parsed result which is an iterator into a dict. 25 return {ent.key: ent for ent in parser} 26 27 28def ftl_resource_to_ast(code): 29 return fluent_parser.parse(ftl(code)) 30 31 32def ftl_resource_to_json(code): 33 return fluent_parser.parse(ftl(code)).to_json() 34 35 36def ftl_pattern_to_json(code): 37 ps = FluentParserStream(ftl(code)) 38 return fluent_parser.maybe_get_pattern(ps).to_json() 39 40 41def to_json(merged_iter): 42 return { 43 path: resource.to_json() 44 for path, resource in merged_iter 45 } 46 47 48LOCALIZABLE_ENTRIES = (FTL.Message, FTL.Term) 49 50 51def get_message(body, ident): 52 """Get message called `ident` from the `body` iterable.""" 53 for entity in body: 54 if isinstance(entity, LOCALIZABLE_ENTRIES) and entity.id.name == ident: 55 return entity 56 57 58def get_transform(body, ident): 59 """Get entity called `ident` from the `body` iterable.""" 60 for transform in body: 61 if transform.id.name == ident: 62 return transform 63 64 65def skeleton(node): 66 """Create a skeleton copy of the given node. 67 68 For localizable entries, the value is None and the attributes are {}. 69 That's not a valid Fluent entry, so it requires further manipulation to 70 set values and/or attributes. 71 """ 72 if isinstance(node, LOCALIZABLE_ENTRIES): 73 return type(node)(id=node.id.clone(), value=None) 74 return node.clone() 75 76 77def ftl(code): 78 """Nicer indentation for FTL code. 79 80 The code returned by this function is meant to be compared against the 81 output of the FTL Serializer. The input code will end with a newline to 82 match the output of the serializer. 83 """ 84 85 # The code might be triple-quoted. 86 code = code.lstrip('\n') 87 88 return textwrap.dedent(code) 89 90 91def fold(fun, node, init): 92 """Reduce `node` to a single value using `fun`. 93 94 Apply `fun` against an accumulator and each subnode of `node` (in postorder 95 traversal) to reduce it to a single value. 96 """ 97 98 def fold_(vals, acc): 99 if not vals: 100 return acc 101 102 head = list(vals)[0] 103 tail = list(vals)[1:] 104 105 if isinstance(head, FTL.BaseNode): 106 acc = fold(fun, head, acc) 107 if isinstance(head, list): 108 acc = fold_(head, acc) 109 if isinstance(head, dict): 110 acc = fold_(head.values(), acc) 111 112 return fold_(tail, fun(acc, head)) 113 114 return fold_(vars(node).values(), init) 115