| OLD | NEW |
| 1 #!/usr/bin/python | 1 #!/usr/bin/python |
| 2 # | 2 # |
| 3 # Copyright (c) 2011 The Chromium Authors. All rights reserved. | 3 # Copyright (c) 2011 The Chromium Authors. All rights reserved. |
| 4 # Use of this source code is governed by a BSD-style license that can be | 4 # Use of this source code is governed by a BSD-style license that can be |
| 5 # found in the LICENSE file. | 5 # found in the LICENSE file. |
| 6 | 6 |
| 7 """ Lexer for PPAPI IDL """ | 7 """ Lexer for PPAPI IDL """ |
| 8 | 8 |
| 9 # | 9 # |
| 10 # IDL Lexer | 10 # IDL Lexer |
| (...skipping 31 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 42 # IDL Lexer | 42 # IDL Lexer |
| 43 # | 43 # |
| 44 class IDLLexer(object): | 44 class IDLLexer(object): |
| 45 # 'tokens' is a value required by lex which specifies the complete list | 45 # 'tokens' is a value required by lex which specifies the complete list |
| 46 # of valid token types. | 46 # of valid token types. |
| 47 tokens = [ | 47 tokens = [ |
| 48 # Symbol and keywords types | 48 # Symbol and keywords types |
| 49 'COMMENT', | 49 'COMMENT', |
| 50 'DESCRIBE', | 50 'DESCRIBE', |
| 51 'ENUM', | 51 'ENUM', |
| 52 'LABEL', |
| 52 'SYMBOL', | 53 'SYMBOL', |
| 54 'INLINE', |
| 53 'INTERFACE', | 55 'INTERFACE', |
| 54 'STRUCT', | 56 'STRUCT', |
| 55 'TYPEDEF', | 57 'TYPEDEF', |
| 56 | 58 |
| 57 # Data types | 59 # Data types |
| 58 'FLOAT', | 60 'FLOAT', |
| 59 'OCT', | 61 'OCT', |
| 60 'INT', | 62 'INT', |
| 61 'HEX', | 63 'HEX', |
| 62 'STRING', | 64 'STRING', |
| 63 | 65 |
| 64 # Operators | 66 # Operators |
| 65 'LSHIFT' | 67 'LSHIFT' |
| 66 ] | 68 ] |
| 67 | 69 |
| 68 # 'keywords' is a map of string to token type. All SYMBOL tokens are | 70 # 'keywords' is a map of string to token type. All SYMBOL tokens are |
| 69 # matched against keywords, to determine if the token is actually a keyword. | 71 # matched against keywords, to determine if the token is actually a keyword. |
| 70 keywords = { | 72 keywords = { |
| 73 'attribute' : 'ATTRIBUTE', |
| 71 'describe' : 'DESCRIBE', | 74 'describe' : 'DESCRIBE', |
| 72 'enum' : 'ENUM', | 75 'enum' : 'ENUM', |
| 76 'label' : 'LABEL', |
| 73 'interface' : 'INTERFACE', | 77 'interface' : 'INTERFACE', |
| 74 'readonly' : 'READONLY', | 78 'readonly' : 'READONLY', |
| 75 'struct' : 'STRUCT', | 79 'struct' : 'STRUCT', |
| 76 'typedef' : 'TYPEDEF', | 80 'typedef' : 'TYPEDEF', |
| 77 } | 81 } |
| 78 | 82 |
| 79 # 'literals' is a value expected by lex which specifies a list of valid | 83 # 'literals' is a value expected by lex which specifies a list of valid |
| 80 # literal tokens, meaning the token type and token value are identical. | 84 # literal tokens, meaning the token type and token value are identical. |
| 81 literals = '"*.(){}[],;:=+-' | 85 literals = '"*.(){}[],;:=+-' |
| 82 | 86 |
| (...skipping 24 matching lines...) Expand all Loading... |
| 107 def t_STRING(self, t): | 111 def t_STRING(self, t): |
| 108 r'"[^"]*"' | 112 r'"[^"]*"' |
| 109 t.value = t.value[1:-1] | 113 t.value = t.value[1:-1] |
| 110 self.AddLines(t.value.count('\n')) | 114 self.AddLines(t.value.count('\n')) |
| 111 return t | 115 return t |
| 112 | 116 |
| 113 # A C or C++ style comment: /* xxx */ or // | 117 # A C or C++ style comment: /* xxx */ or // |
| 114 def t_COMMENT(self, t): | 118 def t_COMMENT(self, t): |
| 115 r'(/\*(.|\n)*?\*/)|(//.*)' | 119 r'(/\*(.|\n)*?\*/)|(//.*)' |
| 116 self.AddLines(t.value.count('\n')) | 120 self.AddLines(t.value.count('\n')) |
| 121 return t |
| 117 | 122 |
| 118 # C++ comments should keep the newline | 123 # Return a "preprocessor" inline block |
| 119 if t.value[:2] == '//': t.value += '\n' | 124 def t_INLINE(self, t): |
| 125 r'\#inline (.|\n)*\#endinl.*' |
| 126 self.AddLines(t.value.count('\n')) |
| 120 return t | 127 return t |
| 121 | 128 |
| 122 # A symbol or keyword. | 129 # A symbol or keyword. |
| 123 def t_KEYWORD_SYMBOL(self, t): | 130 def t_KEYWORD_SYMBOL(self, t): |
| 124 r'[A-Za-z][A-Za-z_0-9]*' | 131 r'[A-Za-z][A-Za-z_0-9]*' |
| 125 | 132 |
| 126 #All non-keywords are assumed to be symbols | 133 #All non-keywords are assumed to be symbols |
| 127 t.type = self.keywords.get(t.value, 'SYMBOL') | 134 t.type = self.keywords.get(t.value, 'SYMBOL') |
| 128 return t | 135 return t |
| 129 | 136 |
| 130 def t_ANY_error(self, t): | 137 def t_ANY_error(self, t): |
| 138 msg = "Unrecognized input" |
| 131 line = self.lexobj.lineno | 139 line = self.lexobj.lineno |
| 140 |
| 141 # If that line has not been accounted for, then we must have hit |
| 142 # EoF, so compute the beginning of the line that caused the problem. |
| 143 if line >= len(self.index): |
| 144 # Find the offset in the line of the first word causing the issue |
| 145 word = t.value.split()[0] |
| 146 offs = self.lines[line - 1].find(word) |
| 147 # Add the computed line's starting position |
| 148 self.index.append(self.lexobj.lexpos - offs) |
| 149 msg = "Unexpected EoF reached after" |
| 150 |
| 132 pos = self.lexobj.lexpos - self.index[line] | 151 pos = self.lexobj.lexpos - self.index[line] |
| 133 file = self.lexobj.filename | 152 file = self.lexobj.filename |
| 134 out = self.ErrorMessage(file, line, pos, "Unrecognized input") | 153 out = self.ErrorMessage(file, line, pos, msg) |
| 135 sys.stderr.write(out + '\n') | 154 sys.stderr.write(out + '\n') |
| 136 | 155 |
| 137 def AddLines(self, count): | 156 def AddLines(self, count): |
| 138 # Set the lexer position for the beginning of the next line. In the case | 157 # Set the lexer position for the beginning of the next line. In the case |
| 139 # of multiple lines, tokens can not exist on any of the lines except the | 158 # of multiple lines, tokens can not exist on any of the lines except the |
| 140 # last one, so the recorded value for previous lines are unused. We still | 159 # last one, so the recorded value for previous lines are unused. We still |
| 141 # fill the array however, to make sure the line count is correct. | 160 # fill the array however, to make sure the line count is correct. |
| 142 self.lexobj.lineno += count | 161 self.lexobj.lineno += count |
| 143 for i in range(count): | 162 for i in range(count): |
| 144 self.index.append(self.lexobj.lexpos) | 163 self.index.append(self.lexobj.lexpos) |
| (...skipping 37 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 182 for filename in filenames: | 201 for filename in filenames: |
| 183 data = open(filename).read() | 202 data = open(filename).read() |
| 184 lexer.SetData(filename, data) | 203 lexer.SetData(filename, data) |
| 185 if verbose: sys.stdout.write(' Loaded %s...\n' % filename) | 204 if verbose: sys.stdout.write(' Loaded %s...\n' % filename) |
| 186 while 1: | 205 while 1: |
| 187 t = lexer.lexobj.token() | 206 t = lexer.lexobj.token() |
| 188 if t is None: break | 207 if t is None: break |
| 189 outlist.append(t) | 208 outlist.append(t) |
| 190 return outlist | 209 return outlist |
| 191 | 210 |
| 211 |
| 212 def TokensFromText(text): |
| 213 lexer = IDLLexer() |
| 214 lexer.SetData('unknown', text) |
| 215 outlist = [] |
| 216 while 1: |
| 217 t = lexer.lexobj.token() |
| 218 if t is None: break |
| 219 outlist.append(t.value) |
| 220 return outlist |
| 221 |
| 192 # | 222 # |
| 193 # TextToTokens | 223 # TextToTokens |
| 194 # | 224 # |
| 195 # From a block of text, generate a list of tokens | 225 # From a block of text, generate a list of tokens |
| 196 # | 226 # |
| 197 def TextToTokens(source): | 227 def TextToTokens(source): |
| 198 lexer = IDLLexer() | 228 lexer = IDLLexer() |
| 199 outlist = [] | 229 outlist = [] |
| 200 lexer.SetData('AUTO', source) | 230 lexer.SetData('AUTO', source) |
| 201 while 1: | 231 while 1: |
| 202 t = lexer.lexobj.token() | 232 t = lexer.lexobj.token() |
| 203 if t is None: break | 233 if t is None: break |
| 204 outlist.append(t.value) | 234 outlist.append(t.value) |
| 205 return outlist | 235 return outlist |
| 206 | 236 |
| 207 | 237 |
| 208 # | 238 # |
| 209 # TestSame | 239 # TestSame |
| 210 # | 240 # |
| 211 # From a set of token values, generate a new source text by joining with a | 241 # From a set of token values, generate a new source text by joining with a |
| 212 # single space. The new source is then tokenized and compared against the | 242 # single space. The new source is then tokenized and compared against the |
| 213 # old set. | 243 # old set. |
| 214 # | 244 # |
| 215 def TestSame(values): | 245 def TestSame(values1): |
| 216 src1 = ' '.join(values) | 246 # Recreate the source from the tokens. We use newline instead of whitespace |
| 217 src2 = ' '.join(TextToTokens(src1)) | 247 # since the '//' and #inline regex are line sensitive. |
| 248 text = '\n'.join(values1) |
| 249 values2 = TextToTokens(text) |
| 250 |
| 251 count1 = len(values1) |
| 252 count2 = len(values2) |
| 253 if count1 != count2: |
| 254 print "Size mismatch original %d vs %d\n" % (count1, count2) |
| 255 if count1 > count2: count1 = count2 |
| 256 |
| 257 for i in range(count1): |
| 258 if values1[i] != values2[i]: |
| 259 print "%d >>%s<< >>%s<<" % (i, values1[i], values2[i]) |
| 218 | 260 |
| 219 if GetOption('output'): | 261 if GetOption('output'): |
| 220 sys.stdout.write('Generating original.txt and tokenized.txt\n') | 262 sys.stdout.write('Generating original.txt and tokenized.txt\n') |
| 221 open('original.txt', 'w').write(src1) | 263 open('original.txt', 'w').write(src1) |
| 222 open('tokenized.txt', 'w').write(src2) | 264 open('tokenized.txt', 'w').write(src2) |
| 223 | 265 |
| 224 if src1 == src2: | 266 if values1 == values2: |
| 225 sys.stdout.write('Same: Pass\n') | 267 sys.stdout.write('Same: Pass\n') |
| 226 return 0 | 268 return 0 |
| 227 | 269 |
| 270 print "****************\n%s\n%s***************\n" % (src1, src2) |
| 228 sys.stdout.write('Same: Failed\n') | 271 sys.stdout.write('Same: Failed\n') |
| 229 return -1 | 272 return -1 |
| 230 | 273 |
| 231 | 274 |
| 232 # | 275 # |
| 233 # TestExpect | 276 # TestExpect |
| 234 # | 277 # |
| 235 # From a set of tokens pairs, verify the type field of the second matches | 278 # From a set of tokens pairs, verify the type field of the second matches |
| 236 # the value of the first, so that: | 279 # the value of the first, so that: |
| 237 # INT 123 FLOAT 1.1 | 280 # INT 123 FLOAT 1.1 |
| (...skipping 38 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 276 return -1 | 319 return -1 |
| 277 return 0 | 320 return 0 |
| 278 | 321 |
| 279 except lex.LexError as le: | 322 except lex.LexError as le: |
| 280 sys.stderr.write('%s\n' % str(le)) | 323 sys.stderr.write('%s\n' % str(le)) |
| 281 return -1 | 324 return -1 |
| 282 | 325 |
| 283 if __name__ == '__main__': | 326 if __name__ == '__main__': |
| 284 sys.exit(Main(sys.argv[1:])) | 327 sys.exit(Main(sys.argv[1:])) |
| 285 | 328 |
| OLD | NEW |