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