Skip to content

Commit a9ac448

Browse files
committed
subtypes can have any length, pattern matching treats such cases correctly
1 parent cd84627 commit a9ac448

5 files changed

Lines changed: 78 additions & 10 deletions

File tree

CHANGELOG.md

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
# Changelog
22

3-
## [0.10.1] - work in progress
3+
## [0.11.0] - work in progress
44

55
### Added
66

@@ -15,6 +15,7 @@
1515

1616
### Changed
1717

18+
- subtypes can have any length, pattern matching treats such cases correctly.
1819
- transforms propagate per-atom `tok_pos`/`text_span` through rewrites.
1920
- REPL `/load` now uses `ParseResult.from_dict` (was dropping `tokens`/`tok_pos`).
2021
- REPL shows multiple results if available.

src/hyperbase/hyperedge.py

Lines changed: 10 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -330,14 +330,17 @@ def connector_mtype(self) -> str | None:
330330

331331
def atom_with_type(self, atom_type: str) -> Atom | None:
332332
"""Returns the first atom found in the edge that has the given
333-
'atom_type', or whose type starts with 'atom_type'.
333+
'atom_type'. A bare main type (single character) matches any subtype;
334+
a type with a subtype must match exactly.
334335
If no such atom is found, returns None.
335336
336337
For example, given the edge (+/B a/Cn b/Cp) and the 'atom_type'
337-
c, this function returns:
338+
'C', this function returns:
338339
a/Cn
339340
If the 'atom_type' is 'Cp', the it will return:
340341
b/Cp
342+
Querying for 'Cm' would NOT match an atom typed 'Cmath' --
343+
only an exact 'Cm' would.
341344
"""
342345
for item in self:
343346
atom: Atom | None = item.atom_with_type(atom_type)
@@ -612,11 +615,12 @@ def connector_type(self) -> str | None:
612615

613616
def atom_with_type(self, atom_type: str) -> Atom | None:
614617
et = self.type()
615-
n = len(atom_type)
616-
if len(et) >= n and et[:n] == atom_type:
618+
if len(atom_type) == 1:
619+
if et and et[0] == atom_type:
620+
return self
621+
elif et == atom_type:
617622
return self
618-
else:
619-
return None
623+
return None
620624

621625
def argroles(self) -> str:
622626
if "argroles" in self._cache:

src/hyperbase/patterns/matcher.py

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -163,12 +163,15 @@ def _matches_atomic_pattern(edge: Hyperedge, atomic_pattern: Atom) -> bool:
163163
if len(ap_parts) > 1:
164164
pos = 1
165165

166-
# type match
166+
# type match: a bare main type (single character) matches any
167+
# subtype; a type that includes a subtype must match exactly.
167168
ap_role = atomic_pattern.role()
168169
ap_type = ap_role[0]
169170
e_type = edge.type()
170-
n = len(ap_type)
171-
if len(e_type) < n or e_type[:n] != ap_type:
171+
if len(ap_type) == 1:
172+
if not e_type or e_type[0] != ap_type:
173+
return False
174+
elif e_type != ap_type:
172175
return False
173176

174177
e_atom = edge.inner_atom()

tests/test_hyperedge.py

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -436,6 +436,24 @@ def test_non_atom_mtype9(self):
436436
def test_non_atom_mtype10(self):
437437
assert hedge("(and/J (is/Pd.so hyperbase/Cp.s great/C))").mtype() == "R"
438438

439+
def test_atom_type_long_subtype(self):
440+
assert hedge("exp/Cmath").type() == "Cmath"
441+
assert hedge("exp/Cmath").mtype() == "C"
442+
443+
def test_atom_role_long_subtype(self):
444+
assert hedge("was/Ppast.so").role() == ["Ppast", "so"]
445+
assert hedge("was/Ppast.so").type() == "Ppast"
446+
assert hedge("was/Ppast.so").mtype() == "P"
447+
assert hedge("was/Ppast.so").argroles() == "so"
448+
449+
def test_non_atom_type_long_subtype(self):
450+
assert hedge("(was/Ppast.so john/C art/C)").type() == "Rpast"
451+
assert hedge("(was/Ppast.so john/C art/C)").mtype() == "R"
452+
453+
def test_non_atom_type_long_modifier_subtype(self):
454+
assert hedge("(red/Mcolor shoes/Cc.p)").type() == "Cc"
455+
assert hedge("(before/Tlong noon/C)").type() == "Slong"
456+
439457
def test_connector_type1(self):
440458
assert hedge("hyperbase/Cp.s/1").connector_type() is None
441459

@@ -553,6 +571,26 @@ def test_atom_with_type6(self):
553571
def test_atom_with_type7(self):
554572
assert hedge("a/Cn").atom_with_type("P") is None
555573

574+
def test_atom_with_type_long_subtype1(self):
575+
edge = hedge("(is/Pd.so john/Cmath rich/C)")
576+
assert edge.atom_with_type("Cmath") == hedge("john/Cmath")
577+
578+
def test_atom_with_type_long_subtype2(self):
579+
edge = hedge("(is/Pd.so john/Cmath rich/C)")
580+
assert edge.atom_with_type("C") == hedge("john/Cmath")
581+
582+
def test_atom_with_type_long_subtype3(self):
583+
edge = hedge("(is/Pd.so john/Cmath rich/C)")
584+
assert edge.atom_with_type("Cmusic") is None
585+
586+
def test_atom_with_type_partial_subtype_no_match(self):
587+
edge = hedge("(is/Pd.so john/Cmath rich/C)")
588+
assert edge.atom_with_type("Cm") is None
589+
590+
def test_atom_with_type_short_subtype_no_match_against_long(self):
591+
edge = hedge("(is/Pd.so john/Cmath rich/Cm)")
592+
assert edge.atom_with_type("Cm") == hedge("rich/Cm")
593+
556594
def test_argroles_connector_atom1(self):
557595
edge = hedge("s/Bp.am")
558596
assert edge.argroles() == "am"

tests/test_patterns.py

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -115,6 +115,28 @@ def test_match_pattern_atomic_wildcard4(self):
115115
== []
116116
)
117117

118+
def test_match_pattern_bare_main_type_matches_long_subtype(self):
119+
assert match_pattern("(is/Pd john/Cmath rich/C)", "(is/Pd */C rich/C)") == [{}]
120+
121+
def test_match_pattern_bare_main_type_matches_short_subtype(self):
122+
assert match_pattern("(is/Pd john/Cp.s rich/C)", "(is/Pd */C rich/C)") == [{}]
123+
124+
def test_match_pattern_exact_subtype_matches(self):
125+
assert match_pattern("(is/Pd john/Cm rich/C)", "(is/Pd */Cm rich/C)") == [{}]
126+
127+
def test_match_pattern_short_subtype_does_not_match_long(self):
128+
assert match_pattern("(is/Pd john/Cmath rich/C)", "(is/Pd */Cm rich/C)") == []
129+
130+
def test_match_pattern_long_subtype_exact_match(self):
131+
assert match_pattern("(is/Pd john/Cmath rich/C)", "(is/Pd */Cmath rich/C)") == [
132+
{}
133+
]
134+
135+
def test_match_pattern_long_subtype_does_not_match_other_long(self):
136+
assert (
137+
match_pattern("(is/Pd john/Cmath rich/C)", "(is/Pd */Cmusic rich/C)") == []
138+
)
139+
118140
def test_match_pattern_non_atomic_wildcard1(self):
119141
assert match_pattern(
120142
"(is/Pd hyperbase/Cp.s (fairly/M great/C))", "(is/Pd hyperbase/Cp.s (PROP))"

0 commit comments

Comments
 (0)