xref: /aosp_15_r20/external/emboss/compiler/back_end/util/code_template.py (revision 99e0aae7469b87d12f0ad23e61142c2d74c1ef70)
1# Copyright 2019 Google LLC
2#
3# Licensed under the Apache License, Version 2.0 (the "License");
4# you may not use this file except in compliance with the License.
5# You may obtain a copy of the License at
6#
7#     https://www.apache.org/licenses/LICENSE-2.0
8#
9# Unless required by applicable law or agreed to in writing, software
10# distributed under the License is distributed on an "AS IS" BASIS,
11# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12# See the License for the specific language governing permissions and
13# limitations under the License.
14
15"""A formatter for code templates.
16
17Use the format_template function to render a code template.
18"""
19
20import collections
21import re
22import string
23
24
25def format_template(template, **kwargs):
26  """format_template acts like str.format, but uses ${name} instead of {name}.
27
28  format_template acts like a str.format, except that instead of using { and }
29  to delimit substitutions, format_template uses ${name}.  This simplifies
30  templates of source code in most languages, which frequently use "{" and "}",
31  but very rarely use "$".
32
33  See the documentation for string.Template for details about
34  template strings and the format of substitutions.
35
36  Arguments:
37    template: A template to format.
38    **kwargs: Keyword arguments for string.Template.substitute.
39
40  Returns:
41    A formatted string.
42  """
43  return template.substitute(**kwargs)
44
45
46def parse_templates(text):
47  """Parses text into a namedtuple of templates.
48
49  parse_templates will split its argument into templates by searching for lines
50  of the form:
51
52      [punctuation] " ** " [name] " ** " [punctuation]
53
54  e.g.:
55
56      // ** struct_field_accessor ** ////////
57
58  Leading and trailing punctuation is ignored, and [name] is used as the name
59  of the template.  [name] should match [A-Za-z][A-Za-z0-9_]* -- that is, it
60  should be a valid ASCII Python identifier.
61
62  Additionally any `//` style comment without leading space of the form:
63  ```C++
64  // This is an emboss developer related comment, it's useful internally
65  // but not relevant to end-users of generated code.
66  ```
67  will be stripped out of the generated code.
68
69  If a template wants to define a comment that will be included in the
70  generated code a C-style comment is recommended:
71  ```C++
72  /** This will be included in the generated source. */
73
74  /**
75   * So will this!
76   */
77  ```
78
79  Arguments:
80    text: The text to parse into templates.
81
82  Returns:
83    A namedtuple object whose attributes are the templates from text.
84  """
85  delimiter_re = re.compile(r"^\W*\*\* ([A-Za-z][A-Za-z0-9_]*) \*\*\W*$")
86  comment_re = re.compile(r"^\s*//.*$")
87  templates = {}
88  name = None
89  template = []
90  def finish_template(template):
91    return string.Template("\n".join(template))
92
93  for line in text.splitlines():
94    if delimiter_re.match(line):
95      if name:
96        templates[name] = finish_template(template)
97      name = delimiter_re.match(line).group(1)
98      template = []
99    else:
100      if not comment_re.match(line):
101        template.append(line)
102  if name:
103    templates[name] = finish_template(template)
104  return collections.namedtuple("Templates",
105                                list(templates.keys()))(**templates)
106