• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1#!/usr/bin/env vpython
2#
3# [VPYTHON:BEGIN]
4# wheel: <
5#   name: "infra/python/wheels/perfect-hash-py2_py3"
6#   version: "version:0.2.1"
7# >
8# [VPYTHON:END]
9#
10# Copyright 2018 The ANGLE Project Authors. All rights reserved.
11# Use of this source code is governed by a BSD-style license that can be
12# found in the LICENSE file.
13#
14# gen_builtin_symbols.py:
15#  Code generation for the built-in symbol tables.
16
17from collections import OrderedDict
18from datetime import date
19from perfect_hash import generate_hash, Hash2
20import argparse
21import hashlib
22import json
23import re
24import os
25import sys
26import random
27
28template_immutablestring_cpp = """// GENERATED FILE - DO NOT EDIT.
29// Generated by {script_name} using data from {variable_data_source_name} and
30// {function_data_source_name}.
31//
32// Copyright {copyright_year} The ANGLE Project Authors. All rights reserved.
33// Use of this source code is governed by a BSD-style license that can be
34// found in the LICENSE file.
35//
36// ImmutableString_autogen.cpp: Wrapper for static or pool allocated char arrays, that are guaranteed to be
37// valid and unchanged for the duration of the compilation.
38// Implements mangledNameHash using perfect hash function from gen_builtin_symbols.py
39
40#include "compiler/translator/ImmutableString.h"
41
42std::ostream &operator<<(std::ostream &os, const sh::ImmutableString &str)
43{{
44    return os.write(str.data(), str.length());
45}}
46
47#if defined(_MSC_VER)
48#    pragma warning(disable : 4309)  // truncation of constant value
49#endif
50
51
52namespace
53{{
54
55constexpr int kT1[] = {{{S1}}};
56constexpr int kT2[] = {{{S2}}};
57constexpr int kG[] = {{{G}}};
58
59int HashG(const char *key, const int *T)
60{{
61    int sum = 0;
62
63    for (int i = 0; key[i] != '\\0'; i++)
64    {{
65        sum += T[i] * key[i];
66        sum %= {NG};
67    }}
68    return kG[sum];
69}}
70
71int PerfectHash(const char *key)
72{{
73    if (strlen(key) > {NS})
74        return 0;
75
76    return (HashG(key, kT1) + HashG(key, kT2)) % {NG};
77}}
78
79}}
80
81namespace sh
82{{
83
84template <>
85const size_t ImmutableString::FowlerNollVoHash<4>::kFnvPrime = 16777619u;
86
87template <>
88const size_t ImmutableString::FowlerNollVoHash<4>::kFnvOffsetBasis = 0x811c9dc5u;
89
90template <>
91const size_t ImmutableString::FowlerNollVoHash<8>::kFnvPrime =
92    static_cast<size_t>(1099511628211ull);
93
94template <>
95const size_t ImmutableString::FowlerNollVoHash<8>::kFnvOffsetBasis =
96    static_cast<size_t>(0xcbf29ce484222325ull);
97
98uint32_t ImmutableString::mangledNameHash() const
99{{
100    return PerfectHash(data());
101}}
102
103}}  // namespace sh
104"""
105
106template_immutablestringtest_cpp = """// GENERATED FILE - DO NOT EDIT.
107// Generated by {script_name} using data from {function_data_source_name}.
108//
109// Copyright {copyright_year} The ANGLE Project Authors. All rights reserved.
110// Use of this source code is governed by a BSD-style license that can be
111// found in the LICENSE file.
112//
113// ImmutableString_test_autogen.cpp:
114//   Tests for matching script-generated hashes with runtime computed hashes.
115
116#include "compiler/translator/ImmutableString.h"
117#include "gtest/gtest.h"
118
119namespace sh
120{{
121
122TEST(ImmutableStringTest, ScriptGeneratedHashesMatch)
123{{
124{script_generated_hash_tests}
125}}
126
127}}  // namespace sh
128"""
129
130# The header file has a "get" function for each variable. They are used in traversers.
131# It also declares id values of built-ins with human readable names, so they can be used to identify built-ins.
132template_builtin_header = """// GENERATED FILE - DO NOT EDIT.
133// Generated by {script_name} using data from {variable_data_source_name} and
134// {function_data_source_name}.
135//
136// Copyright {copyright_year} The ANGLE Project Authors. All rights reserved.
137// Use of this source code is governed by a BSD-style license that can be
138// found in the LICENSE file.
139//
140// BuiltIn_autogen.h:
141//   Compile-time initialized built-ins.
142
143#ifndef COMPILER_TRANSLATOR_TREEUTIL_BUILTIN_AUTOGEN_H_
144#define COMPILER_TRANSLATOR_TREEUTIL_BUILTIN_AUTOGEN_H_
145
146#include "compiler/translator/SymbolUniqueId.h"
147
148namespace sh
149{{
150
151class TVariable;
152
153class BuiltInId
154{{
155public:
156
157{builtin_id_declarations}
158
159}};  // class BuiltInId
160
161namespace BuiltInVariable
162{{
163
164{get_variable_declarations}
165
166}}  // namespace BuiltInVariable
167
168}}  // namespace sh
169
170#endif  // COMPILER_TRANSLATOR_TREEUTIL_BUILTIN_AUTOGEN_H_
171"""
172
173template_symboltable_h = """// GENERATED FILE - DO NOT EDIT.
174// Generated by {script_name} using data from {variable_data_source_name} and
175// {function_data_source_name}.
176//
177// Copyright {copyright_year} The ANGLE Project Authors. All rights reserved.
178// Use of this source code is governed by a BSD-style license that can be
179// found in the LICENSE file.
180//
181// SymbolTable_autogen.h:
182//   Autogenerated member variables of TSymbolTable.
183
184#ifndef COMPILER_TRANSLATOR_SYMBOLTABLE_AUTOGEN_H_
185#define COMPILER_TRANSLATOR_SYMBOLTABLE_AUTOGEN_H_
186
187namespace sh
188{{
189
190class TSymbolTableBase
191{{
192  protected:
193    TSymbolTableBase() = default;
194{declare_member_variables}
195}};
196
197}}  // namespace sh
198
199#endif  // COMPILER_TRANSLATOR_SYMBOLTABLE_AUTOGEN_H_
200"""
201
202# By having the variables defined in a cpp file we ensure that there's just one instance of each of the declared variables.
203template_symboltable_cpp = """// GENERATED FILE - DO NOT EDIT.
204// Generated by {script_name} using data from {variable_data_source_name} and
205// {function_data_source_name}.
206//
207// Copyright {copyright_year} The ANGLE Project Authors. All rights reserved.
208// Use of this source code is governed by a BSD-style license that can be
209// found in the LICENSE file.
210//
211// SymbolTable_autogen.cpp:
212//   Compile-time initialized built-ins.
213
214#include "compiler/translator/SymbolTable.h"
215
216#include "angle_gl.h"
217#include "compiler/translator/tree_util/BuiltIn_autogen.h"
218#include "compiler/translator/ImmutableString.h"
219#include "compiler/translator/StaticType.h"
220#include "compiler/translator/Symbol.h"
221#include "compiler/translator/SymbolTable.h"
222
223namespace sh
224{{
225
226// Since some of the BuiltInId declarations are used outside of constexpr expressions, we need to
227// have these definitions without an initializer. C++17 should eventually remove the need for this.
228{builtin_id_definitions}
229
230const int TSymbolTable::kLastBuiltInId = {last_builtin_id};
231
232namespace BuiltInName
233{{
234
235constexpr const ImmutableString _empty("");
236{name_declarations}
237
238}}  // namespace BuiltInName
239
240// TODO(oetuaho): Would be nice to make this a class instead of a namespace so that we could friend
241// this from TVariable. Now symbol constructors taking an id have to be public even though they're
242// not supposed to be accessible from outside of here. http://anglebug.com/2390
243namespace BuiltInVariable
244{{
245
246{variable_declarations}
247
248{get_variable_definitions}
249
250}}  // namespace BuiltInVariable
251
252namespace BuiltInParameters
253{{
254
255{parameter_declarations}
256
257}}  // namespace BuiltInParameters
258
259namespace UnmangledBuiltIns
260{{
261
262{unmangled_builtin_declarations}
263
264}}  // namespace UnmangledBuiltIns
265
266// TODO(oetuaho): Would be nice to make this a class instead of a namespace so that we could friend
267// this from TFunction. Now symbol constructors taking an id have to be public even though they're
268// not supposed to be accessible from outside of here. http://anglebug.com/2390
269namespace BuiltInFunction
270{{
271
272{function_declarations}
273
274}}  // namespace BuiltInFunction
275
276void TSymbolTable::initializeBuiltInVariables(sh::GLenum shaderType,
277                                              ShShaderSpec spec,
278                                              const ShBuiltInResources &resources)
279{{
280    const TSourceLoc zeroSourceLoc = {{0, 0, 0, 0}};
281{init_member_variables}
282}}
283
284const TSymbol *TSymbolTable::findBuiltIn(const ImmutableString &name,
285                                         int shaderVersion) const
286{{
287    if (name.length() > {max_mangled_name_length})
288    {{
289        return nullptr;
290    }}
291    uint32_t nameHash = name.mangledNameHash();
292{get_builtin}
293}}
294
295const UnmangledBuiltIn *TSymbolTable::getUnmangledBuiltInForShaderVersion(const ImmutableString &name, int shaderVersion)
296{{
297    if (name.length() > {max_unmangled_name_length})
298    {{
299        return nullptr;
300    }}
301    uint32_t nameHash = name.mangledNameHash();
302{get_unmangled_builtin}
303}}
304
305}}  // namespace sh
306"""
307
308template_parsecontext_header = """// GENERATED FILE - DO NOT EDIT.
309// Generated by {script_name} using data from {variable_data_source_name} and
310// {function_data_source_name}.
311//
312// Copyright {copyright_year} The ANGLE Project Authors. All rights reserved.
313// Use of this source code is governed by a BSD-style license that can be
314// found in the LICENSE file.
315//
316// ParseContext_autogen.h:
317//   Helpers for built-in related checks.
318
319#ifndef COMPILER_TRANSLATOR_PARSECONTEXT_AUTOGEN_H_
320#define COMPILER_TRANSLATOR_PARSECONTEXT_AUTOGEN_H_
321
322namespace sh
323{{
324
325namespace BuiltInGroup
326{{
327
328{is_in_group_definitions}
329
330}}  // namespace BuiltInGroup
331
332}}  // namespace sh
333
334#endif  // COMPILER_TRANSLATOR_PARSECONTEXT_AUTOGEN_H_
335
336"""
337
338parsed_variables = None
339
340basic_types_enumeration = [
341    'Void', 'Float', 'Int', 'UInt', 'Bool', 'AtomicCounter', 'YuvCscStandardEXT', 'Sampler2D',
342    'Sampler3D', 'SamplerCube', 'Sampler2DArray', 'SamplerExternalOES', 'SamplerExternal2DY2YEXT',
343    'Sampler2DRect', 'Sampler2DMS', 'Sampler2DMSArray', 'ISampler2D', 'ISampler3D', 'ISamplerCube',
344    'ISampler2DArray', 'ISampler2DMS', 'ISampler2DMSArray', 'USampler2D', 'USampler3D',
345    'USamplerCube', 'USampler2DArray', 'USampler2DMS', 'USampler2DMSArray', 'Sampler2DShadow',
346    'SamplerCubeShadow', 'Sampler2DArrayShadow', 'Image2D', 'IImage2D', 'UImage2D', 'Image3D',
347    'IImage3D', 'UImage3D', 'Image2DArray', 'IImage2DArray', 'UImage2DArray', 'ImageCube',
348    'IImageCube', 'UImageCube'
349]
350
351id_counter = 0
352
353
354def set_working_dir():
355    script_dir = os.path.dirname(os.path.abspath(__file__))
356    os.chdir(script_dir)
357
358
359def get_basic_mangled_name(basic):
360    index = basic_types_enumeration.index(basic)
361    if index < 26:
362        return '0' + chr(ord('A') + index)
363    return '0' + chr(ord('a') + index - 26)
364
365
366levels = ['ESSL3_1_BUILTINS', 'ESSL3_BUILTINS', 'ESSL1_BUILTINS', 'COMMON_BUILTINS']
367
368
369def get_shader_version_condition_for_level(level):
370    if level == 'ESSL3_1_BUILTINS':
371        return 'shaderVersion >= 310'
372    elif level == 'ESSL3_BUILTINS':
373        return 'shaderVersion >= 300'
374    elif level == 'ESSL1_BUILTINS':
375        return 'shaderVersion == 100'
376    elif level == 'COMMON_BUILTINS':
377        return ''
378    else:
379        raise Exception('Unsupported symbol table level')
380
381
382class GroupedList:
383    """"Class for storing a list of objects grouped by symbol table level and condition."""
384
385    def __init__(self):
386        self.objs = OrderedDict()
387        self.max_name_length = 0
388        # We need to add all the levels here instead of lazily since they must be in a specific order.
389        for l in levels:
390            self.objs[l] = OrderedDict()
391
392    def add_obj(self, level, condition, name, obj):
393        if (level not in levels):
394            raise Exception('Unexpected level: ' + str(level))
395        if condition not in self.objs[level]:
396            self.objs[level][condition] = OrderedDict()
397        self.objs[level][condition][name] = obj
398        if len(name) > self.max_name_length:
399            self.max_name_length = len(name)
400
401    def has_key(self, level, condition, name):
402        if (level not in levels):
403            raise Exception('Unexpected level: ' + str(level))
404        if condition not in self.objs[level]:
405            return False
406        return (name in self.objs[level][condition])
407
408    def get(self, level, condition, name):
409        if self.has_key(level, condition, name):
410            return self.objs[level][condition][name]
411        return None
412
413    def get_max_name_length(self):
414        return self.max_name_length
415
416    def get_switch_code(self, hashfn, script_generated_hash_tests):
417        code = []
418        for level in levels:
419            if len(self.objs[level]) == 0:
420                continue
421            level_condition = get_shader_version_condition_for_level(level)
422            if level_condition != '':
423                code.append('if ({condition})\n {{'.format(condition=level_condition))
424
425            for condition, objs in self.objs[level].iteritems():
426                if len(objs) > 0:
427                    if condition != 'NO_CONDITION':
428                        condition_header = '  if ({condition})\n {{'.format(condition=condition)
429                        code.append(condition_header.replace('shaderType', 'mShaderType'))
430
431                    switch = {}
432                    for name, obj in objs.iteritems():
433                        name_hash = mangledNameHash(name, hashfn, script_generated_hash_tests)
434                        if name_hash not in switch:
435                            switch[name_hash] = []
436                        switch[name_hash].append(obj['hash_matched_code'])
437
438                    code.append('switch(nameHash) {')
439                    for name_hash, obj in sorted(switch.iteritems()):
440                        code.append('case 0x' + ('%08x' % name_hash) + 'u:\n{')
441                        code += obj
442                        code.append('break;\n}')
443                    code.append('}')
444
445                    if condition != 'NO_CONDITION':
446                        code.append('}')
447
448            if level_condition != '':
449                code.append('}')
450        code.append('return nullptr;')
451        return '\n'.join(code)
452
453
454class TType:
455
456    def __init__(self, glsl_header_type):
457        if isinstance(glsl_header_type, basestring):
458            self.data = self.parse_type(glsl_header_type)
459        else:
460            self.data = glsl_header_type
461        self.normalize()
462
463    def normalize(self):
464        # Note that this will set primarySize and secondarySize also on genTypes. In that case they
465        # are overridden when the specific types are generated.
466        if 'primarySize' not in self.data:
467            if ('secondarySize' in self.data):
468                raise Exception(
469                    'Unexpected secondarySize on type that does not have primarySize set')
470            self.data['primarySize'] = 1
471        if 'secondarySize' not in self.data:
472            self.data['secondarySize'] = 1
473        if 'precision' not in self.data:
474            self.data['precision'] = 'Undefined'
475        if 'qualifier' not in self.data:
476            self.data['qualifier'] = 'Global'
477
478    def get_statictype_string(self):
479        template_type = 'StaticType::Get<Ebt{basic}, Ebp{precision}, Evq{qualifier}, {primarySize}, {secondarySize}>()'
480        return template_type.format(**self.data)
481
482    def get_dynamic_type_string(self):
483        template_type = 'new TType(Ebt{basic}, Ebp{precision}, Evq{qualifier}, {primarySize}, {secondarySize})'
484        return template_type.format(**self.data)
485
486    def get_mangled_name(self):
487        mangled_name = ''
488
489        size_key = (self.data['secondarySize'] - 1) * 4 + self.data['primarySize'] - 1
490        if size_key < 10:
491            mangled_name += chr(ord('0') + size_key)
492        else:
493            mangled_name += chr(ord('A') + size_key - 10)
494
495        mangled_name += get_basic_mangled_name(self.data['basic'])
496        return mangled_name
497
498    def get_human_readable_name(self):
499        name = self.data['basic']
500        name += str(self.data['primarySize'])
501        if self.data['secondarySize'] > 1:
502            name += 'x' + str(self.data['secondarySize'])
503        return name
504
505    def is_vector(self):
506        return self.data['primarySize'] > 1 and self.data['secondarySize'] == 1
507
508    def is_matrix(self):
509        return self.data['secondarySize'] > 1
510
511    def get_object_size(self):
512        return self.data['primarySize'] * self.data['secondarySize']
513
514    def specific_sampler_or_image_type(self, basic_type_prefix):
515        if 'genType' in self.data:
516            type = {}
517            if 'basic' not in self.data:
518                type['basic'] = {'': 'Float', 'I': 'Int', 'U': 'UInt'}[basic_type_prefix]
519                type['primarySize'] = self.data['primarySize']
520            else:
521                type['basic'] = basic_type_prefix + self.data['basic']
522                type['primarySize'] = 1
523            type['precision'] = 'Undefined'
524            return TType(type)
525        return self
526
527    def specific_type(self, vec_size):
528        type = {}
529        if 'genType' in self.data:
530            type['basic'] = self.data['basic']
531            type['precision'] = self.data['precision']
532            type['qualifier'] = self.data['qualifier']
533            type['primarySize'] = vec_size
534            type['secondarySize'] = 1
535            return TType(type)
536        return self
537
538    def parse_type(self, glsl_header_type):
539        if glsl_header_type.startswith('out '):
540            type_obj = self.parse_type(glsl_header_type[4:])
541            type_obj['qualifier'] = 'Out'
542            return type_obj
543        if glsl_header_type.startswith('inout '):
544            type_obj = self.parse_type(glsl_header_type[6:])
545            type_obj['qualifier'] = 'InOut'
546            return type_obj
547
548        basic_type_map = {
549            'float': 'Float',
550            'int': 'Int',
551            'uint': 'UInt',
552            'bool': 'Bool',
553            'void': 'Void',
554            'atomic_uint': 'AtomicCounter',
555            'yuvCscStandardEXT': 'YuvCscStandardEXT'
556        }
557
558        if glsl_header_type in basic_type_map:
559            return {'basic': basic_type_map[glsl_header_type]}
560
561        type_obj = {}
562
563        basic_type_prefix_map = {'': 'Float', 'i': 'Int', 'u': 'UInt', 'b': 'Bool', 'v': 'Void'}
564
565        vec_re = re.compile(r'^([iub]?)vec([234]?)$')
566        vec_match = vec_re.match(glsl_header_type)
567        if vec_match:
568            type_obj['basic'] = basic_type_prefix_map[vec_match.group(1)]
569            if vec_match.group(2) == '':
570                # Type like "ivec" that represents either ivec2, ivec3 or ivec4
571                type_obj['genType'] = 'vec'
572            else:
573                # vec with specific size
574                type_obj['primarySize'] = int(vec_match.group(2))
575            return type_obj
576
577        mat_re = re.compile(r'^mat([234])(x([234]))?$')
578        mat_match = mat_re.match(glsl_header_type)
579        if mat_match:
580            type_obj['basic'] = 'Float'
581            if len(glsl_header_type) == 4:
582                mat_size = int(mat_match.group(1))
583                type_obj['primarySize'] = mat_size
584                type_obj['secondarySize'] = mat_size
585            else:
586                type_obj['primarySize'] = int(mat_match.group(1))
587                type_obj['secondarySize'] = int(mat_match.group(3))
588            return type_obj
589
590        gen_re = re.compile(r'^gen([IUB]?)Type$')
591        gen_match = gen_re.match(glsl_header_type)
592        if gen_match:
593            type_obj['basic'] = basic_type_prefix_map[gen_match.group(1).lower()]
594            type_obj['genType'] = 'yes'
595            return type_obj
596
597        if glsl_header_type.startswith('sampler'):
598            type_obj['basic'] = glsl_header_type[0].upper() + glsl_header_type[1:]
599            return type_obj
600
601        if glsl_header_type.startswith('gsampler') or glsl_header_type.startswith('gimage'):
602            type_obj['basic'] = glsl_header_type[1].upper() + glsl_header_type[2:]
603            type_obj['genType'] = 'sampler_or_image'
604            return type_obj
605
606        if glsl_header_type == 'gvec4':
607            return {'primarySize': 4, 'genType': 'sampler_or_image'}
608        if glsl_header_type == 'gvec3':
609            return {'primarySize': 3, 'genType': 'sampler_or_image'}
610
611        raise Exception('Unrecognized type: ' + str(glsl_header_type))
612
613
614class HashFunction:
615
616    def __init__(self, f1, f2, G):
617        self.f1 = f1
618        self.f2 = f2
619        self.G = G
620
621    def hash(self, key):
622        return (self.G[self.f1(key)] + self.G[self.f2(key)]) % len(self.G)
623
624
625def get_parsed_functions(functions_txt_filename):
626
627    def parse_function_parameters(parameters):
628        if parameters == '':
629            return []
630        parametersOut = []
631        parameters = parameters.split(', ')
632        for parameter in parameters:
633            parametersOut.append(TType(parameter.strip()))
634        return parametersOut
635
636    lines = []
637    with open(functions_txt_filename) as f:
638        lines = f.readlines()
639    lines = [
640        line.strip() for line in lines if line.strip() != '' and not line.strip().startswith('//')
641    ]
642
643    fun_re = re.compile(r'^(\w+) (\w+)\((.*)\);$')
644
645    parsed_functions = OrderedDict()
646    group_stack = []
647    default_metadata = {}
648
649    for line in lines:
650        fun_match = fun_re.match(line)
651        if line.startswith('GROUP BEGIN '):
652            group_rest = line[12:].strip()
653            group_parts = group_rest.split(' ', 1)
654            current_group = {'functions': [], 'name': group_parts[0], 'subgroups': {}}
655            if len(group_parts) > 1:
656                group_metadata = json.loads(group_parts[1])
657                current_group.update(group_metadata)
658            group_stack.append(current_group)
659        elif line.startswith('GROUP END '):
660            group_end_name = line[10:].strip()
661            current_group = group_stack[-1]
662            if current_group['name'] != group_end_name:
663                raise Exception('GROUP END: Unexpected function group name "' + group_end_name +
664                                '" was expecting "' + current_group['name'] + '"')
665            group_stack.pop()
666            is_top_level_group = (len(group_stack) == 0)
667            if is_top_level_group:
668                parsed_functions[current_group['name']] = current_group
669                default_metadata = {}
670            else:
671                super_group = group_stack[-1]
672                super_group['subgroups'][current_group['name']] = current_group
673        elif line.startswith('DEFAULT METADATA'):
674            line_rest = line[16:].strip()
675            default_metadata = json.loads(line_rest)
676        elif fun_match:
677            return_type = fun_match.group(1)
678            name = fun_match.group(2)
679            parameters = fun_match.group(3)
680            function_props = {
681                'name': name,
682                'returnType': TType(return_type),
683                'parameters': parse_function_parameters(parameters)
684            }
685            function_props.update(default_metadata)
686            group_stack[-1]['functions'].append(function_props)
687        else:
688            raise Exception('Unexpected function input line: ' + line)
689
690    return parsed_functions
691
692
693def mangledNameHash(str, hashfn, script_generated_hash_tests, save_test=True):
694    hash = hashfn.hash(str)
695    if save_test:
696        sanity_check = '    ASSERT_EQ(0x{hash}u, ImmutableString("{str}").mangledNameHash());'.format(
697            hash=('%08x' % hash), str=str)
698        script_generated_hash_tests.update({sanity_check: None})
699    return hash
700
701
702def get_function_names(group, mangled_names):
703    if 'functions' in group:
704        for function_props in group['functions']:
705            function_name = function_props['name']
706            mangled_names.append(function_name)
707            function_variants = gen_function_variants(function_name, function_props)
708            for function_props in function_variants:
709                parameters = get_parameters(function_props)
710                mangled_names.append(get_function_mangled_name(function_name, parameters))
711    if 'subgroups' in group:
712        for subgroup_name, subgroup in group['subgroups'].iteritems():
713            get_function_names(subgroup, mangled_names)
714
715
716def get_variable_names(group, mangled_names):
717    if 'variables' in group:
718        for variable_name, props in group['variables'].iteritems():
719            mangled_names.append(variable_name)
720    if 'subgroups' in group:
721        for subgroup_name, subgroup in group['subgroups'].iteritems():
722            get_variable_names(subgroup, mangled_names)
723
724
725def get_suffix(props):
726    if 'suffix' in props:
727        return props['suffix']
728    return ''
729
730
731def get_extension(props):
732    if 'extension' in props:
733        return props['extension']
734    return 'UNDEFINED'
735
736
737def get_op(name, function_props):
738    if 'op' not in function_props:
739        raise Exception('function op not defined')
740    if function_props['op'] == 'auto':
741        return name[0].upper() + name[1:]
742    return function_props['op']
743
744
745def get_known_to_not_have_side_effects(function_props):
746    if 'op' in function_props and function_props['op'] != 'CallBuiltInFunction':
747        if 'hasSideEffects' in function_props:
748            return 'false'
749        else:
750            for param in get_parameters(function_props):
751                if 'qualifier' in param.data and (param.data['qualifier'] == 'Out' or
752                                                  param.data['qualifier'] == 'InOut'):
753                    return 'false'
754            return 'true'
755    return 'false'
756
757
758def get_parameters(function_props):
759    if 'parameters' in function_props:
760        return function_props['parameters']
761    return []
762
763
764def get_function_mangled_name(function_name, parameters):
765    mangled_name = function_name + '('
766    for param in parameters:
767        mangled_name += param.get_mangled_name()
768    return mangled_name
769
770
771def get_function_human_readable_name(function_name, parameters):
772    name = function_name
773    for param in parameters:
774        name += '_' + param.get_human_readable_name()
775    return name
776
777
778def get_unique_identifier_name(function_name, parameters):
779    unique_name = function_name + '_'
780    for param in parameters:
781        unique_name += param.get_mangled_name()
782    return unique_name
783
784
785def get_variable_name_to_store_parameter(param):
786    unique_name = 'pt'
787    if 'qualifier' in param.data:
788        if param.data['qualifier'] == 'Out':
789            unique_name += '_o_'
790        if param.data['qualifier'] == 'InOut':
791            unique_name += '_io_'
792    unique_name += param.get_mangled_name()
793    return unique_name
794
795
796def get_variable_name_to_store_parameters(parameters):
797    if len(parameters) == 0:
798        return 'empty'
799    unique_name = 'p'
800    for param in parameters:
801        if 'qualifier' in param.data:
802            if param.data['qualifier'] == 'Out':
803                unique_name += '_o_'
804            if param.data['qualifier'] == 'InOut':
805                unique_name += '_io_'
806        unique_name += param.get_mangled_name()
807    return unique_name
808
809
810def define_constexpr_variable(template_args, variable_declarations):
811    template_variable_declaration = 'constexpr const TVariable kVar_{name_with_suffix}(BuiltInId::{name_with_suffix}, BuiltInName::{name}, SymbolType::BuiltIn, TExtension::{extension}, {type});'
812    variable_declarations.append(template_variable_declaration.format(**template_args))
813
814
815def gen_function_variants(function_name, function_props):
816    function_variants = []
817    parameters = get_parameters(function_props)
818    function_is_gen_type = False
819    gen_type = set()
820    for param in parameters:
821        if 'genType' in param.data:
822            if param.data['genType'] not in ['sampler_or_image', 'vec', 'yes']:
823                raise Exception('Unexpected value of genType "' + str(param.data['genType']) +
824                                '" should be "sampler_or_image", "vec", or "yes"')
825            gen_type.add(param.data['genType'])
826            if len(gen_type) > 1:
827                raise Exception('Unexpected multiple values of genType set on the same function: '
828                                + str(list(gen_type)))
829    if len(gen_type) == 0:
830        function_variants.append(function_props)
831        return function_variants
832
833    # If we have a gsampler_or_image then we're generating variants for float, int and uint
834    # samplers.
835    if 'sampler_or_image' in gen_type:
836        types = ['', 'I', 'U']
837        for type in types:
838            variant_props = function_props.copy()
839            variant_parameters = []
840            for param in parameters:
841                variant_parameters.append(param.specific_sampler_or_image_type(type))
842            variant_props['parameters'] = variant_parameters
843            variant_props['returnType'] = function_props[
844                'returnType'].specific_sampler_or_image_type(type)
845            function_variants.append(variant_props)
846        return function_variants
847
848    # If we have a normal gentype then we're generating variants for different sizes of vectors.
849    sizes = range(1, 5)
850    if 'vec' in gen_type:
851        sizes = range(2, 5)
852    for size in sizes:
853        variant_props = function_props.copy()
854        variant_parameters = []
855        for param in parameters:
856            variant_parameters.append(param.specific_type(size))
857        variant_props['parameters'] = variant_parameters
858        variant_props['returnType'] = function_props['returnType'].specific_type(size)
859        function_variants.append(variant_props)
860    return function_variants
861
862
863def process_single_function_group(
864        condition, group_name, group, parameter_declarations, name_declarations,
865        unmangled_function_if_statements, unmangled_builtin_declarations,
866        defined_function_variants, builtin_id_declarations, builtin_id_definitions,
867        defined_parameter_names, variable_declarations, function_declarations,
868        script_generated_hash_tests, get_builtin_if_statements):
869    global id_counter
870
871    if 'functions' not in group:
872        return
873
874    for function_props in group['functions']:
875        function_name = function_props['name']
876        level = function_props['level']
877        extension = get_extension(function_props)
878        template_args = {
879            'name': function_name,
880            'name_with_suffix': function_name + get_suffix(function_props),
881            'level': level,
882            'extension': extension,
883            'op': get_op(function_name, function_props),
884            'known_to_not_have_side_effects': get_known_to_not_have_side_effects(function_props)
885        }
886
887        function_variants = gen_function_variants(function_name, function_props)
888
889        template_name_declaration = 'constexpr const ImmutableString {name_with_suffix}("{name}");'
890        name_declaration = template_name_declaration.format(**template_args)
891        if not name_declaration in name_declarations:
892            name_declarations.add(name_declaration)
893
894        template_unmangled_if = """if (name == BuiltInName::{name_with_suffix})
895{{
896    return &UnmangledBuiltIns::{extension};
897}}"""
898        unmangled_if = template_unmangled_if.format(**template_args)
899        unmangled_builtin_no_condition = unmangled_function_if_statements.get(
900            level, 'NO_CONDITION', function_name)
901        if unmangled_builtin_no_condition != None and unmangled_builtin_no_condition[
902                'extension'] == 'UNDEFINED':
903            # We already have this unmangled name without a condition nor extension on the same level. No need to add a duplicate with a condition.
904            pass
905        elif (not unmangled_function_if_statements.has_key(
906                level, condition, function_name)) or extension == 'UNDEFINED':
907            # We don't have this unmangled builtin recorded yet or we might replace an unmangled builtin from an extension with one from core.
908            unmangled_function_if_statements.add_obj(level, condition, function_name, {
909                'hash_matched_code': unmangled_if,
910                'extension': extension
911            })
912            unmangled_builtin_declarations.add(
913                'constexpr const UnmangledBuiltIn {extension}(TExtension::{extension});'.format(
914                    **template_args))
915
916        for function_props in function_variants:
917            template_args['id'] = id_counter
918
919            parameters = get_parameters(function_props)
920
921            template_args['unique_name'] = get_unique_identifier_name(
922                template_args['name_with_suffix'], parameters)
923
924            if template_args['unique_name'] in defined_function_variants:
925                continue
926            defined_function_variants.add(template_args['unique_name'])
927
928            template_args['param_count'] = len(parameters)
929            template_args['return_type'] = function_props['returnType'].get_statictype_string()
930            template_args['mangled_name'] = get_function_mangled_name(function_name, parameters)
931            template_args['human_readable_name'] = get_function_human_readable_name(
932                template_args['name_with_suffix'], parameters)
933            template_args['mangled_name_length'] = len(template_args['mangled_name'])
934
935            template_builtin_id_declaration = '    static constexpr const TSymbolUniqueId {human_readable_name} = TSymbolUniqueId({id});'
936            builtin_id_declarations.append(template_builtin_id_declaration.format(**template_args))
937            template_builtin_id_definition = 'constexpr const TSymbolUniqueId BuiltInId::{human_readable_name};'
938            builtin_id_definitions.append(template_builtin_id_definition.format(**template_args))
939
940            parameters_list = []
941            for param in parameters:
942                unique_param_name = get_variable_name_to_store_parameter(param)
943                param_template_args = {
944                    'name': '_empty',
945                    'name_with_suffix': unique_param_name,
946                    'type': param.get_statictype_string(),
947                    'extension': 'UNDEFINED'
948                }
949                if unique_param_name not in defined_parameter_names:
950                    id_counter += 1
951                    param_template_args['id'] = id_counter
952                    template_builtin_id_declaration = '    static constexpr const TSymbolUniqueId {name_with_suffix} = TSymbolUniqueId({id});'
953                    builtin_id_declarations.append(
954                        template_builtin_id_declaration.format(**param_template_args))
955                    define_constexpr_variable(param_template_args, variable_declarations)
956                    defined_parameter_names.add(unique_param_name)
957                parameters_list.append(
958                    '&BuiltInVariable::kVar_{name_with_suffix}'.format(**param_template_args))
959
960            template_args['parameters_var_name'] = get_variable_name_to_store_parameters(
961                parameters)
962            if len(parameters) > 0:
963                template_args['parameters_list'] = ', '.join(parameters_list)
964                template_parameter_list_declaration = 'constexpr const TVariable *{parameters_var_name}[{param_count}] = {{ {parameters_list} }};'
965                parameter_declarations[template_args[
966                    'parameters_var_name']] = template_parameter_list_declaration.format(
967                        **template_args)
968            else:
969                template_parameter_list_declaration = 'constexpr const TVariable **{parameters_var_name} = nullptr;'
970                parameter_declarations[template_args[
971                    'parameters_var_name']] = template_parameter_list_declaration.format(
972                        **template_args)
973
974            template_function_declaration = 'constexpr const TFunction kFunction_{unique_name}(BuiltInId::{human_readable_name}, BuiltInName::{name_with_suffix}, TExtension::{extension}, BuiltInParameters::{parameters_var_name}, {param_count}, {return_type}, EOp{op}, {known_to_not_have_side_effects});'
975            function_declarations.append(template_function_declaration.format(**template_args))
976
977            template_mangled_name_declaration = 'constexpr const ImmutableString {unique_name}("{mangled_name}");'
978            name_declarations.add(template_mangled_name_declaration.format(**template_args))
979            template_mangled_if = """if (name == BuiltInName::{unique_name})
980{{
981    return &BuiltInFunction::kFunction_{unique_name};
982}}"""
983            mangled_if = template_mangled_if.format(**template_args)
984            get_builtin_if_statements.add_obj(level, condition, template_args['mangled_name'],
985                                              {'hash_matched_code': mangled_if})
986
987            id_counter += 1
988
989
990def process_function_group(group_name, group, parameter_declarations, name_declarations,
991                           unmangled_function_if_statements, unmangled_builtin_declarations,
992                           defined_function_variants, builtin_id_declarations,
993                           builtin_id_definitions, defined_parameter_names, variable_declarations,
994                           function_declarations, script_generated_hash_tests,
995                           get_builtin_if_statements, is_in_group_definitions):
996    global id_counter
997    first_id = id_counter
998
999    condition = 'NO_CONDITION'
1000    if 'condition' in group:
1001        condition = group['condition']
1002
1003    process_single_function_group(
1004        condition, group_name, group, parameter_declarations, name_declarations,
1005        unmangled_function_if_statements, unmangled_builtin_declarations,
1006        defined_function_variants, builtin_id_declarations, builtin_id_definitions,
1007        defined_parameter_names, variable_declarations, function_declarations,
1008        script_generated_hash_tests, get_builtin_if_statements)
1009
1010    if 'subgroups' in group:
1011        for subgroup_name, subgroup in group['subgroups'].iteritems():
1012            process_function_group(
1013                group_name + subgroup_name, subgroup, parameter_declarations, name_declarations,
1014                unmangled_function_if_statements, unmangled_builtin_declarations,
1015                defined_function_variants, builtin_id_declarations, builtin_id_definitions,
1016                defined_parameter_names, variable_declarations, function_declarations,
1017                script_generated_hash_tests, get_builtin_if_statements, is_in_group_definitions)
1018
1019    if 'queryFunction' in group:
1020        template_args = {'first_id': first_id, 'last_id': id_counter - 1, 'group_name': group_name}
1021        template_is_in_group_definition = """bool is{group_name}(const TFunction *func)
1022{{
1023    int id = func->uniqueId().get();
1024    return id >= {first_id} && id <= {last_id};
1025}}"""
1026        is_in_group_definitions.append(template_is_in_group_definition.format(**template_args))
1027
1028
1029def prune_parameters_arrays(parameter_declarations, function_declarations):
1030    # We can share parameters arrays between functions in case one array is a subarray of another.
1031    parameter_variable_name_replacements = {}
1032    used_param_variable_names = set()
1033    for param_variable_name, param_declaration in sorted(
1034            parameter_declarations.iteritems(), key=lambda item: -len(item[0])):
1035        replaced = False
1036        for used in used_param_variable_names:
1037            if used.startswith(param_variable_name):
1038                parameter_variable_name_replacements[param_variable_name] = used
1039                replaced = True
1040                break
1041        if not replaced:
1042            used_param_variable_names.add(param_variable_name)
1043
1044    for i in xrange(len(function_declarations)):
1045        for replaced, replacement in parameter_variable_name_replacements.iteritems():
1046            function_declarations[i] = function_declarations[i].replace(
1047                'BuiltInParameters::' + replaced + ',', 'BuiltInParameters::' + replacement + ',')
1048
1049    return [
1050        value for key, value in parameter_declarations.iteritems()
1051        if key in used_param_variable_names
1052    ]
1053
1054
1055def process_single_variable_group(condition, group_name, group, builtin_id_declarations,
1056                                  builtin_id_definitions, name_declarations, init_member_variables,
1057                                  get_variable_declarations, get_builtin_if_statements,
1058                                  declare_member_variables, variable_declarations,
1059                                  get_variable_definitions, variable_name_count):
1060    global id_counter
1061    if 'variables' not in group:
1062        return
1063    for variable_name, props in group['variables'].iteritems():
1064        level = props['level']
1065        template_args = {
1066            'id': id_counter,
1067            'name': variable_name,
1068            'name_with_suffix': variable_name + get_suffix(props),
1069            'level': props['level'],
1070            'extension': get_extension(props),
1071            'class': 'TVariable'
1072        }
1073
1074        template_builtin_id_declaration = '    static constexpr const TSymbolUniqueId {name_with_suffix} = TSymbolUniqueId({id});'
1075        builtin_id_declarations.append(template_builtin_id_declaration.format(**template_args))
1076        template_builtin_id_definition = 'constexpr const TSymbolUniqueId BuiltInId::{name_with_suffix};'
1077        builtin_id_definitions.append(template_builtin_id_definition.format(**template_args))
1078
1079        template_name_declaration = 'constexpr const ImmutableString {name}("{name}");'
1080        name_declarations.add(template_name_declaration.format(**template_args))
1081
1082        is_member = True
1083        template_init_variable = ''
1084
1085        if 'type' in props:
1086            if props['type']['basic'] != 'Bool' and 'precision' not in props['type']:
1087                raise Exception('Missing precision for variable ' + variable_name)
1088            template_args['type'] = TType(props['type']).get_statictype_string()
1089
1090        if 'fields' in props:
1091            # Handle struct and interface block definitions.
1092            template_args['class'] = props['class']
1093            template_args['fields'] = 'fields_{name_with_suffix}'.format(**template_args)
1094            init_member_variables.append(
1095                '    TFieldList *{fields} = new TFieldList();'.format(**template_args))
1096            for field_name, field_type in props['fields'].iteritems():
1097                template_args['field_name'] = field_name
1098                template_args['field_type'] = TType(field_type).get_dynamic_type_string()
1099                template_name_declaration = 'constexpr const ImmutableString {field_name}("{field_name}");'
1100                name_declarations.add(template_name_declaration.format(**template_args))
1101                template_add_field = '    {fields}->push_back(new TField({field_type}, BuiltInName::{field_name}, zeroSourceLoc, SymbolType::BuiltIn));'
1102                init_member_variables.append(template_add_field.format(**template_args))
1103            template_init_temp_variable = '    {class} *{name_with_suffix} = new {class}(BuiltInId::{name_with_suffix}, BuiltInName::{name}, TExtension::{extension}, {fields});'
1104            init_member_variables.append(template_init_temp_variable.format(**template_args))
1105            if 'private' in props and props['private']:
1106                is_member = False
1107            else:
1108                template_init_variable = '    mVar_{name_with_suffix} = {name_with_suffix};'
1109
1110        elif 'initDynamicType' in props:
1111            # Handle variables whose type can't be expressed as TStaticType
1112            # (type is a struct or has variable array size for example).
1113            template_args['type_name'] = 'type_{name_with_suffix}'.format(**template_args)
1114            template_args['type'] = template_args['type_name']
1115            template_args['initDynamicType'] = props['initDynamicType'].format(**template_args)
1116            template_init_variable = """    {initDynamicType}
1117    {type_name}->realize();
1118    mVar_{name_with_suffix} = new TVariable(BuiltInId::{name_with_suffix}, BuiltInName::{name}, SymbolType::BuiltIn, TExtension::{extension}, {type});"""
1119
1120        elif 'value' in props:
1121            # Handle variables with constant value, such as gl_MaxDrawBuffers.
1122            if props['value'] != 'resources':
1123                raise Exception('Unrecognized value source in variable properties: ' +
1124                                str(props['value']))
1125            resources_key = variable_name[3:]
1126            if 'valueKey' in props:
1127                resources_key = props['valueKey']
1128            template_args['value'] = 'resources.' + resources_key
1129            template_args['object_size'] = TType(props['type']).get_object_size()
1130            template_init_variable = """    mVar_{name_with_suffix} = new TVariable(BuiltInId::{name_with_suffix}, BuiltInName::{name}, SymbolType::BuiltIn, TExtension::{extension}, {type});
1131    {{
1132        TConstantUnion *unionArray = new TConstantUnion[{object_size}];
1133        unionArray[0].setIConst({value});
1134        mVar_{name_with_suffix}->shareConstPointer(unionArray);
1135    }}"""
1136            if template_args['object_size'] > 1:
1137                template_init_variable = """    mVar_{name_with_suffix} = new TVariable(BuiltInId::{name_with_suffix}, BuiltInName::{name}, SymbolType::BuiltIn, TExtension::{extension}, {type});
1138    {{
1139        TConstantUnion *unionArray = new TConstantUnion[{object_size}];
1140        for (size_t index = 0u; index < {object_size}; ++index)
1141        {{
1142            unionArray[index].setIConst({value}[index]);
1143        }}
1144        mVar_{name_with_suffix}->shareConstPointer(unionArray);
1145    }}"""
1146
1147        else:
1148            # Handle variables that can be stored as constexpr TVariable like
1149            # gl_Position, gl_FragColor etc.
1150            define_constexpr_variable(template_args, variable_declarations)
1151            is_member = False
1152
1153            template_get_variable_declaration = 'const TVariable *{name_with_suffix}();'
1154            get_variable_declarations.append(
1155                template_get_variable_declaration.format(**template_args))
1156
1157            template_get_variable_definition = """const TVariable *{name_with_suffix}()
1158{{
1159    return &kVar_{name_with_suffix};
1160}}
1161"""
1162            get_variable_definitions.append(
1163                template_get_variable_definition.format(**template_args))
1164
1165            if level != 'GLSL_BUILTINS':
1166                template_name_if = """if (name == BuiltInName::{name})
1167{{
1168    return &BuiltInVariable::kVar_{name_with_suffix};
1169}}"""
1170                name_if = template_name_if.format(**template_args)
1171                get_builtin_if_statements.add_obj(level, condition, template_args['name'],
1172                                                  {'hash_matched_code': name_if})
1173
1174        if is_member:
1175            get_condition = condition
1176            init_conditionally = (
1177                condition != 'NO_CONDITION' and variable_name_count[variable_name] == 1)
1178            if init_conditionally:
1179                # Instead of having the condition if statement at lookup, it's cheaper to have it at initialization time.
1180                init_member_variables.append(
1181                    '    if ({condition})\n    {{'.format(condition=condition))
1182                template_args[
1183                    'condition_comment'] = '\n    // Only initialized if {condition}'.format(
1184                        condition=condition)
1185                get_condition = 'NO_CONDITION'
1186            else:
1187                template_args['condition_comment'] = ''
1188            init_member_variables.append(template_init_variable.format(**template_args))
1189            if init_conditionally:
1190                init_member_variables.append('    }')
1191
1192            template_declare_member_variable = '{class} *mVar_{name_with_suffix} = nullptr;'
1193            declare_member_variables.append(
1194                template_declare_member_variable.format(**template_args))
1195
1196            if level != 'GLSL_BUILTINS':
1197                template_name_if = """if (name == BuiltInName::{name})
1198{{{condition_comment}
1199    return mVar_{name_with_suffix};
1200}}"""
1201                name_if = template_name_if.format(**template_args)
1202                get_builtin_if_statements.add_obj(level, get_condition, variable_name,
1203                                                  {'hash_matched_code': name_if})
1204
1205        id_counter += 1
1206
1207
1208def count_variable_names(group, variable_name_count):
1209    if 'variables' in group:
1210        for name in group['variables'].iterkeys():
1211            if name not in variable_name_count:
1212                variable_name_count[name] = 1
1213            else:
1214                variable_name_count[name] += 1
1215    if 'subgroups' in group:
1216        for subgroup_name, subgroup in group['subgroups'].iteritems():
1217            count_variable_names(subgroup, variable_name_count)
1218
1219
1220def process_variable_group(parent_condition, group_name, group, builtin_id_declarations,
1221                           builtin_id_definitions, name_declarations, init_member_variables,
1222                           get_variable_declarations, get_builtin_if_statements,
1223                           declare_member_variables, variable_declarations,
1224                           get_variable_definitions, variable_name_count):
1225    global id_counter
1226    condition = 'NO_CONDITION'
1227    if 'condition' in group:
1228        condition = group['condition']
1229
1230    if parent_condition != 'NO_CONDITION':
1231        if condition == 'NO_CONDITION':
1232            condition = parent_condition
1233        else:
1234            condition = '({cond1}) && ({cond2})'.format(cond1=parent_condition, cond2=condition)
1235
1236    process_single_variable_group(condition, group_name, group, builtin_id_declarations,
1237                                  builtin_id_definitions, name_declarations, init_member_variables,
1238                                  get_variable_declarations, get_builtin_if_statements,
1239                                  declare_member_variables, variable_declarations,
1240                                  get_variable_definitions, variable_name_count)
1241
1242    if 'subgroups' in group:
1243        for subgroup_name, subgroup in group['subgroups'].iteritems():
1244            process_variable_group(
1245                condition, subgroup_name, subgroup, builtin_id_declarations,
1246                builtin_id_definitions, name_declarations, init_member_variables,
1247                get_variable_declarations, get_builtin_if_statements, declare_member_variables,
1248                variable_declarations, get_variable_definitions, variable_name_count)
1249
1250
1251def main():
1252    random.seed(0)
1253    set_working_dir()
1254
1255    parser = argparse.ArgumentParser()
1256    parser.add_argument(
1257        '--dump-intermediate-json',
1258        help='Dump parsed function data as a JSON file builtin_functions.json',
1259        action="store_true")
1260    parser.add_argument('auto_script_command', nargs='?', default='')
1261    args = parser.parse_args()
1262
1263    test_filename = '../../tests/compiler_tests/ImmutableString_test_autogen.cpp'
1264    variables_json_filename = 'builtin_variables.json'
1265    functions_txt_filename = 'builtin_function_declarations.txt'
1266
1267    # auto_script parameters.
1268    if args.auto_script_command != '':
1269        inputs = [
1270            functions_txt_filename,
1271            variables_json_filename,
1272        ]
1273        outputs = [
1274            'ImmutableString_autogen.cpp',
1275            'ParseContext_autogen.h',
1276            'SymbolTable_autogen.cpp',
1277            'SymbolTable_autogen.h',
1278            'tree_util/BuiltIn_autogen.h',
1279            test_filename,
1280        ]
1281
1282        if args.auto_script_command == 'inputs':
1283            print ','.join(inputs)
1284        elif args.auto_script_command == 'outputs':
1285            print ','.join(outputs)
1286        else:
1287            print('Invalid script parameters')
1288            return 1
1289        return 0
1290
1291    # Declarations of symbol unique ids
1292    builtin_id_declarations = []
1293
1294    # Definitions of symbol unique ids needed for those ids used outside of constexpr expressions.
1295    builtin_id_definitions = []
1296
1297    # Declarations of name string variables
1298    name_declarations = set()
1299
1300    # Declarations of builtin TVariables
1301    variable_declarations = []
1302
1303    # Declarations of builtin TFunctions
1304    function_declarations = []
1305
1306    # Functions for querying the pointer to a specific TVariable.
1307    get_variable_declarations = []
1308    get_variable_definitions = []
1309
1310    # Code for defining TVariables stored as members of TSymbolTable.
1311    declare_member_variables = []
1312    init_member_variables = []
1313
1314    # Code for querying builtins.
1315    get_builtin_if_statements = GroupedList()
1316
1317    # Declarations of UnmangledBuiltIn objects
1318    unmangled_builtin_declarations = set()
1319
1320    # Code for querying builtin function unmangled names.
1321    unmangled_function_if_statements = GroupedList()
1322
1323    # Code for testing that script-generated hashes match with runtime computed hashes.
1324    script_generated_hash_tests = OrderedDict()
1325
1326    # Functions for testing whether a builtin belongs in group.
1327    is_in_group_definitions = []
1328
1329    # Counts of variables with a certain name string:
1330    variable_name_count = {}
1331
1332    # Declarations of parameter arrays for builtin TFunctions. Map from C++ variable name to the full
1333    # declaration.
1334    parameter_declarations = {}
1335
1336    defined_function_variants = set()
1337    defined_parameter_names = set()
1338
1339    parsed_functions = get_parsed_functions(functions_txt_filename)
1340
1341    if args.dump_intermediate_json:
1342        with open('builtin_functions.json', 'w') as outfile:
1343
1344            def serialize_obj(obj):
1345                if isinstance(obj, TType):
1346                    return obj.data
1347                else:
1348                    raise "Cannot serialize to JSON: " + str(obj)
1349
1350            json.dump(
1351                parsed_functions, outfile, indent=4, separators=(',', ': '), default=serialize_obj)
1352
1353    with open(variables_json_filename) as f:
1354        parsed_variables = json.load(f, object_pairs_hook=OrderedDict)
1355
1356    # This script uses a perfect hash function to avoid dealing with collisions
1357    names = []
1358    for group_name, group in parsed_functions.iteritems():
1359        get_function_names(group, names)
1360    for group_name, group in parsed_variables.iteritems():
1361        get_variable_names(group, names)
1362    # Remove duplicates
1363    names = list(dict.fromkeys(names))
1364    names_dict = dict(zip(names, range(1, len(names) + 1)))
1365    # Generate the perfect hash function
1366    f1, f2, G = generate_hash(names_dict, Hash2)
1367    hashfn = HashFunction(f1, f2, G)
1368    S1 = f1.salt
1369    S2 = f2.salt
1370
1371    for group_name, group in parsed_functions.iteritems():
1372        process_function_group(
1373            group_name, group, parameter_declarations, name_declarations,
1374            unmangled_function_if_statements, unmangled_builtin_declarations,
1375            defined_function_variants, builtin_id_declarations, builtin_id_definitions,
1376            defined_parameter_names, variable_declarations, function_declarations,
1377            script_generated_hash_tests, get_builtin_if_statements, is_in_group_definitions)
1378
1379    parameter_declarations = prune_parameters_arrays(parameter_declarations, function_declarations)
1380
1381    for group_name, group in parsed_variables.iteritems():
1382        count_variable_names(group, variable_name_count)
1383
1384    for group_name, group in parsed_variables.iteritems():
1385        process_variable_group('NO_CONDITION', group_name, group, builtin_id_declarations,
1386                               builtin_id_definitions, name_declarations, init_member_variables,
1387                               get_variable_declarations, get_builtin_if_statements,
1388                               declare_member_variables, variable_declarations,
1389                               get_variable_definitions, variable_name_count)
1390
1391    output_strings = {
1392        'script_name':
1393            os.path.basename(__file__),
1394        'copyright_year':
1395            date.today().year,
1396        'builtin_id_declarations':
1397            '\n'.join(builtin_id_declarations),
1398        'builtin_id_definitions':
1399            '\n'.join(builtin_id_definitions),
1400        'last_builtin_id':
1401            id_counter - 1,
1402        'name_declarations':
1403            '\n'.join(sorted(list(name_declarations))),
1404        'function_data_source_name':
1405            functions_txt_filename,
1406        'function_declarations':
1407            '\n'.join(function_declarations),
1408        'parameter_declarations':
1409            '\n'.join(sorted(parameter_declarations)),
1410        'is_in_group_definitions':
1411            '\n'.join(is_in_group_definitions),
1412        'variable_data_source_name':
1413            variables_json_filename,
1414        'variable_declarations':
1415            '\n'.join(sorted(variable_declarations)),
1416        'get_variable_declarations':
1417            '\n'.join(sorted(get_variable_declarations)),
1418        'get_variable_definitions':
1419            '\n'.join(sorted(get_variable_definitions)),
1420        'unmangled_builtin_declarations':
1421            '\n'.join(sorted(unmangled_builtin_declarations)),
1422        'declare_member_variables':
1423            '\n'.join(declare_member_variables),
1424        'init_member_variables':
1425            '\n'.join(init_member_variables),
1426        'get_unmangled_builtin':
1427            unmangled_function_if_statements.get_switch_code(hashfn, script_generated_hash_tests),
1428        'get_builtin':
1429            get_builtin_if_statements.get_switch_code(hashfn, script_generated_hash_tests),
1430        'max_unmangled_name_length':
1431            unmangled_function_if_statements.get_max_name_length(),
1432        'max_mangled_name_length':
1433            get_builtin_if_statements.get_max_name_length(),
1434        'script_generated_hash_tests':
1435            '\n'.join(script_generated_hash_tests.iterkeys()),
1436        'S1':
1437            str(S1).replace('[', ' ').replace(']', ' '),
1438        'S2':
1439            str(S2).replace('[', ' ').replace(']', ' '),
1440        'G':
1441            str(G).replace('[', ' ').replace(']', ' '),
1442        'NG':
1443            len(G),
1444        'NS':
1445            len(S1)
1446    }
1447
1448    with open('ImmutableString_autogen.cpp', 'wt') as outfile_cpp:
1449        output_cpp = template_immutablestring_cpp.format(**output_strings)
1450        outfile_cpp.write(output_cpp)
1451
1452    with open(test_filename, 'wt') as outfile_cpp:
1453        output_cpp = template_immutablestringtest_cpp.format(**output_strings)
1454        outfile_cpp.write(output_cpp)
1455
1456    with open('tree_util/BuiltIn_autogen.h', 'wt') as outfile_header:
1457        output_header = template_builtin_header.format(**output_strings)
1458        outfile_header.write(output_header)
1459
1460    with open('SymbolTable_autogen.cpp', 'wt') as outfile_cpp:
1461        output_cpp = template_symboltable_cpp.format(**output_strings)
1462        outfile_cpp.write(output_cpp)
1463
1464    with open('ParseContext_autogen.h', 'wt') as outfile_header:
1465        output_header = template_parsecontext_header.format(**output_strings)
1466        outfile_header.write(output_header)
1467
1468    with open('SymbolTable_autogen.h', 'wt') as outfile_h:
1469        output_h = template_symboltable_h.format(**output_strings)
1470        outfile_h.write(output_h)
1471
1472    return 0
1473
1474
1475if __name__ == '__main__':
1476    sys.exit(main())
1477