OLD | NEW |
(Empty) | |
| 1 #!/usr/bin/python |
| 2 # |
| 3 # Copyright 2012 The Native Client Authors. All rights reserved. |
| 4 # Use of this source code is governed by a BSD-style license that can |
| 5 # be found in the LICENSE file. |
| 6 # Copyright 2012, Google Inc. |
| 7 # |
| 8 |
| 9 """ |
| 10 A simple recursive-descent parser for the table file format. |
| 11 |
| 12 The grammar implemented here is roughly (taking some liberties with whitespace |
| 13 and comment parsing): |
| 14 |
| 15 table_file ::= ( BLANK_LINE | table_def ) end_of_file ; |
| 16 table_def ::= "--" IDENT CITATION NL |
| 17 table_header |
| 18 ( table_row )+ ; |
| 19 table_header ::= ( IDENT "(" BITRANGE ")" )+ ; |
| 20 table_row ::= ( PATTERN )+ ACTION ; |
| 21 |
| 22 IDENT = /[a-z0-9_]+/ |
| 23 CITATION = "(" /[^)]+/ ")" |
| 24 BITRANGE = /[0-9]+/ (":" /[0-9]+/)? |
| 25 PATTERN = /[10x_]+/ |
| 26 ACTION = ( "=" IDENT | "->" IDENT ) ( "(" IDENT ")" )? |
| 27 NL = a newline |
| 28 BLANK_LINE = what you might expect it to be |
| 29 """ |
| 30 |
| 31 import re |
| 32 import dgen_core |
| 33 |
| 34 # These globals track the parser state. |
| 35 _in = None |
| 36 _line_no = None |
| 37 _tables = None |
| 38 _line = None |
| 39 _last_row = None |
| 40 |
| 41 |
| 42 def parse_tables(input): |
| 43 """Entry point for the parser. Input should be a file or file-like.""" |
| 44 global _in, _line_no, _tables |
| 45 _in = input |
| 46 _line_no = 0 |
| 47 _tables = [] |
| 48 next_line() |
| 49 |
| 50 while not end_of_file(): |
| 51 blank_line() or table_def() or unexpected() |
| 52 |
| 53 return _tables |
| 54 |
| 55 |
| 56 def blank_line(): |
| 57 if _line: |
| 58 return False |
| 59 |
| 60 next_line(); |
| 61 return True |
| 62 |
| 63 |
| 64 def table_def(): |
| 65 global _last_row |
| 66 |
| 67 m = re.match(r'^-- ([^ ]+) \(([^)]+)\)', _line) |
| 68 if not m: return False |
| 69 |
| 70 table = dgen_core.Table(m.group(1), m.group(2)) |
| 71 next_line() |
| 72 while blank_line(): pass |
| 73 |
| 74 table_header(table) |
| 75 _last_row = None |
| 76 while not end_of_file() and not blank_line(): |
| 77 table_row(table) |
| 78 |
| 79 _tables.append(table) |
| 80 return True |
| 81 |
| 82 |
| 83 def table_header(table): |
| 84 for col in _line.split(): |
| 85 m = re.match(r'^([a-z0-9_]+)\(([0-9]+)(:([0-9]+))?\)$', col, re.I) |
| 86 if not m: raise Exception('Invalid column header: %s' % col) |
| 87 |
| 88 hi_bit = int(m.group(2)) |
| 89 if m.group(4): |
| 90 lo_bit = int(m.group(4)) |
| 91 else: |
| 92 lo_bit = hi_bit |
| 93 table.add_column(m.group(1), hi_bit, lo_bit) |
| 94 next_line() |
| 95 |
| 96 |
| 97 def table_row(table): |
| 98 global _last_row |
| 99 |
| 100 row = _line.split() |
| 101 for i in range(0, len(row)): |
| 102 if row[i] == '"': row[i] = _last_row[i] |
| 103 _last_row = row |
| 104 |
| 105 action = row[-1] |
| 106 patterns = row[:-1] |
| 107 table.add_row(patterns, action) |
| 108 next_line() |
| 109 |
| 110 |
| 111 def end_of_file(): |
| 112 return _line is None |
| 113 |
| 114 |
| 115 def next_line(): |
| 116 global _line_no, _line |
| 117 |
| 118 _line_no += 1 |
| 119 _line = _in.readline() |
| 120 if _line: |
| 121 _line = re.sub(r'#.*', '', _line).strip() |
| 122 else: |
| 123 _line = None |
| 124 |
| 125 |
| 126 def unexpected(): |
| 127 raise Exception('Line %d: Unexpected line in input: %s' % (_line_no, _line)) |
OLD | NEW |