File size: 3,913 Bytes
ed6bec6
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
from typing import List, Dict, Any
from .glyph_dictionary_loader import get_dictionary
from .glyph_validator import validate_sequence, validate_roles


class GlyphSyntaxError(Exception): #strict syntax order of glyphs
    pass


ROLE_ORDER = [
    "actor",
    "action",
    "object",
    "modifier",
    "context"
]

CONTEXT_ORDER = [
    "place",
    "time",
    "emotion",
    "sensory",
    "social"
]


def _get_role(entry: Dict[str, Any]) -> str:
    """Return the highest-priority role for a glyph."""
    roles = entry.get("roles", [])
    for r in ROLE_ORDER:
        if r in roles:
            return r
    return "context"


def _get_context_type(entry: Dict[str, Any]) -> str:
    """Return the context subtype."""
    category = entry.get("category", "")

    if category.startswith("context_place"):
        return "place"
    if category.startswith("context_time"):
        return "time"
    if category.startswith("context_emotion") or category.startswith("emotion_context"):
        return "emotion"
    if category.startswith("context_sensory") or category.startswith("sensory_context"):
        return "sensory"
    if category.startswith("context_social") or category.startswith("social_context"):
        return "social"

    return "unknown"


def parse_syntax(glyphs: List[str]) -> Dict[str, Any]:
    """
    Strict syntax parser enforcing:
    - required roles
    - strict ordering
    - single actor/action/object
    - context last
    - deterministic structure
    """
    validate_sequence(glyphs)
    validate_roles(glyphs)

    dictionary = get_dictionary()

    syntax_tree = {
        "actor": None,
        "action": None,
        "object": None,
        "modifiers": [],
        "context": {
            "place": [],
            "time": [],
            "emotion": [],
            "sensory": [],
            "social": []
        },
        "raw": glyphs
    }

    last_role_index = -1
    last_context_index = -1

    for g in glyphs:
        entries = dictionary.get_entry_by_glyph(g)
        entry = entries[0]  # canonical
        role = _get_role(entry)

        # Enforce ordering
        role_index = ROLE_ORDER.index(role)
        if role_index < last_role_index:
            raise GlyphSyntaxError(
                f"Invalid ordering: {g} ({role}) appears after a later role."
            )
        last_role_index = role_index

        # Assign roles
        if role == "actor":
            if syntax_tree["actor"] is not None:
                raise GlyphSyntaxError("Multiple actors not allowed.")
            syntax_tree["actor"] = entry

        elif role == "action":
            if syntax_tree["action"] is not None:
                raise GlyphSyntaxError("Multiple actions not allowed.")
            syntax_tree["action"] = entry

        elif role == "object":
            if syntax_tree["object"] is not None:
                raise GlyphSyntaxError("Multiple objects not allowed.")
            syntax_tree["object"] = entry

        elif role == "modifier":
            syntax_tree["modifiers"].append(entry)

        elif role == "context":
            ctx_type = _get_context_type(entry)
            if ctx_type == "unknown":
                raise GlyphSyntaxError(f"Unknown context type for glyph {g}")

            # Enforce context ordering
            ctx_index = CONTEXT_ORDER.index(ctx_type)
            if ctx_index < last_context_index:
                raise GlyphSyntaxError(
                    f"Context out of order: {ctx_type} appears after later context."
                )
            last_context_index = ctx_index

            syntax_tree["context"][ctx_type].append(entry)

    return syntax_tree


def get_syntax_tree(glyphs: List[str]) -> Dict[str, Any]:
    return parse_syntax(glyphs)


def is_valid_order(glyphs: List[str]) -> bool:
    try:
        parse_syntax(glyphs)
        return True
    except Exception:
        return False