Skip to content

Commit c494b9c

Browse files
authored
Merge pull request #4954 from MatWise/feature/stl-from-expression
STL frontend: expr2stl conversion
2 parents 20adcee + f84bf71 commit c494b9c

File tree

5 files changed

+424
-6
lines changed

5 files changed

+424
-6
lines changed

src/statement-list/converters/expr2statement_list.cpp

Lines changed: 281 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -10,12 +10,105 @@ Author: Matthias Weiss, [email protected]
1010

1111
#include <ansi-c/expr2c.h>
1212

13+
#include <util/suffix.h>
14+
#include <util/symbol_table.h>
15+
16+
#include <algorithm>
17+
#include <cstring>
18+
#include <iostream>
19+
20+
/// STL code for an AND instruction.
21+
#define AND 'A'
22+
23+
/// STL code for an OR instruction.
24+
#define OR 'O'
25+
26+
/// STL code for a XOR instruction.
27+
#define XOR 'X'
28+
29+
/// Postfix for any negated boolean instruction.
30+
#define NOT_POSTFIX 'N'
31+
32+
/// STL code for a NOT instruction.
33+
#define NOT "NOT"
34+
35+
/// Separator between the instruction code and it's operand.
36+
#define OPERAND_SEPARATOR ' '
37+
38+
/// Separator between the end of an instruction and the next one.
39+
#define LINE_SEPARATOR ";\n"
40+
41+
/// Separator for the end of an instruction that introduces a new layer of
42+
/// nesting.
43+
#define NESTING_OPEN_LINE_SEPARATOR "(;\n"
44+
45+
/// Separator for the end of an instruction that closes a nesting layer. Also
46+
/// known as the NESTING CLOSED instruction.
47+
#define NESTING_CLOSED_LINE_SEPARATOR ");\n"
48+
49+
/// Prefix for the reference to any variable.
50+
#define REFERENCE_FLAG '#'
51+
52+
/// CBMC-internal separator for variable scopes.
53+
#define SCOPE_SEPARATOR "::"
54+
55+
/// Modifies the parameters of a binary equal expression with the help of its
56+
/// symmetry properties. This function basically converts the operands to
57+
/// operands of a XOR expression so that the whole expression can be treated as
58+
/// such. This can reduce complexity in some cases.
59+
/// \param lhs: Left side of the binary expression.
60+
/// \param rhs: Right side of the binary expression.
61+
/// \return: List of instrumented operands.
62+
static std::vector<exprt>
63+
instrument_equal_operands(const exprt &lhs, const exprt &rhs)
64+
{
65+
std::vector<exprt> result;
66+
if(ID_not != lhs.id() && ID_not != rhs.id())
67+
{
68+
// lhs == rhs is equivalent to X lhs; XN rhs;
69+
result.push_back(lhs);
70+
result.push_back(not_exprt{rhs});
71+
}
72+
else if(ID_not != lhs.id() && ID_not == rhs.id())
73+
{
74+
// lhs == !rhs is equivalent to X lhs; X rhs;
75+
result.push_back(lhs);
76+
result.push_back(rhs.op0());
77+
}
78+
else if(ID_not == lhs.id() && ID_not != rhs.id())
79+
{
80+
// !lhs == rhs is equivalent to X lhs; X rhs;
81+
result.push_back(lhs.op0());
82+
result.push_back(rhs);
83+
}
84+
else // ID_not == lhs.id() && ID_not == rhs.id()
85+
{
86+
// !lhs == !rhs is equivalent to X lhs; XN rhs;
87+
result.push_back(lhs.op0());
88+
result.push_back(rhs);
89+
}
90+
return result;
91+
}
92+
93+
/// Checks if the expression or one of its parameters is not of type bool.
94+
/// \param expr: Expression to verify.
95+
/// \return: True if the expression and its operands are not or only partially
96+
/// of type bool, false otherwise.
97+
static bool is_not_bool(const exprt &expr)
98+
{
99+
if(!expr.is_boolean())
100+
return true;
101+
for(const exprt &op : expr.operands())
102+
if(!op.is_boolean())
103+
return true;
104+
return false;
105+
}
106+
13107
std::string expr2stl(const exprt &expr, const namespacet &ns)
14108
{
15-
// TODO: Implement expr2stl.
16-
// Expand this section so that it is able to properly convert from
17-
// CBMC expressions to STL code.
18-
return expr2c(expr, ns);
109+
expr2stlt expr2stl{ns};
110+
111+
return expr2stl.convert(expr);
19112
}
20113

21114
std::string type2stl(const typet &type, const namespacet &ns)
@@ -25,3 +118,187 @@ std::string type2stl(const typet &type, const namespacet &ns)
25118
// CBMC types to STL code.
26119
return type2c(type, ns);
27120
}
121+
122+
expr2stlt::expr2stlt(const namespacet &ns)
123+
: inside_bit_string(false), is_reference(false), ns(ns)
124+
{
125+
}
126+
127+
std::string expr2stlt::convert(const exprt &expr)
128+
{
129+
// Redirect to expr2c if no boolean expression.
130+
if(is_not_bool(expr))
131+
return expr2c(expr, ns);
132+
133+
if(ID_and == expr.id())
134+
convert(to_and_expr(expr));
135+
else if(ID_or == expr.id())
136+
convert(to_or_expr(expr));
137+
else if(ID_xor == expr.id())
138+
convert(to_xor_expr(expr));
139+
else if(ID_notequal == expr.id())
140+
convert(to_notequal_expr(expr));
141+
else if(ID_equal == expr.id())
142+
convert(to_equal_expr(expr));
143+
else if(ID_symbol == expr.id())
144+
convert(to_symbol_expr(expr));
145+
else if(ID_not == expr.id() && expr.op0().type().id() == ID_bool)
146+
convert(to_not_expr(expr));
147+
else // TODO: support more instructions in expr2stl.
148+
return expr2c(expr, ns);
149+
150+
return result.str();
151+
}
152+
153+
void expr2stlt::convert(const and_exprt &expr)
154+
{
155+
std::vector<exprt> operands = expr.operands();
156+
convert_multiary_bool(operands, AND);
157+
}
158+
159+
void expr2stlt::convert(const or_exprt &expr)
160+
{
161+
std::vector<exprt> operands = expr.operands();
162+
convert_multiary_bool(operands, OR);
163+
}
164+
165+
void expr2stlt::convert(const xor_exprt &expr)
166+
{
167+
std::vector<exprt> operands = expr.operands();
168+
convert_multiary_bool(operands, XOR);
169+
}
170+
171+
void expr2stlt::convert(const notequal_exprt &expr)
172+
{
173+
std::vector<exprt> operands = expr.operands();
174+
convert_multiary_bool(operands, XOR);
175+
}
176+
177+
void expr2stlt::convert(const equal_exprt &expr)
178+
{
179+
std::vector<exprt> operands =
180+
instrument_equal_operands(expr.lhs(), expr.rhs());
181+
convert_multiary_bool(operands, XOR);
182+
}
183+
184+
void expr2stlt::convert(const not_exprt &expr)
185+
{
186+
// If a NOT expression is being handled here it must always mark the
187+
// beginning of a new bit string.
188+
PRECONDITION(!inside_bit_string);
189+
190+
if(ID_symbol == expr.op().id())
191+
{
192+
// Use AN to load the symbol.
193+
is_reference = true;
194+
result << AND << NOT_POSTFIX << OPERAND_SEPARATOR;
195+
convert(to_symbol_expr(expr.op()));
196+
result << LINE_SEPARATOR;
197+
}
198+
else
199+
{
200+
// Use NOT to negate the RLO after the wrapped expression was loaded.
201+
convert(expr.op());
202+
result << NOT LINE_SEPARATOR;
203+
}
204+
}
205+
206+
void expr2stlt::convert(const symbol_exprt &expr)
207+
{
208+
if(is_reference)
209+
{
210+
result << REFERENCE_FLAG;
211+
is_reference = false;
212+
}
213+
result << id2string(id_shorthand(expr.get_identifier()));
214+
}
215+
216+
void expr2stlt::convert_multiary_bool(
217+
std::vector<exprt> &operands,
218+
const char operation)
219+
{
220+
if(inside_bit_string)
221+
convert_multiary_bool_operands(operands, operation);
222+
else
223+
{
224+
convert_first_non_trivial_operand(operands);
225+
convert_multiary_bool_operands(operands, operation);
226+
}
227+
}
228+
229+
void expr2stlt::convert_multiary_bool_operands(
230+
const std::vector<exprt> &operands,
231+
const char operation)
232+
{
233+
for(const exprt &op : operands)
234+
{
235+
if(ID_not == op.id())
236+
{
237+
result << operation << NOT_POSTFIX;
238+
convert_bool_operand(op.op0());
239+
}
240+
else
241+
{
242+
result << operation;
243+
convert_bool_operand(op);
244+
}
245+
}
246+
}
247+
248+
void expr2stlt::convert_bool_operand(const exprt &op)
249+
{
250+
if(op.id() == ID_symbol)
251+
{
252+
is_reference = true;
253+
result << OPERAND_SEPARATOR;
254+
convert(to_symbol_expr(op));
255+
result << LINE_SEPARATOR;
256+
}
257+
else
258+
{
259+
inside_bit_string = false;
260+
result << NESTING_OPEN_LINE_SEPARATOR;
261+
convert(op);
262+
result << NESTING_CLOSED_LINE_SEPARATOR;
263+
}
264+
}
265+
266+
void expr2stlt::convert_first_non_trivial_operand(std::vector<exprt> &operands)
267+
{
268+
exprt non_trivial_op;
269+
for(auto it{begin(operands)}; it != end(operands); ++it)
270+
{
271+
if(
272+
(ID_symbol == it->id()) ||
273+
(ID_not == it->id() && ID_symbol == it->op0().id()))
274+
continue;
275+
else
276+
{
277+
non_trivial_op = *it;
278+
operands.erase(it);
279+
break;
280+
}
281+
}
282+
// Important for some scenarios: Convert complex op first, set bit string
283+
// flag to true afterwards.
284+
if(non_trivial_op.is_not_nil())
285+
convert(non_trivial_op);
286+
287+
inside_bit_string = true;
288+
}
289+
290+
irep_idt expr2stlt::id_shorthand(const irep_idt &identifier)
291+
{
292+
const symbolt *symbol;
293+
std::string shorthand = id2string(identifier);
294+
if(
295+
!ns.lookup(identifier, symbol) && !symbol->base_name.empty() &&
296+
has_suffix(shorthand, id2string(symbol->base_name)))
297+
return symbol->base_name;
298+
299+
const std::string::size_type pos = shorthand.rfind(SCOPE_SEPARATOR);
300+
if(pos != std::string::npos)
301+
shorthand.erase(0, pos + std::strlen(SCOPE_SEPARATOR));
302+
303+
return shorthand;
304+
}

0 commit comments

Comments
 (0)