def test_same_local_and_imported_rule_names(self): """ Local rules with the same name have precedence over imported rules. """ grammar = Grammar("test") local_z = Rule("Z", True, "z") grammar.add_rule(local_z) grammar.add_import(Import("grammars.test1.Z")) self.assertEqual(grammar.get_rule("Z"), local_z)
def test_resolve_imports_cycles(self): """ Grammars that import from one another are resolved correctly. """ memo = {} grammar = Grammar("test") grammar.add_import(Import("grammars.cycle-test1.rule1")) grammar.add_import(Import("grammars.cycle-test2.rule2")) grammar.resolve_imports(memo) self.assertIn("grammars.cycle-test1", memo) self.assertIn("grammars.cycle-test2", memo) cycle_test1 = memo["grammars.cycle-test1"] cycle_test2 = memo["grammars.cycle-test2"] cycle_test1.resolve_imports() cycle_test2.resolve_imports() expected_environment = { "test": grammar, "grammars.cycle-test1": cycle_test1, "grammars.cycle-test1.rule1": cycle_test1.get_rule("rule1"), "grammars.cycle-test1.Y": cycle_test1.get_rule("Y"), "grammars.cycle-test2": cycle_test2, "grammars.cycle-test2.rule2": cycle_test2.get_rule("rule2"), "grammars.cycle-test2.X": cycle_test2.get_rule("X"), } self.assertIs(cycle_test1.import_environment["grammars.cycle-test2"], cycle_test2) self.assertIs(cycle_test2.import_environment["grammars.cycle-test1"], cycle_test1) for grammar_ in (cycle_test1, cycle_test2): self.assertDictEqual(grammar_.import_environment, expected_environment)
def test_resolve_imports_single(self): """ Grammar.resolve_imports() correctly handles importing a single rule. """ memo = {} grammar = Grammar("test") grammar.add_import(Import("grammars.test1.Z")) grammar.resolve_imports(memo) expected_grammar = self.grammars.test1 Z, = expected_grammar.get_rules("Z") self.assertDictEqual( memo, { "test": grammar, "grammars.test1": expected_grammar, "grammars.test1.Z": Z })
def test_fully_qualified_rule_reference(self): """ Fully-qualified rule references do not require import statements. """ grammar = Grammar("test") fully_qualified_ref = "grammars.test1.W" named_ref = NamedRuleRef(fully_qualified_ref) rule = Rule("rule", True, named_ref) grammar.add_rule(rule) expected_rule = self.grammars.test1.get_rule("W") for x in range(2): self.assertEqual(named_ref.referenced_rule, expected_rule) self.assertEqual(grammar.get_rule(fully_qualified_ref), expected_rule) self.assertEqual(grammar.find_matching_rules("w"), [rule]) # Check that the import statement is allowed. grammar.add_import(Import(fully_qualified_ref))
def test_resolve_imports_wildcard(self): """ Grammar.resolve_imports() correctly handles wildcard import statements. """ memo = {} grammar = Grammar("test") grammar.add_import(Import("grammars.test1.*")) grammar.resolve_imports(memo) expected_grammar = self.grammars.test1 Z, W = expected_grammar.get_rules("Z", "W") self.assertDictEqual( memo, { "test": grammar, "grammars.test1": expected_grammar, "grammars.test1.Z": Z, "grammars.test1.W": W, "grammars.test1.*": [Z, W] })
def test_ambiguous_qualified_names(self): """ Imported rules with the same qualified name must be fully referenced. """ grammar = Grammar("test") grammar.add_import(Import("grammars.test6.rule")) grammar.add_import(Import("grammars2.test6.rule")) self.assertRaises(GrammarError, grammar.get_rule, "rule") self.assertRaises(GrammarError, grammar.get_rule, "test6.rule") expected1 = parse_grammar_string(""" #JSGF V1.0; grammar grammars.test6; public <rule> = test grammars one; """) expected2 = parse_grammar_string(""" #JSGF V1.0; grammar grammars2.test6; public <rule> = test grammars two; """) self.assertEqual(grammar.get_rule("grammars.test6.rule"), expected1.get_rule("rule")) self.assertEqual(grammar.get_rule("grammars2.test6.rule"), expected2.get_rule("rule"))
def test_resolve_imports_multiple_grammars(self): """ Similar import statements in multiple grammars are handled efficiently. """ memo = {} grammar = Grammar("test") grammar.add_import(Import("grammars.test4.BackspaceRule")) grammar.add_import(Import("grammars.test5.DeleteRule")) grammar.resolve_imports(memo) self.assertIn("grammars.test4", memo) self.assertIn("grammars.test5", memo) test4 = memo["grammars.test4"] test5 = memo["grammars.test5"] # The import environments of 'test', 'test4', 'test5' and 'numbers' should be # the same. resolve_imports() should update the import environment of every # grammar present in the memo dictionary. test4.resolve_imports(memo) test5.resolve_imports(memo) self.assertIn("grammars.numbers", memo) numbers = memo["grammars.numbers"] expected_environment = { "test": grammar, "grammars.test4": test4, "grammars.test4.BackspaceRule": test4.get_rule("BackspaceRule"), "grammars.test5": test5, "grammars.test5.DeleteRule": test5.get_rule("DeleteRule"), "grammars.numbers": numbers, "grammars.numbers.1to39": numbers.get_rule("1to39"), } for grammar_ in (grammar, test4, test5, numbers): self.assertDictEqual(grammar_.import_environment, expected_environment) # The 'numbers' grammar should have only be parsed once, even though it is # used by two separate grammars. for grammar_ in (test4, test5): self.assertIs(grammar_.import_environment["grammars.numbers"], numbers)
def test_ambiguous_imported_rules(self): """ Imported rules with the same name cannot be referenced locally. """ grammar = Grammar("test") grammar.add_import(Import("grammars.test2.rule")) grammar.add_import(Import("grammars.test3.rule")) self.assertRaises(GrammarError, grammar.get_rule, "rule")