• R/O
  • SSH

Commit

Tags
Keine Tags

Frequently used words (click to add to your profile)

javac++androidlinuxc#windowsobjective-ccocoa誰得qtpythonphprubygameguibathyscaphec計画中(planning stage)翻訳omegatframeworktwitterdomtestvb.netdirectxゲームエンジンbtronarduinopreviewer

Castle: The best Real-Time/Embedded/HighTech language EVER. Attempt 2


Commit MetaInfo

Revision8a2d8be44debb6113b2229ef5cdccb567c5a2a74 (tree)
Zeit2022-02-12 02:26:37
AutorAlbert Mietus < albert AT mietus DOT nl >
CommiterAlbert Mietus < albert AT mietus DOT nl >

Log Message

We can now parse alternativies (OrderedChoice)

Ändern Zusammenfassung

Diff

diff -r 27e007fd92d5 -r 8a2d8be44deb castle/ast/peg.py
--- a/castle/ast/peg.py Tue Feb 08 22:55:16 2022 +0100
+++ b/castle/ast/peg.py Fri Feb 11 18:26:37 2022 +0100
@@ -13,13 +13,14 @@
1313 """With this MixIn PEG-classes get the ``.value`` property"""
1414
1515 def __init__(self, *, value=None, **kwargs):
16+ logger.debug(f'{self._typeName(self)}.MixIn_value_attribute:: value:=' +
17+ ('[[' +', '.join(f'{v}:{type(v).__name__}' for v in value) + ']]') if isinstance(value, list) else f's>>{value}<<')
1618 super().__init__(**kwargs)
17- logger.debug(f'{self._typeName(self)}:: value:={value}:{self._typeName(value)}')
1819 self._value=value
1920
2021 @property
2122 def value(self):
22- logger.debug(f'{self._typeName(self)}:: @property={self._value}')
23+ logger.debug(f'{self._typeName(self)}:: @value={self._value}')
2324 return self._value
2425
2526
@@ -27,17 +28,21 @@
2728 """With this MixIn PEG-classes get the ``.expr`` property"""
2829
2930 def __init__(self, *, expr=None, **kwargs):
31+ logger.debug(f'{self._typeName(self)}.MixIn_expr_attribute:: expr:={self._valType(expr)}')
3032 super().__init__(**kwargs)
3133 self._expr = expr
3234
3335 @property
3436 def expr(self):
37+ logger.debug(f'{self._typeName(self)}:: @expr={self._expr}')
3538 return self._expr
3639
3740
3841 class MixIn_children_tuple:
3942 """With this MixIn PEG-class get the ``.children`` property; and sequence-alike methods"""
4043 def __init__(self, *, children, **kwargs):
44+ logger.debug(f'{self._typeName(self)}.MixIn_children_tuple:: children[{len(children)}]:=' +
45+ ('[[' +', '.join(f'{c}:{type(c).__name__}' for c in children) + ']]') if isinstance(children, list) else f's>>{children}<<')
4146 super().__init__(**kwargs)
4247 self._childeren = tuple(children)
4348
@@ -125,8 +130,14 @@
125130 def __len__(self): return len(self._value)
126131 def __getitem__(self, n): return self._value[n]
127132
133+ def __str__(self): # mostly for debugging
134+ return "Seq{{" + " ; ".join(f"{c}" for c in self._value) + "}}" # XXX ToDo: _value -> children
128135
129-class OrderedChoice(Expression):pass # It a an set of alternatives
136+class OrderedChoice(MixIn_children_tuple, Expression): # A | B | C | ... the order is relevant
137+ """OC: A _tuple_ of alternative expressions"""
138+
139+ def __str__(self): # mostly for debugging
140+ return "OC{{" + " | ".join(f"{c}" for c in self._childeren) + "}}"
130141
131142 class Optional(Quantity):pass
132143 class ZeroOrMore(Quantity):pass
diff -r 27e007fd92d5 -r 8a2d8be44deb castle/readers/parser/grammar.py
--- a/castle/readers/parser/grammar.py Tue Feb 08 22:55:16 2022 +0100
+++ b/castle/readers/parser/grammar.py Fri Feb 11 18:26:37 2022 +0100
@@ -4,14 +4,17 @@
44
55 def peg_grammar(): return rules, EOF
66 def rules(): return OneOrMore(rule)
7-def rule(): return rule_name, '<-', expressions, ";"
7+def rule(): return rule_name, '<-', expression, ";"
88
9-def expressions(): return ( OneOrMore(single_expr), Optional( '|' , expressions ) )
10-def single_expr(): return ( [ rule_crossref, term, group, predicate ], op_quantity)
9+def expression(): return expressions, op_alternative
10+def expressions(): return OneOrMore(single_expr)
11+def single_expr(): return [ rule_crossref, term, group, predicate ], op_quantity
1112
13+def op_alternative(): return Optional( '|' , expression )
1214 def op_quantity(): return Optional([ '?' , '*' , '+' , '#' ])
15+
1316 def term(): return [ str_term, regex_term ]
14-def group(): return '(', expressions, ')'
17+def group(): return '(', expression, ')'
1518 def predicate(): return ['&','!'], single_expr
1619
1720 def str_term(): return [ (S3, str_no_s3, S3),
diff -r 27e007fd92d5 -r 8a2d8be44deb castle/readers/parser/visitor.py
--- a/castle/readers/parser/visitor.py Tue Feb 08 22:55:16 2022 +0100
+++ b/castle/readers/parser/visitor.py Fri Feb 11 18:26:37 2022 +0100
@@ -17,9 +17,11 @@
1717 #NO_VISITOR_NEEDED: visit_term
1818 #NO_VISITOR_NEEDED: visit_re_no_slash
1919 #NO_VISITOR_NEEDED: visit_group
20-#NO_VISITOR_NEEDED: visit_op_quantity (before: vist_expr_quantity)
20+#NO_VISITOR_NEEDED: visit_op_quantity -- handle in visit_single_expr
2121
2222 class PegVisitor(arpeggio.PTNodeVisitor):
23+ def _logstr_node_children(self, node, children):
24+ return f'>>{node}<< children[{len(children)}] >>' + ", ".join(f'{c}:{type(c).__name__}' for c in children) + '<<'
2325
2426 def visit_str_term(self, node, children):
2527 return peg.StrTerm(value=node[1], parse_tree=node)
@@ -33,7 +35,7 @@
3335 def visit_rule_crossref(self, node, children):
3436 return peg.ID(name=str(node), parse_tree=node)
3537
36- def visit_rule(self, node, children): # Name '<-' expressions ';'
38+ def visit_rule(self, node, children): # Name '<-' expression ';'
3739 return peg.Rule(name=children[0],expr=children[1], parse_tree=node)
3840
3941
@@ -62,11 +64,32 @@
6264 raise NotImplementedError("visit_single_expr, len>2") # XXX -- Is this possible?
6365
6466
65- def visit_expressions(self, node, children): # OneOrMore(single_expr), Optional( '|' , expressions )
66- logger.debug(f'visit_expressions:: >>{node}<< #children={len(children)} children={children}:{type(children)}')
67+ def visit_expression(self, node, children): # ( expressions, op_alternatives )
68+ logger.debug('visit_expression::' + self._logstr_node_children(node, children))
69+ if len(children) == 1: #Only expressions
70+ return children[0]
71+ elif len(children) == 2: # So, having with alternatives
72+ # The 1st kid is a peg.Sequence ``expr``
73+ # The 2nd kid can be a
74+ # - peg.Sequence `` | expr ``, OR
75+ # - peg.OrderedChoice `` | expr | expr``
76+ # In all cased a (single) OrderedChoice with a list of alternatives should be returned.
77+# return peg.OrderedChoice(children = ((children[0].value,) +
78+# children[1]._childeren if isinstance(children[1], peg.OrderedChoice) else children[1].value), #XXX HACK
79+# parse_tree=node)
80+ if isinstance(children[1], peg.OrderedChoice):
81+ alternatives = [children[0]] + [alt for alt in children[1]]
82+ else:
83+ alternatives = children
84+ return peg.OrderedChoice(children = alternatives, parse_tree=node)
85+ else:
86+ raise NotImplementedError("visit_expression, len>2")
87+
88+
89+ def visit_expressions(self, node, children): # OneOrMore(single_expr)
90+ logger.debug(f'visit_expressions::{self._logstr_node_children(node, children)}')
6791 return peg.Sequence(value=children, parse_tree=node)
6892
69- raise NotImplementedError("visit_expressions, len>1 :: peg.OrderedChoice")
7093
7194 def visit_predicate(self, node, children):
7295 token_2_predicate = {'&': peg.AndPredicate,
diff -r 27e007fd92d5 -r 8a2d8be44deb pytst/readers/parser/d1_parse_tree/test_3_expr.py
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/pytst/readers/parser/d1_parse_tree/test_3_expr.py Fri Feb 11 18:26:37 2022 +0100
@@ -0,0 +1,65 @@
1+import pytest
2+import logging;logger = logging.getLogger(__name__)
3+
4+from castle.readers.parser import grammar
5+
6+import arpeggio
7+
8+R, S, X = grammar.regex_term.__name__, grammar.str_term.__name__, grammar.rule_crossref.__name__ # shortcut in grammar
9+P = grammar.predicate.__name__
10+G = grammar.group.__name__
11+
12+def parse_expression(txt, pattern=None):
13+ parser = arpeggio.ParserPython(grammar.expression)
14+ parse_tree = parser.parse(txt)
15+ logger.debug("\nPARSE-TREE\n" + parse_tree.tree_str()+'\n')
16+
17+ assert parse_tree.position_end == len(txt) , f"Not parsed whole input; Only: >>{txt[parse_tree.position: parse_tree.position_end]}<<; Not: >>{txt[parse_tree.position_end:]}<<."
18+ assert parse_tree.rule_name == "expression"
19+
20+ if pattern: validate_pattern(parse_tree, pattern=pattern)
21+
22+ return parse_tree
23+
24+def validate_pattern(pt, pattern=None):
25+ expressions = pt[0]
26+ assert len(expressions) == len(pattern), f"Not correct number-of-element"
27+
28+ for p, s in zip(pattern, expressions):
29+ if p is None: continue
30+ if p == X:
31+ assert s[0].rule_name == p
32+ elif p in (S,R):
33+ assert s[0][0].rule_name == p # S => T => str/regex
34+ elif isinstance(p, tuple): # Group: '(' ... ')'
35+ assert s[0].rule_name == G
36+ validate_pattern(s[0][1:-1][0], pattern=p) # G=>E=>
37+ elif p == P:
38+ assert False, "To Do: Predicate"
39+ else:
40+ assert False, "To Do: More"
41+
42+
43+def test_simple_1(): parse_expression(r"abc", pattern=[X])
44+def test_simple_2(): parse_expression(r'A Bc', pattern=[X, X])
45+
46+def test_string_1(): parse_expression(r"'abc'", pattern=[S])
47+def test_regexp_1(): parse_expression(r"/re/", pattern=[R])
48+
49+def test_mix(): parse_expression(r'/regex/ "string" crossref crossref', pattern=[R,S, X, X])
50+
51+def test_sub(): parse_expression(r'( A B )', pattern=[(X, X)])
52+def test_mix_nosub(): parse_expression(r'/regex/ "string" ( A B ) crossref', pattern=[R,S, None, X])
53+def test_mix_sub(): parse_expression(r'/regex/ "string" ( A B ) crossref', pattern=[R,S, (X, X), X])
54+
55+def test_sub_sub(): parse_expression(r'level0 ( level1_1 (level2a level2b ) level1_2) level0', pattern=[X, (X, (X,X), X), X])
56+
57+
58+def test_bug1(): parse_expression(r"""( rule_crossref | term | group | predicate ) ( '?' | '*' | '+' | '#' )?""")
59+def test_bug1a(): parse_expression(r"""( rule_crossref | term | group | predicate )""")
60+def test_bug1a1(): parse_expression(r"""A | B | C | D""")
61+def test_bug1a2(): parse_expression(r"""(A | B | C | D)""")
62+def test_bug1b(): parse_expression(r"""( rule_crossref | term | group | predicate ) ( '?' | '*' | '+' | '#' )""")
63+def test_bug1c(): parse_expression(r"""( '?' | '*' | '+' | '#' )""")
64+
65+
diff -r 27e007fd92d5 -r 8a2d8be44deb pytst/readers/parser/d1_parse_tree/test_3_expressions.py
--- a/pytst/readers/parser/d1_parse_tree/test_3_expressions.py Tue Feb 08 22:55:16 2022 +0100
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,59 +0,0 @@
1-import pytest
2-import logging;logger = logging.getLogger(__name__)
3-
4-from castle.readers.parser import grammar
5-
6-import arpeggio
7-
8-R, S, X = grammar.regex_term.__name__, grammar.str_term.__name__, grammar.rule_crossref.__name__ # shortcut in grammar
9-P = grammar.predicate.__name__
10-G = grammar.group.__name__
11-
12-def parse_expressions(txt, pattern=None):
13- parser = arpeggio.ParserPython(grammar.expressions)
14- parse_tree = parser.parse(txt)
15- logger.info("\nPARSE-TREE\n" + parse_tree.tree_str()+'\n')
16-
17- assert parse_tree.position_end == len(txt) , f"Not parsed whole input; Only: >>{txt[parse_tree.position: parse_tree.position_end]}<<; Not: >>{txt[parse_tree.position_end:]}<<."
18- assert parse_tree.rule_name == "expressions"
19-
20- if pattern: validate_pattern(parse_tree, pattern=pattern)
21-
22- return parse_tree
23-
24-def validate_pattern(pt, pattern=None):
25- assert len(pt) == len(pattern), f"Not correct number-of-element"
26-
27- for p, s in zip(pattern, pt): # E <- S* (| E)?
28- if p is None: continue
29- if p == X:
30- assert s[0].rule_name == p
31- elif p in (S,R):
32- assert s[0][0].rule_name == p # S => T => str/regex
33- elif isinstance(p, tuple): # Group: '(' ... ')'
34- assert s[0].rule_name == G
35- validate_pattern(s[0][1:-1][0], pattern=p) # G=>E=>
36- elif p == P:
37- assert False, "To Do: Predicate"
38- else:
39- assert False, "To Do: More"
40-
41-
42-def test_simple_1(): parse_expressions(r"abc", pattern=[X])
43-def test_simple_2(): parse_expressions(r'A Bc', pattern=[X, X])
44-
45-def test_string_1(): parse_expressions(r"'abc'", pattern=[S])
46-def test_regexp_1(): parse_expressions(r"/re/", pattern=[R])
47-
48-def test_mix(): parse_expressions(r'/regex/ "string" crossref crossref', pattern=[R,S, X, X])
49-
50-def test_sub(): parse_expressions(r'( A B )', pattern=[(X, X)])
51-def test_mix_nosub(): parse_expressions(r'/regex/ "string" ( A B ) crossref', pattern=[R,S, None, X])
52-def test_mix_sub(): parse_expressions(r'/regex/ "string" ( A B ) crossref', pattern=[R,S, (X, X), X])
53-
54-def test_sub_sub(): parse_expressions(r'level0 ( level1_1 (level2a level2b ) level1_2) level0', pattern=[X, (X, (X,X), X), X])
55-
56-
57-
58-
59-
diff -r 27e007fd92d5 -r 8a2d8be44deb pytst/readers/parser/d1_parse_tree/test_4_rule.py
--- a/pytst/readers/parser/d1_parse_tree/test_4_rule.py Tue Feb 08 22:55:16 2022 +0100
+++ b/pytst/readers/parser/d1_parse_tree/test_4_rule.py Fri Feb 11 18:26:37 2022 +0100
@@ -9,22 +9,22 @@
99 def parse_rule(txt, pattern=None):
1010 parser = arpeggio.ParserPython(grammar.rule)
1111 tree = parser.parse(txt)
12- logger.info(f'\nTREE\n{tree.tree_str()}')
12+ logger.debug(f'\nTREE\n{tree.tree_str()}')
1313
1414 assert tree.position_end == len(txt) , f"Not parsed whole input; Only: >>{txt[tree.position: tree.position_end]}<<; Not: >>{txt[tree.position_end:]}<<."
1515 assert len(tree) == 4, "A rule should have length=4; ..."
1616 assert tree[0].rule_name == "rule_name", " at [0], the name of the rule"
1717 assert str(tree[1]) == '<-', " then a arrow"
18- assert tree[2].rule_name == "expressions", " at [2] an ordered_choice"
18+ assert tree[2].rule_name == "expression", " at [2] an expression"
1919 assert str(tree[3]) == ';', " and the the closing ':'"
2020
2121 return tree
2222
2323
24-def test_simple(): parse_rule(r"R <- A B C ;")
24+def test_simple(): parse_rule(r"R <- A B C ;")
2525 def test_OC(): parse_rule(r"Alts <- This | That | Or So ;")
2626
27-def test_rule_rule(): parse_rule(r"""RULE <- RULE_NAME '<-' ORDERED_CHOICE ';' ;""")
27+def test_rule_rule(): parse_rule(r"""RULE <- RULE_NAME '<-' ORDERED_CHOICE ';' ;""")
2828 def test_expression_rule(): parse_rule(r"""
2929 expression <- regex_term
3030 | rule_crossref
diff -r 27e007fd92d5 -r 8a2d8be44deb pytst/readers/parser/d2_ast/test_3_Seq.py
--- a/pytst/readers/parser/d2_ast/test_3_Seq.py Tue Feb 08 22:55:16 2022 +0100
+++ b/pytst/readers/parser/d2_ast/test_3_Seq.py Fri Feb 11 18:26:37 2022 +0100
@@ -1,6 +1,5 @@
11 """Test that a sequence of expressions is an Expression()
2-
3- Note: the value of Expression() is a list-subclass; which is fine. But use it as list!!"""
2+"""
43
54 import pytest
65 import logging; logger = logging.getLogger(__name__)
@@ -23,17 +22,24 @@
2322 ast = parse(txt, grammar.single_expr)
2423
2524
26-def test_seq_of_two_as_expressions():
25+def test_seq_of_two_as_expression():
2726 txt = "A B"
28- ast = parse(txt, grammar.expressions)
27+ ast = parse(txt, grammar.expression)
2928
3029 assert_Seq(ast, 2, ids=('A', 'B'))
3130 assert isinstance(ast.value, list), "It will be an `arpeggio.SemanticActionResult` which is a subclass of list"
3231
32+def test_seq_of_three_as_expression():
33+ txt = "A B C"
34+ ast = parse(txt, grammar.expression)
35+
36+ assert_Seq(ast, 3, ids=('A', 'B', 'C'))
37+ assert isinstance(ast.value, list), "It will be an `arpeggio.SemanticActionResult` which is a subclass of list"
38+
3339
3440 def test_seq_of_three_with_quantification():
3541 txt = "A? B+ C*"
36- ast = parse(txt, grammar.expressions)
42+ ast = parse(txt, grammar.expression)
3743
3844 assert_Seq(ast, 3)
3945
@@ -46,7 +52,31 @@
4652 assert_ID(ast[2].expr, 'C'), "The 3th one is a 'C'"
4753
4854
55+def assert_OC(ast, length_pattern):
56+ assert isinstance(ast, peg.OrderedChoice)
57+ assert isinstance(ast, peg.Expression), "An OrderedChoice is also a Expression"
58+ assert len(ast) == len(length_pattern), "Not the correct number of alternatives"
59+ for i, (alt, l) in enumerate(zip(ast, length_pattern)):
60+ if l is not None:
61+ assert len(alt) == l, f'The {i}th alternative does not match the specified length -- {alt}'
62+ logger.debug(f'OC-alt[{i}] ==> {alt}')
63+
4964 def test_OrderedChoice_of_two_alternatives():
5065 txt = "A | B"
51- ast = parse(txt, grammar.expressions)
52- assert False # XXX ToBoDone
66+ ast = parse(txt, grammar.expression)
67+ logger.debug(f"OC.2:: {ast}")
68+ assert_OC(ast, length_pattern=[1,1])
69+
70+def test_OrderedChoice_of_three_alternatives():
71+ txt = "A | B | C"
72+ ast = parse(txt, grammar.expression)
73+ logger.debug(f"OC.3:: {ast}")
74+ assert_OC(ast, length_pattern=[1,1,1])
75+
76+
77+def test_OrderedChoice_of_long_alternatives():
78+ txt = "A | b1 b2 | C"
79+ ast = parse(txt, grammar.expression)
80+ logger.debug(f"OC.long:: {ast}")
81+
82+ assert_OC(ast, length_pattern=[1,2,1])