HAL  v4.5.0-124-g47ab54673
The Hardware Analyzer - a comprehensive reverse engineering and manipulation framework for gate-level netlists.
boolean_function.cpp
Go to the documentation of this file.
2 
3 #include <unordered_set>
4 
10 
11 #include <algorithm>
12 #include <bitset>
13 #include <boost/spirit/home/x3.hpp>
14 #include <chrono>
15 #include <map>
16 #include <iomanip>
17 
18 namespace hal
19 {
20  template<>
21  std::map<BooleanFunction::Value, std::string> EnumStrings<BooleanFunction::Value>::data = {{BooleanFunction::Value::ZERO, "0"},
22  {BooleanFunction::Value::ONE, "1"},
23  {BooleanFunction::Value::X, "X"},
24  {BooleanFunction::Value::Z, "Z"}};
25 
27  {
28  switch (v)
29  {
30  case ZERO:
31  return std::string("0");
32  case ONE:
33  return std::string("1");
34  case X:
35  return std::string("X");
36  case Z:
37  return std::string("Z");
38  }
39 
40  return std::string("X");
41  }
42 
43  namespace
44  {
45  static std::vector<char> char_map = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F'};
46  // namespace
47 
48  static Result<std::string> to_bin(const std::vector<BooleanFunction::Value>& value)
49  {
50  if (value.size() == 0)
51  {
52  return ERR("could not convert bit-vector to binary string: bit-vector is empty");
53  }
54 
55  std::string res = "";
56  res.reserve(value.size());
57 
58  for (auto v : value)
59  {
60  res += enum_to_string<BooleanFunction::Value>(v);
61  }
62 
63  return OK(res);
64  }
65 
66  static Result<std::string> to_oct(const std::vector<BooleanFunction::Value>& value)
67  {
68  int bitsize = value.size();
69  if (bitsize == 0)
70  {
71  return ERR("could not convert bit-vector to octal string: bit-vector is empty");
72  }
73 
74  u8 first_bits = bitsize % 3;
75 
76  u8 index = 0;
77  u8 mask = 0;
78 
79  u8 v1, v2, v3;
80 
81  // result string prep
82  std::string res = "";
83  res.reserve((bitsize + 2) / 3);
84 
85  // deal with 1 or 2 leading bits
86  for (u8 i = 0; i < first_bits; i++)
87  {
88  v1 = value.at(i);
89  index = (index << 1) | v1;
90  mask |= v1;
91  }
92 
93  if (first_bits)
94  {
95  if ((mask & 0x80) > 0) // mask "sign" bit set if 'X' or 'Z' among first bits in vector
96  res += 'X';
97  else
98  res += (char_map[index]);
99  }
100 
101  // deal with 3-bit blocks (left to right)
102  for (int i = bitsize % 3; i < bitsize; i += 3)
103  {
104  v1 = value[i];
105  v2 = value[i + 1];
106  v3 = value[i + 2];
107 
108  index = (v1 << 2) | (v2 << 1) | v3; // cannot exceed char_map range as index always < 16, no further check required
109  mask = (v1 | v2 | v3);
110 
111  if ((mask & 0x80) > 0) // mask "sign" bit set if 'X' or 'Z' among tested 3 bits
112  res += 'X';
113  else
114  res += (char_map[index]);
115  }
116  return OK(res);
117  }
118 
119  static Result<std::string> to_dec(const std::vector<BooleanFunction::Value>& value)
120  {
121  int bitsize = value.size();
122  if (bitsize == 0)
123  {
124  return ERR("could not convert bit-vector to decimal string: bit-vector is empty");
125  }
126 
127  if (bitsize > 64)
128  {
129  return ERR("could not convert bit-vector to decimal string: bit-vector has length " + std::to_string(bitsize) + ", but only up to 64 bits are supported for decimal conversion");
130  }
131 
132  u64 tmp = 0;
133  u8 x_flag = 0;
134 
135  for (auto it = value.rbegin(); it != value.rend(); it++)
136  {
137  x_flag |= *it >> 1;
138  tmp <<= 1;
139  tmp |= *it;
140  }
141 
142  if (x_flag)
143  {
144  return OK(std::string("X"));
145  }
146  return OK(std::to_string(tmp));
147  }
148 
149  static Result<std::string> to_hex(const std::vector<BooleanFunction::Value>& value)
150  {
151  int bitsize = value.size();
152  if (bitsize == 0)
153  {
154  return ERR("could not convert bit-vector to hexadecimal string: bit-vector is empty");
155  }
156 
157  u8 first_bits = bitsize & 0x3;
158 
159  u8 index = 0;
160  u8 mask = 0;
161 
162  u8 v1, v2, v3, v4;
163 
164  // result string prep
165  std::string res = "";
166  res.reserve((bitsize + 3) / 4);
167 
168  // deal with 1-3 leading bits
169  for (u8 i = 0; i < first_bits; i++)
170  {
171  v1 = value.at(i);
172  index = (index << 1) | v1;
173  mask |= v1;
174  }
175 
176  if (first_bits)
177  {
178  if ((mask & 0x80) > 0) // mask "sign" bit set if 'X' or 'Z' among first bits in vector
179  res += 'X';
180  else
181  res += (char_map[index]);
182  }
183 
184  // deal with 4-bit blocks (left to right)
185  for (int i = bitsize & 0x3; i < bitsize; i += 4)
186  {
187  v1 = value[i];
188  v2 = value[i + 1];
189  v3 = value[i + 2];
190  v4 = value[i + 3];
191 
192  index = ((v1 << 3) | (v2 << 2) | (v3 << 1) | v4) & 0xF;
193  mask = (v1 | v2 | v3 | v4);
194 
195  if ((mask & 0x80) > 0) // mask "sign" bit set if 'X' or 'Z' among tested 4 bits
196  res += 'X';
197  else
198  res += (char_map[index]);
199  }
200 
201  return OK(res);
202  }
203  } // namespace
204 
205  Result<std::string> BooleanFunction::to_string(const std::vector<BooleanFunction::Value>& value, u8 base)
206  {
207  switch (base)
208  {
209  case 2:
210  return to_bin(value);
211  case 8:
212  return to_oct(value);
213  case 10:
214  return to_dec(value);
215  case 16:
216  return to_hex(value);
217  default:
218  return ERR("could not convert bit-vector to string: invalid value '" + std::to_string(base) + "' given for base");
219  }
220  }
221 
222  Result<u64> BooleanFunction::to_u64(const std::vector<BooleanFunction::Value>& value)
223  {
224  if (value.size() > 64)
225  {
226  return ERR("cannot translate vector of values to u64 numeral: can only support vectors up to 64 bits and got vector of size " + std::to_string(value.size()) + ".");
227  }
228 
229  u64 val = 0;
230  for (auto it = value.rbegin(); it != value.rend(); it++)
231  {
232  if ((*it != BooleanFunction::Value::ZERO) && (*it != BooleanFunction::Value::ONE))
233  {
234  return ERR("cannot translate vector of values to u64 numeral: found value other than ZERO or ONE: " + BooleanFunction::to_string(*it) + ".");
235  }
236 
237  val <<= 1;
238  val |= *it;
239  }
240 
241  return OK(val);
242  }
243 
244  std::ostream& operator<<(std::ostream& os, BooleanFunction::Value v)
245  {
246  return os << BooleanFunction::to_string(v);
247  }
248 
250  {
251  }
252 
253  Result<BooleanFunction> BooleanFunction::build(std::vector<BooleanFunction::Node>&& nodes)
254  {
255  if (auto res = BooleanFunction::validate(BooleanFunction(std::move(nodes))); res.is_error())
256  {
257  return ERR_APPEND(res.get_error(), "could not build Boolean function from vector of nodes: failed to validate Boolean function");
258  }
259  else
260  {
261  return res;
262  }
263  }
264 
266  {
268  }
269 
271  {
272  return BooleanFunction(Node::Constant({value}));
273  }
274 
275  BooleanFunction BooleanFunction::Const(const std::vector<BooleanFunction::Value>& values)
276  {
277  return BooleanFunction(Node::Constant(values));
278  }
279 
281  {
282  auto values = std::vector<BooleanFunction::Value>();
283  values.reserve(size);
284  for (auto i = 0; i < size; i++)
285  {
286  values.emplace_back(((value >> i) & 1) ? BooleanFunction::Value::ONE : BooleanFunction::Value::ZERO);
287  }
288 
289  return BooleanFunction::Const(values);
290  }
291 
293  {
295  }
296 
298  {
299  if ((p0.size() != p1.size()) || (p0.size() != size))
300  {
301  return ERR("could not join Boolean functions using AND operation: bit-sizes do not match (p0 = " + std::to_string(p0.size()) + ", p1 = " + std::to_string(p1.size())
302  + ", size = " + std::to_string(size) + ")");
303  }
304 
305  return OK(BooleanFunction(Node::Operation(NodeType::And, size), std::move(p0), std::move(p1)));
306  }
307 
309  {
310  if ((p0.size() != p1.size()) || (p0.size() != size))
311  {
312  return ERR("could not join Boolean functions using OR operation: bit-sizes do not match (p0 = " + std::to_string(p0.size()) + ", p1 = " + std::to_string(p1.size())
313  + ", size = " + std::to_string(size) + ").");
314  }
315 
316  return OK(BooleanFunction(Node::Operation(NodeType::Or, size), std::move(p0), std::move(p1)));
317  }
318 
320  {
321  if (p0.size() != size)
322  {
323  return ERR("could not invert Boolean function using NOT operation: bit-sizes do not match (p0 = " + std::to_string(p0.size()) + ", size = " + std::to_string(size) + ").");
324  }
325 
326  return OK(BooleanFunction(Node::Operation(NodeType::Not, size), std::move(p0)));
327  }
328 
330  {
331  if ((p0.size() != p1.size()) || (p0.size() != size))
332  {
333  return ERR("could not join Boolean functions using XOR operation: bit-sizes do not match (p0 = " + std::to_string(p0.size()) + ", p1 = " + std::to_string(p1.size())
334  + ", size = " + std::to_string(size) + ").");
335  }
336 
337  return OK(BooleanFunction(Node::Operation(NodeType::Xor, size), std::move(p0), std::move(p1)));
338  }
339 
341  {
342  if ((p0.size() != p1.size()) || (p0.size() != size))
343  {
344  return ERR("could not join Boolean functions using ADD operation: bit-sizes do not match (p0 = " + std::to_string(p0.size()) + ", p1 = " + std::to_string(p1.size())
345  + ", size = " + std::to_string(size) + ").");
346  }
347 
348  return OK(BooleanFunction(Node::Operation(NodeType::Add, size), std::move(p0), std::move(p1)));
349  }
350 
352  {
353  if ((p0.size() != p1.size()) || (p0.size() != size))
354  {
355  return ERR("could not join Boolean functions using SUB operation: bit-sizes do not match (p0 = " + std::to_string(p0.size()) + ", p1 = " + std::to_string(p1.size())
356  + ", size = " + std::to_string(size) + ").");
357  }
358 
359  return OK(BooleanFunction(Node::Operation(NodeType::Sub, size), std::move(p0), std::move(p1)));
360  }
361 
363  {
364  if ((p0.size() != p1.size()) || (p0.size() != size))
365  {
366  return ERR("could not join Boolean functions using MUL operation: bit-sizes do not match (p0 = " + std::to_string(p0.size()) + ", p1 = " + std::to_string(p1.size())
367  + ", size = " + std::to_string(size) + ").");
368  }
369 
370  return OK(BooleanFunction(Node::Operation(NodeType::Mul, size), std::move(p0), std::move(p1)));
371  }
372 
374  {
375  if ((p0.size() != p1.size()) || (p0.size() != size))
376  {
377  return ERR("could not join Boolean functions using SDIV operation: bit-sizes do not match (p0 = " + std::to_string(p0.size()) + ", p1 = " + std::to_string(p1.size())
378  + ", size = " + std::to_string(size) + ").");
379  }
380 
381  return OK(BooleanFunction(Node::Operation(NodeType::Sdiv, size), std::move(p0), std::move(p1)));
382  }
383 
385  {
386  if ((p0.size() != p1.size()) || (p0.size() != size))
387  {
388  return ERR("could not join Boolean functions using UDIV operation: bit-sizes do not match (p0 = " + std::to_string(p0.size()) + ", p1 = " + std::to_string(p1.size())
389  + ", size = " + std::to_string(size) + ").");
390  }
391 
392  return OK(BooleanFunction(Node::Operation(NodeType::Udiv, size), std::move(p0), std::move(p1)));
393  }
394 
396  {
397  if ((p0.size() != p1.size()) || (p0.size() != size))
398  {
399  return ERR("could not join Boolean functions using SREM operation: bit-sizes do not match (p0 = " + std::to_string(p0.size()) + ", p1 = " + std::to_string(p1.size())
400  + ", size = " + std::to_string(size) + ").");
401  }
402 
403  return OK(BooleanFunction(Node::Operation(NodeType::Srem, size), std::move(p0), std::move(p1)));
404  }
405 
407  {
408  if ((p0.size() != p1.size()) || (p0.size() != size))
409  {
410  return ERR("could not join Boolean functions using UREM operation: bit-sizes do not match (p0 = " + std::to_string(p0.size()) + ", p1 = " + std::to_string(p1.size())
411  + ", size = " + std::to_string(size) + ").");
412  }
413 
414  return OK(BooleanFunction(Node::Operation(NodeType::Urem, size), std::move(p0), std::move(p1)));
415  }
416 
418  {
419  if (!p1.is_index() || !p2.is_index())
420  {
421  return ERR("could not apply slice operation: function types do not match (p1 and p2 must be of type 'BooleanFunction::Index')");
422  }
423  if ((p0.size() != p1.size()) || (p1.size() != p2.size()))
424  {
425  return ERR("could not apply slice operation: bit-sizes do not match (p0 = " + std::to_string(p0.size()) + ", p1 = " + std::to_string(p1.size()) + ", p2 = " + std::to_string(p2.size())
426  + " - sizes must be equal)");
427  }
428 
429  auto start = p1.get_index_value().get();
430  auto end = p2.get_index_value().get();
431  if ((start > end) || (start >= p0.size()) || (end >= p0.size()) || (end - start + 1) != size)
432  {
433  return ERR("could not apply SLICE operation: bit indices are not valid, p1 must be larger or equal than p1 and smaller than p0 (p0 = " + std::to_string(p0.size())
434  + ", p1 = " + std::to_string(start) + ", p2 = " + std::to_string(end) + ")");
435  }
436 
437  return OK(BooleanFunction(Node::Operation(NodeType::Slice, size), std::move(p0), std::move(p1), std::move(p2)));
438  }
439 
441  {
442  if ((p0.size() + p1.size()) != size)
443  {
444  return ERR("could not apply CONCAT operation: function input widths do not match (p0 = " + std::to_string(p0.size()) + "-bit, p1 = " + std::to_string(p1.size())
445  + "-bit, size = " + std::to_string(size) + ").");
446  }
447 
448  return OK(BooleanFunction(Node::Operation(NodeType::Concat, size), std::move(p0), std::move(p1)));
449  }
450 
452  {
453  if (p0.size() > size || p1.size() != size)
454  {
455  return ERR("could not apply ZEXT operation: function input width does not match (p0 = " + std::to_string(p0.size()) + "-bit, p1 = " + std::to_string(p1.size())
456  + "-bit, size = " + std::to_string(size) + ").");
457  }
458 
459  if (!p1.has_index_value(size))
460  {
461  return ERR("could not apply ZEXT operation: p1 does not encode size (p1 = " + p1.to_string() + ", size = " + std::to_string(size) + ").");
462  }
463 
464  return OK(BooleanFunction(Node::Operation(NodeType::Zext, size), std::move(p0), std::move(p1)));
465  }
466 
468  {
469  if (p0.size() > size || p1.size() != size)
470  {
471  return ERR("could not apply SEXT operation: function input width does not match (p0 = " + std::to_string(p0.size()) + "-bit, p1 = " + std::to_string(p1.size())
472  + "-bit, size = " + std::to_string(size) + ").");
473  }
474 
475  if (!p1.has_index_value(size))
476  {
477  return ERR("could not apply SEXT operation: p1 does not encode size (p1 = " + p1.to_string() + ", size = " + std::to_string(size) + ").");
478  }
479 
480  return OK(BooleanFunction(Node::Operation(NodeType::Sext, size), std::move(p0), std::move(p1)));
481  }
482 
484  {
485  if (p0.size() != size || p1.size() != size)
486  {
487  return ERR("could not apply SHL operation: function input width does not match (p0 = " + std::to_string(p0.size()) + "-bit, p1 = " + std::to_string(p1.size())
488  + "-bit, size = " + std::to_string(size) + ").");
489  }
490 
491  if (!p1.is_index())
492  {
493  return ERR("could not apply SHL operation: p1 is not an index.");
494  }
495 
496  return OK(BooleanFunction(Node::Operation(NodeType::Shl, size), std::move(p0), std::move(p1)));
497  }
498 
500  {
501  if (p0.size() != size || p1.size() != size)
502  {
503  return ERR("could not apply LSHR operation: function input width does not match (p0 = " + std::to_string(p0.size()) + "-bit, p1 = " + std::to_string(p1.size())
504  + "-bit, size = " + std::to_string(size) + ").");
505  }
506 
507  if (!p1.is_index())
508  {
509  return ERR("could not apply LSHR operation: p1 is not an index.");
510  }
511 
512  return OK(BooleanFunction(Node::Operation(NodeType::Lshr, size), std::move(p0), std::move(p1)));
513  }
514 
516  {
517  if (p0.size() != size || p1.size() != size)
518  {
519  return ERR("could not apply ASHR operation: function input width does not match (p0 = " + std::to_string(p0.size()) + "-bit, p1 = " + std::to_string(p1.size())
520  + "-bit, size = " + std::to_string(size) + ").");
521  }
522 
523  if (!p1.is_index())
524  {
525  return ERR("could not apply ASHR operation: p1 is not an index.");
526  }
527 
528  return OK(BooleanFunction(Node::Operation(NodeType::Ashr, size), std::move(p0), std::move(p1)));
529  }
530 
532  {
533  if (p0.size() != size || p1.size() != size)
534  {
535  return ERR("could not apply ROL operation: function input width does not match (p0 = " + std::to_string(p0.size()) + "-bit, p1 = " + std::to_string(p1.size())
536  + "-bit, size = " + std::to_string(size) + ").");
537  }
538 
539  if (!p1.is_index())
540  {
541  return ERR("could not apply ROL operation: p1 is not an index.");
542  }
543 
544  return OK(BooleanFunction(Node::Operation(NodeType::Rol, size), std::move(p0), std::move(p1)));
545  }
546 
548  {
549  if (p0.size() != size || p1.size() != size)
550  {
551  return ERR("could not apply ROR operation: function input width does not match (p0 = " + std::to_string(p0.size()) + "-bit, p1 = " + std::to_string(p1.size())
552  + "-bit, size = " + std::to_string(size) + ").");
553  }
554 
555  if (!p1.is_index())
556  {
557  return ERR("could not apply ROR operation: p1 is not an index.");
558  }
559 
560  return OK(BooleanFunction(Node::Operation(NodeType::Ror, size), std::move(p0), std::move(p1)));
561  }
562 
564  {
565  if (p0.size() != p1.size() || size != 1)
566  {
567  return ERR("could not apply EQ operation: function input width does not match (p0 = " + std::to_string(p0.size()) + "-bit, p1 = " + std::to_string(p1.size())
568  + "-bit, size = " + std::to_string(size) + ").");
569  }
570 
571  return OK(BooleanFunction(Node::Operation(NodeType::Eq, size), std::move(p0), std::move(p1)));
572  }
573 
575  {
576  if (p0.size() != p1.size() || size != 1)
577  {
578  return ERR("could not apply SLE operation: function input width does not match (p0 = " + std::to_string(p0.size()) + "-bit, p1 = " + std::to_string(p1.size())
579  + "-bit, size = " + std::to_string(size) + ").");
580  }
581 
582  return OK(BooleanFunction(Node::Operation(NodeType::Sle, size), std::move(p0), std::move(p1)));
583  }
584 
586  {
587  if (p0.size() != p1.size() || size != 1)
588  {
589  return ERR("could not apply SLT operation: function input width does not match (p0 = " + std::to_string(p0.size()) + "-bit, p1 = " + std::to_string(p1.size())
590  + "-bit, size = " + std::to_string(size) + ").");
591  }
592 
593  return OK(BooleanFunction(Node::Operation(NodeType::Slt, size), std::move(p0), std::move(p1)));
594  }
595 
597  {
598  if (p0.size() != p1.size() || size != 1)
599  {
600  return ERR("could not apply ULE operation: function input width does not match (p0 = " + std::to_string(p0.size()) + "-bit, p1 = " + std::to_string(p1.size())
601  + "-bit, size = " + std::to_string(size) + ").");
602  }
603 
604  return OK(BooleanFunction(Node::Operation(NodeType::Ule, size), std::move(p0), std::move(p1)));
605  }
606 
608  {
609  if (p0.size() != p1.size() || size != 1)
610  {
611  return ERR("could not apply ULT operation: function input width does not match (p0 = " + std::to_string(p0.size()) + "-bit, p1 = " + std::to_string(p1.size())
612  + "-bit, size = " + std::to_string(size) + ").");
613  }
614 
615  return OK(BooleanFunction(Node::Operation(NodeType::Ult, size), std::move(p0), std::move(p1)));
616  }
617 
619  {
620  if (p0.size() != 1 || p1.size() != size || p2.size() != size)
621  {
622  return ERR("could not apply ITE operation: function input width does not match (p0 = " + std::to_string(p0.size()) + "-bit, p1 = " + std::to_string(p1.size())
623  + "-bit, p2 = " + std::to_string(p2.size()) + "-bit, size = " + std::to_string(size) + ").");
624  }
625 
626  return OK(BooleanFunction(Node::Operation(NodeType::Ite, size), std::move(p0), std::move(p1), std::move(p2)));
627  }
628 
629  std::ostream& operator<<(std::ostream& os, const BooleanFunction& f)
630  {
631  return os << f.to_string();
632  }
633 
635  {
636  return BooleanFunction::And(this->clone(), other.clone(), this->size()).get();
637  }
638 
640  {
641  *this = BooleanFunction::And(this->clone(), other.clone(), this->size()).get();
642  return *this;
643  }
644 
646  {
647  return BooleanFunction::Not(this->clone(), this->size()).get();
648  }
649 
651  {
652  return BooleanFunction::Or(this->clone(), other.clone(), this->size()).get();
653  }
654 
656  {
657  *this = BooleanFunction::Or(this->clone(), other.clone(), this->size()).get();
658  return *this;
659  }
660 
662  {
663  return BooleanFunction::Xor(this->clone(), other.clone(), this->size()).get();
664  }
665 
667  {
668  *this = BooleanFunction::Xor(this->clone(), other.clone(), this->size()).get();
669  return *this;
670  }
671 
673  {
674  return BooleanFunction::Add(this->clone(), other.clone(), this->size()).get();
675  }
676 
678  {
679  *this = BooleanFunction::Add(this->clone(), other.clone(), this->size()).get();
680  return *this;
681  }
682 
684  {
685  return BooleanFunction::Sub(this->clone(), other.clone(), this->size()).get();
686  }
687 
689  {
690  *this = BooleanFunction::Sub(this->clone(), other.clone(), this->size()).get();
691  return *this;
692  }
693 
695  {
696  return BooleanFunction::Mul(this->clone(), other.clone(), this->size()).get();
697  }
698 
700  {
701  *this = BooleanFunction::Mul(this->clone(), other.clone(), this->size()).get();
702  return *this;
703  }
704 
706  {
707  if (this->m_nodes.size() != other.m_nodes.size())
708  {
709  return false;
710  }
711 
712  for (auto i = 0ul; i < this->m_nodes.size(); i++)
713  {
714  if (this->m_nodes[i] != other.m_nodes[i])
715  {
716  return false;
717  }
718  }
719  return true;
720  }
721 
723  {
724  return !(*this == other);
725  }
726 
728  {
729  if (this->m_nodes.size() < other.m_nodes.size())
730  {
731  return true;
732  }
733  if (this->m_nodes.size() > other.m_nodes.size())
734  {
735  return false;
736  }
737 
738  // compare the nodes directly instead of their string representation, this operator is on the hot path
739  // of every symbolic state lookup and formatting two strings per comparison dominated the evaluation
740  return std::lexicographical_compare(this->m_nodes.begin(), this->m_nodes.end(), other.m_nodes.begin(), other.m_nodes.end());
741  }
742 
744  {
745  return this->m_nodes.empty();
746  }
747 
749  {
750  auto function = BooleanFunction();
751  function.m_nodes.reserve(this->m_nodes.size());
752 
753  for (const auto& node : this->m_nodes)
754  {
755  function.m_nodes.emplace_back(node);
756  }
757 
758  return function;
759  }
760 
762  {
763  return this->get_top_level_node().size;
764  }
765 
767  {
768  return (this->is_empty()) ? false : this->get_top_level_node().is(type);
769  }
770 
772  {
773  return (this->is_empty()) ? false : this->get_top_level_node().is_variable();
774  }
775 
776  bool BooleanFunction::has_variable_name(const std::string& variable_name) const
777  {
778  return (this->is_empty()) ? false : this->get_top_level_node().has_variable_name(variable_name);
779  }
780 
782  {
783  if (this->is_empty())
784  {
785  return ERR("Boolean function is empty");
786  }
787 
788  return this->get_top_level_node().get_variable_name();
789  }
790 
792  {
793  return (this->is_empty()) ? false : this->get_top_level_node().is_constant();
794  }
795 
796  bool BooleanFunction::has_constant_value(const std::vector<Value>& value) const
797  {
798  return (this->is_empty()) ? false : this->get_top_level_node().has_constant_value(value);
799  }
800 
802  {
803  return (this->is_empty()) ? false : this->get_top_level_node().has_constant_value(value);
804  }
805 
807  {
808  if (this->is_empty())
809  {
810  return ERR("Boolean function is empty");
811  }
812 
813  return this->get_top_level_node().get_constant_value();
814  }
815 
817  {
818  if (this->is_empty())
819  {
820  return ERR("Boolean function is empty");
821  }
822 
824  }
825 
827  {
828  return (this->is_empty()) ? false : this->get_top_level_node().is_index();
829  }
830 
832  {
833  return (this->is_empty()) ? false : this->get_top_level_node().has_index_value(value);
834  }
835 
837  {
838  if (this->is_empty())
839  {
840  return ERR("Boolean function is empty");
841  }
842 
843  return this->get_top_level_node().get_index_value();
844  }
845 
847  {
848  return this->m_nodes.back();
849  }
850 
852  {
853  return this->m_nodes.size();
854  }
855 
856  const std::vector<BooleanFunction::Node>& BooleanFunction::get_nodes() const
857  {
858  return this->m_nodes;
859  }
860 
861  std::vector<BooleanFunction> BooleanFunction::get_parameters() const
862  {
869 
870  auto coverage = this->compute_node_coverage();
871  switch (this->get_top_level_node().get_arity())
872  {
873  case 0: {
874  return {};
875  }
876  case 1: {
877  return {BooleanFunction(std::vector<Node>({this->m_nodes.begin(), this->m_nodes.end() - 1}))};
878  }
879  case 2: {
880  auto index = this->length() - coverage[this->length() - 2] - 1;
881 
882  return {BooleanFunction(std::vector<Node>({this->m_nodes.begin(), this->m_nodes.begin() + index})),
883  BooleanFunction(std::vector<Node>({this->m_nodes.begin() + index, this->m_nodes.end() - 1}))};
884  }
885  case 3: {
886  auto index0 = this->length() - coverage[this->length() - 3] - coverage[this->length() - 2] - 1;
887  auto index1 = this->length() - coverage[this->length() - 2] - 1;
888 
889  return {BooleanFunction(std::vector<Node>({this->m_nodes.begin(), this->m_nodes.begin() + index0})),
890  BooleanFunction(std::vector<Node>({this->m_nodes.begin() + index0, this->m_nodes.begin() + index1})),
891  BooleanFunction(std::vector<Node>({this->m_nodes.begin() + index1, this->m_nodes.end() - 1}))};
892  }
893 
894  default:
895  assert(false && "not implemented reached.");
896  }
897 
898  return {};
899  }
900 
901  std::set<std::string> BooleanFunction::get_variable_names() const
902  {
903  auto variable_names = std::set<std::string>();
904  for (const auto& node : this->m_nodes)
905  {
906  if (node.is_variable())
907  {
908  variable_names.insert(node.variable);
909  }
910  }
911  return variable_names;
912  }
913 
914  Result<std::string> BooleanFunction::default_printer(const BooleanFunction::Node& node, std::vector<std::string>&& operands)
915  {
916  if (node.get_arity() != operands.size())
917  {
918  return ERR("could not print Boolean function: node arity of " + std::to_string(node.get_arity()) + " does not match number of operands of " + std::to_string(operands.size()));
919  }
920 
921  switch (node.type)
922  {
926  return OK(node.to_string());
927 
929  return OK("(" + operands[0] + " & " + operands[1] + ")");
931  return OK("(! " + operands[0] + ")");
933  return OK("(" + operands[0] + " | " + operands[1] + ")");
935  return OK("(" + operands[0] + " ^ " + operands[1] + ")");
936 
938  return OK("(" + operands[0] + " + " + operands[1] + ")");
940  return OK("(" + operands[0] + " - " + operands[1] + ")");
942  return OK("(" + operands[0] + " * " + operands[1] + ")");
944  return OK("(" + operands[0] + " /s " + operands[1] + ")");
946  return OK("(" + operands[0] + " / " + operands[1] + ")");
948  return OK("(" + operands[0] + " \%s " + operands[1] + ")");
950  return OK("(" + operands[0] + " \% " + operands[1] + ")");
951 
953  return OK("(" + operands[0] + " ++ " + operands[1] + ")");
955  return OK("Slice(" + operands[0] + ", " + operands[1] + ", " + operands[2] + ")");
957  return OK("Zext(" + operands[0] + ", " + operands[1] + ")");
959  return OK("Sext(" + operands[0] + ", " + operands[1] + ")");
960 
962  return OK("(" + operands[0] + " << " + operands[1] + ")");
964  return OK("(" + operands[0] + " >> " + operands[1] + ")");
966  return OK("(" + operands[0] + " >>a " + operands[1] + ")");
968  return OK("(" + operands[0] + " <<r " + operands[1] + ")");
970  return OK("(" + operands[0] + " >>r " + operands[1] + ")");
971 
973  return OK("(" + operands[0] + " == " + operands[1] + ")");
975  return OK("(" + operands[0] + " <s " + operands[1] + ")");
977  return OK("(" + operands[0] + " <=s " + operands[1] + ")");
979  return OK("(" + operands[0] + " < " + operands[1] + ")");
981  return OK("(" + operands[0] + " <= " + operands[1] + ")");
983  return OK("Ite(" + operands[0] + ", " + operands[1] + ", " + operands[2] + ")");
984 
985  default:
986  return ERR("could not print Boolean function: unsupported node type '" + std::to_string(node.type) + "'");
987  }
988  }
989 
990  Result<std::string> BooleanFunction::algebraic_printer(const BooleanFunction::Node& node, std::vector<std::string>&& operands)
991  {
992  if (node.get_arity() != operands.size())
993  {
994  return ERR("could not print Boolean function: node arity of " + std::to_string(node.get_arity()) + " does not match number of operands of " + std::to_string(operands.size()));
995  }
996 
997  switch (node.type)
998  {
1001  return OK(node.to_string());
1002 
1004  return OK("CONST" + std::string(node.has_constant_value(0) ? "0" : "1"));
1005 
1007  return OK("(" + operands[0] + "*" + operands[1] + ")");
1009  return OK("(! " + operands[0] + ")");
1011  return OK("(" + operands[0] + "+" + operands[1] + ")");
1012 
1013  default:
1014  return ERR("could not print Boolean function: unsupported node type '" + std::to_string(node.type) + "'");
1015  }
1016  }
1017 
1018  std::string BooleanFunction::to_string(std::function<Result<std::string>(const BooleanFunction::Node& node, std::vector<std::string>&& operands)>&& printer) const
1019  {
1020  // (1) early termination in case the Boolean function is empty
1021  if (this->m_nodes.empty())
1022  {
1023  return "<empty>";
1024  }
1025 
1026  // (2) iterate the list of nodes and setup string from leafs to root
1027  std::vector<std::string> stack;
1028  for (const auto& node : this->m_nodes)
1029  {
1030  std::vector<std::string> operands;
1031 
1032  if (stack.size() < node.get_arity())
1033  {
1034  // log_error("netlist", "Cannot fetch {} nodes from the stack (= imbalanced stack with {} parts - {}).", node->get_arity(), stack.size(), this->to_string_in_reverse_polish_notation());
1035  return "";
1036  }
1037 
1038  std::move(stack.end() - static_cast<u64>(node.get_arity()), stack.end(), std::back_inserter(operands));
1039  stack.erase(stack.end() - static_cast<u64>(node.get_arity()), stack.end());
1040 
1041  if (auto res = printer(node, std::move(operands)); res.is_ok())
1042  {
1043  stack.emplace_back(res.get());
1044  }
1045  else
1046  {
1047  log_error("netlist", "Cannot translate BooleanFunction::Node '{}' to a string: {}.", node.to_string(), res.get_error().get());
1048  return "";
1049  }
1050  }
1051 
1052  switch (stack.size())
1053  {
1054  case 1:
1055  return stack.back();
1056  default: {
1057  // log_error("netlist", "Cannot translate BooleanFunction (= imbalanced stack with {} remaining parts).", stack.size());
1058  return "";
1059  }
1060  }
1061  }
1062 
1064  {
1067 
1068  static const std::vector<std::tuple<ParserType, std::function<Result<std::vector<Token>>(const std::string&)>>> parsers = {
1071  {ParserType::LibertyNoSpace, BooleanFunctionParser::parse_with_liberty_grammar}
1072  };
1073 
1074  for (const auto& [parser_type, parser] : parsers)
1075  {
1076  std::string sanitized_expression = expression;
1077  ParserType used_parser_type = parser_type;
1078 
1079  if (parser_type == ParserType::LibertyNoSpace)
1080  {
1081  sanitized_expression.erase(
1082  std::remove(sanitized_expression.begin(), sanitized_expression.end(), ' '),
1083  sanitized_expression.end()
1084  );
1085  used_parser_type = ParserType::Liberty;
1086  }
1087 
1088  auto tokens = parser(sanitized_expression);
1089  // (1) skip if parser cannot translate to tokens
1090  if (tokens.is_error())
1091  {
1092  continue;
1093  }
1094 
1095  // (2) skip if cannot translate to valid reverse-polish notation
1096  tokens = BooleanFunctionParser::reverse_polish_notation(tokens.get(), sanitized_expression, used_parser_type);
1097  if (tokens.is_error())
1098  {
1099  continue;
1100  }
1101  // (3) skip if reverse-polish notation tokens are no valid Boolean function
1102  auto function = BooleanFunctionParser::translate(tokens.get(), sanitized_expression);
1103  if (function.is_error())
1104  {
1105  continue;
1106  }
1107  return function;
1108  }
1109  return ERR("could not parse Boolean function from string: no parser available for '" + expression + "'");
1110  }
1111 
1113  {
1114  auto simplified = Simplification::local_simplification(*this).map<BooleanFunction>([](const auto& s) { return Simplification::abc_simplification(s); }).map<BooleanFunction>([](const auto& s) {
1116  });
1117 
1118  return (simplified.is_ok()) ? simplified.get() : this->clone();
1119  }
1120 
1122  {
1123  auto simplified = Simplification::local_simplification(*this);
1124 
1125  return (simplified.is_ok()) ? simplified.get() : this->clone();
1126  }
1127 
1128  BooleanFunction BooleanFunction::substitute(const std::string& old_variable_name, const std::string& new_variable_name) const
1129  {
1130  auto function = this->clone();
1131  for (auto i = 0u; i < this->m_nodes.size(); i++)
1132  {
1133  if (this->m_nodes[i].has_variable_name(old_variable_name))
1134  {
1135  function.m_nodes[i] = Node::Variable(new_variable_name, this->m_nodes[i].size);
1136  }
1137  }
1138 
1139  return function;
1140  }
1141 
1142  Result<BooleanFunction> BooleanFunction::substitute(const std::string& name, const BooleanFunction& replacement) const
1143  {
1144  // Helper function to substitute a variable with a Boolean function.
1145  auto substitute_variable = [](const auto& node, auto&& operands, auto var_name, auto repl) -> BooleanFunction {
1146  if (node.has_variable_name(var_name))
1147  {
1148  return repl.clone();
1149  }
1150  return BooleanFunction(node.clone(), std::move(operands));
1151  };
1152 
1153  std::vector<BooleanFunction> stack;
1154  for (const auto& node : this->m_nodes)
1155  {
1156  std::vector<BooleanFunction> operands;
1157  std::move(stack.end() - static_cast<i64>(node.get_arity()), stack.end(), std::back_inserter(operands));
1158  stack.erase(stack.end() - static_cast<i64>(node.get_arity()), stack.end());
1159 
1160  stack.emplace_back(substitute_variable(node, std::move(operands), name, replacement));
1161  }
1162 
1163  switch (stack.size())
1164  {
1165  case 1:
1166  return OK(stack.back());
1167  default:
1168  return ERR("could not replace variable '" + name + "' with Boolean function '" + replacement.to_string() + "': validation failed, the operations may be imbalanced");
1169  }
1170  }
1171 
1172  BooleanFunction BooleanFunction::substitute(const std::map<std::string, std::string>& substitutions) const
1173  {
1174  auto function = this->clone();
1175  for (auto i = 0u; i < this->m_nodes.size(); i++)
1176  {
1177  if (const auto var_name_res = this->m_nodes[i].get_variable_name(); var_name_res.is_ok())
1178  {
1179  if (const auto it = substitutions.find(var_name_res.get()); it != substitutions.end())
1180  {
1181  function.m_nodes[i] = Node::Variable(it->second, this->m_nodes[i].size);
1182  }
1183  }
1184  }
1185 
1186  return function;
1187  }
1188 
1189  Result<BooleanFunction> BooleanFunction::substitute(const std::map<std::string, BooleanFunction>& substitutions) const
1190  {
1191  // Helper function to find the replacement for a variable and substitute it with a Boolean function.
1192  //
1193  // node - Node.
1194  // operands - Operands of node.
1195  // returns the AST replacement.
1196  auto substitute_variable = [substitutions](const auto& node, auto&& operands) -> BooleanFunction {
1197  if (node.is_variable())
1198  {
1199  if (auto repl_it = substitutions.find(node.variable); repl_it != substitutions.end())
1200  {
1201  return repl_it->second.clone();
1202  }
1203  }
1204  return BooleanFunction(node.clone(), std::move(operands));
1205  };
1206 
1207  std::vector<BooleanFunction> stack;
1208  for (const auto& node : this->m_nodes)
1209  {
1210  std::vector<BooleanFunction> operands;
1211  std::move(stack.end() - static_cast<i64>(node.get_arity()), stack.end(), std::back_inserter(operands));
1212  stack.erase(stack.end() - static_cast<i64>(node.get_arity()), stack.end());
1213 
1214  stack.emplace_back(substitute_variable(node, std::move(operands)));
1215  }
1216 
1217  switch (stack.size())
1218  {
1219  case 1:
1220  return OK(stack.back());
1221  default:
1222  return ERR("could not carry out multiple substitutions: validation failed, the operations may be imbalanced");
1223  }
1224  }
1225 
1226  Result<BooleanFunction::Value> BooleanFunction::evaluate(const std::unordered_map<std::string, Value>& inputs) const
1227  {
1228  // (0) workaround to preserve the API functionality
1229  if (this->m_nodes.empty())
1230  {
1231  return OK(BooleanFunction::Value::X);
1232  }
1233 
1234  // (1) validate whether the input sizes match the boolean function
1235  if (this->size() != 1)
1236  {
1237  return ERR("could not evaluate Boolean function '" + this->to_string() + "': using single-bit evaluation on a Boolean function of size " + std::to_string(this->size()) + " is illegal");
1238  }
1239 
1240  // (2) translate the input to n-bit to use the generic function
1241  auto generic_inputs = std::unordered_map<std::string, std::vector<Value>>();
1242  for (const auto& [name, value] : inputs)
1243  {
1244  generic_inputs.emplace(name, std::vector<Value>({value}));
1245  }
1246 
1247  auto value = this->evaluate(generic_inputs);
1248  if (value.is_ok())
1249  {
1250  // TODO i find that this is incorrect behavior -> only because the variables are single bit does not mean the whole result is -> does not take concat into account
1251  return OK(value.get()[0]);
1252  }
1253 
1254  return ERR(value.get_error());
1255  }
1256 
1257  Result<std::vector<BooleanFunction::Value>> BooleanFunction::evaluate(const std::unordered_map<std::string, std::vector<Value>>& inputs) const
1258  {
1259  // (0) workaround to preserve the API functionality
1260  if (this->m_nodes.empty())
1261  {
1262  return OK(std::vector<BooleanFunction::Value>({BooleanFunction::Value::X}));
1263  }
1264 
1265  // (1) validate whether the input sizes match the boolean function.
1266  // Walk the nodes once and look up each variable, rather than comparing every input name against
1267  // every node, since compute_truth_table() calls this for each of its rows.
1268  for (const auto& node : this->m_nodes)
1269  {
1270  if (!node.is_variable())
1271  {
1272  continue;
1273  }
1274 
1275  if (const auto it = inputs.find(node.variable); it != inputs.end() && node.size != it->second.size())
1276  {
1277  return ERR("could not evaluate Boolean function '" + this->to_string() + "': as the size of vairbale " + node.variable + " with size " + std::to_string(node.size)
1278  + " does not match the size of the provided input (" + std::to_string(it->second.size()) + ")");
1279  }
1280  }
1281 
1282  // (2) initialize the symbolic state using the input variables
1283  auto symbolic_execution = SMT::SymbolicExecution();
1284  for (const auto& [name, value] : inputs)
1285  {
1286  symbolic_execution.state.set(BooleanFunction::Var(name, value.size()), BooleanFunction::Const(value));
1287  }
1288 
1289  // (3) analyze the evaluation result and check whether the result is a
1290  // constant boolean function
1291  auto result = symbolic_execution.evaluate(*this);
1292  if (result.is_ok())
1293  {
1294  if (auto value = result.get(); value.is_constant())
1295  {
1296  return OK(value.get_top_level_node().constant);
1297  }
1298  return OK(std::vector<BooleanFunction::Value>(this->size(), BooleanFunction::Value::X));
1299  }
1300  return ERR(result.get_error());
1301  }
1302 
1303  Result<std::vector<std::vector<BooleanFunction::Value>>> BooleanFunction::compute_truth_table_bitwise(const std::vector<std::string>& variables) const
1304  {
1305  // only single-bit bitwise logic is handled here, anything else falls back to the general implementation
1306  if (this->size() != 1)
1307  {
1308  return ERR("not a single-bit function");
1309  }
1310  // Every variable has to be part of the truth table and every constant has to be Boolean, so that no value is
1311  // ever unknown. The general implementation evaluates symbolically and therefore simplifies, which cancels
1312  // correlated unknowns: `x ^ x` is zero to it even for an unknown `x`, while evaluating three-valued logic
1313  // yields `X`. Refusing the cases that can produce an unknown keeps both implementations in agreement instead
1314  // of trading a correct answer for a faster one.
1315  const std::unordered_set<std::string> known_variables(variables.begin(), variables.end());
1316  for (const auto& node : this->m_nodes)
1317  {
1318  if (node.size != 1)
1319  {
1320  return ERR("not a single-bit function");
1321  }
1322  switch (node.type)
1323  {
1324  case NodeType::And:
1325  case NodeType::Or:
1326  case NodeType::Not:
1327  case NodeType::Xor:
1328  break;
1329  case NodeType::Constant:
1330  if (node.constant.size() != 1 || (node.constant[0] != Value::ZERO && node.constant[0] != Value::ONE))
1331  {
1332  return ERR("constant is not Boolean");
1333  }
1334  break;
1335  case NodeType::Variable:
1336  if (known_variables.find(node.variable) == known_variables.end())
1337  {
1338  return ERR("function has a variable that is not part of the truth table");
1339  }
1340  break;
1341  default:
1342  return ERR("not a bitwise function");
1343  }
1344  }
1345 
1346  // A row of the truth table is one assignment of the variables, and the value of variable i in row r is bit i
1347  // of r. Instead of evaluating the function once per row, evaluate it once per 64 rows: every intermediate
1348  // value becomes a 64-bit word holding that value for 64 consecutive rows at once, and a gate becomes a single
1349  // bitwise instruction. No value can ever be unknown, as the checks above rejected every function that holds a
1350  // variable outside the truth table or a constant that is not Boolean, so one word per value is enough and the
1351  // operations are plain two-valued logic.
1352 
1353  // the value of variable i within a chunk of 64 consecutive rows, which for i < 6 is a fixed pattern and for
1354  // larger i is constant across the whole chunk
1355  static constexpr u64 PATTERN[6] = {
1356  0xAAAAAAAAAAAAAAAAull,
1357  0xCCCCCCCCCCCCCCCCull,
1358  0xF0F0F0F0F0F0F0F0ull,
1359  0xFF00FF00FF00FF00ull,
1360  0xFFFF0000FFFF0000ull,
1361  0xFFFFFFFF00000000ull,
1362  };
1363 
1364  std::unordered_map<std::string, u32> variable_index;
1365  for (u32 i = 0; i < variables.size(); i++)
1366  {
1367  variable_index[variables[i]] = i;
1368  }
1369 
1370  const u64 num_rows = u64(1) << variables.size();
1371  std::vector<Value> result(num_rows, Value::ZERO);
1372 
1373  std::vector<u64> stack;
1374  stack.reserve(this->m_nodes.size());
1375 
1376  for (u64 base = 0; base < num_rows; base += 64)
1377  {
1378  const u64 rows_in_chunk = std::min<u64>(64, num_rows - base);
1379  const u64 chunk_mask = (rows_in_chunk == 64) ? ~u64(0) : ((u64(1) << rows_in_chunk) - 1);
1380 
1381  stack.clear();
1382  for (const auto& node : this->m_nodes)
1383  {
1384  if (node.type == NodeType::Variable)
1385  {
1386  const u32 i = variable_index.at(node.variable);
1387  stack.push_back((i < 6) ? PATTERN[i] : (((base >> i) & 1) ? ~u64(0) : u64(0)));
1388  continue;
1389  }
1390 
1391  if (node.type == NodeType::Constant)
1392  {
1393  stack.push_back((node.constant[0] == Value::ONE) ? ~u64(0) : u64(0));
1394  continue;
1395  }
1396 
1397  const u16 arity = node.get_arity();
1398  if (stack.size() < arity)
1399  {
1400  return ERR("could not compute truth table: malformed node list");
1401  }
1402 
1403  if (node.type == NodeType::Not)
1404  {
1405  const u64 a = stack.back();
1406  stack.pop_back();
1407  stack.push_back(~a);
1408  continue;
1409  }
1410 
1411  const u64 b = stack.back();
1412  stack.pop_back();
1413  const u64 a = stack.back();
1414  stack.pop_back();
1415 
1416  if (node.type == NodeType::And)
1417  {
1418  stack.push_back(a & b);
1419  }
1420  else if (node.type == NodeType::Or)
1421  {
1422  stack.push_back(a | b);
1423  }
1424  else // NodeType::Xor
1425  {
1426  stack.push_back(a ^ b);
1427  }
1428  }
1429 
1430  if (stack.size() != 1)
1431  {
1432  return ERR("could not compute truth table: malformed node list");
1433  }
1434 
1435  const u64 out = stack.back() & chunk_mask;
1436  for (u64 bit = 0; bit < rows_in_chunk; bit++)
1437  {
1438  result[base + bit] = (out & (u64(1) << bit)) ? Value::ONE : Value::ZERO;
1439  }
1440  }
1441 
1442  return OK(std::vector<std::vector<Value>>({std::move(result)}));
1443  }
1444 
1445  Result<std::vector<std::vector<BooleanFunction::Value>>> BooleanFunction::compute_truth_table(const std::vector<std::string>& ordered_variables, bool remove_unknown_variables) const
1446  {
1447  auto variable_names_in_function = this->get_variable_names();
1448 
1449  // (1) check that each variable is just a single bit, otherwise we do
1450  // not generate a truth-table
1451  for (const auto& node : this->m_nodes)
1452  {
1453  if (node.is_variable() && node.size != 1)
1454  {
1455  return ERR("could not compute truth table for Boolean function '" + this->to_string() + "': unable to generate a truth-table for Boolean function with variables of > 1-bit");
1456  }
1457  }
1458 
1459  // (2) select either parameter or the Boolean function variables
1460  auto variables = ordered_variables;
1461  if (variables.empty())
1462  {
1463  variables = std::vector<std::string>(variable_names_in_function.begin(), variable_names_in_function.end());
1464  }
1465 
1466  // (3) remove any unknown variables from the truth table
1467  if (remove_unknown_variables)
1468  {
1469  variables.erase(
1470  std::remove_if(variables.begin(), variables.end(), [&variable_names_in_function](const auto& s) { return variable_names_in_function.find(s) == variable_names_in_function.end(); }),
1471  variables.end());
1472  }
1473 
1474  // (4.1) check that the function is not empty, otherwise we return a
1475  // Boolean function with a truth-table with 'X' values
1476  if (this->m_nodes.empty())
1477  {
1478  return OK(std::vector<std::vector<Value>>(1, std::vector<Value>(1 << variables.size(), Value::X)));
1479  }
1480 
1481  // (4.2) safety-check in case the number of variables is too large to process. Every additional variable
1482  // doubles the number of rows, so the limit bounds both the runtime and the size of the result.
1483  if (variables.size() > MAX_TRUTH_TABLE_VARIABLES)
1484  {
1485  return ERR("could not compute truth table for Boolean function '" + this->to_string() + "': unable to generate truth-table with more than "
1486  + std::to_string(MAX_TRUTH_TABLE_VARIABLES) + " variables");
1487  }
1488 
1489  // (4.3) evaluate the whole truth table at once if the function only consists of bitwise operations on single
1490  // bits, which is what a function extracted from a gate-level subgraph looks like. The general path below
1491  // runs a symbolic execution per row, which walks and simplifies the entire node list every single time.
1492  if (const auto res = compute_truth_table_bitwise(variables); res.is_ok())
1493  {
1494  return res;
1495  }
1496 
1497  std::vector<std::vector<Value>> truth_table(this->size(), std::vector<Value>(1 << variables.size(), Value::ZERO));
1498 
1499  // (5) iterate the truth-table rows and set each column accordingly
1500  for (auto value = 0u; value < ((u32)1 << variables.size()); value++)
1501  {
1502  std::unordered_map<std::string, std::vector<Value>> input;
1503  auto tmp = value;
1504  for (const auto& variable : variables)
1505  {
1506  input[variable] = ((tmp & 1) == 0) ? std::vector<Value>({Value::ZERO}) : std::vector<Value>({Value::ONE});
1507  tmp >>= 1;
1508  }
1509  auto result = this->evaluate(input);
1510  if (result.is_error())
1511  {
1512  return ERR(result.get_error());
1513  }
1514  auto output = result.get();
1515  for (auto index = 0u; index < truth_table.size(); index++)
1516  {
1517  truth_table[index][value] = output[index];
1518  }
1519  }
1520 
1521  return OK(truth_table);
1522  }
1523 
1524  Result<std::string> BooleanFunction::get_truth_table_as_string(const std::vector<std::string>& ordered_inputs, std::string function_name, bool remove_unknown_inputs) const
1525  {
1526  std::vector<std::string> inputs;
1527  auto inputs_set = this->get_variable_names();
1528  if (ordered_inputs.empty())
1529  {
1530  inputs = std::vector<std::string>(inputs_set.begin(), inputs_set.end());
1531  }
1532  else
1533  {
1534  inputs = ordered_inputs;
1535  }
1536 
1537  if (remove_unknown_inputs)
1538  {
1539  inputs.erase(std::remove_if(inputs.begin(), inputs.end(), [&inputs_set](const auto& s) { return inputs_set.find(s) == inputs_set.end(); }), inputs.end());
1540  }
1541 
1542  const auto res = compute_truth_table(inputs, false);
1543  if (res.is_error())
1544  {
1545  return ERR_APPEND(res.get_error(), "could not print truth table for Boolean function '" + this->to_string() + "': unable to compute truth table");
1546  }
1547  const auto truth_table = res.get();
1548 
1549  std::stringstream str("");
1550 
1551  u32 num_inputs = inputs.size();
1552  u32 num_outputs = truth_table.size();
1553 
1554  // table headers
1555  std::vector<u32> in_widths;
1556  for (const auto& var : inputs)
1557  {
1558  in_widths.push_back(var.size());
1559  str << " " << var << " |";
1560  }
1561 
1562  std::vector<u32> out_widths;
1563  if (function_name.empty())
1564  {
1565  function_name = "O";
1566  }
1567  if (num_outputs == 1)
1568  {
1569  str << "| " << function_name << " ";
1570  out_widths.push_back(function_name.size());
1571  }
1572  else
1573  {
1574  for (u32 i = 0; i < num_outputs; i++)
1575  {
1576  std::string var = function_name + "(" + std::to_string(i) + ")";
1577  str << "| " << var << " ";
1578  out_widths.push_back(var.size());
1579  }
1580  }
1581  str << std::endl;
1582 
1583  // rule below headers
1584  for (u32 i = 0; i < num_inputs; i++)
1585  {
1586  str << std::setw(in_widths.at(i) + 3) << std::setfill('-') << "+";
1587  }
1588  for (u32 i = 0; i < num_outputs; i++)
1589  {
1590  str << "+" << std::setw(out_widths.at(i) + 2) << std::setfill('-') << "-";
1591  }
1592  str << std::endl;
1593 
1594  // table values
1595  for (u32 i = 0; i < (u32)(1 << num_inputs); i++)
1596  {
1597  for (u32 j = 0; j < num_inputs; j++)
1598  {
1599  str << " " << std::left << std::setw(in_widths.at(j)) << std::setfill(' ') << ((i >> j) & 1) << " |";
1600  }
1601 
1602  for (u32 k = 0; k < num_outputs; k++)
1603  {
1604  str << "| " << std::left << std::setw(out_widths.at(k)) << std::setfill(' ') << truth_table.at(k).at(i) << " ";
1605  }
1606  str << std::endl;
1607  }
1608  return OK(str.str());
1609  }
1610 
1611  z3::expr BooleanFunction::to_z3(z3::context& context, const std::map<std::string, z3::expr>& var2expr) const
1612  {
1613  // Helper function to reduce a abstract syntax subtree to z3 expressions
1614  //
1615  // node - Boolean function node.
1616  // p - Boolean function node parameters.
1617  // returns (1) status (true on success, false otherwise),
1618  // (2) SMT-LIB string representation of node and operands.
1619  auto reduce_to_z3 = [&context, &var2expr](const auto& node, auto&& p) -> std::tuple<bool, z3::expr> {
1620  if (node.get_arity() != p.size())
1621  {
1622  return {false, z3::expr(context)};
1623  }
1624 
1625  switch (node.type)
1626  {
1628  return {true, context.bv_val(node.index, node.size)};
1630  // since our constants are defined as arbitrary bit-vectors,
1631  // we have to concat each bit just to be on the safe side
1632  auto constant = context.bv_val(node.constant.front(), 1);
1633  for (u32 i = 1; i < node.constant.size(); i++)
1634  {
1635  const auto bit = node.constant.at(i);
1636  constant = z3::concat(context.bv_val(bit, 1), constant);
1637  }
1638  return {true, constant};
1639  }
1641  if (auto it = var2expr.find(node.variable); it != var2expr.end())
1642  {
1643  return {true, it->second};
1644  }
1645  return {true, context.bv_const(node.variable.c_str(), node.size)};
1646  }
1647 
1649  return {true, p[0] & p[1]};
1651  return {true, p[0] | p[1]};
1653  return {true, ~p[0]};
1655  return {true, p[0] ^ p[1]};
1657  return {true, p[0].extract(p[2].get_numeral_uint(), p[1].get_numeral_uint())};
1659  return {true, z3::concat(p[0], p[1])};
1661  return {true, z3::sext(p[0], p[1].get_numeral_uint())};
1662 
1663  default:
1664  log_error("netlist", "Not implemented reached for nodetype {} in z3 conversion", node.type);
1665  return {false, z3::expr(context)};
1666  }
1667  };
1668 
1669  std::vector<z3::expr> stack;
1670  for (const auto& node : this->m_nodes)
1671  {
1672  std::vector<z3::expr> operands;
1673  std::move(stack.end() - static_cast<i64>(node.get_arity()), stack.end(), std::back_inserter(operands));
1674  stack.erase(stack.end() - static_cast<i64>(node.get_arity()), stack.end());
1675 
1676  if (auto [ok, reduction] = reduce_to_z3(node, std::move(operands)); ok)
1677  {
1678  stack.emplace_back(reduction);
1679  }
1680  else
1681  {
1682  return z3::expr(context);
1683  }
1684  }
1685 
1686  switch (stack.size())
1687  {
1688  case 1:
1689  return stack.back();
1690  default:
1691  return z3::expr(context);
1692  }
1693  }
1694 
1695  BooleanFunction::BooleanFunction(std::vector<BooleanFunction::Node>&& nodes) : m_nodes(nodes)
1696  {
1697  }
1698 
1699  BooleanFunction::BooleanFunction(BooleanFunction::Node&& node, std::vector<BooleanFunction>&& p) : BooleanFunction()
1700  {
1701  auto size = 1;
1702  for (const auto& parameter : p)
1703  {
1704  size += parameter.size();
1705  }
1706  this->m_nodes.reserve(size);
1707 
1708  for (auto&& parameter : p)
1709  {
1710  this->m_nodes.insert(this->m_nodes.end(), parameter.m_nodes.begin(), parameter.m_nodes.end());
1711  }
1712  this->m_nodes.emplace_back(node);
1713  }
1714 
1715  std::string BooleanFunction::to_string_in_reverse_polish_notation() const
1716  {
1717  std::string s;
1718  for (const auto& node : this->m_nodes)
1719  {
1720  s += node.to_string() + " ";
1721  }
1722  return s;
1723  }
1724 
1725  Result<BooleanFunction> BooleanFunction::validate(BooleanFunction&& function)
1726  {
1731  if (auto coverage = function.compute_node_coverage(); coverage.back() != function.length())
1732  {
1733  auto str = function.to_string_in_reverse_polish_notation();
1734  return ERR("could not validate '" + str + "': imbalanced function with coverage '" + std::to_string(coverage.back()) + " != " + std::to_string(function.length()));
1735  }
1736 
1737  return OK(std::move(function));
1738  }
1739 
1740  std::vector<u32> BooleanFunction::compute_node_coverage() const
1741  {
1742  auto coverage = std::vector<u32>(this->m_nodes.size(), (u32)-1);
1743 
1749  auto get = [](const auto& cov, size_t index) -> u32 { return (index < cov.size()) ? cov[index] : -1; };
1750 
1758  auto set = [](auto& cov, size_t index, u32 x = 0, u32 y = 0, u32 z = 0) { cov[index] = ((x != (u32)-1) && (y != (u32)-1) && (z != (u32)-1)) ? (x + y + z + 1) : (u32)-1; };
1759 
1760  for (auto i = 0ul; i < this->m_nodes.size(); i++)
1761  {
1762  auto arity = this->m_nodes[i].get_arity();
1763 
1764  switch (arity)
1765  {
1766  case 0: {
1767  set(coverage, i);
1768  break;
1769  }
1770  case 1: {
1771  auto x = get(coverage, i - 1);
1772  set(coverage, i, x);
1773  break;
1774  }
1775  case 2: {
1776  auto x = get(coverage, i - 1);
1777  auto y = get(coverage, i - 1 - x);
1778  set(coverage, i, x, y);
1779  break;
1780  }
1781  case 3: {
1782  auto x = get(coverage, i - 1);
1783  auto y = get(coverage, i - 1 - x);
1784  auto z = get(coverage, i - 1 - x - y);
1785  set(coverage, i, x, y, z);
1786  break;
1787  }
1788  }
1789  }
1790 
1791  return coverage;
1792  }
1793 
1795  {
1796  return Node(_type, _size, {}, {}, {});
1797  }
1798 
1799  BooleanFunction::Node BooleanFunction::Node::Constant(const std::vector<BooleanFunction::Value> _constant)
1800  {
1801  return Node(NodeType::Constant, _constant.size(), _constant, {}, {});
1802  }
1803 
1805  {
1806  return Node(NodeType::Index, _size, {}, _index, {});
1807  }
1808 
1809  BooleanFunction::Node BooleanFunction::Node::Variable(const std::string _variable, u16 _size)
1810  {
1811  return Node(NodeType::Variable, _size, {}, {}, _variable);
1812  }
1813 
1814  bool BooleanFunction::Node::operator==(const Node& other) const
1815  {
1816  return std::tie(this->type, this->size, this->constant, this->index, this->variable) == std::tie(other.type, other.size, other.constant, other.index, other.variable);
1817  }
1818 
1819  bool BooleanFunction::Node::operator!=(const Node& other) const
1820  {
1821  return !(*this == other);
1822  }
1823 
1824  bool BooleanFunction::Node::operator<(const Node& other) const
1825  {
1826  return std::tie(this->type, this->size, this->constant, this->index, this->variable) < std::tie(other.type, other.size, other.constant, other.index, other.variable);
1827  }
1828 
1830  {
1831  return Node(this->type, this->size, this->constant, this->index, this->variable);
1832  }
1833 
1835  {
1836  switch (this->type)
1837  {
1838  case NodeType::Constant: {
1839  std::string str;
1840  for (const auto& value : this->constant)
1841  {
1842  str = enum_to_string(value) + str;
1843  }
1844  return "0b" + str;
1845  }
1846 
1847  case NodeType::Index:
1848  return std::to_string(this->index);
1849  case NodeType::Variable:
1850  return this->variable;
1851 
1852  case NodeType::And:
1853  return "&";
1854  case NodeType::Or:
1855  return "|";
1856  case NodeType::Not:
1857  return "~";
1858  case NodeType::Xor:
1859  return "^";
1860 
1861  case NodeType::Add:
1862  return "+";
1863  case NodeType::Sub:
1864  return "-";
1865  case NodeType::Mul:
1866  return "*";
1867  case NodeType::Sdiv:
1868  return "/s";
1869  case NodeType::Udiv:
1870  return "/";
1871  case NodeType::Srem:
1872  return "\%s";
1873  case NodeType::Urem:
1874  return "\%";
1875 
1876  case NodeType::Concat:
1877  return "++";
1878  case NodeType::Slice:
1879  return "Slice";
1880  case NodeType::Zext:
1881  return "Zext";
1882  case NodeType::Sext:
1883  return "Sext";
1884 
1885  case NodeType::Shl:
1886  return "<<";
1887  case NodeType::Lshr:
1888  return ">>";
1889  case NodeType::Ashr:
1890  return ">>a";
1891  case NodeType::Rol:
1892  return "<<r";
1893  case NodeType::Ror:
1894  return ">>r";
1895 
1896  case NodeType::Eq:
1897  return "==";
1898  case NodeType::Sle:
1899  return "<=s";
1900  case NodeType::Slt:
1901  return "<s";
1902  case NodeType::Ule:
1903  return "<=";
1904  case NodeType::Ult:
1905  return "<";
1906  case NodeType::Ite:
1907  return "Ite";
1908 
1909  default:
1910  return "unsupported node type '" + std::to_string(this->type) + "'.";
1911  }
1912  }
1913 
1915  {
1917  }
1918 
1920  {
1921  static const std::map<u16, u16> type2arity = {
1930  };
1931 
1932  return type2arity.at(type);
1933  }
1934 
1935  bool BooleanFunction::Node::is(u16 _type) const
1936  {
1937  return this->type == _type;
1938  }
1939 
1941  {
1943  }
1944 
1945  bool BooleanFunction::Node::has_constant_value(const std::vector<Value>& value) const
1946  {
1947  return this->is_constant() && (this->constant == value);
1948  }
1949 
1951  {
1952  if (!this->is_constant())
1953  {
1954  return false;
1955  }
1956 
1957  auto bv_value = std::vector<BooleanFunction::Value>({});
1958  bv_value.reserve(this->size);
1959  for (auto i = 0u; i < this->constant.size(); i++)
1960  {
1961  bv_value.emplace_back((value & (1 << i)) ? BooleanFunction::Value::ONE : BooleanFunction::Value::ZERO);
1962  }
1963  return this->constant == bv_value;
1964  }
1965 
1967  {
1968  if (!this->is_constant())
1969  {
1970  return ERR("Node is not a constant");
1971  }
1972 
1973  return OK(this->constant);
1974  }
1975 
1977  {
1978  if (!this->is_constant())
1979  {
1980  return ERR("Node is not a constant");
1981  }
1982 
1983  if (this->size > 64)
1984  {
1985  return ERR("Node constant has size > 64");
1986  }
1987 
1988  if (std::any_of(this->constant.begin(), this->constant.end(), [](auto v) { return v != BooleanFunction::Value::ONE && v != BooleanFunction::Value::ZERO; }))
1989  {
1990  return ERR("Node constant is undefined or high-impedance");
1991  }
1992 
1993  u64 val = 0;
1994  for (auto it = this->constant.rbegin(); it != this->constant.rend(); it++)
1995  {
1996  val <<= 1;
1997  val |= *it;
1998  }
1999 
2000  return OK(val);
2001  }
2002 
2004  {
2005  return this->is(BooleanFunction::NodeType::Index);
2006  }
2007 
2009  {
2010  return this->is_index() && (this->index == value);
2011  }
2012 
2014  {
2015  if (!this->is_index())
2016  {
2017  return ERR("Node is not an index");
2018  }
2019 
2020  return OK(this->index);
2021  }
2022 
2024  {
2026  }
2027 
2028  bool BooleanFunction::Node::has_variable_name(const std::string& value) const
2029  {
2030  return this->is_variable() && (this->variable == value);
2031  }
2032 
2034  {
2035  if (!this->is_variable())
2036  {
2037  return ERR("Node is not a variable");
2038  }
2039 
2040  return OK(this->variable);
2041  }
2042 
2044  {
2045  return !this->is_operand();
2046  }
2047 
2049  {
2050  return this->is_constant() || this->is_variable() || this->is_index();
2051  }
2052 
2054  {
2055  return (this->type == NodeType::And) || (this->type == NodeType::Or) || (this->type == NodeType::Xor) || (this->type == NodeType::Add) || (this->type == NodeType::Mul)
2056  || (this->type == NodeType::Eq);
2057  }
2058 
2059  BooleanFunction::Node::Node(u16 _type, u16 _size, std::vector<BooleanFunction::Value> _constant, u16 _index, std::string _variable)
2060  : type(_type), size(_size), constant(_constant), index(_index), variable(_variable)
2061  {
2062  }
2063 
2064 } // namespace hal
u32 size
static Result< BooleanFunction > Slt(BooleanFunction &&p0, BooleanFunction &&p1, u16 size)
bool has_index_value(u16 index) const
BooleanFunction operator+(const BooleanFunction &other) const
static Result< BooleanFunction > Ite(BooleanFunction &&p0, BooleanFunction &&p1, BooleanFunction &&p2, u16 size)
BooleanFunction operator&(const BooleanFunction &other) const
static Result< BooleanFunction > Eq(BooleanFunction &&p0, BooleanFunction &&p1, u16 size)
static BooleanFunction Var(const std::string &name, u16 size=1)
static constexpr u32 MAX_TRUTH_TABLE_VARIABLES
static Result< BooleanFunction > Xor(BooleanFunction &&p0, BooleanFunction &&p1, u16 size)
bool operator==(const BooleanFunction &other) const
Result< std::string > get_variable_name() const
BooleanFunction operator^(const BooleanFunction &other) const
const BooleanFunction::Node & get_top_level_node() const
static Result< BooleanFunction > Add(BooleanFunction &&p0, BooleanFunction &&p1, u16 size)
std::set< std::string > get_variable_names() const
static Result< BooleanFunction > Lshr(BooleanFunction &&p0, BooleanFunction &&p1, u16 size)
bool has_constant_value(const std::vector< Value > &value) const
static Result< BooleanFunction > Zext(BooleanFunction &&p0, BooleanFunction &&p1, u16 size)
Result< std::vector< Value > > get_constant_value() const
static Result< BooleanFunction > Mul(BooleanFunction &&p0, BooleanFunction &&p1, u16 size)
bool has_variable_name(const std::string &variable_name) const
static Result< BooleanFunction > Ule(BooleanFunction &&p0, BooleanFunction &&p1, u16 size)
static Result< BooleanFunction > Sub(BooleanFunction &&p0, BooleanFunction &&p1, u16 size)
static Result< BooleanFunction > Sext(BooleanFunction &&p0, BooleanFunction &&p1, u16 size)
static Result< BooleanFunction > Udiv(BooleanFunction &&p0, BooleanFunction &&p1, u16 size)
BooleanFunction operator~() const
static Result< BooleanFunction > Concat(BooleanFunction &&p0, BooleanFunction &&p1, u16 size)
static Result< BooleanFunction > Ult(BooleanFunction &&p0, BooleanFunction &&p1, u16 size)
static BooleanFunction Index(u16 index, u16 size)
z3::expr to_z3(z3::context &context, const std::map< std::string, z3::expr > &var2expr={}) const
Result< std::vector< std::vector< Value > > > compute_truth_table(const std::vector< std::string > &ordered_variables={}, bool remove_unknown_variables=false) const
BooleanFunction & operator-=(const BooleanFunction &other)
Result< Value > evaluate(const std::unordered_map< std::string, Value > &inputs) const
static Result< BooleanFunction > Sle(BooleanFunction &&p0, BooleanFunction &&p1, u16 size)
static Result< BooleanFunction > build(std::vector< Node > &&nodes)
BooleanFunction operator|(const BooleanFunction &other) const
static Result< BooleanFunction > from_string(const std::string &expression)
static Result< BooleanFunction > Slice(BooleanFunction &&p0, BooleanFunction &&p1, BooleanFunction &&p2, u16 size)
const std::vector< BooleanFunction::Node > & get_nodes() const
BooleanFunction & operator*=(const BooleanFunction &other)
bool is(u16 type) const
BooleanFunction simplify() const
BooleanFunction operator-(const BooleanFunction &other) const
Result< std::string > get_truth_table_as_string(const std::vector< std::string > &ordered_variables={}, std::string function_name="", bool remove_unknown_variables=false) const
bool operator<(const BooleanFunction &other) const
BooleanFunction & operator&=(const BooleanFunction &other)
BooleanFunction clone() const
Value
represents the type of the node
static Result< BooleanFunction > Or(BooleanFunction &&p0, BooleanFunction &&p1, u16 size)
bool operator!=(const BooleanFunction &other) const
static Result< BooleanFunction > Urem(BooleanFunction &&p0, BooleanFunction &&p1, u16 size)
BooleanFunction & operator^=(const BooleanFunction &other)
static Result< BooleanFunction > Ror(BooleanFunction &&p0, BooleanFunction &&p1, u16 size)
BooleanFunction & operator+=(const BooleanFunction &other)
static Result< BooleanFunction > Sdiv(BooleanFunction &&p0, BooleanFunction &&p1, u16 size)
BooleanFunction & operator|=(const BooleanFunction &other)
static std::string to_string(Value value)
BooleanFunction simplify_local() const
static BooleanFunction Const(const BooleanFunction::Value &value)
BooleanFunction operator*(const BooleanFunction &other) const
static Result< BooleanFunction > Ashr(BooleanFunction &&p0, BooleanFunction &&p1, u16 size)
static Result< u64 > to_u64(const std::vector< BooleanFunction::Value > &value)
static Result< BooleanFunction > Not(BooleanFunction &&p0, u16 size)
static Result< BooleanFunction > And(BooleanFunction &&p0, BooleanFunction &&p1, u16 size)
std::vector< BooleanFunction > get_parameters() const
Result< u16 > get_index_value() const
BooleanFunction substitute(const std::string &old_variable_name, const std::string &new_variable_name) const
static Result< BooleanFunction > Shl(BooleanFunction &&p0, BooleanFunction &&p1, u16 size)
Result< u64 > get_constant_value_u64() const
static Result< BooleanFunction > Rol(BooleanFunction &&p0, BooleanFunction &&p1, u16 size)
static Result< BooleanFunction > Srem(BooleanFunction &&p0, BooleanFunction &&p1, u16 size)
uint64_t u64
Definition: defines.h:42
uint16_t u16
Definition: defines.h:40
uint32_t u32
Definition: defines.h:41
int64_t i64
Definition: defines.h:37
uint8_t u8
Definition: defines.h:39
#define log_error(channel,...)
Definition: log.h:78
#define ERR(message)
Definition: result.h:60
#define OK(...)
Definition: result.h:56
#define ERR_APPEND(prev_error, message)
Definition: result.h:64
Result< std::vector< Token > > parse_with_standard_grammar(const std::string &expression)
Result< std::vector< Token > > parse_with_liberty_grammar(const std::string &expression)
ParserType
ParserType refers to the parser identifier.
Definition: parser.h:39
Result< BooleanFunction > translate(std::vector< Token > &&tokens, const std::string &expression)
Result< std::vector< Token > > reverse_polish_notation(std::vector< Token > &&tokens, const std::string &expression, const ParserType &parser)
Result< BooleanFunction > local_simplification(const BooleanFunction &function)
void remove(std::filesystem::path file_path)
Definition: defines.h:45
std::ostream & operator<<(std::ostream &os, T e)
Definition: enums.h:136
std::string enum_to_string(T e)
Definition: enums.h:53
PinType type
std::string name
std::vector< BooleanFunction::Value > constant
The (optional) constant value of the node.
u16 type
The type of the node.
u16 size
The bit-size of the node.
static u16 get_arity_of_type(u16 type)
static Node Constant(const std::vector< BooleanFunction::Value > value)
bool has_index_value(u16 value) const
std::string variable
The (optional) variable name of the node.
bool has_variable_name(const std::string &variable_name) const
static Node Operation(u16 type, u16 size)
bool has_constant_value(const std::vector< Value > &value) const
Result< std::vector< Value > > get_constant_value() const
static Node Index(u16 index, u16 size)
Result< u16 > get_index_value() const
bool operator!=(const Node &other) const
bool operator==(const Node &other) const
bool operator<(const Node &other) const
u16 index
The (optional) index value of the node.
static Node Variable(const std::string variable, u16 size)
Result< std::string > get_variable_name() const
Result< u64 > get_constant_value_u64() const
Token refers to a token identifier and accompanied data.
Definition: parser.h:67