1#!/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()) 247