links-notation 0.9.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,14 @@
1
+ """
2
+ Platform.Protocols.Lino - Python implementation
3
+
4
+ Lino (Links Notation) is a simple, intuitive format for representing
5
+ structured data as links between references.
6
+ """
7
+
8
+ from .link import Link
9
+ from .parser import Parser
10
+ from .formatter import format_links
11
+
12
+ __version__ = "0.7.0"
13
+
14
+ __all__ = ["Link", "Parser", "format_links"]
@@ -0,0 +1,24 @@
1
+ """
2
+ Formatter for Lino notation.
3
+
4
+ Provides utilities for formatting Link objects back into Lino notation strings.
5
+ """
6
+
7
+ from typing import List
8
+ from .link import Link
9
+
10
+
11
+ def format_links(links: List[Link], less_parentheses: bool = False) -> str:
12
+ """
13
+ Format a list of links into Lino notation.
14
+
15
+ Args:
16
+ links: List of Link objects to format
17
+ less_parentheses: If True, omit parentheses where safe
18
+
19
+ Returns:
20
+ Formatted string in Lino notation
21
+ """
22
+ if not links:
23
+ return ''
24
+ return '\n'.join(link.format(less_parentheses) for link in links)
links_notation/link.py ADDED
@@ -0,0 +1,178 @@
1
+ """
2
+ Link class representing a Lino link with optional ID and values.
3
+ """
4
+
5
+ from typing import List, Optional
6
+
7
+
8
+ class Link:
9
+ """
10
+ Represents a link in Lino notation.
11
+
12
+ A link can be:
13
+ - A simple reference (id only, no values)
14
+ - A link with id and values
15
+ - A link with only values (no id)
16
+ """
17
+
18
+ def __init__(self, link_id: Optional[str] = None, values: Optional[List['Link']] = None):
19
+ """
20
+ Initialize a Link.
21
+
22
+ Args:
23
+ link_id: Optional identifier for the link
24
+ values: Optional list of child links
25
+ """
26
+ self.id = link_id
27
+ self.values = values if values is not None else []
28
+ self._is_from_path_combination = False
29
+
30
+ def __str__(self) -> str:
31
+ """String representation using standard formatting."""
32
+ return self.format(False)
33
+
34
+ def __repr__(self) -> str:
35
+ """Developer-friendly representation."""
36
+ return f"Link(id={self.id!r}, values={self.values!r})"
37
+
38
+ def __eq__(self, other) -> bool:
39
+ """Check equality with another Link."""
40
+ if not isinstance(other, Link):
41
+ return False
42
+ if self.id != other.id:
43
+ return False
44
+ if len(self.values) != len(other.values):
45
+ return False
46
+ return all(v1 == v2 for v1, v2 in zip(self.values, other.values))
47
+
48
+ def get_values_string(self) -> str:
49
+ """Get formatted string of all values."""
50
+ if not self.values:
51
+ return ''
52
+ return ' '.join(Link.get_value_string(v) for v in self.values)
53
+
54
+ def simplify(self) -> 'Link':
55
+ """
56
+ Simplify the link structure.
57
+ - If no values, return self
58
+ - If single value, return that value
59
+ - Otherwise return new Link with simplified values
60
+ """
61
+ if not self.values:
62
+ return self
63
+ elif len(self.values) == 1:
64
+ return self.values[0]
65
+ else:
66
+ new_values = [v.simplify() for v in self.values]
67
+ return Link(self.id, new_values)
68
+
69
+ def combine(self, other: 'Link') -> 'Link':
70
+ """Combine this link with another to create a compound link."""
71
+ return Link(None, [self, other])
72
+
73
+ @staticmethod
74
+ def get_value_string(value: 'Link') -> str:
75
+ """Get string representation of a value."""
76
+ return value.to_link_or_id_string()
77
+
78
+ @staticmethod
79
+ def escape_reference(reference: Optional[str]) -> str:
80
+ """
81
+ Escape a reference string if it contains special characters.
82
+
83
+ Args:
84
+ reference: The reference string to escape
85
+
86
+ Returns:
87
+ Escaped reference with quotes if needed
88
+ """
89
+ if not reference or not reference.strip():
90
+ return ''
91
+
92
+ # Check if single quotes are needed
93
+ needs_single_quotes = any(c in reference for c in [':', '(', ')', ' ', '\t', '\n', '\r', '"'])
94
+
95
+ if needs_single_quotes:
96
+ return f"'{reference}'"
97
+ elif "'" in reference:
98
+ return f'"{reference}"'
99
+ else:
100
+ return reference
101
+
102
+ def to_link_or_id_string(self) -> str:
103
+ """Convert to string, using just ID if no values, otherwise full format."""
104
+ if not self.values:
105
+ return Link.escape_reference(self.id) if self.id is not None else ''
106
+ return str(self)
107
+
108
+ def format(self, less_parentheses: bool = False, is_compound_value: bool = False) -> str:
109
+ """
110
+ Format the link as a string.
111
+
112
+ Args:
113
+ less_parentheses: If True, omit parentheses when safe
114
+ is_compound_value: If True, this is a value in a compound link
115
+
116
+ Returns:
117
+ Formatted string representation
118
+ """
119
+ # Empty link
120
+ if self.id is None and not self.values:
121
+ return '' if less_parentheses else '()'
122
+
123
+ # Link with only ID, no values
124
+ if not self.values:
125
+ escaped_id = Link.escape_reference(self.id)
126
+ # When used as a value in a compound link, wrap in parentheses
127
+ if is_compound_value:
128
+ return f'({escaped_id})'
129
+ return escaped_id if (less_parentheses and not self.needs_parentheses(self.id)) else f'({escaped_id})'
130
+
131
+ # Format values recursively
132
+ values_str = ' '.join(self.format_value(v) for v in self.values)
133
+
134
+ # Link with values only (null id)
135
+ if self.id is None:
136
+ if less_parentheses:
137
+ # Check if all values are simple (no nested values)
138
+ all_simple = all(not v.values for v in self.values)
139
+ if all_simple:
140
+ # Format each value without extra wrapping
141
+ return ' '.join(Link.escape_reference(v.id) for v in self.values)
142
+ # For mixed or complex values, return without outer wrapper
143
+ return values_str
144
+ # For normal mode, wrap in parentheses
145
+ return f'({values_str})'
146
+
147
+ # Link with ID and values
148
+ id_str = Link.escape_reference(self.id)
149
+ with_colon = f'{id_str}: {values_str}'
150
+ return with_colon if (less_parentheses and not self.needs_parentheses(self.id)) else f'({with_colon})'
151
+
152
+ def format_value(self, value: 'Link') -> str:
153
+ """
154
+ Format a single value within this link.
155
+
156
+ Args:
157
+ value: The value link to format
158
+
159
+ Returns:
160
+ Formatted string for the value
161
+ """
162
+ # Check if we're in a compound link from path combinations
163
+ is_compound_from_paths = self._is_from_path_combination
164
+
165
+ # For compound links from paths, format values with parentheses
166
+ if is_compound_from_paths:
167
+ return value.format(False, True)
168
+
169
+ # Simple link with just an ID - don't wrap in parentheses when used as a value
170
+ if not value.values:
171
+ return Link.escape_reference(value.id)
172
+
173
+ # Complex value with its own structure - format it normally with parentheses
174
+ return value.format(False, False)
175
+
176
+ def needs_parentheses(self, s: Optional[str]) -> bool:
177
+ """Check if a string needs to be wrapped in parentheses."""
178
+ return s and any(c in s for c in [' ', ':', '(', ')'])
@@ -0,0 +1,350 @@
1
+ """
2
+ Parser for Lino notation.
3
+
4
+ This module provides parsing functionality for Links Notation (Lino),
5
+ converting text into structured Link objects.
6
+ """
7
+
8
+ from typing import List, Optional, Dict, Any
9
+ from .link import Link
10
+
11
+
12
+ class ParseError(Exception):
13
+ """Exception raised when parsing fails."""
14
+
15
+
16
+ class Parser:
17
+ """
18
+ Parser for Lino notation.
19
+
20
+ Handles both inline and indented syntax for defining links.
21
+ """
22
+
23
+ def __init__(self):
24
+ """Initialize the parser."""
25
+ self.indentation_stack = [0]
26
+ self.pos = 0
27
+ self.text = ""
28
+ self.lines = []
29
+
30
+ def parse(self, input_text: str) -> List[Link]:
31
+ """
32
+ Parse Lino notation text into a list of Link objects.
33
+
34
+ Args:
35
+ input_text: Text in Lino notation
36
+
37
+ Returns:
38
+ List of parsed Link objects
39
+
40
+ Raises:
41
+ ParseError: If parsing fails
42
+ """
43
+ try:
44
+ if not input_text or not input_text.strip():
45
+ return []
46
+
47
+ self.text = input_text
48
+ self.lines = input_text.split('\n')
49
+ self.pos = 0
50
+ self.indentation_stack = [0]
51
+
52
+ raw_result = self._parse_document()
53
+ return self._transform_result(raw_result)
54
+ except Exception as e:
55
+ raise ParseError(f"Parse error: {str(e)}") from e
56
+
57
+ def _parse_document(self) -> List[Dict]:
58
+ """Parse the entire document."""
59
+ self.pos = 0
60
+ links = []
61
+
62
+ while self.pos < len(self.lines):
63
+ line = self.lines[self.pos]
64
+ if line.strip(): # Skip empty lines
65
+ element = self._parse_element(0)
66
+ if element:
67
+ links.append(element)
68
+ else:
69
+ self.pos += 1
70
+
71
+ return links
72
+
73
+ def _parse_element(self, current_indent: int) -> Optional[Dict]:
74
+ """Parse a single element (link or reference) at given indentation."""
75
+ if self.pos >= len(self.lines):
76
+ return None
77
+
78
+ line = self.lines[self.pos]
79
+ indent = len(line) - len(line.lstrip(' '))
80
+
81
+ if indent < current_indent:
82
+ return None
83
+
84
+ content = line.strip()
85
+ if not content:
86
+ self.pos += 1
87
+ return None
88
+
89
+ self.pos += 1
90
+
91
+ # Try to parse the line
92
+ element = self._parse_line_content(content)
93
+
94
+ # Check for children (indented lines that follow)
95
+ children = []
96
+ child_indent = indent + 2 # Expect at least 2 spaces for child
97
+
98
+ while self.pos < len(self.lines):
99
+ next_line = self.lines[self.pos]
100
+ next_indent = len(next_line) - len(next_line.lstrip(' '))
101
+
102
+ if next_line.strip() and next_indent > indent:
103
+ # This is a child
104
+ child = self._parse_element(child_indent if not children else indent + 2)
105
+ if child:
106
+ children.append(child)
107
+ else:
108
+ break
109
+
110
+ if children:
111
+ element['children'] = children
112
+
113
+ return element
114
+
115
+ def _parse_line_content(self, content: str) -> Dict:
116
+ """Parse the content of a single line."""
117
+ # Try multiline link format: (id: values) or (values)
118
+ if content.startswith('(') and content.endswith(')'):
119
+ inner = content[1:-1].strip()
120
+ return self._parse_parenthesized(inner)
121
+
122
+ # Try indented ID syntax: id:
123
+ if content.endswith(':'):
124
+ id_part = content[:-1].strip()
125
+ ref = self._extract_reference(id_part)
126
+ return {'id': ref, 'values': [], 'is_indented_id': True}
127
+
128
+ # Try single-line link: id: values
129
+ if ':' in content and not (content.startswith('"') or content.startswith("'")):
130
+ parts = content.split(':', 1)
131
+ if len(parts) == 2:
132
+ id_part = parts[0].strip()
133
+ values_part = parts[1].strip()
134
+ ref = self._extract_reference(id_part)
135
+ values = self._parse_values(values_part)
136
+ return {'id': ref, 'values': values}
137
+
138
+ # Simple value list
139
+ values = self._parse_values(content)
140
+ return {'values': values}
141
+
142
+ def _parse_parenthesized(self, inner: str) -> Dict:
143
+ """Parse content within parentheses."""
144
+ # Check for id: values format
145
+ colon_pos = self._find_colon_outside_quotes(inner)
146
+ if colon_pos >= 0:
147
+ id_part = inner[:colon_pos].strip()
148
+ values_part = inner[colon_pos + 1:].strip()
149
+ ref = self._extract_reference(id_part)
150
+ values = self._parse_values(values_part)
151
+ return {'id': ref, 'values': values}
152
+
153
+ # Just values
154
+ values = self._parse_values(inner)
155
+ return {'values': values}
156
+
157
+ def _find_colon_outside_quotes(self, text: str) -> int:
158
+ """Find the position of a colon that's not inside quotes."""
159
+ in_single = False
160
+ in_double = False
161
+
162
+ for i, char in enumerate(text):
163
+ if char == "'" and not in_double:
164
+ in_single = not in_single
165
+ elif char == '"' and not in_single:
166
+ in_double = not in_double
167
+ elif char == ':' and not in_single and not in_double:
168
+ return i
169
+
170
+ return -1
171
+
172
+ def _parse_values(self, text: str) -> List[Dict]:
173
+ """Parse a space-separated list of values."""
174
+ if not text:
175
+ return []
176
+
177
+ values = []
178
+ current = ""
179
+ in_single = False
180
+ in_double = False
181
+ paren_depth = 0
182
+
183
+ i = 0
184
+ while i < len(text):
185
+ char = text[i]
186
+
187
+ if char == "'" and not in_double:
188
+ in_single = not in_single
189
+ current += char
190
+ elif char == '"' and not in_single:
191
+ in_double = not in_double
192
+ current += char
193
+ elif char == '(' and not in_single and not in_double:
194
+ paren_depth += 1
195
+ current += char
196
+ elif char == ')' and not in_single and not in_double:
197
+ paren_depth -= 1
198
+ current += char
199
+ elif char == ' ' and not in_single and not in_double and paren_depth == 0:
200
+ # End of current value
201
+ if current.strip():
202
+ values.append(self._parse_value(current.strip()))
203
+ current = ""
204
+ else:
205
+ current += char
206
+
207
+ i += 1
208
+
209
+ # Add last value
210
+ if current.strip():
211
+ values.append(self._parse_value(current.strip()))
212
+
213
+ return values
214
+
215
+ def _parse_value(self, value: str) -> Dict:
216
+ """Parse a single value (could be a reference or nested link)."""
217
+ # Nested link in parentheses
218
+ if value.startswith('(') and value.endswith(')'):
219
+ inner = value[1:-1].strip()
220
+ return self._parse_parenthesized(inner)
221
+
222
+ # Simple reference
223
+ ref = self._extract_reference(value)
224
+ return {'id': ref}
225
+
226
+ def _extract_reference(self, text: str) -> str:
227
+ """Extract reference, handling quoted strings."""
228
+ text = text.strip()
229
+
230
+ # Double quoted
231
+ if text.startswith('"') and text.endswith('"'):
232
+ return text[1:-1]
233
+
234
+ # Single quoted
235
+ if text.startswith("'") and text.endswith("'"):
236
+ return text[1:-1]
237
+
238
+ # Unquoted
239
+ return text
240
+
241
+ def _transform_result(self, raw_result: List[Dict]) -> List[Link]:
242
+ """Transform raw parse result into Link objects."""
243
+ links = []
244
+
245
+ for item in raw_result:
246
+ if item:
247
+ self._collect_links(item, [], links)
248
+
249
+ return links
250
+
251
+ def _collect_links(self, item: Dict, parent_path: List[Link], result: List[Link]) -> None:
252
+ """
253
+ Recursively collect links from parse tree.
254
+
255
+ Handles both inline and indented syntax, flattening the hierarchy
256
+ appropriately.
257
+ """
258
+ if not item:
259
+ return
260
+
261
+ children = item.get('children', [])
262
+
263
+ # Special case: indented ID syntax (id: followed by children)
264
+ if item.get('is_indented_id') and item.get('id') and not item.get('values') and children:
265
+ child_values = []
266
+ for child in children:
267
+ # Extract the reference from child's values
268
+ if child.get('values') and len(child['values']) == 1:
269
+ child_values.append(self._transform_link(child['values'][0]))
270
+ else:
271
+ child_values.append(self._transform_link(child))
272
+
273
+ link_with_children = {
274
+ 'id': item['id'],
275
+ 'values': child_values
276
+ }
277
+ current_link = self._transform_link(link_with_children)
278
+
279
+ if not parent_path:
280
+ result.append(current_link)
281
+ else:
282
+ result.append(self._combine_path_elements(parent_path, current_link))
283
+
284
+ # Regular indented structure
285
+ elif children:
286
+ current_link = self._transform_link(item)
287
+
288
+ # Add the link combined with parent path
289
+ if not parent_path:
290
+ result.append(current_link)
291
+ else:
292
+ result.append(self._combine_path_elements(parent_path, current_link))
293
+
294
+ # Process each child with this item in the path
295
+ new_path = parent_path + [current_link]
296
+
297
+ for child in children:
298
+ self._collect_links(child, new_path, result)
299
+
300
+ # Leaf item or item with inline values
301
+ else:
302
+ current_link = self._transform_link(item)
303
+
304
+ if not parent_path:
305
+ result.append(current_link)
306
+ else:
307
+ result.append(self._combine_path_elements(parent_path, current_link))
308
+
309
+ def _combine_path_elements(self, path_elements: List[Link], current: Link) -> Link:
310
+ """Combine path elements into a single link."""
311
+ if not path_elements:
312
+ return current
313
+
314
+ if len(path_elements) == 1:
315
+ combined = Link(None, [path_elements[0], current])
316
+ combined._is_from_path_combination = True
317
+ return combined
318
+
319
+ # For multiple path elements, build proper nesting
320
+ parent_path = path_elements[:-1]
321
+ last_element = path_elements[-1]
322
+
323
+ # Build the parent structure
324
+ parent = self._combine_path_elements(parent_path, last_element)
325
+
326
+ # Add current element to the built structure
327
+ combined = Link(None, [parent, current])
328
+ combined._is_from_path_combination = True
329
+ return combined
330
+
331
+ def _transform_link(self, item: Any) -> Link:
332
+ """Transform a parsed item into a Link object."""
333
+ if isinstance(item, Link):
334
+ return item
335
+
336
+ if not isinstance(item, dict):
337
+ return Link(str(item))
338
+
339
+ # Simple reference
340
+ if 'id' in item and 'values' not in item:
341
+ return Link(item['id'])
342
+
343
+ # Link with values
344
+ if 'values' in item:
345
+ link_id = item.get('id')
346
+ values = [self._transform_link(v) for v in item['values']]
347
+ return Link(link_id, values)
348
+
349
+ # Default
350
+ return Link(item.get('id'))
@@ -0,0 +1,187 @@
1
+ Metadata-Version: 2.4
2
+ Name: links-notation
3
+ Version: 0.9.0
4
+ Summary: Python implementation of the Lino protocol parser
5
+ Author-email: LinksPlatform <noreply@linksplatform.com>
6
+ License: Unlicense
7
+ Project-URL: Homepage, https://github.com/link-foundation/links-notation
8
+ Project-URL: Repository, https://github.com/link-foundation/links-notation
9
+ Project-URL: Issues, https://github.com/link-foundation/links-notation/issues
10
+ Keywords: lino,parser,links,notation,protocol
11
+ Classifier: Development Status :: 4 - Beta
12
+ Classifier: Intended Audience :: Developers
13
+ Classifier: License :: Public Domain
14
+ Classifier: Programming Language :: Python :: 3
15
+ Classifier: Programming Language :: Python :: 3.8
16
+ Classifier: Programming Language :: Python :: 3.9
17
+ Classifier: Programming Language :: Python :: 3.10
18
+ Classifier: Programming Language :: Python :: 3.11
19
+ Classifier: Programming Language :: Python :: 3.12
20
+ Classifier: Topic :: Software Development :: Libraries :: Python Modules
21
+ Classifier: Topic :: Text Processing
22
+ Requires-Python: >=3.8
23
+ Description-Content-Type: text/markdown
24
+
25
+ # Platform.Protocols.Lino - Python
26
+
27
+ [![PyPI version](https://img.shields.io/pypi/v/platform-lino.svg)](https://pypi.org/project/platform-lino/)
28
+ [![Python versions](https://img.shields.io/pypi/pyversions/platform-lino.svg)](https://pypi.org/project/platform-lino/)
29
+ [![License](https://img.shields.io/badge/license-Unlicense-blue.svg)](../LICENSE)
30
+
31
+ Python implementation of the Lino (Links Notation) protocol parser.
32
+
33
+ ## Installation
34
+
35
+ ```bash
36
+ pip install platform-lino
37
+ ```
38
+
39
+ ## Quick Start
40
+
41
+ ```python
42
+ from platform_lino import Parser
43
+
44
+ parser = Parser()
45
+ links = parser.parse("papa (lovesMama: loves mama)")
46
+
47
+ # Access parsed links
48
+ for link in links:
49
+ print(link)
50
+ ```
51
+
52
+ ## Usage
53
+
54
+ ### Basic Parsing
55
+
56
+ ```python
57
+ from platform_lino import Parser, format_links
58
+
59
+ parser = Parser()
60
+
61
+ # Parse simple links
62
+ links = parser.parse("(papa: loves mama)")
63
+ print(links[0].id) # 'papa'
64
+ print(len(links[0].values)) # 2
65
+
66
+ # Format links back to string
67
+ output = format_links(links)
68
+ print(output) # (papa: loves mama)
69
+ ```
70
+
71
+ ### Working with Link Objects
72
+
73
+ ```python
74
+ from platform_lino import Link
75
+
76
+ # Create links programmatically
77
+ link = Link('parent', [Link('child1'), Link('child2')])
78
+ print(str(link)) # (parent: child1 child2)
79
+
80
+ # Access link properties
81
+ print(link.id) # 'parent'
82
+ print(link.values[0].id) # 'child1'
83
+
84
+ # Combine links
85
+ combined = link.combine(Link('another'))
86
+ print(str(combined)) # ((parent: child1 child2) another)
87
+ ```
88
+
89
+ ### Indented Syntax
90
+
91
+ ```python
92
+ parser = Parser()
93
+
94
+ # Parse indented notation
95
+ text = """3:
96
+ papa
97
+ loves
98
+ mama"""
99
+
100
+ links = parser.parse(text)
101
+ # Produces: (3: papa loves mama)
102
+ ```
103
+
104
+ ## API Reference
105
+
106
+ ### Parser
107
+
108
+ The main parser class for Lino notation.
109
+
110
+ - `parse(input_text: str) -> List[Link]`: Parse Lino text into Link objects
111
+
112
+ ### Link
113
+
114
+ Represents a link in Lino notation.
115
+
116
+ - `__init__(id: Optional[str] = None, values: Optional[List[Link]] = None)`
117
+ - `format(less_parentheses: bool = False) -> str`: Format as string
118
+ - `simplify() -> Link`: Simplify link structure
119
+ - `combine(other: Link) -> Link`: Combine with another link
120
+
121
+ ### format_links
122
+
123
+ Format a list of links into Lino notation.
124
+
125
+ - `format_links(links: List[Link], less_parentheses: bool = False) -> str`
126
+
127
+ ## Examples
128
+
129
+ ### Doublets (2-tuple)
130
+
131
+ ```python
132
+ parser = Parser()
133
+ text = """
134
+ papa (lovesMama: loves mama)
135
+ son lovesMama
136
+ daughter lovesMama
137
+ """
138
+ links = parser.parse(text)
139
+ ```
140
+
141
+ ### Triplets (3-tuple)
142
+
143
+ ```python
144
+ text = """
145
+ papa has car
146
+ mama has house
147
+ (papa and mama) are happy
148
+ """
149
+ links = parser.parse(text)
150
+ ```
151
+
152
+ ### Quoted References
153
+
154
+ ```python
155
+ # References with special characters need quotes
156
+ text = '("has space": "value with: colon")'
157
+ links = parser.parse(text)
158
+ ```
159
+
160
+ ## Development
161
+
162
+ ### Running Tests
163
+
164
+ ```bash
165
+ # Install development dependencies
166
+ pip install pytest
167
+
168
+ # Run tests
169
+ pytest
170
+ ```
171
+
172
+ ### Building
173
+
174
+ ```bash
175
+ pip install build
176
+ python -m build
177
+ ```
178
+
179
+ ## License
180
+
181
+ This project is released into the public domain under the [Unlicense](../LICENSE).
182
+
183
+ ## Links
184
+
185
+ - [Main Repository](https://github.com/link-foundation/links-notation)
186
+ - [PyPI Package](https://pypi.org/project/platform-lino/)
187
+ - [Documentation](https://link-foundation.github.io/links-notation/)
@@ -0,0 +1,8 @@
1
+ links_notation/__init__.py,sha256=PYY7mi-4h7kb-J7skq0WejhadC3-3oPTCy55_kMshTE,327
2
+ links_notation/formatter.py,sha256=yj_xgdaszHslJxTnuq2WSpKpsSruhez9zn6pO5QcZjo,589
3
+ links_notation/link.py,sha256=JjVDBEzOT8RRWXLbtl39DDMqqQCETWuHbPm_h5M3IiA,6193
4
+ links_notation/parser.py,sha256=O_mhTS9vjYP6CYfh2fuRDLSFFyBauMSHt4ujZbIfrcI,11252
5
+ links_notation-0.9.0.dist-info/METADATA,sha256=ep2_nwnU2mXU_pQGQQo_VzZHlVzs1Dtpi5CU559TTH8,4199
6
+ links_notation-0.9.0.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
7
+ links_notation-0.9.0.dist-info/top_level.txt,sha256=KyOejsXELNXBmuP4E4qBB8bRmuQWi4iwfpvprt2J8ZI,15
8
+ links_notation-0.9.0.dist-info/RECORD,,
@@ -0,0 +1,5 @@
1
+ Wheel-Version: 1.0
2
+ Generator: setuptools (80.9.0)
3
+ Root-Is-Purelib: true
4
+ Tag: py3-none-any
5
+
@@ -0,0 +1 @@
1
+ links_notation