blob: b0349c0c309c7cac329113ce3bf459d2541ed4ff [file] [log] [blame]
Enrico Granata4c43add2017-03-15 18:01:34 -07001#!/usr/bin/env python3
2#
3# Copyright (C) 2017 The Android Open Source Project
4#
5# Licensed under the Apache License, Version 2.0 (the "License");
6# you may not use this file except in compliance with the License.
7# You may obtain a copy of the License at
8#
9# http://www.apache.org/licenses/LICENSE-2.0
10#
11# Unless required by applicable law or agreed to in writing, software
12# distributed under the License is distributed on an "AS IS" BASIS,
13# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14# See the License for the specific language governing permissions and
15# limitations under the License.
16#
17
18# A parser for enum types defined in HIDL.
19# This script can parse HIDL files and generate a parse tree.
20# To use, import and call parse("path/to/file.hal")
21# It will return a Python dictionary with two keys:
22# - header: an instance of Header
23# - enums: a dictionary of EnumDecl objects by name
24# This script cannot parse structs for now, but that would be easy to add.
25
26# It requires 'ply' (Python Lex/Yacc).
27
28import ply
29
30tokens = ('package', 'import', 'enum',
31 'COLON', 'IDENTIFIER', 'COMMENT', 'NUMBER', 'HEX', 'OR', 'EQUALS',
32 'LPAREN', 'RPAREN', 'LBRACE', 'RBRACE', 'DOT', 'SEMICOLON', 'VERSION',
33 'COMMA', 'SHIFT')
34
35t_COLON = r':'
36t_NUMBER = r'[0-9]+'
37t_HEX = r'0x[0-9A-Fa-f]+'
38t_OR = r'\|'
39t_EQUALS = r'='
40t_LPAREN = r'\('
41t_RPAREN = r'\)'
42t_SHIFT = r'<<'
43
44def t_COMMENT(t):
45 r'(/\*(.|\n)*?\*/)|(//.*)'
46 pass
47
48t_LBRACE = r'{'
49t_RBRACE = r'}'
50t_DOT = r'\.'
51t_SEMICOLON = r';'
52t_VERSION = r'@[0-9].[0-9]'
53t_COMMA = r','
54t_ignore = ' \n\t'
55
56def t_IDENTIFIER(t):
57 r'[a-zA-Z_][a-zA-Z_0-9]*'
58 if t.value == 'package':
59 t.type = 'package'
60 elif t.value == 'import':
61 t.type = 'import'
62 elif t.value == 'enum':
63 t.type = 'enum'
64 return t
65
66def t_error(t):
67 t.type = t.value[0]
68 t.value = t.value[0]
69 t.lexer.skip(1)
70 return t
71
72import ply.lex as lex
73lexer = lex.lex()
74
75class EnumHeader(object):
76 def __init__(self, name, base):
77 self.name = name
78 self.base = base
79
80 def __str__(self):
81 return '%s%s' % (self.name, ' %s' % self.base if self.base else '')
82
83class EnumDecl(object):
84 def __init__(self, header, cases):
85 self.header = header
86 self.cases = cases
87
88 def __str__(self):
89 return '%s {\n%s\n}' % (self.header,
90 '\n'.join(str(x) for x in self.cases))
91
92 def __repr__(self):
93 return self.__str__()
94
95class EnumCase(object):
96 def __init__(self, name, value):
97 self.name = name
98 self.value = value
99
100 def __str__(self):
101 return '%s = %s' % (self.name, self.value)
102
103class PackageID(object):
104 def __init__(self, name, version):
105 self.name = name
106 self.version = version
107
108 def __str__(self):
109 return '%s%s' % (self.name, self.version)
110
111class Package(object):
112 def __init__(self, package):
113 self.package = package
114
115 def __str__(self):
116 return 'package %s' % self.package
117
118class Import(object):
119 def __init__(self, package):
120 self.package = package
121
122 def __str__(self):
123 return 'import %s' % self.package
124
125class Header(object):
126 def __init__(self, package, imports):
127 self.package = package
128 self.imports = imports
129
130 def __str__(self):
131 return str(self.package) + "\n" + \
132 '\n'.join(str(x) for x in self.imports)
133
134# Error rule for syntax errors
135def p_error(p):
136 print("Syntax error in input: %s" % p)
137
138def p_document(t):
139 'document : header enum_decls'
140 enums = {}
141 for enum in t[2]:
142 enums[enum.header.name] = enum
143 t[0] = {'header' : t[1], 'enums' : enums}
144
145def p_enum_decls_1(t):
146 'enum_decls : enum_decl'
147 t[0] = [t[1]]
148def p_enum_decls_2(t):
149 'enum_decls : enum_decls enum_decl'
150 t[0] = t[1] + [t[2]]
151
152def p_enum_cases_1(t):
153 'enum_cases : enum_case'
154 t[0] = [t[1]]
155def p_enum_cases_2(t):
156 'enum_cases : enum_cases COMMA enum_case'
157 t[0] = t[1] + [t[3]]
158
159def p_enum_base_1(t):
160 'enum_base : VERSION COLON COLON IDENTIFIER'
161 t[0] = '%s::%s' % (t[1], t[4])
162def p_enum_base_2(t):
163 'enum_base : IDENTIFIER'
164 t[0] = t[1]
165
166def p_enum_header_1(t):
167 'enum_header : enum IDENTIFIER'
168 t[0] = EnumHeader(t[2], None)
169def p_enum_header_2(t):
170 'enum_header : enum IDENTIFIER COLON enum_base'
171 t[0] = EnumHeader(t[2], t[4])
172
173def p_enum_decl_1(t):
174 'enum_decl : enum_header LBRACE enum_cases RBRACE SEMICOLON'
175 t[0] = EnumDecl(t[1], t[3])
176def p_enum_decl_2(t):
177 'enum_decl : enum_header LBRACE enum_cases COMMA RBRACE SEMICOLON'
178 t[0] = EnumDecl(t[1], t[3])
179
180def p_enum_value_1(t):
181 '''enum_value : NUMBER
182 | HEX
183 | IDENTIFIER'''
184 t[0] = t[1]
185def p_enum_value_2(t):
186 'enum_value : enum_value SHIFT NUMBER'
187 t[0] = '%s << %s' % (t[1], t[3])
188def p_enum_value_3(t):
189 'enum_value : enum_value OR enum_value'
190 t[0] = "%s | %s" % (t[1], t[3])
191def p_enum_value_4(t):
192 'enum_value : LPAREN enum_value RPAREN'
193 t[0] = t[2]
194def p_enum_value_5(t):
195 'enum_value : IDENTIFIER COLON IDENTIFIER'
196 t[0] = '%s:%s' % (t[1],t[3])
197
198def p_enum_case(t):
199 'enum_case : IDENTIFIER EQUALS enum_value'
200 t[0] = EnumCase(t[1], t[3])
201
202def p_header_1(t):
203 'header : package_decl'
204 t[0] = Header(t[1], [])
205
206def p_header_2(t):
207 'header : package_decl import_decls'
208 t[0] = Header(t[1], t[2])
209
210def p_import_decls_1(t):
211 'import_decls : import_decl'
212 t[0] = [t[1]]
213
214def p_import_decls_2(t):
215 'import_decls : import_decls import_decl'
216 t[0] = t[1] + [t[2]]
217
218def p_package_decl(t):
219 'package_decl : package package_ID SEMICOLON'
220 t[0] = Package(t[2])
221
222def p_import_decl(t):
223 'import_decl : import package_ID SEMICOLON'
224 t[0] = Import(t[2])
225
226def p_package_ID(t):
227 'package_ID : dotted_identifier VERSION'
228 t[0] = PackageID(t[1], t[2])
229
230def p_dotted_identifier_1(t):
231 'dotted_identifier : IDENTIFIER'
232 t[0] = t[1]
233def p_dotted_identifier_2(t):
234 'dotted_identifier : dotted_identifier DOT IDENTIFIER'
235 t[0] = t[1] + '.' + t[3]
236
237class SilentLogger(object):
238 def warning(*args):
239 pass
240
241import ply.yacc as yacc
242parser = yacc.yacc(debug=False, write_tables=False, errorlog=SilentLogger())
243import sys
244
245def parse(filename):
246 return parser.parse(open(filename, 'r').read())