def test_dict_list_ref(self): element = self.dict_list_ref state = self.translator.translate_dict_list_ref(TranslationState(element)) expected_rule = jsgf.HiddenRule("fruit_dict", jsgf.AlternativeSet("apple", "mango")) self.assertEqual(jsgf.RuleRef(expected_rule), state.expansion) self.assertListEqual([expected_rule], state.dependencies)
def test_list_referencing_rule(self): element = self.fruit_list_ref r = Rule("fav_fruit", element, exported=True) state = self.translator.translate_rule(r) list_rule = jsgf.HiddenRule("fruit", jsgf.AlternativeSet(*self.fruit_list)) expected_rule = LinkedRule("fav_fruit", True, jsgf.RuleRef(list_rule), r) self.assertEqual(state.jsgf_rule, expected_rule)
def translate_list(lst): """ Translate a dragonfly List object into a JSGF rule representing it. The returned rule is a HiddenRule because lists can only be referenced by rules using ListRef, not spoken directly. :type lst: DragonflyList :return: HiddenRule """ return jsgf.HiddenRule(lst.name, jsgf.AlternativeSet(*lst))
def _compile_alternative(self, element, *args, **kwargs): children = element.children if len(children) > 1: return jsgf.AlternativeSet( *[self.compile_element(c, *args, **kwargs) for c in children]) elif len(children) == 1: # Skip redundant (1 child) alternatives. return self.compile_element(children[0], *args, **kwargs) else: # Compile an Empty element for empty alternatives. return self.compile_element(elements_.Empty(), *args, **kwargs)
def translate_dict_list(dict_list): """ Translate a dragonfly DictList object into a JSGF rule representing it. The returned rule is a HiddenRule because dict lists can only be referenced by rules using DictListRef, not spoken directly. :type dict_list: DragonflyDictList :return: HiddenRule """ keys = dict_list.keys() keys.sort() return jsgf.HiddenRule(dict_list.name, jsgf.AlternativeSet(*keys))
def test_repeated_referenced_dictation(self): """ Test that dragonfly rules containing Dictation referenced by a RuleRef with a Repetition element as an ancestor is translated correctly, with rules joined as necessary. """ g = Grammar("test") # Add two simple rules, a more complex MappingRule and a rule referencing it # that allows repetition of its mappings r1 = Rule("dict", Dictation("dictation")) r2 = Rule("test1", Repetition(RuleRef(r1)), exported=True) r3 = MappingRule("mapping", mapping={ "testing": ActionBase(), "hello <dictation>": ActionBase(), "<dictation>": ActionBase(), }, extras=[Dictation("dictation")]) r4 = Rule("test2", Repetition(RuleRef(r3)), exported=True) g.add_rule(r2) g.add_rule(r4) # Translate the 'test' grammar and make some assertions translated = self.translator.translate_grammar(g) expected_test1 = LinkedRule( "test1", True, jsgf.Repeat(jsgf.ext.Dictation()), r1 ) # Note: this is not how the engine sees MappingRules with Dictation # elements: they are further processed later on into JSGF SequenceRules. # This is the format the rules should be in for the later processing. expected_test2 = LinkedRule("test2", True, jsgf.Repeat( jsgf.AlternativeSet( "testing", jsgf.Sequence("hello", jsgf.ext.Dictation()), jsgf.ext.Dictation() ) ), r4) # Neither of the referenced rules should be in the final grammar, only the # two joined rules self.assertListEqual(translated.rules, [expected_test1, expected_test2])
def test_dict_list(self): actual = self.translator.translate_dict_list(self.dict_list) expected = jsgf.HiddenRule("fruit_dict", jsgf.AlternativeSet("apple", "mango")) self.assertEqual(expected, actual)
def test_list_ref(self): element = self.fruit_list_ref state = self.translator.translate_list_ref(TranslationState(element)) jsgf_rule = jsgf.HiddenRule("fruit", jsgf.AlternativeSet(*self.fruit_list)) self.assertEqual(jsgf.RuleRef(jsgf_rule), state.expansion) self.assertListEqual([jsgf_rule], state.dependencies)
def get_jsgf_equiv(self, state): """ Take a TranslationState object containing a dragonfly element return the same object with the 'expansion' member set to the equivalent JSGF expansion object. :type state: TranslationState :return TranslationState """ element = state.element def get_equiv_children(): children = [] for child in state.element.children: state.element = child self.get_jsgf_equiv(state) children.append(state.expansion) return children if isinstance(element, Literal): # Literal == word list text = " ".join(element.words) state.expansion = jsgf.Literal(text) elif isinstance(element, (RuleRef, Rule)): # Translate the dragonfly rule reference self.translate_rule_ref(state) # DictListRef should be checked before ListRef because it is a subclass elif isinstance(element, (DictListRef, DictList)): self.translate_dict_list_ref(state) elif isinstance(element, (ListRef, List)): self.translate_list_ref(state) elif isinstance(element, Dictation): # A Sphinx decoder for handling dictation will be prepared and used # when necessary. state.expansion = jsgf.ext.Dictation() elif isinstance(element, Impossible): # VoidRef is the equivalent of dragonfly's Impossible element. # It is also automatically used by pyjsgf if an expansion that should # have at least one child (e.g. Sequence/Repeat) doesn't have any at # compile time. state.expansion = VoidRef() elif element.children == (): # improbable ElementBase case state.expansion = jsgf.Expansion([]) # Repetition should be checked before Sequence because it is a subclass elif isinstance(element, Repetition): # Repetition should have at least one child assert len(state.element.children) > 0 if self._has_dictation_descendant(element): self.translate_repetition_with_dictation(state) else: # Translate the first child only. The second child is of the form: # [first_child [first_child ... first_child ] ... ] # This is how matching a limited number of repetitions is done. # Limitation (min/max) values for Repetition elements are ignored # for the Sphinx engine for the moment. equiv_child = get_equiv_children()[0] state.expansion = PatchedRepeat(equiv_child) elif isinstance(element, Sequence): state.expansion = jsgf.Sequence(*get_equiv_children()) elif isinstance(element, Alternative): if len(state.element.children) == 1: # Skip redundant (1 child) alternatives state.element = state.element.children[0] self.get_jsgf_equiv(state) else: state.expansion = jsgf.AlternativeSet(*get_equiv_children()) elif isinstance(element, Optional): equiv_children = get_equiv_children() if len(equiv_children) != 1: raise TranslationError("Optional grouping may only have 1 child.") state.expansion = jsgf.OptionalGrouping(equiv_children[0]) return state
def get_jsgf_equiv(self, state): """ Take a TranslationState object containing a dragonfly element return the same object with the 'expansion' member set to the equivalent JSGF expansion object. :type state: TranslationState :return TranslationState """ element = state.element def get_equiv_children(): children = [] for child in state.element.children: state.element = child self.get_jsgf_equiv(state) children.append(state.expansion) return children if isinstance(element, Literal): # Literal == word list state.expansion = jsgf.Literal(element.gstring()) elif isinstance(element, (RuleRef, Rule)): # Translate the dragonfly rule reference self.translate_rule_ref(state) # DictListRef should be checked before ListRef because it is a subclass elif isinstance(element, (DictListRef, DictList)): self.translate_dict_list_ref(state) elif isinstance(element, (ListRef, List)): self.translate_list_ref(state) elif isinstance(element, Dictation): # A Sphinx decoder for handling dictation will be prepared and used # when necessary. state.expansion = jsgf.ext.Dictation() elif isinstance(element, Impossible): state.expansion = JSGFImpossible() elif element.children == (): # improbable ElementBase case state.expansion = jsgf.Expansion([]) # Repetition should be checked before Sequence because it is a subclass elif isinstance(element, Repetition): # Repetition should have at least one child assert len(state.element.children) > 0 if self._has_dictation_descendant(element): self.translate_repetition_with_dictation(state) elif len(element.children) == 1: state.expansion = jsgf.Repeat(*get_equiv_children()) else: state.expansion = jsgf.Sequence(*get_equiv_children()) elif isinstance(element, Sequence): state.expansion = jsgf.Sequence(*get_equiv_children()) elif isinstance(element, Alternative): if len(state.element.children) == 1: # Skip redundant (1 child) alternatives state.element = state.element.children[0] self.get_jsgf_equiv(state) else: state.expansion = jsgf.AlternativeSet(*get_equiv_children()) elif isinstance(element, Optional): equiv_children = get_equiv_children() if len(equiv_children) != 1: raise TranslationError( "Optional grouping may only have 1 child.") state.expansion = jsgf.OptionalGrouping(equiv_children[0]) return state