HAL  v4.5.0-257-ge24279b4e
The Hardware Analyzer - a comprehensive reverse engineering and manipulation framework for gate-level netlists.
parser_types.cpp
Go to the documentation of this file.
3 
4 #include <deque>
5 #include <stack>
6 
7 namespace hal
8 {
9  namespace BooleanFunctionParser
10  {
12  {
13  return Token(TokenType::And, {}, {});
14  }
15 
17  {
18  return Token(TokenType::Not, {}, {});
19  }
20 
22  {
23  return Token(TokenType::NotSuffix, {}, {});
24  }
25 
27  {
28  return Token(TokenType::Or, {}, {});
29  }
30 
32  {
33  return Token(TokenType::Xor, {}, {});
34  }
35 
37  {
38  return Token(TokenType::Eq, {}, {});
39  }
40 
42  {
43  return Token(TokenType::Question, {}, {});
44  }
45 
47  {
48  return Token(TokenType::Colon, {}, {});
49  }
50 
52  {
53  return Token(TokenType::Add, {}, {});
54  }
55 
57  {
58  return Token(TokenType::Sub, {}, {});
59  }
60 
62  {
63  return Token(TokenType::Mul, {}, {});
64  }
65 
67  {
68  return Token(TokenType::Variable, std::make_tuple(name, size), {});
69  }
70 
71  Token Token::Constant(std::vector<BooleanFunction::Value> constant)
72  {
73  return Token(TokenType::Constant, std::make_tuple("", 0), constant);
74  }
75 
77  {
78  return Token(TokenType::BracketOpen, {}, {});
79  }
80 
82  {
83  return Token(TokenType::BracketClose, {}, {});
84  }
85 
86  unsigned Token::precedence(const ParserType& parser) const
87  {
88  static const std::map<ParserType, std::map<TokenType, unsigned>> parser2precedence = {
90  std::map<TokenType, unsigned>({
92  {TokenType::Not, 4},
93  {TokenType::And, 3},
94  {TokenType::Xor, 2},
95  {TokenType::Or, 1},
96  })},
98  std::map<TokenType, unsigned>({
99  {TokenType::Not, 8},
100  {TokenType::Mul, 7},
101  {TokenType::Add, 6},
102  {TokenType::Sub, 6},
103  {TokenType::Eq, 5},
104  {TokenType::And, 4},
105  {TokenType::Xor, 3},
106  {TokenType::Or, 2},
107  {TokenType::Question, 1},
108  {TokenType::Colon, 1},
109  })},
110  };
111 
112  const auto& precedence = parser2precedence.at(parser);
113  if (auto iter = precedence.find(this->type); iter != precedence.end())
114  {
115  return iter->second;
116  }
117 
118  return 9;
119  }
120 
122  {
123  // Arithmetic operators follow C-style left-associativity. All other operators
124  // remain right-associative (the long-standing default for the BF parser).
125  return this->type == TokenType::Add || this->type == TokenType::Sub || this->type == TokenType::Mul;
126  }
127 
128  bool Token::is(TokenType _type) const
129  {
130  return this->type == _type;
131  }
132 
133  std::ostream& operator<<(std::ostream& os, const Token& token)
134  {
135  static const std::map<TokenType, std::string> type2str = {
136  {TokenType::And, "And"},
137  {TokenType::Or, "Or"},
138  {TokenType::Not, "Not"},
139  {TokenType::NotSuffix, "NotSuffix"},
140  {TokenType::Xor, "Xor"},
141  {TokenType::Eq, "Eq"},
142  {TokenType::Question, "Question"},
143  {TokenType::Colon, "Colon"},
144  {TokenType::Add, "Add"},
145  {TokenType::Sub, "Sub"},
146  {TokenType::Mul, "Mul"},
147 
148  {TokenType::BracketOpen, "BracketOpen"},
149  {TokenType::BracketClose, "BracketClose"},
150 
151  {TokenType::Variable, "Variable"},
152  {TokenType::Constant, "Constant"},
153  };
154 
155  os << "Token { " << type2str.at(token.type);
156 
157  if (token.type == TokenType::Variable)
158  {
159  os << ", " << std::get<0>(token.variable) << " (" << std::get<1>(token.variable) << ")";
160  }
161  if (token.type == TokenType::Constant)
162  {
163  os << ", "
164  << "0b";
165  for (auto i = token.constant.size(); i-- != 0;)
166  {
167  os << token.constant[i];
168  }
169  }
170 
171  os << " }";
172  return os;
173  }
174 
175  std::string Token::to_string() const
176  {
177  std::stringstream ss;
178  ss << *this;
179  return ss.str();
180  }
181 
182  Token::Token(TokenType _type, std::tuple<std::string, u16> _variable, std::vector<BooleanFunction::Value> _constant) : type(_type), variable(_variable), constant(_constant)
183  {
184  }
185 
186  Result<std::vector<Token>> reverse_polish_notation(std::vector<Token>&& tokens, const std::string& expression, const ParserType& parser)
187  {
188  // Validate ternary structure: every `:` must be preceded (in source order) by an
189  // unmatched `?`, and every `?` must eventually have a matching `:`. This catches
190  // isolated `?`/`:`, wrong order (e.g. `a : b ? c`), and unbalanced/incomplete
191  // ternaries before the Shunting Yard step silently accepts them.
192  int ternary_depth = 0;
193  for (const auto& tok : tokens)
194  {
195  if (tok.is(TokenType::Question))
196  {
197  ++ternary_depth;
198  }
199  else if (tok.is(TokenType::Colon))
200  {
201  if (ternary_depth == 0)
202  {
203  return ERR("could not translate '" + expression + "' to reverse polish notation: unmatched ':' in ternary expression");
204  }
205  --ternary_depth;
206  }
207  }
208  if (ternary_depth != 0)
209  {
210  return ERR("could not translate '" + expression + "' to reverse polish notation: unmatched '?' in ternary expression");
211  }
212 
213  std::stack<Token> operator_stack;
214  std::vector<Token> output;
215 
216  for (auto&& token : tokens)
217  {
218  switch (token.type)
219  {
220  // (1) constants / variables are always pushed into output queue
221  case TokenType::Constant:
222  case TokenType::Variable: {
223  output.emplace_back(token);
224  break;
225  }
226 
227  // (2) operations are pushed into the operator stack with respect
228  // to the operator precedence and bracket level
229  case TokenType::And:
230  case TokenType::Or:
231  case TokenType::Not:
232  case TokenType::Xor:
233  case TokenType::Eq:
234  case TokenType::Question:
235  case TokenType::Colon:
236  case TokenType::Add:
237  case TokenType::Sub:
238  case TokenType::Mul: {
239  const bool left_assoc = token.is_left_associative();
240  while (!operator_stack.empty() && !operator_stack.top().is(TokenType::BracketOpen)
241  && (operator_stack.top().precedence(parser) > token.precedence(parser)
242  || (left_assoc && operator_stack.top().precedence(parser) == token.precedence(parser))))
243  {
244  output.emplace_back(operator_stack.top());
245  operator_stack.pop();
246  }
247  operator_stack.push(token);
248  break;
249  }
250 
251  // (3) the liberty grammar contains suffix not operations that
252  // invert the previous operation, i.e, acts similiar to
253  // a "not" operation in reverse-polish notation. hence,
254  // we simply push a "not" the output as the previous
255  // expression has been already translated to the reverse-
256  // polish notation in the output vector.
257  case TokenType::NotSuffix: {
258  output.emplace_back(Token::Not());
259  break;
260  }
261 
262  // (4) open brackets are always pushed into the operator stack
263  case TokenType::BracketOpen: {
264  operator_stack.push(token);
265  break;
266  }
267 
268  // (5) close brackets push remaining operations into the output
269  // queue until the bracket level is closed
271  while (!operator_stack.empty() && !operator_stack.top().is(TokenType::BracketOpen))
272  {
273  output.emplace_back(operator_stack.top());
274  operator_stack.pop();
275  }
276 
277  operator_stack.pop();
278 
279  break;
280  }
281  }
282  }
283 
284  while (!operator_stack.empty())
285  {
286  if (operator_stack.top().is(TokenType::BracketOpen))
287  {
288  return ERR("could not translate '" + expression + "' to reverse polish notation: bracket level is invalid");
289  }
290 
291  output.emplace_back(operator_stack.top());
292  operator_stack.pop();
293  }
294  return OK(output);
295  }
296 
297  Result<BooleanFunction> translate(std::vector<Token>&& tokens, const std::string& expression)
298  {
299  auto nodes = std::vector<BooleanFunction::Node>();
300  nodes.reserve(tokens.size());
301 
302  for (auto&& token : tokens)
303  {
307  && nodes.empty())
308  {
309  return ERR("could not translate tokens: tokens are imbalanced, i.e., the operation comes before the operand nodes");
310  }
311 
312  switch (token.type)
313  {
315  nodes.emplace_back(BooleanFunction::Node::Operation(BooleanFunction::NodeType::And, nodes.back().size));
316  break;
318  nodes.emplace_back(BooleanFunction::Node::Operation(BooleanFunction::NodeType::Or, nodes.back().size));
319  break;
321  nodes.emplace_back(BooleanFunction::Node::Operation(BooleanFunction::NodeType::Not, nodes.back().size));
322  break;
324  nodes.emplace_back(BooleanFunction::Node::Operation(BooleanFunction::NodeType::Xor, nodes.back().size));
325  break;
327  nodes.emplace_back(BooleanFunction::Node::Operation(BooleanFunction::NodeType::Add, nodes.back().size));
328  break;
330  nodes.emplace_back(BooleanFunction::Node::Operation(BooleanFunction::NodeType::Sub, nodes.back().size));
331  break;
333  nodes.emplace_back(BooleanFunction::Node::Operation(BooleanFunction::NodeType::Mul, nodes.back().size));
334  break;
336  // Eq returns a 1-bit boolean result regardless of operand width.
338  break;
340  // `?` is a syntactic marker; the actual Ite node is emitted on `:`.
341  break;
343  // The result size of Ite matches the size of the then/else branches.
344  // The branches are the two top-most nodes (then below else on the stack).
345  if (nodes.size() < 3)
346  {
347  return ERR("could not translate tokens: ternary `:` requires three preceding operands");
348  }
349  const u16 ite_size = nodes.back().size;
351  break;
352  }
353 
355  nodes.emplace_back(BooleanFunction::Node::Variable(std::get<0>(token.variable), std::get<1>(token.variable)));
356  break;
358  nodes.emplace_back(BooleanFunction::Node::Constant(token.constant));
359  break;
360 
361  default:
362  return ERR("could not translate tokens: unable to handle '" + token.to_string() + "' in '" + expression + "'");
363  }
364  }
365 
366  if (auto res = BooleanFunction::build(std::move(nodes)); res.is_error())
367  {
368  return ERR_APPEND(res.get_error(), "could not translate tokens: unable to build Boolean function from vector of nodes");
369  }
370  else
371  {
372  return res;
373  }
374  }
375  } // namespace BooleanFunctionParser
376 } // namespace hal
u32 size
static Result< BooleanFunction > build(std::vector< Node > &&nodes)
uint16_t u16
Definition: defines.h:40
#define ERR(message)
Definition: result.h:60
#define OK(...)
Definition: result.h:56
#define ERR_APPEND(prev_error, message)
Definition: result.h:64
ParserType
ParserType refers to the parser identifier.
Definition: parser.h:41
@ Liberty
refers to the 'Liberty' Boolean function parser
@ Standard
refers to the 'Standard' Boolean function parser
std::ostream & operator<<(std::ostream &os, const Token &token)
Result< BooleanFunction > translate(std::vector< Token > &&tokens, const std::string &expression)
TokenType
TokenType refers to a token identifier for a Boolean function string.
Definition: parser.h:52
Result< std::vector< Token > > reverse_polish_notation(std::vector< Token > &&tokens, const std::string &expression, const ParserType &parser)
Definition: defines.h:45
PinType type
std::string name
static Node Constant(const std::vector< BooleanFunction::Value > value)
static Node Operation(u16 type, u16 size)
static Node Variable(const std::string variable, u16 size)
Token refers to a token identifier and accompanied data.
Definition: parser.h:75
std::tuple< std::string, u16 > variable
optional value and bit-size in case token is a variable
Definition: parser.h:83
TokenType type
refers to the underlying token type identifier
Definition: parser.h:81
static Token Constant(std::vector< BooleanFunction::Value > value)
Token(TokenType _type, std::tuple< std::string, u16 > variable, std::vector< BooleanFunction::Value > constant)
Construct a new initialized Token.
bool is(TokenType type) const
static Token Variable(std::string name, u16 size)
std::vector< BooleanFunction::Value > constant
optional value in case token is a constant
Definition: parser.h:85
unsigned precedence(const ParserType &type) const