summaryrefslogtreecommitdiffstats
path: root/tools/json_to_struct/element_generator.py
blob: e34753294e3be067a71387cb08d502941df15642 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
# Copyright (c) 2012 The Chromium Authors. All rights reserved.
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.

import json
import struct_generator

def _JSONToCString16(json_string_literal):
  """Converts a JSON string literal to a C++ UTF-16 string literal. This is
  done by converting \\u#### to \\x####.
  """
  c_string_literal = json_string_literal
  escape_index = c_string_literal.find('\\')
  while escape_index > 0:
    if c_string_literal[escape_index + 1] == 'u':
      # We close the C string literal after the 4 hex digits and reopen it right
      # after, otherwise the Windows compiler will sometimes try to get more
      # than 4 characters in the hex string.
      c_string_literal = (c_string_literal[0:escape_index + 1] + 'x' +
          c_string_literal[escape_index + 2:escape_index + 6] + '" L"' +
          c_string_literal[escape_index + 6:])
    escape_index = c_string_literal.find('\\', escape_index + 6)
  return c_string_literal

def _GenerateString(content, lines, indent='  '):
  """Generates an UTF-8 string to be included in a static structure initializer.
  If content is not specified, uses NULL.
  """
  if content is None:
    lines.append(indent + 'NULL,')
  else:
    # json.dumps quotes the string and escape characters as required.
    lines.append(indent + '%s,' % json.dumps(content))

def _GenerateString16(content, lines, indent='  '):
  """Generates an UTF-16 string to be included in a static structure
  initializer. If content is not specified, uses NULL.
  """
  if content is None:
    lines.append(indent + 'NULL,')
  else:
    # json.dumps quotes the string and escape characters as required.
    lines.append(indent + 'L%s,' % _JSONToCString16(json.dumps(content)))

def _GenerateArrayVariableName(element_name, field_name, field_name_count):
  # Generates a unique variable name for an array variable.
  var = 'array_%s_%s' % (element_name, field_name)
  if var not in field_name_count:
    field_name_count[var] = 0
    return var
  new_var = '%s_%d' % (var, field_name_count[var])
  field_name_count[var] += 1
  return new_var

def _GenerateArray(element_name, field_info, content, lines, indent,
                   field_name_count):
  """Generates an array to be included in a static structure initializer. If
  content is not specified, uses NULL. The array is assigned to a temporary
  variable which is initialized before the structure.
  """
  if content is None:
    lines.append(indent + 'NULL,')
    lines.append(indent + '0,')  # Size of the array.
    return

  # Create a new array variable and use it in the structure initializer.
  # This prohibits nested arrays. Add a clash detection and renaming mechanism
  # to solve the problem.
  var = _GenerateArrayVariableName(element_name, field_info['field'],
                                   field_name_count)
  lines.append(indent + '%s,' % var)
  lines.append(indent + '%s,' % len(content))  # Size of the array.
  # Generate the array content.
  array_lines = []
  field_info['contents']['field'] = var;
  array_lines.append(struct_generator.GenerateField(
                     field_info['contents']) + '[] = {')
  for subcontent in content:
    GenerateFieldContent(element_name, field_info['contents'], subcontent,
                         array_lines, indent, field_name_count)
  array_lines.append('};')
  # Prepend the generated array so it is initialized before the structure.
  lines.reverse()
  array_lines.reverse()
  lines.extend(array_lines)
  lines.reverse()

def _GenerateStruct(element_name, field_info, content, lines, indent,
                    field_name_count):
  """Generates a struct to be included in a static structure initializer. If
  content is not specified, uses {0}.
  """
  if content is None:
    lines.append(indent + '{0},')
    return

  fields = field_info['fields']
  lines.append(indent + '{')
  for field in fields:
    subcontent = content.get(field['field'])
    GenerateFieldContent(element_name, field, subcontent, lines, '  ' + indent,
                         field_name_count)
  lines.append(indent + '},')

def GenerateFieldContent(element_name, field_info, content, lines, indent,
                         field_name_count):
  """Generate the content of a field to be included in the static structure
  initializer. If the field's content is not specified, uses the default value
  if one exists.
  """
  if content is None:
    content = field_info.get('default', None)
  type = field_info['type']
  if type == 'int' or type == 'enum':
    lines.append('%s%s,' % (indent, content))
  elif type == 'string':
    _GenerateString(content, lines, indent)
  elif type == 'string16':
    _GenerateString16(content, lines, indent)
  elif type == 'array':
    _GenerateArray(element_name, field_info, content, lines, indent,
                   field_name_count)
  elif type == 'struct':
    _GenerateStruct(element_name, field_info, content, lines, indent,
                    field_name_count)
  else:
    raise RuntimeError('Unknown field type "%s"' % type)

def GenerateElement(type_name, schema, element_name, element, field_name_count):
  """Generate the static structure initializer for one element.
  """
  lines = [];
  lines.append('const %s %s = {' % (type_name, element_name));
  for field_info in schema:
    content = element.get(field_info['field'], None)
    if (content == None and not field_info.get('optional', False)):
      raise RuntimeError('Mandatory field "%s" omitted in element "%s".' %
                         (field_info['field'], element_name))
    GenerateFieldContent(element_name, field_info, content, lines, '  ',
                         field_name_count)
  lines.append('};')
  return '\n'.join(lines)

def GenerateElements(type_name, schema, description, field_name_count={}):
  """Generate the static structure initializer for all the elements in the
  description['elements'] dictionary, as well as for any variables in
  description['int_variables'].
  """
  result = [];
  for var_name, value in description.get('int_variables', {}).items():
    result.append('const int %s = %s;' % (var_name, value))
  result.append('')

  for element_name, element in description.get('elements', {}).items():
    result.append(GenerateElement(type_name, schema, element_name, element,
                                  field_name_count))
    result.append('')
  return '\n'.join(result)