You cannot select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
91 lines
2.6 KiB
Python
91 lines
2.6 KiB
Python
"""
|
|
A simple implementation of parsing and creating BIRD's OSPF state descriptions
|
|
|
|
The code-facing data structure is list of directives, which are tuples of string
|
|
(directive) and list of sub-directives. For example, the following input:
|
|
|
|
```
|
|
BIRD 2.0.9 ready.
|
|
|
|
area 0.0.0.0
|
|
|
|
router 172.23.100.1
|
|
distance 30
|
|
network [172.23.100.4-3] metric 10
|
|
network [172.23.100.10-2] metric 10
|
|
|
|
```
|
|
|
|
would be parsed into:
|
|
|
|
```python3
|
|
[('BIRD 2.0.9 ready.', []),
|
|
('area 0.0.0.0', [
|
|
('router 172.23.100.1', [
|
|
('distance 30', []),
|
|
('network [172.23.100.4-3] metric 10', []),
|
|
('network [172.23.100.10-2] metric 10', []),
|
|
]),
|
|
]), # end of area directive
|
|
] # end of the whole representation
|
|
```
|
|
|
|
We expect that users will read the representation sequentially, so we do not
|
|
provide any means of accessing individual sub-directives directly.
|
|
|
|
Here we only deal with syntax. What the directives mean and how to act on them
|
|
is up to modules using this. Therefore, we only handle strings and do not try
|
|
to understand them here. We only strip comments and whitespace. (We are
|
|
guessing here that the '#' symbol will not appear in real BIRD output.)
|
|
|
|
We use lists instead of tuples just for convenience. When encoding, we SHOULD
|
|
support other sequences as well. Our users likewise SHOULD not depend on this
|
|
being a list. Supplying a string instead of a tuple with an empty sequence is
|
|
supported and equivalent to specifying the tuple, both representing a directive
|
|
without children. Using empty list is currently considered canonical.
|
|
|
|
We provide functions dump, dumps, load and loads, similar to the API of json,
|
|
marshal, pickle and possibly others. Note however that unlike those, we cannot
|
|
represent arbitrary data, just the tuple-list trees described above.
|
|
"""
|
|
|
|
import re
|
|
|
|
def _tree_lines(tree: list, depth):
|
|
result = []
|
|
for child in tree:
|
|
if isinstance(child, str):
|
|
node = child
|
|
subtree = []
|
|
else:
|
|
node, subtree = child
|
|
result.append('\t'*depth + node)
|
|
result.extend(_tree_lines(subtree, depth+1))
|
|
return result
|
|
|
|
def dumps(tree):
|
|
return '\n'.join(_tree_lines(tree, 0))
|
|
|
|
def dump(tree, fp):
|
|
fp.write(dumps(tree))
|
|
|
|
def loads(s):
|
|
line_regex = re.compile(r'(\t*)([^#]*)(#.*)?')
|
|
result = []
|
|
open_directives = [result]
|
|
for line in s.splitlines():
|
|
indent, directive, comment = line_regex.match(line).groups()
|
|
directive = directive.strip()
|
|
indent = len(indent)
|
|
if directive == '': continue
|
|
new_node = (directive, [])
|
|
open_directives = open_directives[:indent+1]
|
|
open_directives[indent].append(new_node)
|
|
open_directives.append(new_node[1])
|
|
return result
|
|
|
|
def load(fp):
|
|
return loads(fp.read())
|
|
|
|
|