1use expressions::expression;
2use expressions::Expression;
3use idents::{string, symbol};
4use nom::branch::alt;
5use nom::bytes::complete::tag;
6use nom::combinator::map;
7use nom::combinator::opt;
8use nom::IResult;
9use whitespace::opt_space;
10
11#[derive(Debug, PartialEq)]
12pub enum AssignOperator {
13 Equals,
14 Plus,
15 Minus,
16 Multiply,
17 Divide,
18 ShiftLeft,
19 ShiftRight,
20 And,
21 Or,
22}
23
24#[derive(Debug, PartialEq)]
25pub enum Statement {
26 Assign {
27 name: String,
28 operator: AssignOperator,
29 expression: Box<Expression>,
30 },
31 Hidden {
32 name: String,
33 expression: Box<Expression>,
34 },
35 Provide {
36 name: String,
37 expression: Box<Expression>,
38 },
39 ProvideHidden {
40 name: String,
41 expression: Box<Expression>,
42 },
43 Assert {
44 expr: Box<Expression>,
45 text: String,
46 },
47}
48
49fn assign_operator(input: &str) -> IResult<&str, AssignOperator> {
50 map(
51 alt((
52 tag("="),
53 tag("+="),
54 tag("-="),
55 tag("*="),
56 tag("/="),
57 tag("<<="),
58 tag(">>="),
59 tag("&="),
60 tag("|="),
61 )),
62 |op: &str| match op {
63 "=" => AssignOperator::Equals,
64 "+=" => AssignOperator::Plus,
65 "-=" => AssignOperator::Minus,
66 "*=" => AssignOperator::Multiply,
67 "/=" => AssignOperator::Divide,
68 "<<=" => AssignOperator::ShiftLeft,
69 ">>=" => AssignOperator::ShiftRight,
70 "&=" => AssignOperator::And,
71 "|=" => AssignOperator::Or,
72 _ => panic!("wrong operator"),
73 },
74 )(input)
75}
76
77fn special_assign(input: &str) -> IResult<&str, Statement> {
78 let (input, keyword) = alt((tag("PROVIDE_HIDDEN"), tag("PROVIDE"), tag("HIDDEN")))(input)?;
79 let (input, _) = wsc!(tag("("))(input)?;
80 let (input, name) = symbol(input)?;
81 let (input, _) = wsc!(tag("="))(input)?;
82 let (input, expr) = expression(input)?;
83 let (input, _) = wsc!(tag(")"))(input)?;
84 let (input, _) = tag(";")(input)?;
85 Ok((
86 input,
87 match keyword {
88 "HIDDEN" => Statement::Hidden {
89 name: name.into(),
90 expression: Box::new(expr),
91 },
92 "PROVIDE" => Statement::Provide {
93 name: name.into(),
94 expression: Box::new(expr),
95 },
96 "PROVIDE_HIDDEN" => Statement::ProvideHidden {
97 name: name.into(),
98 expression: Box::new(expr),
99 },
100 _ => panic!("invalid assign keyword"),
101 },
102 ))
103}
104
105fn assign(input: &str) -> IResult<&str, Statement> {
106 let (input, name) = symbol(input)?;
107 let (input, op) = wsc!(assign_operator)(input)?;
108 let (input, expr) = expression(input)?;
109 let (input, _) = opt_space(input)?;
110 let (input, _) = tag(";")(input)?;
111 Ok((
112 input,
113 Statement::Assign {
114 name: name.into(),
115 operator: op,
116 expression: Box::new(expr),
117 },
118 ))
119}
120
121fn assert_stmt(input: &str) -> IResult<&str, Statement> {
122 let (input, _) = tag("ASSERT")(input)?;
123 let (input, _) = wsc!(tag("("))(input)?;
124 let (input, expr) = expression(input)?;
125 let (input, _) = wsc!(tag(","))(input)?;
126 let (input, text) = string(input)?;
127 let (input, _) = wsc!(tag(")"))(input)?;
128 let (input, _) = opt(tag(";"))(input)?;
129 Ok((
130 input,
131 Statement::Assert {
132 expr: Box::new(expr),
133 text: text.into(),
134 },
135 ))
136}
137
138pub fn statement(input: &str) -> IResult<&str, Statement> {
139 alt((special_assign, assign, assert_stmt))(input)
140}
141
142#[cfg(test)]
143mod tests {
144 use expressions::Expression;
145 use statements::*;
146
147 #[test]
148 fn test_statement() {
149 assert_done!(
150 statement("A = 11 ;"),
151 Statement::Assign {
152 name: "A".into(),
153 operator: AssignOperator::Equals,
154 expression: Box::new(Expression::Number(11)),
155 }
156 );
157 assert_done!(
158 statement("PROVIDE ( x = x ) ;"),
159 Statement::Provide {
160 name: "x".into(),
161 expression: Box::new(Expression::Ident("x".into())),
162 }
163 );
164 assert_done!(statement("PROBLEM += HELLO ( WORLD , 0 ) + 1 ;"));
165 }
166}