#!/usr/bin/python3 # Copyright 2019 The ANGLE Project Authors. All rights reserved. # Use of this source code is governed by a BSD-style license that can be # found in the LICENSE file. # # generate_parser_tools.py: # Common functionality to call flex and bison to generate lexer and parser of # the translator and preprocessor. import os import platform import subprocess import sys is_linux = platform.system() == 'Linux' is_windows = platform.system() == 'Windows' def get_tool_path_platform(tool_name, platform): exe_path = os.path.join(sys.path[0], '..', '..', '..', 'tools', 'flex-bison', platform) return os.path.join(exe_path, tool_name) def get_tool_path(tool_name): if is_linux: platform = 'linux' ext = '' else: assert (is_windows) platform = 'windows' ext = '.exe' return get_tool_path_platform(tool_name + ext, platform) def get_tool_file_sha1s(): files = [ get_tool_path_platform('flex', 'linux'), get_tool_path_platform('bison', 'linux'), get_tool_path_platform('flex.exe', 'windows'), get_tool_path_platform('bison.exe', 'windows'), get_tool_path_platform('m4.exe', 'windows') ] files += [ get_tool_path_platform(dll, 'windows') for dll in ['msys-2.0.dll', 'msys-iconv-2.dll', 'msys-intl-8.dll'] ] return [f + '.sha1' for f in files] def run_flex(basename): flex = get_tool_path('flex') input_file = basename + '.l' output_source = basename + '_lex_autogen.cpp' flex_args = [flex, '--noline', '--nounistd', '--outfile=' + output_source, input_file] flex_env = os.environ.copy() if is_windows: flex_env['M4'] = get_tool_path_platform('m4.exe', 'windows') process = subprocess.Popen(flex_args, env=flex_env, cwd=sys.path[0]) process.communicate() if process.returncode != 0: return process.returncode # Patch flex output for 64-bit. The patch is simple enough that we could do a string # replacement. More importantly, the location of the line of code that needs to be substituted # can vary based on flex version, and the string substitution will find the correct place # automatically. patch_in = """\n\t\tYY_INPUT( (&YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[number_to_move]),\n\t\t\tyyg->yy_n_chars, num_to_read );""" patch_out = """ yy_size_t ret = 0; YY_INPUT( (&YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[number_to_move]), ret, num_to_read ); yyg->yy_n_chars = static_cast(ret);""" with open(output_source, 'r') as flex_output: output = flex_output.read() # If flex's output changes such that this line no longer exists, the patch needs to be # updated, or possibly removed. assert (output.find(patch_in) != -1) patched = output.replace(patch_in, patch_out) # Remove all tab characters from output. WebKit does not allow any tab characters in source # files. patched = patched.replace('\t', ' ') with open(output_source, 'w') as flex_output_patched: flex_output_patched.write(patched) return 0 def run_bison(basename, generate_header): bison = get_tool_path('bison') input_file = basename + '.y' output_header = basename + '_tab_autogen.h' output_source = basename + '_tab_autogen.cpp' bison_args = [bison, '--no-lines', '--skeleton=yacc.c'] if generate_header: bison_args += ['--defines=' + output_header] bison_args += ['--output=' + output_source, input_file] bison_env = os.environ.copy() bison_env['BISON_PKGDATADIR'] = get_tool_path_platform('', 'third_party') if is_windows: bison_env['M4'] = get_tool_path_platform('m4.exe', 'windows') process = subprocess.Popen(bison_args, env=bison_env, cwd=sys.path[0]) process.communicate() return process.returncode def get_input_files(basename): files = [basename + '.l', basename + '.y'] return [os.path.join(sys.path[0], f) for f in files] def get_output_files(basename, generate_header): optional_header = [basename + '_tab_autogen.h'] if generate_header else [] files = [basename + '_lex_autogen.cpp', basename + '_tab_autogen.cpp'] + optional_header return [os.path.join(sys.path[0], f) for f in files] def generate_parser(basename, generate_header): # Handle inputs/outputs for run_code_generation.py's auto_script if len(sys.argv) > 1: if sys.argv[1] == 'inputs': inputs = get_tool_file_sha1s() inputs += get_input_files(basename) current_file = __file__ if current_file.endswith('.pyc'): current_file = current_file[:-1] inputs += [current_file] print(','.join(inputs)) if sys.argv[1] == 'outputs': print(','.join(get_output_files(basename, generate_header))) return 0 # Call flex and bison to generate the lexer and parser. flex_result = run_flex(basename) if flex_result != 0: print('Failed to run flex. Error %s' % str(flex_result)) return 1 bison_result = run_bison(basename, generate_header) if bison_result != 0: print('Failed to run bison. Error %s' % str(bison_result)) return 2 return 0