Skip to content

Commit e04c5bf

Browse files
committed
EdgeType and ArgRole enums
1 parent 1ac2cf2 commit e04c5bf

8 files changed

Lines changed: 128 additions & 55 deletions

File tree

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44

55
### Added
66
- [] pattern notation for specifying sequences of arguments.
7+
- EdgeType and ArgRole enums.
78

89
### Changed
910
- multiple patterns functions are now Hyperedge/Atom methods: is_wildcard, is_pattern, is_fun_pattern, is_variable, contains_variable, variable_name

src/hyperbase/cli/repl.py

Lines changed: 9 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020
from rich.tree import Tree
2121

2222
from hyperbase.builders import hedge
23+
from hyperbase.constants import EdgeType
2324
from hyperbase.hyperedge import Atom, Hyperedge
2425
from hyperbase.parsers import Parser, get_parser, list_parsers
2526
from hyperbase.parsers.correctness import badness_check
@@ -42,14 +43,14 @@
4243

4344

4445
TYPE_COLORS = {
45-
"C": "#4A9EFF",
46-
"M": "#00E5CC",
47-
"B": "#5DC4FF",
48-
"P": "#FF8C42",
49-
"T": "#00CDB8",
50-
"J": "#00FF87",
51-
"R": "#FFD700",
52-
"S": "#FF6EC7",
46+
EdgeType.CONCEPT: "#4A9EFF",
47+
EdgeType.MODIFIER: "#00E5CC",
48+
EdgeType.BUILDER: "#5DC4FF",
49+
EdgeType.PREDICATE: "#FF8C42",
50+
EdgeType.TRIGGER: "#00CDB8",
51+
EdgeType.CONJUNCTION: "#00FF87",
52+
EdgeType.RELATION: "#FFD700",
53+
EdgeType.SPECIFIER: "#FF6EC7",
5354
}
5455

5556

src/hyperbase/constants.py

Lines changed: 59 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,36 @@
1+
from enum import Enum
2+
3+
4+
class EdgeType(str, Enum):
5+
"""SH edge types."""
6+
7+
CONCEPT = "C"
8+
PREDICATE = "P"
9+
MODIFIER = "M"
10+
BUILDER = "B"
11+
TRIGGER = "T"
12+
CONJUNCTION = "J"
13+
RELATION = "R"
14+
SPECIFIER = "S"
15+
16+
17+
class ArgRole(str, Enum):
18+
"""Argument roles for predicates and builders."""
19+
20+
MAIN = "m"
21+
SUBJECT = "s"
22+
PASSIVE = "p"
23+
AGENT = "a"
24+
COMPLEMENT = "c"
25+
OBJECT = "o"
26+
INDIRECT = "i"
27+
PARATAXIS = "t"
28+
INTERJECTION = "j"
29+
SPECIFICATION = "x"
30+
RELATIVE = "r"
31+
UNDETERMINED = "?"
32+
33+
134
# Pre-defined system atoms
235
compound_noun_builder = "+/B/."
336
possessive_builder = "poss/Bp.am/."
@@ -8,20 +41,32 @@
841

942
# Argument role ordering for normalisation
1043
argrole_order: dict[str, int] = {
11-
"m": -1,
12-
"s": 0,
13-
"p": 1,
14-
"a": 2,
15-
"c": 3,
16-
"o": 4,
17-
"i": 5,
18-
"t": 6,
19-
"j": 7,
20-
"x": 8,
21-
"r": 9,
22-
"?": 10,
44+
ArgRole.MAIN: -1,
45+
ArgRole.SUBJECT: 0,
46+
ArgRole.PASSIVE: 1,
47+
ArgRole.AGENT: 2,
48+
ArgRole.COMPLEMENT: 3,
49+
ArgRole.OBJECT: 4,
50+
ArgRole.INDIRECT: 5,
51+
ArgRole.PARATAXIS: 6,
52+
ArgRole.INTERJECTION: 7,
53+
ArgRole.SPECIFICATION: 8,
54+
ArgRole.RELATIVE: 9,
55+
ArgRole.UNDETERMINED: 10,
2356
}
2457

2558
# Valid argument roles by connector type
26-
valid_p_argroles: set[str] = {"s", "p", "a", "c", "o", "i", "t", "j", "x", "r", "?"}
27-
valid_b_argroles: set[str] = {"m", "a"}
59+
valid_p_argroles: set[str] = {
60+
ArgRole.SUBJECT,
61+
ArgRole.PASSIVE,
62+
ArgRole.AGENT,
63+
ArgRole.COMPLEMENT,
64+
ArgRole.OBJECT,
65+
ArgRole.INDIRECT,
66+
ArgRole.PARATAXIS,
67+
ArgRole.INTERJECTION,
68+
ArgRole.SPECIFICATION,
69+
ArgRole.RELATIVE,
70+
ArgRole.UNDETERMINED,
71+
}
72+
valid_b_argroles: set[str] = {ArgRole.MAIN, ArgRole.AGENT}

src/hyperbase/correctness.py

Lines changed: 27 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
from typing import TYPE_CHECKING
55

66
import hyperbase.constants as const
7+
from hyperbase.constants import EdgeType
78

89
if TYPE_CHECKING:
910
from hyperbase.hyperedge import Atom, Hyperedge
@@ -21,7 +22,14 @@ def _check_atom(atom: Atom) -> dict[Hyperedge, list[tuple[str, str]]]:
2122
errors: list[tuple[str, str]] = []
2223

2324
at = atom.mtype()
24-
if at not in {"C", "P", "M", "B", "T", "J"}:
25+
if at not in {
26+
EdgeType.CONCEPT,
27+
EdgeType.PREDICATE,
28+
EdgeType.MODIFIER,
29+
EdgeType.BUILDER,
30+
EdgeType.TRIGGER,
31+
EdgeType.CONJUNCTION,
32+
}:
2533
errors.append(("bad-atom-type", f"{at} is not a valid atom type"))
2634

2735
if len(errors) > 0:
@@ -36,49 +44,55 @@ def _check_edge(edge: Hyperedge) -> dict[Hyperedge, list[tuple[str, str]]]:
3644

3745
ct = edge[0].mtype()
3846
# check if connector has valid type
39-
if ct not in {"P", "M", "B", "T", "J"}:
47+
if ct not in {
48+
EdgeType.PREDICATE,
49+
EdgeType.MODIFIER,
50+
EdgeType.BUILDER,
51+
EdgeType.TRIGGER,
52+
EdgeType.CONJUNCTION,
53+
}:
4054
errors.append(("conn-bad-type", f"connector has incorrect type: {ct}"))
4155
# check if modifier structure is correct
42-
if ct == "M":
56+
if ct == EdgeType.MODIFIER:
4357
if len(edge) != 2:
4458
errors.append(("mod-1-arg", "modifiers can only have one argument"))
4559
# check if builder structure is correct
46-
elif ct == "B":
60+
elif ct == EdgeType.BUILDER:
4761
if len(edge) != 3:
4862
errors.append(("build-2-args", "builders can only have two arguments"))
4963
for arg in edge[1:]:
5064
at = arg.mtype()
51-
if at != "C":
65+
if at != EdgeType.CONCEPT:
5266
e = f"builder argument {arg!s} has incorrect type: {at}"
5367
errors.append(("build-arg-bad-type", e))
5468
# check if trigger structure is correct
55-
elif ct == "T":
69+
elif ct == EdgeType.TRIGGER:
5670
if len(edge) != 2:
5771
errors.append(("trig-1-arg", "triggers can only have one arguments"))
5872
for arg in edge[1:]:
5973
at = arg.mtype()
60-
if at not in {"C", "R"}:
74+
if at not in {EdgeType.CONCEPT, EdgeType.RELATION}:
6175
e = f"trigger argument {arg!s} has incorrect type: {at}"
6276
errors.append(("trig-bad-arg-type", e))
6377
# check if predicate structure is correct
64-
elif ct == "P":
78+
elif ct == EdgeType.PREDICATE:
6579
for arg in edge[1:]:
6680
at = arg.mtype()
67-
if at not in {"C", "R", "S"}:
81+
if at not in {EdgeType.CONCEPT, EdgeType.RELATION, EdgeType.SPECIFIER}:
6882
e = f"predicate argument {arg!s} has incorrect type: {at}"
6983
errors.append(("pred-arg-bad-type", e))
7084
# check if conjunction structure is correct
71-
elif ct == "J" and len(edge) < 3:
85+
elif ct == EdgeType.CONJUNCTION and len(edge) < 3:
7286
errors.append(
7387
("conj-2-args-min", "conjunctions must have at least two arguments")
7488
)
7589

7690
# check argrole counts
77-
if ct in {"P", "B"}:
91+
if ct in {EdgeType.PREDICATE, EdgeType.BUILDER}:
7892
try:
7993
ars = edge.argroles()
8094
if len(ars) > 0:
81-
if ct == "P":
95+
if ct == EdgeType.PREDICATE:
8296
for ar in ars:
8397
if ar not in const.valid_p_argroles:
8498
errors.append(
@@ -88,7 +102,7 @@ def _check_edge(edge: Hyperedge) -> dict[Hyperedge, list[tuple[str, str]]]:
88102
"for connector of type P",
89103
)
90104
)
91-
elif ct == "B":
105+
elif ct == EdgeType.BUILDER:
92106
for ar in ars:
93107
if ar not in const.valid_b_argroles:
94108
errors.append(

src/hyperbase/hyperedge.py

Lines changed: 17 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,8 @@
44
from dataclasses import dataclass
55
from typing import Any, overload
66

7+
from hyperbase.constants import EdgeType
8+
79

810
@dataclass(frozen=True, init=False, eq=False, repr=False)
911
class Hyperedge:
@@ -222,19 +224,19 @@ def type(self) -> str:
222224
Type inference is performed.
223225
"""
224226
ptype = self[0].type()
225-
if ptype[0] == "P":
226-
outter_type = "R"
227-
elif ptype[0] == "M":
227+
if ptype[0] == EdgeType.PREDICATE:
228+
outter_type = EdgeType.RELATION
229+
elif ptype[0] == EdgeType.MODIFIER:
228230
if len(self) < 2:
229231
raise RuntimeError(
230232
f"Edge is malformed, type cannot be determined: {self!s}"
231233
)
232234
return self[1].type() # type: ignore[no-any-return]
233-
elif ptype[0] == "T":
234-
outter_type = "S"
235-
elif ptype[0] == "B":
236-
outter_type = "C"
237-
elif ptype[0] == "J":
235+
elif ptype[0] == EdgeType.TRIGGER:
236+
outter_type = EdgeType.SPECIFIER
237+
elif ptype[0] == EdgeType.BUILDER:
238+
outter_type = EdgeType.CONCEPT
239+
elif ptype[0] == EdgeType.CONJUNCTION:
238240
if len(self) < 2:
239241
raise RuntimeError(
240242
f"Edge is malformed, type cannot be determined: {self!s}"
@@ -245,7 +247,7 @@ def type(self) -> str:
245247
f"Edge is malformed, type cannot be determined: {self!s}"
246248
)
247249

248-
return f"{outter_type}{ptype[1:]}"
250+
return outter_type + ptype[1:]
249251

250252
def connector_type(self) -> str | None:
251253
"""Returns the type of the edge's connector.
@@ -306,9 +308,12 @@ def argroles(self) -> str:
306308
of/B.ma has argument roles "ma".
307309
"""
308310
et = self.mtype()
309-
if et in {"R", "C"} and self[0].mtype() in {"B", "P"}:
311+
if et in {EdgeType.RELATION, EdgeType.CONCEPT} and self[0].mtype() in {
312+
EdgeType.BUILDER,
313+
EdgeType.PREDICATE,
314+
}:
310315
return self[0].argroles() # type: ignore[no-any-return]
311-
if et not in {"B", "P"}:
316+
if et not in {EdgeType.BUILDER, EdgeType.PREDICATE}:
312317
return ""
313318
return self[1].argroles() # type: ignore[no-any-return]
314319

@@ -552,7 +557,7 @@ def atom_with_type(self, atom_type: str) -> Atom | None:
552557

553558
def argroles(self) -> str:
554559
et = self.mtype()
555-
if et not in {"B", "P"}:
560+
if et not in {EdgeType.BUILDER, EdgeType.PREDICATE}:
556561
return ""
557562
role = self.role()
558563
if len(role) < 2:

src/hyperbase/parsers/correctness.py

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22

33
from typing import Any
44

5+
from hyperbase.constants import EdgeType
56
from hyperbase.hyperedge import Hyperedge
67
from hyperbase.parsers.utils import filter_alphanumeric_strings
78

@@ -36,9 +37,13 @@ def _visit(current_edge: Hyperedge) -> None:
3637

3738
# Junction checks
3839
try:
39-
if current_edge[0].mt == "J":
40+
if current_edge[0].mt == EdgeType.CONJUNCTION:
4041
types = {child.mt for child in current_edge[1:]}
41-
if types != {"R"} and types != {"C"} and types != {"R", "S"}:
42+
if (
43+
types != {EdgeType.RELATION}
44+
and types != {EdgeType.CONCEPT}
45+
and types != {EdgeType.RELATION, EdgeType.SPECIFIER}
46+
):
4247
current_errors.append(
4348
(
4449
"bad-junction-types",

src/hyperbase/patterns/matcher.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66

77
import hyperbase.constants as const
88
from hyperbase import hedge
9+
from hyperbase.constants import EdgeType
910
from hyperbase.hyperedge import Atom, Hyperedge
1011

1112
# tok_pos can be nested lists/ints matching the edge structure
@@ -180,7 +181,7 @@ def _matches_atomic_pattern(edge: Hyperedge, atomic_pattern: Atom) -> bool:
180181
return False
181182

182183
# argroles match
183-
if ap_type[0] in {"B", "P"}:
184+
if ap_type[0] in {EdgeType.BUILDER, EdgeType.PREDICATE}:
184185
ap_argroles_parts = ap_role[1].split("-")
185186
if len(ap_argroles_parts) == 1:
186187
ap_argroles_parts.append("")

src/hyperbase/transforms.py

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -4,14 +4,15 @@
44

55
import hyperbase.constants as const
66
from hyperbase.builders import hedge
7+
from hyperbase.constants import EdgeType
78
from hyperbase.hyperedge import Atom, Hyperedge, UniqueAtom
89

910

1011
def normalise(edge: Hyperedge) -> Hyperedge:
1112
"""Return a normalised copy of the edge (argument roles sorted)."""
1213
if edge.atom:
1314
atom = cast(Atom, edge)
14-
if atom.mtype() in {"B", "P"}:
15+
if atom.mtype() in {EdgeType.BUILDER, EdgeType.PREDICATE}:
1516
ar = atom.argroles()
1617
if len(ar) > 0:
1718
if ar[0] == "{":
@@ -104,11 +105,11 @@ def replace_argroles(edge: Hyperedge, argroles: str | None) -> Hyperedge:
104105
return Atom("/".join(parts))
105106
else:
106107
st = edge.mtype()
107-
if st in {"C", "R"}:
108+
if st in {EdgeType.CONCEPT, EdgeType.RELATION}:
108109
new_edge = [replace_argroles(edge[0], argroles)]
109110
new_edge += edge[1:]
110111
return Hyperedge(new_edge)
111-
elif st in {"P", "B"}:
112+
elif st in {EdgeType.PREDICATE, EdgeType.BUILDER}:
112113
new_edge = [edge[0], replace_argroles(edge[1], argroles)]
113114
new_edge += list(edge[2:])
114115
return Hyperedge(new_edge)
@@ -132,11 +133,11 @@ def insert_argrole(edge: Hyperedge, argrole: str, pos: int) -> Hyperedge:
132133
return replace_argroles(edge, argroles)
133134
else:
134135
st = edge.mtype()
135-
if st in {"C", "R"}:
136+
if st in {EdgeType.CONCEPT, EdgeType.RELATION}:
136137
new_edge = [insert_argrole(edge[0], argrole, pos)]
137138
new_edge += edge[1:]
138139
return Hyperedge(new_edge)
139-
elif st in {"P", "B"}:
140+
elif st in {EdgeType.PREDICATE, EdgeType.BUILDER}:
140141
new_edge = [edge[0], insert_argrole(edge[1], argrole, pos)]
141142
new_edge += list(edge[2:])
142143
return Hyperedge(new_edge)

0 commit comments

Comments
 (0)