Spaces:
Sleeping
Sleeping
| import re | |
| from typing import List, Tuple, Dict, Set | |
| def _parse_list(content: str) -> List[str]: | |
| """ | |
| Parse a string representing a list of literals into a Python list of strings. | |
| Args: | |
| content (str): A string in the form "[a,b,c]" or "a,b,c". | |
| Returns: | |
| List[str]: A list of literal identifiers as strings. | |
| Examples: | |
| >>> _parse_list("[a,b,c]") | |
| ['a', 'b', 'c'] | |
| >>> _parse_list("x,y,z") | |
| ['x', 'y', 'z'] | |
| >>> _parse_list("[]") | |
| [] | |
| """ | |
| content = content.strip() | |
| if content.startswith("[") and content.endswith("]"): | |
| content = content[1:-1].strip() | |
| if not content: | |
| return [] | |
| return [x.strip() for x in content.split(",") if x.strip()] | |
| def _parse_rule_line(line: str) -> Dict[str, Dict[str, Set[str]]]: | |
| """ | |
| Parse a single rule line of the form [rX]: head <- body. | |
| Args: | |
| line (str): Rule string, e.g. "[r1]: p <- q,a". | |
| Returns: | |
| Dict[str, Dict[str, Set[str]]]: Mapping of rule ID to {head: set of body literals}. | |
| Examples: | |
| >>> _parse_rule_line("[r1]: p <- q,a") | |
| {'r1': {'p': {'q', 'a'}}} | |
| >>> _parse_rule_line("[r2]: q <- ") | |
| {'r2': {'q': set()}} | |
| """ | |
| match = re.match(r"\[(r\d+)\]:\s*(\w+)\s*<-\s*(.*)", line) | |
| if match: | |
| r_id = match.group(1) | |
| head = match.group(2) | |
| body = match.group(3).strip() | |
| body_atoms = set(_parse_list(body)) if body else set() | |
| return {r_id: {head: body_atoms}} | |
| return {} | |
| def _parse_pref_line(line: str) -> Dict[str, Set[str]]: | |
| """ | |
| Parse a preference line of the form: | |
| - PREF: a,b > c,d > e | |
| - PREF: a,b > c and a > d | |
| Supports multiple chains joined by "and". | |
| Returns: | |
| Dict[str, Set[str]]: Dictionary mapping each element to all elements | |
| that come after it in the preference chain(s). | |
| Literals with no less-preferred values are omitted. | |
| """ | |
| content = line.split(":", 1)[1].strip() | |
| # Split into separate chains by 'and' | |
| chains = [chain.strip() for chain in content.split("and") if chain.strip()] | |
| pref_dict: Dict[str, Set[str]] = {} | |
| for chain in chains: | |
| groups = [group.strip() for group in chain.split(">")] | |
| parsed_groups = [_parse_list(group) for group in groups] | |
| for i, current_group in enumerate(parsed_groups): | |
| less_preferred = set() | |
| for j in range(i + 1, len(parsed_groups)): | |
| less_preferred.update(parsed_groups[j]) | |
| # Only add mapping if there are less preferred items | |
| if less_preferred: | |
| for literal in current_group: | |
| if literal not in pref_dict: | |
| pref_dict[literal] = set() | |
| pref_dict[literal].update(less_preferred) | |
| return pref_dict | |
| def parse_doc(path: str) -> Tuple[ | |
| List[str], | |
| List[str], | |
| List[Tuple[str, str]], | |
| List[Dict[str, Dict[str, Set[str]]]], | |
| List[Dict[str, Set[str]]] | |
| ]: | |
| """ | |
| Parse a structured document containing literals, assumptions, contraries, | |
| rules, and preferences. | |
| Args: | |
| path (str): Path to the text file to parse. | |
| Returns: | |
| Tuple containing: | |
| - language (List[str]): List of all literals in the language. | |
| - assumptions (List[str]): List of assumed literals. | |
| - contraries (List[Tuple[str, str]]): List of contrary pairs (literal, contrary). | |
| - rules (List[Dict[str, Dict[str, Set[str]]]]): List of rules, | |
| each rule is {rule_id: {head: set of body literals}}. | |
| - preferences (List[Dict[str, Set[str]]]): List of preference dictionaries, | |
| each mapping literals to sets of less preferred literals. | |
| Examples: | |
| >>> # doc.txt content: | |
| >>> # L: [a,b,c] | |
| >>> # A: [a,b] | |
| >>> # C(a): b | |
| >>> # [r1]: p <- q,a | |
| >>> # PREF: a,b > c,d > e > g > f | |
| >>> parse_doc("doc.txt") | |
| (['a','b','c'], ['a','b'], [('a','b')], [{'r1': {'p': {'q','a'}}}], | |
| [{'a': {'c','d','e','g','f'}, 'b': {'c','d','e','g','f'}, ...}]) | |
| """ | |
| language: List[str] = [] | |
| assumptions: List[str] = [] | |
| contraries: List[Tuple[str, str]] = [] | |
| rules: List[Dict[str, Dict[str, Set[str]]]] = [] | |
| preferences: List[Dict[str, Set[str]]] = [] | |
| with open(path, "r") as f: | |
| for line in f: | |
| line = line.strip() | |
| if not line: | |
| continue | |
| if line.startswith("L:"): | |
| language = _parse_list(line.split(":", 1)[1]) | |
| elif line.startswith("A:"): | |
| assumptions = _parse_list(line.split(":", 1)[1]) | |
| elif line.startswith("C("): | |
| match = re.match(r"C\((\w+)\):\s*(\w+)", line) | |
| if match: | |
| contraries.append((match.group(1), match.group(2))) | |
| elif line.startswith("[r"): | |
| rule = _parse_rule_line(line) | |
| if rule: | |
| rules.append(rule) | |
| elif line.startswith("PREF:"): | |
| pref_dict = _parse_pref_line(line) | |
| if pref_dict: | |
| preferences.append(pref_dict) | |
| return language, assumptions, contraries, rules, preferences | |
| if __name__ == "__main__": | |
| language, assumptions, contraries, rules, preferences = parse_doc( | |
| "./backend/data/simple_plus2AND.txt") | |
| print("Language:", language) | |
| print("Assumptions:", assumptions) | |
| print("Contraries:", contraries) | |
| print("Rules:", rules) | |
| print("Preferences:", preferences) |