links-notation 0.9.0__tar.gz

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,3 @@
1
+ include README.md
2
+ include LICENSE
3
+ recursive-include platform_lino *.py
@@ -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,163 @@
1
+ # Platform.Protocols.Lino - Python
2
+
3
+ [![PyPI version](https://img.shields.io/pypi/v/platform-lino.svg)](https://pypi.org/project/platform-lino/)
4
+ [![Python versions](https://img.shields.io/pypi/pyversions/platform-lino.svg)](https://pypi.org/project/platform-lino/)
5
+ [![License](https://img.shields.io/badge/license-Unlicense-blue.svg)](../LICENSE)
6
+
7
+ Python implementation of the Lino (Links Notation) protocol parser.
8
+
9
+ ## Installation
10
+
11
+ ```bash
12
+ pip install platform-lino
13
+ ```
14
+
15
+ ## Quick Start
16
+
17
+ ```python
18
+ from platform_lino import Parser
19
+
20
+ parser = Parser()
21
+ links = parser.parse("papa (lovesMama: loves mama)")
22
+
23
+ # Access parsed links
24
+ for link in links:
25
+ print(link)
26
+ ```
27
+
28
+ ## Usage
29
+
30
+ ### Basic Parsing
31
+
32
+ ```python
33
+ from platform_lino import Parser, format_links
34
+
35
+ parser = Parser()
36
+
37
+ # Parse simple links
38
+ links = parser.parse("(papa: loves mama)")
39
+ print(links[0].id) # 'papa'
40
+ print(len(links[0].values)) # 2
41
+
42
+ # Format links back to string
43
+ output = format_links(links)
44
+ print(output) # (papa: loves mama)
45
+ ```
46
+
47
+ ### Working with Link Objects
48
+
49
+ ```python
50
+ from platform_lino import Link
51
+
52
+ # Create links programmatically
53
+ link = Link('parent', [Link('child1'), Link('child2')])
54
+ print(str(link)) # (parent: child1 child2)
55
+
56
+ # Access link properties
57
+ print(link.id) # 'parent'
58
+ print(link.values[0].id) # 'child1'
59
+
60
+ # Combine links
61
+ combined = link.combine(Link('another'))
62
+ print(str(combined)) # ((parent: child1 child2) another)
63
+ ```
64
+
65
+ ### Indented Syntax
66
+
67
+ ```python
68
+ parser = Parser()
69
+
70
+ # Parse indented notation
71
+ text = """3:
72
+ papa
73
+ loves
74
+ mama"""
75
+
76
+ links = parser.parse(text)
77
+ # Produces: (3: papa loves mama)
78
+ ```
79
+
80
+ ## API Reference
81
+
82
+ ### Parser
83
+
84
+ The main parser class for Lino notation.
85
+
86
+ - `parse(input_text: str) -> List[Link]`: Parse Lino text into Link objects
87
+
88
+ ### Link
89
+
90
+ Represents a link in Lino notation.
91
+
92
+ - `__init__(id: Optional[str] = None, values: Optional[List[Link]] = None)`
93
+ - `format(less_parentheses: bool = False) -> str`: Format as string
94
+ - `simplify() -> Link`: Simplify link structure
95
+ - `combine(other: Link) -> Link`: Combine with another link
96
+
97
+ ### format_links
98
+
99
+ Format a list of links into Lino notation.
100
+
101
+ - `format_links(links: List[Link], less_parentheses: bool = False) -> str`
102
+
103
+ ## Examples
104
+
105
+ ### Doublets (2-tuple)
106
+
107
+ ```python
108
+ parser = Parser()
109
+ text = """
110
+ papa (lovesMama: loves mama)
111
+ son lovesMama
112
+ daughter lovesMama
113
+ """
114
+ links = parser.parse(text)
115
+ ```
116
+
117
+ ### Triplets (3-tuple)
118
+
119
+ ```python
120
+ text = """
121
+ papa has car
122
+ mama has house
123
+ (papa and mama) are happy
124
+ """
125
+ links = parser.parse(text)
126
+ ```
127
+
128
+ ### Quoted References
129
+
130
+ ```python
131
+ # References with special characters need quotes
132
+ text = '("has space": "value with: colon")'
133
+ links = parser.parse(text)
134
+ ```
135
+
136
+ ## Development
137
+
138
+ ### Running Tests
139
+
140
+ ```bash
141
+ # Install development dependencies
142
+ pip install pytest
143
+
144
+ # Run tests
145
+ pytest
146
+ ```
147
+
148
+ ### Building
149
+
150
+ ```bash
151
+ pip install build
152
+ python -m build
153
+ ```
154
+
155
+ ## License
156
+
157
+ This project is released into the public domain under the [Unlicense](../LICENSE).
158
+
159
+ ## Links
160
+
161
+ - [Main Repository](https://github.com/link-foundation/links-notation)
162
+ - [PyPI Package](https://pypi.org/project/platform-lino/)
163
+ - [Documentation](https://link-foundation.github.io/links-notation/)
@@ -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)
@@ -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 [' ', ':', '(', ')'])