generate_initializers.py
Go to the documentation of this file.
1 #!/usr/bin/env python
2 from __future__ import print_function
3 import sys
4 import os
5 import re
6 
7 
8 def to_camel_cased(name):
9  r = re.compile(r"([_\/:][a-z]|[_\/:][0-9])")
10  ret = ""
11  first = True
12  tokens = r.split(name, 1)
13  while True:
14  if len(tokens) > 1:
15  if tokens[1][0] == "_":
16  if first:
17  ret += (
18  tokens[0][0].upper()
19  + tokens[0][1:].lower()
20  + tokens[1][1].upper()
21  )
22  else:
23  ret += tokens[0].lower() + tokens[1][1].upper()
24  else:
25  if first:
26  ret += (
27  tokens[0][0].upper()
28  + tokens[0][1:].lower()
29  + tokens[1][0]
30  + tokens[1][1].upper()
31  )
32  else:
33  ret += tokens[0].lower() + tokens[1][0] + tokens[1][1].upper()
34  tokens = r.split(tokens[2], 1)
35  else:
36  if first:
37  if len(tokens) > 1:
38  ret += tokens[0][0].upper() + tokens[0][1:].lower() + tokens[1][0]
39  else:
40  ret += tokens[0][0].upper() + tokens[0][1:].lower()
41  else:
42  ret += tokens[0].lower()
43  return ret
44  first = False
45 
46 
47 def to_underscores(name, num_pass=0):
48  r = re.compile(r"([A-Za-z][0-9]|[0-9][A-z]|[a-z][A-Z]|[A-Z][A-Z][a-z]|[_\/:][A-z])")
49  ret = ""
50  tokens = r.split(name, 1)
51  while True:
52  if len(tokens) > 1:
53  if (
54  tokens[1][0] == "/"
55  or tokens[1][0] == ":"
56  or tokens[1][0] == "_"
57  or tokens[1][1] == "_"
58  ):
59  ret += tokens[0].lower() + tokens[1][0].lower() + tokens[1][1:].lower()
60  else:
61  ret += (
62  tokens[0].lower()
63  + tokens[1][0].lower()
64  + "_"
65  + tokens[1][1:].lower()
66  )
67  tokens = r.split(tokens[2], 1)
68  else:
69  ret += tokens[0].lower()
70  if num_pass < 1:
71  return to_underscores(ret, num_pass + 1)
72  else:
73  return ret
74 
75 
76 def eprint(*args, **kwargs):
77  print(*args, file=sys.stderr, **kwargs)
78 
79 
80 def eprint(msg):
81  sys.stderr.write(msg + "\n")
82  sys.exit(2)
83 
84 
86  ret = ""
87  for d in data:
88  if "Required" in d:
89  ret += (
90  " " + d["Type"] + " _" + (d["Name"]) + default_argument_value(d) + ","
91  )
92  return ret[0:-1]
93 
94 
95 def constructor_list(data):
96  ret = ""
97  for d in data:
98  if "Required" in d:
99  ret += ",\n " + (d["Name"]) + "(_" + (d["Name"]) + ") "
100  return ret
101 
102 
103 def default_value(data):
104  if data["Value"] is None:
105  return ""
106  elif data["Value"] == "{}":
107  return ""
108  else:
109  return data["Value"]
110 
111 
113  if data["Value"] == None:
114  return ""
115  else:
116  return " = " + data["Value"]
117 
118 
119 def is_required(data):
120  if data["Required"]:
121  return "true"
122  else:
123  return "false"
124 
125 
127  ret = ""
128  for d in data:
129  if "Required" in d and not d["Required"]:
130  ret += ",\n " + (d["Name"]) + "(" + default_value(d) + ") "
131  return ret
132 
133 
135  for d in data:
136  if d["Required"]:
137  return True
138  return False
139 
140 
141 def declaration(data):
142  if "Required" in data:
143  return " " + data["Type"] + " " + (data["Name"]) + ";\n"
144  else:
145  return ""
146 
147 
148 def parser(type_in):
149  parser = ""
150  if type_in == "std::string":
151  return "boost::any_cast<" + type_in + ">(prop.Get())"
152  elif type_in == "exotica::Initializer" or type_in == "Initializer":
153  return "prop.IsInitializerVectorType()?boost::any_cast<std::vector<exotica::Initializer>>(prop.Get()).at(0):boost::any_cast<exotica::Initializer>(prop.Get())"
154  elif (
155  type_in == "std::vector<Initializer>"
156  or type_in == "std::vector<exotica::Initializer>"
157  ):
158  return "boost::any_cast<std::vector<exotica::Initializer>>(prop.Get())"
159  elif type_in == "Eigen::VectorXd":
160  parser = "ParseVector<double,Eigen::Dynamic>"
161  elif type_in == "Eigen::Vector4d":
162  parser = "ParseVector<double,4>"
163  elif type_in == "Eigen::Vector3d":
164  parser = "ParseVector<double,3>"
165  elif type_in == "Eigen::Vector2d":
166  parser = "ParseVector<double,2>"
167  elif type_in == "Eigen::VectorXi":
168  parser = "ParseVector<int,Eigen::Dynamic>"
169  elif type_in == "bool":
170  parser = "ParseBool"
171  elif type_in == "double":
172  parser = "ParseDouble"
173  elif type_in == "int":
174  parser = "ParseInt"
175  elif type_in == "std::vector<std::string>":
176  parser = "ParseList"
177  elif type_in == "std::vector<int>":
178  parser = "ParseIntList"
179  elif type_in == "std::vector<bool>":
180  parser = "ParseBoolList"
181  else:
182  eprint("Unknown data type '" + type_in + "'")
183  sys.exit(2)
184 
185  return (
186  "prop.IsStringType()?"
187  + parser
188  + "(boost::any_cast<std::string>(prop.Get())):boost::any_cast<"
189  + type_in
190  + ">(prop.Get())"
191  )
192 
193 
194 def copy(data):
195  if "Required" in data:
196  return (
197  ' ret.properties_.emplace("'
198  + data["Name"]
199  + '", Property("'
200  + data["Name"]
201  + '", '
202  + is_required(data)
203  + ", boost::any("
204  + (data["Name"])
205  + ")));\n"
206  )
207  else:
208  return ""
209 
210 
211 def add(data):
212  if "Required" in data:
213  return (
214  ' if (other.HasProperty("'
215  + data["Name"]
216  + '")) { const Property& prop = other.properties_.at("'
217  + data["Name"]
218  + '"); if(prop.IsSet()) {'
219  + (data["Name"])
220  + " = "
221  + parser(data["Type"])
222  + ";} }\n"
223  )
224  else:
225  return ""
226 
227 
229  for d in data:
230  if "Required" in d and d["Required"]:
231  return True
232  return False
233 
234 
235 def check(data, name):
236  if "Required" in data and data["Required"]:
237  return (
238  ' if(!other.HasProperty("'
239  + data["Name"]
240  + '") || !other.properties_.at("'
241  + data["Name"]
242  + '").IsSet()) ThrowPretty("Initializer '
243  + name
244  + " requires property "
245  + data["Name"]
246  + ' to be set!");\n'
247  )
248  else:
249  return ""
250 
251 
252 def construct(namespace, class_name_orig, data, include):
253  class_name = class_name_orig + "Initializer"
254  ret = (
255  """// This file was automatically generated. Do not edit this file!
256 #ifndef INITIALIZER_"""
257  + namespace.upper()
258  + "_"
259  + to_underscores(class_name).upper()
260  + """_H
261 #define INITIALIZER_"""
262  + namespace.upper()
263  + "_"
264  + to_underscores(class_name).upper()
265  + """_H
266 
267 #include <exotica_core/property.h>
268 
269 namespace exotica
270 {
271 inline std::vector<Initializer> Get"""
272  + to_camel_cased(namespace)
273  + """Initializers();
274 }
275 
276 namespace exotica
277 {
278 
279 class """
280  + class_name
281  + " : public InitializerBase"
282  + """
283 {
284 public:
285  static std::string GetContainerName() { return """
286  + '"exotica/'
287  + class_name_orig
288  + '"'
289  + """; }
290 
291  """
292  )
293  if needs_default_constructor(data):
294  ret += (
295  class_name
296  + "() : InitializerBase()"
298  + """
299  {
300  }
301 
302  """
303  )
304  ret += (
305  class_name
306  + "("
308  + ") : InitializerBase()"
309  + constructor_list(data)
310  + """
311  {
312  }
313 
314  """
315  + class_name
316  + """(const Initializer& other) : """
317  + class_name
318  + """()
319  {
320 """
321  )
322  for d in data:
323  ret += add(d)
324  ret += (
325  """ }
326 
327  virtual ~"""
328  + class_name
329  + """()
330  {
331  }
332 
333  virtual Initializer GetTemplate() const
334  {
335  return (Initializer)"""
336  + class_name
337  + """();
338  }
339 
340  virtual std::vector<Initializer> GetAllTemplates() const
341  {
342  return Get"""
343  + to_camel_cased(namespace)
344  + """Initializers();
345  }
346 
347  virtual void Check(const Initializer& """
348  + ("other" if has_required_data(data) else "/*other*/")
349  + """) const
350  {
351 """
352  )
353  for d in data:
354  ret += check(d, class_name)
355  ret += """ }
356 
357  operator Initializer()
358  {
359  Initializer ret(GetContainerName());
360 """
361  for d in data:
362  ret += copy(d)
363  ret += """ return ret;
364  }
365 
366 """
367  for d in data:
368  ret += declaration(d)
369  ret += (
370  "};"
371  + """
372 
373 }
374 
375 #include <"""
376  + namespace
377  + "/"
378  + namespace
379  + """_initializers_numerator.h>
380 
381 """
382  )
383  for i in include:
384  ret += "#include <" + i + ".h>\n"
385  ret += """
386 #endif"""
387  return ret
388 
389 
390 def parse_line(line, line_number, function_name):
391  # ignore lines with comments, otherwise comments including ';' will not be recognised
392  if line.startswith("//"):
393  return None
394 
395  last = line.find(";")
396  if last >= 0:
397  line = line[0:last].strip()
398  else:
399  last = line.find("//")
400  if last >= 0:
401  line = line[0:last].strip()
402  else:
403  line = line.strip()
404 
405  if len(line) == 0:
406  return None
407 
408  if line.startswith("include"):
409  return {
410  "Include": line[7:].strip().strip(">").strip("<").strip('"'),
411  "Code": line.strip(),
412  }
413  if line.startswith("extend"):
414  return {
415  "Extends": line[6:].strip().strip(">").strip("<").strip('"'),
416  "Code": line.strip(),
417  }
418  if line.startswith("class"):
419  return {
420  "ClassName": line[5:].strip().strip(">").strip("<").strip('"'),
421  "Code": line.strip(),
422  }
423 
424  if last == -1:
425  eprint("Can't find ';' in '" + function_name + "', on line " + str(line_number))
426  sys.exit(2)
427 
428  required = True
429  if line.startswith("Required"):
430  required = True
431  elif line.startswith("Optional"):
432  required = False
433  else:
434  eprint(
435  "Can't parse 'Required/Optional' tag in '"
436  + function_name
437  + "', on line "
438  + str(line_number)
439  )
440  sys.exit(2)
441 
442  value = None
443  field_type = ""
444  name = ""
445  if not required:
446  eq = line.find("=")
447  has_default_arg = not (eq == -1)
448  if has_default_arg:
449  value = line[eq + 1 : last]
450  else:
451  # we need this to get the parameter name
452  eq = last
453  name_start = line[0:eq].strip().rfind(" ")
454  name = line[name_start:eq].strip()
455  field_type = line[9:name_start].strip()
456  if not has_default_arg:
457  eprint("Optional parameter '" + name + "' requires a default argument!")
458  else:
459  name_start = line[0:last].strip().rfind(" ")
460  name = line[name_start:last].strip()
461  field_type = line[9:name_start].strip()
462 
463  return {"Required": required, "Type": field_type, "Name": name, "Value": value}
464 
465 
466 def parse_file(file_name):
467  with open(file_name) as f:
468  lines = f.readlines()
469  data = []
470  include = []
471  extends = []
472  names = []
473  i = 0
474  optionalOnly = False
475  for l in lines:
476  i = i + 1
477  d = parse_line(l, i, file_name)
478  if d != None:
479  if "Required" in d:
480  if d["Required"] == False:
481  optionalOnly = True
482  else:
483  if optionalOnly:
484  eprint(
485  "Required properties_ have to come before Optional ones, in '"
486  + file_name
487  + "', on line "
488  + str(i)
489  )
490  sys.exit(2)
491  data.append(d)
492  if "Include" in d:
493  include.append(d["Include"])
494  if "Extends" in d:
495  extends.append(d["Extends"])
496  if "ClassName" in d:
497  names.append(d["ClassName"])
498  if len(names) != 1:
499  eprint("Could not parse initializer class name in '" + file_name + "'!")
500  eprint(names)
501  sys.exit(2)
502  return {"Data": data, "Include": include, "Extends": extends, "ClassName": names[0]}
503 
504 
505 def contains_data(type_name, name, list_in):
506  for d in list_in:
507  if d["Type"] == type_name and d["Name"] == name:
508  return d["Class"]
509  return False
510 
511 
512 def contains_include(name, list_in):
513  for d in list_in:
514  if d == name:
515  return True
516  return False
517 
518 
519 def contains_extends(name, list_in):
520  for d in list_in:
521  if d == name:
522  return True
523  return False
524 
525 
526 def collect_extensions(input_files, search_dirs, content):
527  file_content = parse_file(input_files)
528  class_name = file_content["ClassName"]
529  if "Extends" in file_content:
530  for e in file_content["Extends"]:
531  if not contains_extends(e, content["Extends"]):
532  file_name = None
533  ext = e.split("/")
534  for d in search_dirs:
535  ff = d + "/share/" + ext[0] + "/init/" + ext[1] + ".in"
536  if os.path.isfile(ff):
537  file_name = ff
538  break
539  if not file_name:
540  eprint("Cannot find extension '" + e + "'!")
541  content["Extends"].append(e)
542  content = collect_extensions(file_name, search_dirs, content)
543 
544  if "Data" in file_content:
545  for d in file_content["Data"]:
546  cls = contains_data(d["Type"], d["Name"], content["Data"])
547  if cls:
548  for e in content["Data"]:
549  if e["Name"] == d["Name"]:
550  e["Value"] = d["Value"]
551  else:
552  d["Class"] = class_name
553  content["Data"].append(d)
554  if "Include" in file_content:
555  for i in file_content["Include"]:
556  if not contains_include(i, content["Include"]):
557  content["Include"].append(i)
558 
559  content["ClassName"] = class_name
560  return content
561 
562 
563 def sort_data(data):
564  a = []
565  b = []
566  for d in data:
567  if d["Required"]:
568  a.append(d)
569  else:
570  b.append(d)
571  return a + b
572 
573 
574 def generate(input_files, output_files, namespace, search_dirs, devel_dir):
575  print("Generating " + output_files)
576  content = collect_extensions(
577  input_files, search_dirs, {"Data": [], "Include": [], "Extends": []}
578  )
579  txt = construct(
580  namespace, content["ClassName"], sort_data(content["Data"]), content["Include"]
581  )
582  path = os.path.dirname(output_files)
583  if not os.path.exists(path):
584  os.makedirs(path)
585  with open(output_files, "w") as f:
586  f.write(txt)
587  return content["ClassName"]
588 
589 
590 def create_class_init_header(class_inits, file_name):
591  ret = (
592  """// This file was automatically generated. Do not edit this file!
593 #ifndef INITIALIZE_PROJECT_HEADER_"""
594  + namespace.upper()
595  + """_H_
596 #define INITIALIZE_PROJECT_HEADER_"""
597  + namespace.upper()
598  + """_H_
599 
600 #include <exotica_core/property.h>
601 """
602  )
603  for init in class_inits:
604  ret += "#include <" + namespace + "/" + init[0] + "_initializer.h>\n"
605  ret += (
606  """
607 
608 namespace exotica
609 {
610 
611 inline std::vector<Initializer> Get"""
612  + to_camel_cased(namespace)
613  + """Initializers()
614 {
615  std::vector<Initializer> ret;
616 """
617  )
618  for init in class_inits:
619  ret += " ret.push_back(" + init[1] + "Initializer().GetTemplate());\n"
620  ret += """ return ret;
621 }
622 
623 }
624 
625 #endif
626 """
627  path = os.path.dirname(file_name)
628  if not os.path.exists(path):
629  os.makedirs(path)
630  with open(file_name, "w") as f:
631  f.write(ret)
632 
633 
634 if __name__ == "__main__":
635  if len(sys.argv) > 5:
636  offset = 5
637  n = int((len(sys.argv) - offset) / 2)
638  namespace = sys.argv[1]
639  search_dirs = sys.argv[2].split(":")
640  devel_dir = sys.argv[3]
641  class_inits_header_file = sys.argv[4]
642  if not os.path.exists(devel_dir + "/init"):
643  os.makedirs(devel_dir + "/init")
644 
645  for i in range(0, n):
646  input_files = sys.argv[offset + i]
647  class_name = os.path.basename(sys.argv[offset + i][0:-3])
648  with open(input_files, "r") as fi:
649  with open(devel_dir + "/init/" + class_name + ".in", "w") as f:
650  f.write(fi.read())
651 
652  class_inits = []
653  for i in range(0, n):
654  input_files = sys.argv[offset + i]
655  output_files = sys.argv[offset + n + i]
656  class_file_name = os.path.basename(sys.argv[offset + i][0:-3])
657  class_inits.append(
658  (
659  class_file_name,
660  generate(
661  input_files, output_files, namespace, search_dirs, devel_dir
662  ),
663  )
664  )
665 
666  create_class_init_header(class_inits, class_inits_header_file)
667  else:
668  eprint("Initializer generation failure: invalid arguments!")
669  sys.exit(1)
generate_initializers.sort_data
def sort_data(data)
Definition: generate_initializers.py:563
generate_initializers.has_required_data
def has_required_data(data)
Definition: generate_initializers.py:228
generate_initializers.create_class_init_header
def create_class_init_header(class_inits, file_name)
Definition: generate_initializers.py:590
generate_initializers.to_camel_cased
def to_camel_cased(name)
Definition: generate_initializers.py:8
generate_initializers.eprint
def eprint(*args, **kwargs)
Definition: generate_initializers.py:76
generate_initializers.is_required
def is_required(data)
Definition: generate_initializers.py:119
generate_initializers.default_argument_value
def default_argument_value(data)
Definition: generate_initializers.py:112
generate_initializers.parse_file
def parse_file(file_name)
Definition: generate_initializers.py:466
generate_initializers.to_underscores
def to_underscores(name, num_pass=0)
Definition: generate_initializers.py:47
generate_initializers.contains_extends
def contains_extends(name, list_in)
Definition: generate_initializers.py:519
generate_initializers.parser
def parser(type_in)
Definition: generate_initializers.py:148
generate_initializers.default_constructor_list
def default_constructor_list(data)
Definition: generate_initializers.py:126
generate_initializers.generate
def generate(input_files, output_files, namespace, search_dirs, devel_dir)
Definition: generate_initializers.py:574
generate_initializers.add
def add(data)
Definition: generate_initializers.py:211
generate_initializers.check
def check(data, name)
Definition: generate_initializers.py:235
generate_initializers.construct
def construct(namespace, class_name_orig, data, include)
Definition: generate_initializers.py:252
generate_initializers.constructor_list
def constructor_list(data)
Definition: generate_initializers.py:95
generate_initializers.needs_default_constructor
def needs_default_constructor(data)
Definition: generate_initializers.py:134
generate_initializers.contains_data
def contains_data(type_name, name, list_in)
Definition: generate_initializers.py:505
generate_initializers.constructor_argument_list
def constructor_argument_list(data)
Definition: generate_initializers.py:85
generate_initializers.parse_line
def parse_line(line, line_number, function_name)
Definition: generate_initializers.py:390
generate_initializers.declaration
def declaration(data)
Definition: generate_initializers.py:141
generate_initializers.contains_include
def contains_include(name, list_in)
Definition: generate_initializers.py:512
generate_initializers.default_value
def default_value(data)
Definition: generate_initializers.py:103
generate_initializers.collect_extensions
def collect_extensions(input_files, search_dirs, content)
Definition: generate_initializers.py:526
generate_initializers.copy
def copy(data)
Definition: generate_initializers.py:194


exotica_core
Author(s): Yiming Yang, Michael Camilleri
autogenerated on Sun Jun 2 2024 02:58:18