Castle: The best Real-Time/Embedded/HighTech language EVER. Attempt 2
修订版 | 5800f73c267c1a8b3e80a2ebeb809f5d9a6fd66a (tree) |
---|---|
时间 | 2022-02-12 07:09:00 |
作者 | Albert Mietus < albert AT mietus DOT nl > |
Commiter | Albert Mietus < albert AT mietus DOT nl > |
Refactored: renamed expressions into sequence; in GRAMMER (and all other files)
@@ -114,7 +114,7 @@ | ||
114 | 114 | |
115 | 115 | |
116 | 116 | class UnorderedGroup(MixIn_expr_attribute, Group): # It looks like a Quantity, but is a group |
117 | - """See a set (aka "group") of expressions that **all** have to be matched, but the **order** is a don't care. | |
117 | + """A set (aka "group") of expressions that **all** have to be matched, but the **order** is a don't care. | |
118 | 118 | |
119 | 119 | Possible an extension of Arpeggio (see: https://textx.github.io/Arpeggio/stable/grammars/), possible a generic one.""" |
120 | 120 |
@@ -6,8 +6,8 @@ | ||
6 | 6 | def rules(): return OneOrMore(rule) |
7 | 7 | def rule(): return rule_name, '<-', expression, ";" |
8 | 8 | |
9 | -def expression(): return expressions, op_alternative | |
10 | -def expressions(): return OneOrMore(single_expr) | |
9 | +def expression(): return sequence, op_alternative | |
10 | +def sequence(): return OneOrMore(single_expr) | |
11 | 11 | def single_expr(): return [ rule_crossref, term, group, predicate ], op_quantity |
12 | 12 | |
13 | 13 | def op_alternative(): return Optional( '|' , expression ) |
@@ -3,6 +3,7 @@ | ||
3 | 3 | from castle.ast import peg |
4 | 4 | |
5 | 5 | import logging;logger = logging.getLogger(__name__) |
6 | +from typing import Union | |
6 | 7 | |
7 | 8 | class QuantityError(ValueError): pass |
8 | 9 | class PredicateError(ValueError): pass |
@@ -65,9 +66,10 @@ | ||
65 | 66 | raise NotImplementedError("visit_single_expr, len>2") # XXX -- Is this possible? |
66 | 67 | |
67 | 68 | |
68 | - def visit_expression(self, node, children): # expression <- expressionS, op_alternative; op_alternative <- ('|' expression)? | |
69 | + # expression <- sequence, op_alternative; op_alternative <- ('|' expression)? | |
70 | + def visit_expression(self, node, children) -> Union[peg.Sequence, peg.OrderedChoice]: | |
69 | 71 | logger.debug('visit_expression::' + self._logstr_node_children(node, children)) |
70 | - if len(children) == 1: #Only expressions | |
72 | + if len(children) == 1: #Only sequence | |
71 | 73 | return children[0] |
72 | 74 | elif len(children) == 2: # So, having 1 or more alternatives in children[1] |
73 | 75 | # In all cased a (single) OrderedChoice with a list of alternatives should be returned. |
@@ -79,9 +81,9 @@ | ||
79 | 81 | else: |
80 | 82 | raise NotImplementedError("visit_expression, len>2") |
81 | 83 | |
82 | - | |
83 | - def visit_expressions(self, node, children): # OneOrMore(single_expr) | |
84 | - logger.debug(f'visit_expressions::{self._logstr_node_children(node, children)}') | |
84 | + # OneOrMore(single_expr) | |
85 | + def visit_sequence(self, node, children) -> peg.Sequence: | |
86 | + logger.debug(f'visit_sequence::{self._logstr_node_children(node, children)}') | |
85 | 87 | return peg.Sequence(value=children, parse_tree=node) |
86 | 88 | |
87 | 89 |
@@ -59,4 +59,4 @@ | ||
59 | 59 | |
60 | 60 | def test_QAZ_rule_name(): QAZ("""aName""", grammar.rule_name, label="rule_name") |
61 | 61 | def test_QAZ_rule_crossref(): QAZ("""aName""", grammar.rule_crossref, label="rule_crossref") |
62 | -def test_QAZ_expressions(): QAZ("""aName""", grammar.expressions, label="expressions") | |
62 | +def test_QAZ_expression(): QAZ("""aName""", grammar.expression, label="expression") |
@@ -9,7 +9,12 @@ | ||
9 | 9 | P = grammar.predicate.__name__ |
10 | 10 | G = grammar.group.__name__ |
11 | 11 | |
12 | -def parse_expression(txt, pattern=None): | |
12 | + | |
13 | +def validate_expression(txt, pattern): | |
14 | + parse_tree = parse_expression(txt) | |
15 | + validate_pattern(parse_tree, pattern=pattern) | |
16 | + | |
17 | +def parse_expression(txt): | |
13 | 18 | parser = arpeggio.ParserPython(grammar.expression) |
14 | 19 | parse_tree = parser.parse(txt) |
15 | 20 | logger.debug("\nPARSE-TREE\n" + parse_tree.tree_str()+'\n') |
@@ -17,15 +22,14 @@ | ||
17 | 22 | 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 | 23 | assert parse_tree.rule_name == "expression" |
19 | 24 | |
20 | - if pattern: validate_pattern(parse_tree, pattern=pattern) | |
21 | - | |
22 | 25 | return parse_tree |
23 | 26 | |
27 | + | |
24 | 28 | def validate_pattern(pt, pattern=None): |
25 | - expressions = pt[0] | |
26 | - assert len(expressions) == len(pattern), f"Not correct number-of-element" | |
29 | + elements = pt[0] | |
30 | + assert len(elements) == len(pattern), f"Not correct number-of-element" | |
27 | 31 | |
28 | - for p, s in zip(pattern, expressions): | |
32 | + for p, s in zip(pattern, elements): | |
29 | 33 | if p is None: continue |
30 | 34 | if p == X: |
31 | 35 | assert s[0].rule_name == p |
@@ -40,19 +44,19 @@ | ||
40 | 44 | assert False, "To Do: More" |
41 | 45 | |
42 | 46 | |
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]) | |
47 | +def test_simple_1(): validate_expression(r"abc", pattern=[X]) | |
48 | +def test_simple_2(): validate_expression(r'A Bc', pattern=[X, X]) | |
48 | 49 | |
49 | -def test_mix(): parse_expression(r'/regex/ "string" crossref crossref', pattern=[R,S, X, X]) | |
50 | +def test_string_1(): validate_expression(r"'abc'", pattern=[S]) | |
51 | +def test_regexp_1(): validate_expression(r"/re/", pattern=[R]) | |
50 | 52 | |
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]) | |
53 | +def test_mix(): validate_expression(r'/regex/ "string" crossref crossref', pattern=[R,S, X, X]) | |
54 | 54 | |
55 | -def test_sub_sub(): parse_expression(r'level0 ( level1_1 (level2a level2b ) level1_2) level0', pattern=[X, (X, (X,X), X), X]) | |
55 | +def test_sub(): validate_expression(r'( A B )', pattern=[(X, X)]) | |
56 | +def test_mix_nosub(): validate_expression(r'/regex/ "string" ( A B ) crossref', pattern=[R,S, None, X]) | |
57 | +def test_mix_sub(): validate_expression(r'/regex/ "string" ( A B ) crossref', pattern=[R,S, (X, X), X]) | |
58 | + | |
59 | +def test_sub_sub(): validate_expression(r'level0 ( level1_1 (level2a level2b ) level1_2) level0', pattern=[X, (X, (X,X), X), X]) | |
56 | 60 | |
57 | 61 | |
58 | 62 | def test_bug1(): parse_expression(r"""( rule_crossref | term | group | predicate ) ( '?' | '*' | '+' | '#' )?""") |
@@ -50,9 +50,9 @@ | ||
50 | 50 | regex_variants(txt:='''r"""re__rstr_d3"""''', expect=txt[4:-3]) |
51 | 51 | |
52 | 52 | |
53 | -def test_term_as_expressions(): # A term is **ALSO an expressions | |
53 | +def test_term_as_expression(): # A term is **ALSO an expression | |
54 | 54 | txt="'a string'" |
55 | - ast = parse(txt, grammar.expressions) | |
55 | + ast = parse(txt, grammar.expression) | |
56 | 56 | # result is same a above |
57 | 57 | assert isinstance(ast, peg.Expression), "A (str)term is also an Expression" |
58 | 58 | assert len(ast.value) == 1, "An expression with length==1" |
@@ -15,18 +15,18 @@ | ||
15 | 15 | |
16 | 16 | |
17 | 17 | def test_rule_crossref(): |
18 | - """The rule's expressions can also refer an ID""" | |
18 | + """The rule's expression can also refer an ID""" | |
19 | 19 | |
20 | 20 | txt="aRef" |
21 | 21 | ast = parse(txt, grammar.rule_crossref) |
22 | 22 | assert_ID(ast, name=txt) |
23 | 23 | |
24 | 24 | |
25 | -def test_ID_as_expressions(): | |
25 | +def test_ID_as_expression(): | |
26 | 26 | """ An ID is also an expression""" |
27 | 27 | |
28 | 28 | txt="aRef" |
29 | - ast = parse(txt, grammar.expressions) | |
29 | + ast = parse(txt, grammar.expression) | |
30 | 30 | |
31 | 31 | assert isinstance(ast, peg.Expression), "A crossref is also an Expression" |
32 | 32 | assert len(ast.value) == 1, "An expression with length==1" |
@@ -1,4 +1,4 @@ | ||
1 | -"""Test that a sequence of expressions is an Expression() | |
1 | +"""Test that a kind of sequence's are Expression() | |
2 | 2 | """ |
3 | 3 | |
4 | 4 | import pytest |
@@ -4,15 +4,15 @@ | ||
4 | 4 | rules <- rule+ ; |
5 | 5 | rule <- rule_name '<-' expression ';' ; |
6 | 6 | |
7 | -expression <- expressions op_alternative ; | |
8 | -expressions <- single_expr+ ; | |
7 | +expression <- sequence op_alternative ; | |
8 | +sequence <- single_expr+ ; | |
9 | 9 | single_expr <- ( rule_crossref | term | group | predicate ) op_quantity ; |
10 | 10 | |
11 | 11 | op_alternative <- ( '|' expression )? ; |
12 | 12 | op_quantity <- ( '?' | '*' | '+' | '#' )? ; |
13 | 13 | |
14 | 14 | term <- str_term | regex_term ; |
15 | -group <- '(' expressions ')' ; | |
15 | +group <- '(' expression ')' ; | |
16 | 16 | predicate <- ( '&' | '!' ) single_expr ; |
17 | 17 | |
18 | 18 | str_term <- "'" str_no_s1 "'" |
@@ -14,8 +14,3 @@ | ||
14 | 14 | id_rn = peg.ID(name="rule_crossref") |
15 | 15 | id_xr = peg.ID(name="ID") |
16 | 16 | |
17 | - | |
18 | -## expressions = peg.Sequence(value=id_xr | |
19 | -## rule = peg.Rule(name=id_rn, expr=expressions) | |
20 | - | |
21 | -## Template("""def {{rule.name}}()\treturn{rule.expro |