mavtemplate.py 5.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133
  1. #!/usr/bin/env python
  2. '''
  3. simple templating system for mavlink generator
  4. Copyright Andrew Tridgell 2011
  5. Released under GNU GPL version 3 or later
  6. '''
  7. from builtins import object
  8. from .mavparse import MAVParseError
  9. class MAVTemplate(object):
  10. '''simple templating system'''
  11. def __init__(self,
  12. start_var_token="${",
  13. end_var_token="}",
  14. start_rep_token="${{",
  15. end_rep_token="}}",
  16. trim_leading_lf=True,
  17. checkmissing=True):
  18. self.start_var_token = start_var_token
  19. self.end_var_token = end_var_token
  20. self.start_rep_token = start_rep_token
  21. self.end_rep_token = end_rep_token
  22. self.trim_leading_lf = trim_leading_lf
  23. self.checkmissing = checkmissing
  24. def find_end(self, text, start_token, end_token, ignore_end_token=None):
  25. '''find the of a token.
  26. Returns the offset in the string immediately after the matching end_token'''
  27. if not text.startswith(start_token):
  28. raise MAVParseError("invalid token start")
  29. offset = len(start_token)
  30. nesting = 1
  31. while nesting > 0:
  32. idx1 = text[offset:].find(start_token)
  33. idx2 = text[offset:].find(end_token)
  34. # Check for false positives due to another similar token
  35. # For example, make sure idx2 points to the second '}' in ${{field: ${name}}}
  36. if ignore_end_token:
  37. combined_token = ignore_end_token + end_token
  38. if text[offset+idx2:offset+idx2+len(combined_token)] == combined_token:
  39. idx2 += len(ignore_end_token)
  40. if idx1 == -1 and idx2 == -1:
  41. raise MAVParseError("token nesting error")
  42. if idx1 == -1 or idx1 > idx2:
  43. offset += idx2 + len(end_token)
  44. nesting -= 1
  45. else:
  46. offset += idx1 + len(start_token)
  47. nesting += 1
  48. return offset
  49. def find_var_end(self, text):
  50. '''find the of a variable'''
  51. return self.find_end(text, self.start_var_token, self.end_var_token)
  52. def find_rep_end(self, text):
  53. '''find the of a repitition'''
  54. return self.find_end(text, self.start_rep_token, self.end_rep_token, ignore_end_token=self.end_var_token)
  55. def substitute(self, text, subvars={},
  56. trim_leading_lf=None, checkmissing=None):
  57. '''substitute variables in a string'''
  58. if trim_leading_lf is None:
  59. trim_leading_lf = self.trim_leading_lf
  60. if checkmissing is None:
  61. checkmissing = self.checkmissing
  62. # handle repititions
  63. while True:
  64. subidx = text.find(self.start_rep_token)
  65. if subidx == -1:
  66. break
  67. endidx = self.find_rep_end(text[subidx:])
  68. if endidx == -1:
  69. raise MAVParseError("missing end macro in %s" % text[subidx:])
  70. part1 = text[0:subidx]
  71. part2 = text[subidx+len(self.start_rep_token):subidx+(endidx-len(self.end_rep_token))]
  72. part3 = text[subidx+endidx:]
  73. a = part2.split(':')
  74. field_name = a[0]
  75. rest = ':'.join(a[1:])
  76. v = None
  77. if isinstance(subvars, dict):
  78. v = subvars.get(field_name, None)
  79. else:
  80. v = getattr(subvars, field_name, None)
  81. if v is None:
  82. raise MAVParseError('unable to find field %s' % field_name)
  83. t1 = part1
  84. for f in v:
  85. t1 += self.substitute(rest, f, trim_leading_lf=False, checkmissing=False)
  86. if len(v) != 0 and t1[-1] in ["\n", ","]:
  87. t1 = t1[:-1]
  88. t1 += part3
  89. text = t1
  90. if trim_leading_lf:
  91. if text[0] == '\n':
  92. text = text[1:]
  93. while True:
  94. idx = text.find(self.start_var_token)
  95. if idx == -1:
  96. return text
  97. endidx = text[idx:].find(self.end_var_token)
  98. if endidx == -1:
  99. raise MAVParseError('missing end of variable: %s' % text[idx:idx+10])
  100. varname = text[idx+2:idx+endidx]
  101. if isinstance(subvars, dict):
  102. if not varname in subvars:
  103. if checkmissing:
  104. raise MAVParseError("unknown variable in '%s%s%s'" % (
  105. self.start_var_token, varname, self.end_var_token))
  106. return text[0:idx+endidx] + self.substitute(text[idx+endidx:], subvars,
  107. trim_leading_lf=False, checkmissing=False)
  108. value = subvars[varname]
  109. else:
  110. value = getattr(subvars, varname, None)
  111. if value is None:
  112. if checkmissing:
  113. raise MAVParseError("unknown variable in '%s%s%s'" % (
  114. self.start_var_token, varname, self.end_var_token))
  115. return text[0:idx+endidx] + self.substitute(text[idx+endidx:], subvars,
  116. trim_leading_lf=False, checkmissing=False)
  117. text = text.replace("%s%s%s" % (self.start_var_token, varname, self.end_var_token), str(value))
  118. return text
  119. def write(self, file, text, subvars={}, trim_leading_lf=True):
  120. '''write to a file with variable substitution'''
  121. file.write(self.substitute(text, subvars=subvars, trim_leading_lf=trim_leading_lf))