libqasm
library for handling cQASM files
func-gen.cpp
Go to the documentation of this file.
1 
25 #include <string>
26 #include <vector>
27 #include <iostream>
28 #include <fstream>
29 #include <sstream>
30 
34 namespace func_gen {
35 
40 class Function {
41 public:
42 
46  const std::string cpp_name;
47 
51  const std::string cqasm_name;
52 
57  const std::string cqasm_args;
58 
59 private:
60 
64  static std::string unique_name(const std::string &name, const std::string &args) {
65  if (name == "operator+") {
66  return "op_add_" + args;
67  } else if (name == "operator-") {
68  if (args.size() == 1) {
69  return "op_neg_" + args;
70  } else {
71  return "op_sub_" + args;
72  }
73  } else if (name == "operator*") {
74  return "op_mul_" + args;
75  } else if (name == "operator/") {
76  return "op_div_" + args;
77  } else if (name == "operator//") {
78  return "op_int_div_" + args;
79  } else if (name == "operator%") {
80  return "op_mod_" + args;
81  } else if (name == "operator**") {
82  return "op_pow_" + args;
83  } else if (name == "operator<<") {
84  return "op_shl_" + args;
85  } else if (name == "operator>>") {
86  return "op_sra_" + args;
87  } else if (name == "operator>>>") {
88  return "op_srl_" + args;
89  } else if (name == "operator==") {
90  return "op_eq_" + args;
91  } else if (name == "operator!=") {
92  return "op_ne_" + args;
93  } else if (name == "operator>=") {
94  return "op_ge_" + args;
95  } else if (name == "operator>") {
96  return "op_gt_" + args;
97  } else if (name == "operator<=") {
98  return "op_le_" + args;
99  } else if (name == "operator<") {
100  return "op_lt_" + args;
101  } else if (name == "operator~") {
102  return "op_binv_" + args;
103  } else if (name == "operator&") {
104  return "op_band_" + args;
105  } else if (name == "operator^") {
106  return "op_bxor_" + args;
107  } else if (name == "operator|") {
108  return "op_bor_" + args;
109  } else if (name == "operator!") {
110  return "op_linv_" + args;
111  } else if (name == "operator&&") {
112  return "op_land_" + args;
113  } else if (name == "operator^^") {
114  return "op_lxor_" + args;
115  } else if (name == "operator||") {
116  return "op_lor_" + args;
117  } else if (name == "operator?:") {
118  return "op_tcnd_" + args;
119  } else {
120  return "fn_" + name + "_" + args;
121  }
122  }
123 
124 public:
125 
129  Function(const std::string &name, const std::string &args)
130  : cpp_name(unique_name(name, args)), cqasm_name(name), cqasm_args(args)
131  {}
132 
133 };
134 
138 class Generator {
139 private:
140 
144  std::vector<Function> funcs;
145 
149  std::ofstream header;
150 
154  std::ofstream source;
155 
159  void generate_register_function() {
160  header << R"(
165 void register_into(resolver::FunctionTable &table);
166 )";
167  source << R"(
172 void register_into(resolver::FunctionTable &table) {
173 )";
174  for (const auto &func : funcs) {
175  source << " table.add(";
176  source << "\"" << func.cqasm_name << "\", ";
177  source << "types::from_spec(\"" << func.cqasm_args << "\"), ";
178  source << func.cpp_name;
179  source << ");" << std::endl;
180  }
181  source << std::endl << "}" << std::endl;
182  }
183 
189  void generate_impl_header(const Function &func) {
190  auto proto = "values::Value " + func.cpp_name + "(const values::Values &v)";
191  header << proto << ";" << std::endl;
192  source << std::endl << proto << " {" << std::endl;
193  funcs.push_back(func);
194  }
195 
203  void generate_const_impl_header(const Function &func) {
204  generate_impl_header(func);
205  source << " values::check_const(v);" << std::endl;
206  size_t index = 0;
207  for (auto arg_typ : func.cqasm_args) {
208  source << " auto " << (char)('a' + index) << " = v[" << index << "]";
209  switch (arg_typ) {
210  case 'b': source << "->as_const_bool()->value"; break;
211  case 'a': source << "->as_const_axis()->value"; break;
212  case 'i': source << "->as_const_int()->value"; break;
213  case 'r': source << "->as_const_real()->value"; break;
214  case 'c': source << "->as_const_complex()->value"; break;
215  case 'm': source << "->as_const_real_matrix()->value"; break;
216  case 'u':
217  case 'n': source << "->as_const_complex_matrix()->value"; break;
218  case 's': source << "->as_const_string()->value"; break;
219  case 'j': source << "->as_const_json()->value"; break;
220  }
221  source << ";" << std::endl;
222  index++;
223  }
224  }
225 
242  void generate_impl_footer(const std::string &return_expr, char return_type) {
243  source << " return tree::make<values::";
244  switch (return_type) {
245  case 'b': source << "ConstBool"; break;
246  case 'a': source << "ConstAxis"; break;
247  case 'i': source << "ConstInt"; break;
248  case 'r': source << "ConstReal"; break;
249  case 'c': source << "ConstComplex"; break;
250  case 'm': source << "ConstRealMatrix"; break;
251  case 'n': source << "ConstComplexMatrix"; break;
252  case 's': source << "ConstString"; break;
253  case 'j': source << "ConstJson"; break;
254  case 'Q': source << "QubitRefs"; break;
255  case 'B': source << "BitRefs"; break;
256  default: throw std::invalid_argument("unknown type code");
257  }
258  source << ">(" << return_expr << ");" << std::endl;
259  source << "}" << std::endl;
260  }
261 
262 public:
263 
274  const std::string &name,
275  const char return_type,
276  const std::string &arg_types,
277  const std::string &impl
278  ) {
279  generate_const_impl_header(Function(name, arg_types));
280  generate_impl_footer(impl, return_type);
281  }
282 
287  const std::string &header_filename,
288  const std::string &source_filename
289  ) :
290  funcs(),
291  header(header_filename),
292  source(source_filename)
293  {
294  // Check that the files were opened properly.
295  if (!header.is_open()) {
296  throw std::runtime_error("failed to open header file");
297  }
298  if (!source.is_open()) {
299  throw std::runtime_error("failed to open source file");
300  }
301 
302  // Print the headers for the header and source files.
303  auto pos = header_filename.rfind('/');
304  auto header_name = (pos == header_filename.npos) ? header_filename : header_filename.substr(pos + 1);
305  header << R"(
310 #pragma once
311 
312 #include "cqasm-v1-resolver.hpp"
313 
314 namespace cqasm {
315 namespace v1 {
316 namespace functions {
317 )";
318  source << R"(
323 #include <cmath>
324 #include <complex>
325 #include ")" << header_name << R"("
326 
327 namespace cqasm {
328 namespace v1 {
329 
333 namespace functions {
334 
339 static int64_t div_floor(int64_t a, int64_t b) {
340  int64_t res = a / b;
341  int64_t rem = a % b;
342  // Correct division result downwards if up-rounding happened,
343  // (for non-zero remainder of sign different than the divisor).
344  int64_t corr = (rem != 0 && ((rem < 0) != (b < 0)));
345  return res - corr;
346 }
347 
351 static int64_t mod_floor(int64_t a, int64_t b) {
352  int64_t rem = a % b;
353  if (rem != 0 && ((rem < 0) != (b < 0))) {
354  rem += b;
355  }
356  return rem;
357 }
358 
359 )";
360  }
361 
366  generate_register_function();
367  auto footer = R"(
368 } // namespace functions
369 } // namespace v1
370 } // namespace cqasm
371 )";
372  source << footer;
373  header << footer;
374  }
375 
376 };
377 
378 } // namespace func_gen
379 
383 int main(int argc, char *argv[]) {
384 
385  // Check command line.
386  if (argc != 3) {
387  std::cerr << "Usage: func-gen <header.hpp> <source.cpp" << std::endl;
388  return 1;
389  }
390 
391  // Construct the generator.
392  func_gen::Generator generator{argv[1], argv[2]};
393 
394  // Basic scalar arithmetic operators.
395  generator.generate_const_scalar_op("operator+", 'c', "cc", "a + b");
396  generator.generate_const_scalar_op("operator+", 'r', "rr", "a + b");
397  generator.generate_const_scalar_op("operator+", 'i', "ii", "a + b");
398  generator.generate_const_scalar_op("operator+", 's', "ss", "a + b");
399  generator.generate_const_scalar_op("operator-", 'c', "cc", "a - b");
400  generator.generate_const_scalar_op("operator-", 'r', "rr", "a - b");
401  generator.generate_const_scalar_op("operator-", 'i', "ii", "a - b");
402  generator.generate_const_scalar_op("operator-", 'c', "c", "-a");
403  generator.generate_const_scalar_op("operator-", 'r', "r", "-a");
404  generator.generate_const_scalar_op("operator-", 'i', "i", "-a");
405  generator.generate_const_scalar_op("operator*", 'c', "cc", "a * b");
406  generator.generate_const_scalar_op("operator*", 'r', "rr", "a * b");
407  generator.generate_const_scalar_op("operator*", 'i', "ii", "a * b");
408  generator.generate_const_scalar_op("operator/", 'c', "cc", "a / b");
409  generator.generate_const_scalar_op("operator/", 'r', "rr", "a / b");
410  generator.generate_const_scalar_op("operator//", 'i', "ii", "div_floor(a, b)");
411  generator.generate_const_scalar_op("operator%", 'i', "ii", "mod_floor(a, b)");
412  generator.generate_const_scalar_op("operator**", 'c', "cc", "std::pow(a, b)");
413  generator.generate_const_scalar_op("operator**", 'r', "rr", "std::pow(a, b)");
414 
415  // Relational operators.
416  generator.generate_const_scalar_op("operator==", 'b', "cc", "a == b");
417  generator.generate_const_scalar_op("operator!=", 'b', "cc", "a != b");
418  for (const char *type : {"rr", "ii", "bb"}) {
419  generator.generate_const_scalar_op("operator==", 'b', type, "a == b");
420  generator.generate_const_scalar_op("operator!=", 'b', type, "a != b");
421  generator.generate_const_scalar_op("operator>=", 'b', type, "a >= b");
422  generator.generate_const_scalar_op("operator>", 'b', type, "a > b");
423  generator.generate_const_scalar_op("operator<=", 'b', type, "a <= b");
424  generator.generate_const_scalar_op("operator<", 'b', type, "a < b");
425  }
426 
427  // Bitwise operators.
428  generator.generate_const_scalar_op("operator~", 'i', "i", "~a");
429  generator.generate_const_scalar_op("operator&", 'i', "ii", "a & b");
430  generator.generate_const_scalar_op("operator^", 'i', "ii", "a ^ b");
431  generator.generate_const_scalar_op("operator|", 'i', "ii", "a | b");
432  generator.generate_const_scalar_op("operator<<", 'i', "ii", "a << b");
433  generator.generate_const_scalar_op("operator>>", 'i', "ii", "a >> b");
434  generator.generate_const_scalar_op("operator>>>", 'i', "ii", "(int64_t)(((uint64_t)a) >> b)");
435 
436  // Logical operators.
437  generator.generate_const_scalar_op("operator!", 'b', "b", "!a");
438  generator.generate_const_scalar_op("operator&&", 'b', "bb", "a && b");
439  generator.generate_const_scalar_op("operator^^", 'b', "bb", "!a ^ !b");
440  generator.generate_const_scalar_op("operator||", 'b', "bb", "a || b");
441 
442  // Ternary conditional.
443  for (const char *type : {"bcc", "brr", "bii", "bbb"}) {
444  generator.generate_const_scalar_op("operator?:", type[1], type, "a ? b : c");
445  }
446 
447  // Scalar root, exponent, and trigonometric functions for reals and complex
448  // numbers.
449  for (const char *type : {"c", "r"}) {
450  generator.generate_const_scalar_op("sqrt", type[0], type, "std::sqrt(a)");
451  generator.generate_const_scalar_op("exp", type[0], type, "std::exp(a)");
452  generator.generate_const_scalar_op("log", type[0], type, "std::log(a)");
453  for (const char *prefix : {"", "a"}) {
454  for (const char *suffix : {"", "h"}) {
455  for (const char *func : {"sin", "cos", "tan"}) {
456  std::ostringstream ss;
457  ss << prefix << func << suffix;
458  generator.generate_const_scalar_op(
459  ss.str(), type[0], type, "std::" + ss.str() + "(a)");
460  }
461  }
462  }
463  }
464 
465  // Absolute value function.
466  generator.generate_const_scalar_op("abs", 'r', "r", "std::abs(a)");
467  generator.generate_const_scalar_op("abs", 'i', "i", "std::abs(a)");
468 
469  // Complex number manipulation functions.
470  generator.generate_const_scalar_op("complex", 'c', "rr", "primitives::Complex(a, b)");
471  generator.generate_const_scalar_op("polar", 'c', "rr", "std::polar<double>(a, b)");
472  generator.generate_const_scalar_op("real", 'r', "c", "std::real<double>(a)");
473  generator.generate_const_scalar_op("imag", 'r', "c", "std::imag<double>(a)");
474  generator.generate_const_scalar_op("arg", 'r', "c", "std::arg<double>(a)");
475  generator.generate_const_scalar_op("norm", 'r', "c", "std::norm<double>(a)");
476  generator.generate_const_scalar_op("conj", 'c', "c", "std::conj<double>(a)");
477 
478  return 0;
479 }
void generate_const_scalar_op(const std::string &name, const char return_type, const std::string &arg_types, const std::string &impl)
Generates a basic constant scalar function, such as integer addition for instance.
Definition: func-gen.cpp:273
Generator(const std::string &header_filename, const std::string &source_filename)
Constructs a generator for the function table.
Definition: func-gen.cpp:286
Represents a previously generated function, to be added in the register function once it is generated...
Definition: func-gen.cpp:40
const std::string cqasm_name
Name of the function as exposed to cQASM.
Definition: func-gen.cpp:51
Function(const std::string &name, const std::string &args)
Constructs a normal function.
Definition: func-gen.cpp:129
int main(int argc, char *argv[])
Entry point.
Definition: func-gen.cpp:383
const std::string cpp_name
Name of the function implementation as generated in the C++ file.
Definition: func-gen.cpp:46
Generator class.
Definition: func-gen.cpp:138
const std::string cqasm_args
Argument type spec for overload resolution, type checking, and type promotion.
Definition: func-gen.cpp:57
Namespace for the func-gen program.
Definition: func-gen.cpp:34
~Generator()
Finishes writing the header & source file, then destroys the generator.
Definition: func-gen.cpp:365