mirror of
https://github.com/github/codeql.git
synced 2026-04-11 01:54:00 +02:00
138 lines
4.9 KiB
Python
138 lines
4.9 KiB
Python
import sys
|
|
import os.path
|
|
import glob
|
|
import re
|
|
import json
|
|
|
|
BEGIN_TEMPLATE = re.compile(r"^/\*template\s*$")
|
|
END_TEMPLATE = re.compile(r"^\*/\s*$")
|
|
|
|
def expand_template_params(args, param_arg_map):
|
|
'''Given a list of template arguments that may reference template parameters
|
|
of the current template, return a new list of template arguments with each
|
|
parameter use replaced with the appropriate fully-qualified argument for
|
|
that parameter.'''
|
|
result = []
|
|
for arg in args:
|
|
if arg in param_arg_map:
|
|
result.append(param_arg_map[arg])
|
|
else:
|
|
result.append(arg)
|
|
|
|
return result
|
|
|
|
def find_instantiation(module, args, templates):
|
|
'''Given a template module and a set of template arguments, find the module
|
|
name of the instantiation of that module with those arguments.'''
|
|
template = templates[module]
|
|
for instantiation in template["template_def"]["instantiations"]:
|
|
if instantiation["args"] == args:
|
|
return instantiation["name"]
|
|
return None
|
|
|
|
def instantiate_template(template, instantiation, root, templates):
|
|
'''Create a single instantiation of a template.'''
|
|
template_def = template["template_def"]
|
|
output_components = instantiation["name"].split(".")
|
|
output_path = root
|
|
for component in output_components:
|
|
output_path = os.path.join(output_path, component)
|
|
output_path = output_path + ".qll"
|
|
with open(output_path, "w") as output:
|
|
output.write(
|
|
"""
|
|
/*
|
|
* THIS FILE IS AUTOMATICALLY GENERATED FROM '%s'.
|
|
* DO NOT EDIT MANUALLY.
|
|
*/
|
|
|
|
""" % (template["name"].replace(".", "/") + ".qllt")
|
|
)
|
|
param_arg_map = {}
|
|
for param_index in range(len(template_def["params"])):
|
|
param = template_def["params"][param_index]
|
|
arg = instantiation["args"][param_index]
|
|
output.write("private import %s as %s // Template parameter\n" % (arg, param))
|
|
param_arg_map[param] = arg
|
|
for import_record in template_def["imports"]:
|
|
if "access" in import_record:
|
|
output.write(import_record["access"] + " ")
|
|
imported_module = find_instantiation(import_record["module"],
|
|
expand_template_params(import_record["args"], param_arg_map), templates)
|
|
output.write("import %s // %s<%s>\n" %
|
|
(
|
|
imported_module,
|
|
import_record["module"],
|
|
", ".join(import_record["args"])
|
|
)
|
|
)
|
|
|
|
output.writelines(template_def["body_lines"])
|
|
|
|
def generate_instantiations(template, root, templates):
|
|
'''Create a .qll source file for each instantiation of the specified template.'''
|
|
template_def = template["template_def"]
|
|
if "instantiations" in template_def:
|
|
for instantiation in template_def["instantiations"]:
|
|
instantiate_template(template, instantiation, root, templates)
|
|
|
|
def read_template(template_path, module_name):
|
|
'''Read a .qllt template file from template_path, using module_name as the
|
|
fully qualified name of the module.'''
|
|
with open(template_path) as input:
|
|
in_template = False
|
|
template_text = ""
|
|
template_def = None
|
|
body_lines = []
|
|
for line in iter(input):
|
|
if in_template:
|
|
if END_TEMPLATE.match(line):
|
|
template_def = json.loads(template_text)
|
|
in_template = False
|
|
else:
|
|
template_text += line
|
|
else:
|
|
if BEGIN_TEMPLATE.match(line) and not template_def:
|
|
in_template = True
|
|
else:
|
|
body_lines.append(line)
|
|
|
|
if template_def:
|
|
template_def["body_lines"] = body_lines
|
|
|
|
result = { "name": module_name }
|
|
if template_def:
|
|
result["template_def"] = template_def
|
|
return result
|
|
|
|
def module_name_from_path_impl(path):
|
|
(head, tail) = os.path.split(path)
|
|
if head == "":
|
|
return tail
|
|
else:
|
|
return module_name_from_path(head) + "." + tail
|
|
|
|
def module_name_from_path(path):
|
|
'''Compute the fully qualified name of a module from the path of its .qll[t]
|
|
file. The path should be relative to the library root.'''
|
|
(module_root, ext) = os.path.splitext(path)
|
|
return module_name_from_path_impl(module_root)
|
|
|
|
def main():
|
|
templates = {}
|
|
|
|
root = sys.argv[1]
|
|
for template_path in glob.glob(os.path.join(root, "**\\*.qllt"), recursive = True):
|
|
print(template_path)
|
|
module_name = module_name_from_path(os.path.relpath(template_path, root))
|
|
print(module_name)
|
|
template = read_template(template_path, module_name)
|
|
templates[template["name"]] = template
|
|
|
|
for name, template in templates.items():
|
|
if "template_def" in template:
|
|
generate_instantiations(template, root, templates)
|
|
|
|
if __name__ == "__main__":
|
|
main()
|