Castle: The best Real-Time/Embedded/HighTech language EVER. Attempt 2
Revision | 7652c23a796e7c9b11479fa239b11d051c51f9bd (tree) |
---|---|
Time | 2022-01-16 00:51:27 |
Author | Albert Mietus < albert AT mietus DOT nl > |
Commiter | Albert Mietus < albert AT mietus DOT nl > |
Added support for Predicate
@@ -107,6 +107,6 @@ | ||
107 | 107 | class OneOrMore(Quantity):pass |
108 | 108 | |
109 | 109 | |
110 | -class Predicate(Expression): pass # abstract | |
110 | +class Predicate(MixIn_expr_attribute, Expression): pass # abstract | |
111 | 111 | class AndPredicate(Predicate): pass |
112 | 112 | class NotPredicate(Predicate): pass |
@@ -7,9 +7,9 @@ | ||
7 | 7 | def rule(): return rule_name, '<-', expressions, ";" |
8 | 8 | |
9 | 9 | def expressions(): return ( OneOrMore(single_expr), Optional( '|' , expressions ) ) |
10 | -def single_expr(): return ( [ rule_crossref, term, group, predicate ], expr_quantity) | |
10 | +def single_expr(): return ( [ rule_crossref, term, group, predicate ], op_quantity) | |
11 | 11 | |
12 | -def expr_quantity(): return Optional([ '?' , '*' , '+' , '#' ]) | |
12 | +def op_quantity(): return Optional([ '?' , '*' , '+' , '#' ]) | |
13 | 13 | def term(): return [ str_term, regex_term ] |
14 | 14 | def group(): return '(', expressions, ')' |
15 | 15 | def predicate(): return ['&','!'], single_expr |
@@ -21,8 +21,13 @@ | ||
21 | 21 | |
22 | 22 | def assert_ID(id, name:str=None, err_message="Not correct Name"): |
23 | 23 | assert name is not None |
24 | - assert isinstance(id, peg.ID), "The id should be an ID" | |
24 | + assert isinstance(id, peg.ID), f"The id should be an ID, but is a {type(id)}" | |
25 | 25 | peg.ID.validate_or_raise(id) # with correct syntax |
26 | 26 | assert id.name == name, err_message if err_message else f"Note correct name, expected {name}" |
27 | +precondition_ID = assert_ID # It's not a "validation assert", but a precondition for the test | |
27 | 28 | |
29 | +def precondition_Expressions(expr, *, type=peg.Sequence, length=None): | |
30 | + assert isinstance(expr, type), "PreCondition failed" | |
31 | + if length: | |
32 | + assert len(expr)==length, "PreCondition failed" | |
28 | 33 |
@@ -0,0 +1,29 @@ | ||
1 | +import pytest | |
2 | +import logging; logger = logging.getLogger(__name__) | |
3 | + | |
4 | +import grammar | |
5 | +from castle import peg # has the AST classes | |
6 | + | |
7 | +from . import parse, assert_ID, precondition_ID, precondition_Expressions | |
8 | + | |
9 | +def simple_ID_Predicate_ID(txt, predicateType, rule_name='R', id0='A', predicateID='B', id2='C'): | |
10 | + ast = parse(txt, grammar.rule) | |
11 | + precondition_ID(ast.name, rule_name) | |
12 | + | |
13 | + expr = ast.expr | |
14 | + precondition_Expressions(expr, length=3, type=peg.Sequence) | |
15 | + precondition_ID(expr[0], id0) | |
16 | + precondition_ID(expr[2], id2) | |
17 | + | |
18 | + predicate= expr[1] | |
19 | + logger.debug(f'predicate: {predicate}') | |
20 | + | |
21 | + assert isinstance(predicate, peg.Predicate) | |
22 | + assert isinstance(predicate, predicateType) | |
23 | + assert_ID(predicate.expr, predicateID) | |
24 | + | |
25 | +def test_simple_AndPredicate(): | |
26 | + simple_ID_Predicate_ID("R <- A &B C;", peg.AndPredicate) | |
27 | + | |
28 | +def test_simple_NotPredicate(): | |
29 | + simple_ID_Predicate_ID("R <- A !B C;", peg.NotPredicate) |
@@ -6,6 +6,7 @@ | ||
6 | 6 | import logging;logger = logging.getLogger(__name__) |
7 | 7 | |
8 | 8 | class QuantityError(ValueError): pass |
9 | +class PredicateError(ValueError): pass | |
9 | 10 | |
10 | 11 | |
11 | 12 | #NO_VISITOR_NEEDED: visit_str_no_s1 |
@@ -39,7 +40,7 @@ | ||
39 | 40 | def visit_rule(self, node, children): # Name '<-' expressions ';' |
40 | 41 | return peg.Rule(name=children[0],expr=children[1], parse_tree=node) |
41 | 42 | |
42 | - def visit_single_expr(self, node, children): # [ rule_crossref, term, group, predicate ], expr_quantity | |
43 | + def visit_single_expr(self, node, children): # [ rule_crossref, term, group, predicate ], op_quantity | |
43 | 44 | if len(children) == 1: # No Optional part |
44 | 45 | try: |
45 | 46 | n = f'name={children[0].name}' |
@@ -70,3 +71,21 @@ | ||
70 | 71 | logger.debug(f'visit_expressions:: >>{node}<< len={len(children)} children={children}:{type(children)}') |
71 | 72 | return peg.Sequence(value=children, parse_tree=node) |
72 | 73 | |
74 | + | |
75 | + def visit_predicate(self, node, children): | |
76 | + token_2_predicate = {'&': peg.AndPredicate, | |
77 | + '!': peg.NotPredicate} | |
78 | + logger.debug(f'visit_predicate:: >>{node}<< len={len(children)}') | |
79 | + | |
80 | + if len(children) == 2: | |
81 | + token = children[0] | |
82 | + cls = token_2_predicate.get(token) | |
83 | + if cls: | |
84 | + ast = cls(expr=children[1], parse_tree=node) | |
85 | + return ast | |
86 | + else: | |
87 | + raise PredicateError(f"token '{token}' not recognised") | |
88 | + else: | |
89 | + raise NotImplementedError("visit_predicate, len!=2") # XXX -- Is this possible? | |
90 | + | |
91 | + |