Castle: The best Real-Time/Embedded/HighTech language EVER. Attempt 2
Revision | 2383f011abe75da952ca8a9775e3f8912accacdb (tree) |
---|---|
Time | 2022-01-29 05:18:19 |
Author | Albert Mietus < albert AT mietus DOT nl > |
Commiter | Albert Mietus < albert AT mietus DOT nl > |
Started with AST-2-XML serialization
@@ -1,3 +1,7 @@ | ||
1 | +from .serialization import Serialize | |
2 | + | |
3 | + | |
4 | + | |
1 | 5 | class AST_BASE: |
2 | 6 | """Base class for all Castle ATS nodes""" |
3 | 7 |
@@ -9,26 +13,31 @@ | ||
9 | 13 | def __str__(self): # mostly for debugging |
10 | 14 | return str(type(self).__name__) + "\n\t" + "\n\t".join(f'{n}\t{str(v)}:{type(v).__name__}' for n,v in self.__dict__.items() if n[0]!='_') |
11 | 15 | |
12 | - @staticmethod | |
13 | - def _typeName(x): | |
14 | - return type(x).__name__ | |
15 | - | |
16 | - def _valType(self, x): | |
17 | - return f'{x}:{self._typeName(x)}' | |
18 | - return type(x).__name__ | |
16 | + def serialize(self, strategy="XML") -> str: | |
17 | + return Serialize(strategy).serialize(self) | |
19 | 18 | |
20 | 19 | @property |
21 | 20 | def position(self): return self._parse_tree.position |
22 | 21 | @property |
23 | 22 | def position_end(self): return self._parse_tree.position_end |
24 | 23 | |
24 | + ### Mostly for debugging | |
25 | + @staticmethod | |
26 | + def _typeName(of): | |
27 | + return type(of).__name__ | |
28 | + | |
29 | + def _valType(self, of:None): | |
30 | + if not of: of=self | |
31 | + return f'{of}:{self._typeName(of)}' | |
32 | + | |
25 | 33 | |
26 | 34 | class IDError(ValueError): |
27 | 35 | "The given ID is not valid as an ID" |
28 | 36 | |
29 | -import re | |
37 | + | |
30 | 38 | |
31 | 39 | class ID(AST_BASE): |
40 | + import re | |
32 | 41 | _pattern = re.compile(r'[A-Za-z_][A-Za-z0-9_]*') |
33 | 42 | |
34 | 43 | @staticmethod |
@@ -0,0 +1,86 @@ | ||
1 | +import logging; logger = logging.getLogger(__name__) | |
2 | + | |
3 | +class Serialize (): | |
4 | + def __new__(cls, strategy=None): | |
5 | + if strategy is None: | |
6 | + return object.__new__(cls) | |
7 | + if str(strategy).upper() == "XML": | |
8 | + return XML_Serialize() | |
9 | + else: | |
10 | + raise NotImplementedError(f"No Serializer of {strategy} available") | |
11 | + | |
12 | + def serialize(self, ast): | |
13 | + raise NotImplementedError(f"Implement in subclass") | |
14 | + | |
15 | +from xml.etree import ElementTree as ET | |
16 | + | |
17 | + | |
18 | +class XML_Serialize(Serialize): | |
19 | + def serialize(self, ast) -> str: | |
20 | + logger.debug(f"ast={ast._valType(ast)}") | |
21 | + | |
22 | + tree = self._ast2xml(ast) | |
23 | + return ET.tostring(tree, encoding="unicode") | |
24 | + | |
25 | + | |
26 | + def _ast2xml(self, ast) -> ET.Element: | |
27 | + top = ET.Element('AST2XML', version="0.0") | |
28 | + | |
29 | + method_name = f'{type(ast).__name__}2xml' | |
30 | + visitor = getattr(self, method_name, None) | |
31 | + logger.debug(f'visitor={visitor}') | |
32 | + | |
33 | + if visitor: | |
34 | + visitor(ast=ast, parent=top) # Grow the tree | |
35 | + else: | |
36 | + logger.info(f'No visitor >>{method_name}<<, skipping ... (fingers crossed)') | |
37 | + | |
38 | + logger.debug(f"XXX top={top}") | |
39 | + return top | |
40 | + | |
41 | + | |
42 | + def ID2xml(self, ast, parent) ->None: | |
43 | + logger.debug(f"ast={ast._valType(ast)} parent={parent} ast.name={ast.name}") | |
44 | + ET.SubElement(parent, 'ID', name=ast.name) | |
45 | + | |
46 | + | |
47 | +#NO_VISITOR_NEEDED: PEG2xml ## Pure Abstract | |
48 | +#NO_VISITOR_NEEDED: MixIn_value_attribute2xml ## MixIn | |
49 | +#NO_VISITOR_NEEDED: MixIn_expr_attribute2xml ## MixIn | |
50 | +#NO_VISITOR_NEEDED: MixIn_children_as_tuple2xml ## MixIn | |
51 | +#NO_VISITOR_NEEDED: Terminal2xml ## Pure Abstract | |
52 | +#NO_VISITOR_NEEDED: NonTerminal2xml ## Pure Abstract | |
53 | +#NO_VISITOR_NEEDED: Expression2xml ## Pure Abstract | |
54 | +#NO_VISITOR_NEEDED: Predicate2xml ## Pure Abstract | |
55 | +#NO_VISITOR_NEEDED: Group2xml ## Pure Abstract | |
56 | +#NO_VISITOR_NEEDED: Markers2xml ## Pure Abstract | |
57 | + | |
58 | + | |
59 | + def StrTerm2xml(self, ast, parent) ->None: | |
60 | + logger.debug(f"StrTerm2xml:: value={ast._valType(ast.value)}") | |
61 | + ET.SubElement(parent, 'StrTerm', value=ast.value) | |
62 | + | |
63 | + def RegExpTerm2xml(self, ast, parent) ->None: | |
64 | + logger.debug(f"RegExpTerm2xml:: value={ast._valType(ast.value)}") | |
65 | + ET.SubElement(parent, 'RegExpTerm', value=ast.value) | |
66 | + | |
67 | + | |
68 | + | |
69 | +############# | |
70 | + | |
71 | + def Rule2xml(self, ast, parent) ->None: ... | |
72 | + def Rules2xml(self, ast, parent) ->None: ... | |
73 | + def Setting2xml(self, ast, parent) ->None: ... | |
74 | + def Settings2xml(self, ast, parent) ->None: ... | |
75 | + def Grammar2xml(self, ast, parent) ->None: ... | |
76 | + def UnorderedGroup2xml(self, ast, parent) ->None: ... | |
77 | + def Quantity2xml(self, ast, parent) ->None: ... | |
78 | + def Sequence2xml(self, ast, parent) ->None: ... | |
79 | + def OrderedChoice2xml(self, ast, parent) ->None: ... | |
80 | + def Optional2xml(self, ast, parent) ->None: ... | |
81 | + def ZeroOrMore2xml(self, ast, parent) ->None: ... | |
82 | + def OneOrMore2xml(self, ast, parent) ->None: ... | |
83 | + def AndPredicate2xml(self, ast, parent) ->None: ... | |
84 | + def NotPredicate2xml(self, ast, parent) ->None: ... | |
85 | + | |
86 | + def EOF2xml(self, ast, parent) ->None: pass # Needed |
@@ -0,0 +1,34 @@ | ||
1 | +import pytest | |
2 | +from xml.etree import ElementTree as ET | |
3 | + | |
4 | +from castle.ast import peg, serialization | |
5 | + | |
6 | +@pytest.fixture | |
7 | +def xml_serialize(): | |
8 | + return serialization.Serialize('xml').serialize | |
9 | + | |
10 | + | |
11 | +def assert_xml_Element(txt, tag, | |
12 | + version="0.0", | |
13 | + **attribs,): | |
14 | + tree = ET.fromstring(txt) | |
15 | + if version: | |
16 | + assert tree.attrib['version'] == version | |
17 | + founds = tree.findall(tag) | |
18 | + assert len(founds) == 1, f"Expected only one element; got: {len(founds)} :{founds}" | |
19 | + found = founds[0] | |
20 | + for attrib, value in attribs.items(): | |
21 | + assert found.attrib[attrib] == value | |
22 | + | |
23 | + | |
24 | +def test_ID(xml_serialize): | |
25 | + txt = xml_serialize(peg.ID(name='demo_ID')) | |
26 | + assert_xml_Element(txt, tag='ID', name='demo_ID') | |
27 | + | |
28 | +def test_StrTerm(xml_serialize): | |
29 | + txt = xml_serialize(peg.StrTerm(value='demo string')) | |
30 | + assert_xml_Element(txt, tag='StrTerm', value='demo string') | |
31 | + | |
32 | +def test_RegExpTerm(xml_serialize): | |
33 | + txt = xml_serialize(peg.RegExpTerm(value='demo RegExp')) | |
34 | + assert_xml_Element(txt, tag='RegExpTerm', value='demo RegExp') |