1#!/usr/local/bin/python 2# 3# 4# Licensed to the Apache Software Foundation (ASF) under one 5# or more contributor license agreements. See the NOTICE file 6# distributed with this work for additional information 7# regarding copyright ownership. The ASF licenses this file 8# to you under the Apache License, Version 2.0 (the 9# "License"); you may not use this file except in compliance 10# with the License. You may obtain a copy of the License at 11# 12# http://www.apache.org/licenses/LICENSE-2.0 13# 14# Unless required by applicable law or agreed to in writing, 15# software distributed under the License is distributed on an 16# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 17# KIND, either express or implied. See the License for the 18# specific language governing permissions and limitations 19# under the License. 20# 21# 22# 23# Find places in our code that are part of control statements 24# i.e. "for", "if" and "while". That output is then easily 25# searched for various interesting / complex pattern. 26# 27# 28# USAGE: find-control-statements.py FILE1 FILE2 ... 29# 30 31import sys 32 33header_shown = False 34last_line_num = None 35 36def print_line(fname, line_num, line): 37 """ Print LINE of number LINE_NUM in file FNAME. 38 Show FNAME only once per file and LINE_NUM only for 39 non-consecutive lines. 40 """ 41 global header_shown 42 global last_line_num 43 44 if not header_shown: 45 print('') 46 print(fname) 47 header_shown = True 48 49 if last_line_num and (last_line_num + 1 == line_num): 50 print(" %s" % line), 51 else: 52 print('%5d:%s' % (line_num, line)), 53 54 last_line_num = line_num 55 56def is_control(line, index, word): 57 """ Return whether LINE[INDEX] is actual the start position of 58 control statement WORD. It must be followed by an opening 59 parantheses and only whitespace in between WORD and the '('. 60 """ 61 if index > 0: 62 if not (line[index-1] in [' ', '\t', ';']): 63 return False 64 65 index = index + len(word) 66 parantheses_index = line.find('(', index) 67 if parantheses_index == -1: 68 return False 69 70 while index < parantheses_index: 71 if not (line[index] in [' ', '\t',]): 72 return False 73 74 index += 1 75 76 return True 77 78def find_specific_control(line, control): 79 """ Return the first offset of the control statement CONTROL 80 in LINE, or -1 if it can't be found. 81 """ 82 current = 0 83 84 while current != -1: 85 index = line.find(control, current) 86 if index == -1: 87 break 88 89 if is_control(line, index, control): 90 return index 91 92 current = index + len(control); 93 94 return -1 95 96def find_control(line): 97 """ Return the offset of the first control in LINE or -1 98 if there is none. 99 """ 100 current = 0 101 102 for_index = find_specific_control(line, "for") 103 if_index = find_specific_control(line, "if") 104 while_index = find_specific_control(line, "while") 105 106 first = len(line) 107 if for_index >= 0 and first > for_index: 108 first = for_index 109 if if_index >= 0 and first > if_index: 110 first = if_index 111 if while_index >= 0 and first > while_index: 112 first = while_index 113 114 if first == len(line): 115 return -1 116 return first 117 118def parantheses_delta(line): 119 """ Return the number of opening minus the number of closing 120 parantheses in LINE. Don't count those inside strings or chars. 121 """ 122 escaped = False 123 in_squote = False 124 in_dquote = False 125 126 delta = 0 127 128 for c in line: 129 if escaped: 130 escaped = False 131 132 elif in_dquote: 133 if c == '\\': 134 escaped = True 135 elif c == '"': 136 in_dquote = False 137 138 elif in_squote: 139 if c == '\\': 140 escaped = True 141 elif c == "'": 142 in_squote = False 143 144 elif c == '(': 145 delta += 1 146 elif c == ')': 147 delta -= 1 148 elif c == '"': 149 in_dquote = True 150 elif c == "'": 151 in_squote -= True 152 153 return delta 154 155def scan_file(fname): 156 lines = open(fname).readlines() 157 158 line_num = 1 159 parantheses_level = 0 160 161 for line in lines: 162 163 if parantheses_level > 0: 164 index = 0 165 else: 166 index = find_control(line) 167 168 if index >= 0: 169 print_line(fname, line_num, line) 170 parantheses_level += parantheses_delta(line[index:]) 171 172 line_num += 1 173 174if __name__ == '__main__': 175 for fname in sys.argv[1:]: 176 header_shown = False 177 last_line_num = None 178 scan_file(fname) 179