1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
use super::attribute::*;
use crate::{
ast::*,
parser::{combinator::*, identifier::*, types::*},
};
pub fn inverse_clause(input: &str) -> ParseResult<InverseClause> {
tuple((tag("INVERSE"), many1(inverse_attr)))
.map(|(_inverse, attributes)| InverseClause { attributes })
.parse(input)
}
pub fn inverse_attr(input: &str) -> ParseResult<InverseAttribute> {
let aggregation_option = opt(tuple((
alt((tag("SET"), tag("BAG"))),
opt(bound_spec),
tag("OF"),
)))
.map(|opt| {
if let Some((agg, bound, _of)) = opt {
match agg {
"SET" => AggregationOption::Set { bound },
"BAG" => AggregationOption::Bag { bound },
_ => unreachable!(),
}
} else {
AggregationOption::None
}
});
let attribute_prefix =
opt(tuple((entity_ref, char('.')))).map(|opt| opt.map(|(prefix, _dot)| prefix));
tuple((
attribute_decl,
char(':'),
aggregation_option,
entity_ref,
tag("FOR"),
attribute_prefix,
attribute_ref,
char(';'),
))
.map(
|(name, _comma, dest_aggregation, dest, _for, attribute_prefix, attribute, _semicolon)| {
InverseAttribute {
name,
dest,
dest_aggregation,
attribute,
attribute_prefix,
}
},
)
.parse(input)
}
#[cfg(test)]
mod tests {
use super::AggregationOption;
use crate::ast::*;
use nom::Finish;
#[test]
fn inverse() {
let (residual, (inv, _remarks)) = super::inverse_attr("opens : door FOR handle;")
.finish()
.unwrap();
assert_eq!(residual, "");
assert_eq!(inv.name, "opens");
assert_eq!(inv.dest, "door");
assert_eq!(inv.attribute, "handle");
assert_eq!(inv.dest_aggregation, AggregationOption::None);
assert_eq!(inv.attribute_prefix, None);
}
#[test]
fn inverse_agg() {
let (residual, (inv, _remarks)) =
super::inverse_attr("opens : SET [0:1] OF door FOR handle;")
.finish()
.unwrap();
assert_eq!(residual, "");
assert_eq!(inv.name, "opens");
assert_eq!(inv.dest, "door");
assert_eq!(inv.attribute, "handle");
assert_eq!(
inv.dest_aggregation,
AggregationOption::Set {
bound: Some(Bound {
upper: Expression::real(1.0),
lower: Expression::real(0.0)
})
}
);
assert_eq!(inv.attribute_prefix, None);
}
}