1#!/usr/bin/env python3 2# 3# Copyright (c) 2016 Intel Corporation. 4# 5# SPDX-License-Identifier: Apache-2.0 6 7import copy 8import logging 9import os 10import re 11import sys 12import threading 13 14try: 15 import ply.lex as lex 16 import ply.yacc as yacc 17except ImportError: 18 sys.exit("PLY library for Python 3 not installed.\n" 19 "Please install the ply package using your workstation's\n" 20 "package manager or the 'pip' tool.") 21 22_logger = logging.getLogger('twister') 23 24reserved = { 25 'and' : 'AND', 26 'or' : 'OR', 27 'not' : 'NOT', 28 'in' : 'IN', 29} 30 31tokens = [ 32 "HEX", 33 "STR", 34 "INTEGER", 35 "EQUALS", 36 "NOTEQUALS", 37 "LT", 38 "GT", 39 "LTEQ", 40 "GTEQ", 41 "OPAREN", 42 "CPAREN", 43 "OBRACKET", 44 "CBRACKET", 45 "COMMA", 46 "SYMBOL", 47 "COLON", 48] + list(reserved.values()) 49 50def t_HEX(t): 51 r"0x[0-9a-fA-F]+" 52 t.value = str(int(t.value, 16)) 53 return t 54 55def t_INTEGER(t): 56 r"\d+" 57 t.value = str(int(t.value)) 58 return t 59 60def t_STR(t): 61 r'\"([^\\\n]|(\\.))*?\"|\'([^\\\n]|(\\.))*?\'' 62 # nip off the quotation marks 63 t.value = t.value[1:-1] 64 return t 65 66t_EQUALS = r"==" 67 68t_NOTEQUALS = r"!=" 69 70t_LT = r"<" 71 72t_GT = r">" 73 74t_LTEQ = r"<=" 75 76t_GTEQ = r">=" 77 78t_OPAREN = r"[(]" 79 80t_CPAREN = r"[)]" 81 82t_OBRACKET = r"\[" 83 84t_CBRACKET = r"\]" 85 86t_COMMA = r"," 87 88t_COLON = ":" 89 90def t_SYMBOL(t): 91 r"[A-Za-z_][0-9A-Za-z_]*" 92 t.type = reserved.get(t.value, "SYMBOL") 93 return t 94 95t_ignore = " \t\n" 96 97def t_error(t): 98 raise SyntaxError("Unexpected token '%s'" % t.value) 99 100lex.lex() 101 102precedence = ( 103 ('left', 'OR'), 104 ('left', 'AND'), 105 ('right', 'NOT'), 106 ('nonassoc', 'EQUALS', 'NOTEQUALS', 'GT', 'LT', 'GTEQ', 'LTEQ', 'IN'), 107) 108 109def p_expr_or(p): 110 'expr : expr OR expr' 111 p[0] = ("or", p[1], p[3]) 112 113def p_expr_and(p): 114 'expr : expr AND expr' 115 p[0] = ("and", p[1], p[3]) 116 117def p_expr_not(p): 118 'expr : NOT expr' 119 p[0] = ("not", p[2]) 120 121def p_expr_parens(p): 122 'expr : OPAREN expr CPAREN' 123 p[0] = p[2] 124 125def p_expr_eval(p): 126 """expr : SYMBOL EQUALS const 127 | SYMBOL NOTEQUALS const 128 | SYMBOL GT number 129 | SYMBOL LT number 130 | SYMBOL GTEQ number 131 | SYMBOL LTEQ number 132 | SYMBOL IN list 133 | SYMBOL COLON STR""" 134 p[0] = (p[2], p[1], p[3]) 135 136def p_expr_single(p): 137 """expr : SYMBOL""" 138 p[0] = ("exists", p[1]) 139 140def p_func(p): 141 """expr : SYMBOL OPAREN arg_intr CPAREN""" 142 p[0] = [p[1]] 143 p[0].append(p[3]) 144 145def p_arg_intr_single(p): 146 """arg_intr : const""" 147 p[0] = [p[1]] 148 149def p_arg_intr_mult(p): 150 """arg_intr : arg_intr COMMA const""" 151 p[0] = copy.copy(p[1]) 152 p[0].append(p[3]) 153 154def p_list(p): 155 """list : OBRACKET list_intr CBRACKET""" 156 p[0] = p[2] 157 158def p_list_intr_single(p): 159 """list_intr : const""" 160 p[0] = [p[1]] 161 162def p_list_intr_mult(p): 163 """list_intr : list_intr COMMA const""" 164 p[0] = copy.copy(p[1]) 165 p[0].append(p[3]) 166 167def p_const(p): 168 """const : STR 169 | number""" 170 p[0] = p[1] 171 172def p_number(p): 173 """number : INTEGER 174 | HEX""" 175 p[0] = p[1] 176 177def p_error(p): 178 if p: 179 raise SyntaxError("Unexpected token '%s'" % p.value) 180 else: 181 raise SyntaxError("Unexpected end of expression") 182 183if "PARSETAB_DIR" not in os.environ: 184 parser = yacc.yacc(debug=0) 185else: 186 parser = yacc.yacc(debug=0, outputdir=os.environ["PARSETAB_DIR"]) 187 188def ast_sym(ast, env): 189 if ast in env: 190 return str(env[ast]) 191 return "" 192 193def ast_sym_int(ast, env): 194 if ast in env: 195 v = env[ast] 196 if v.startswith("0x") or v.startswith("0X"): 197 return int(v, 16) 198 else: 199 return int(v, 10) 200 return 0 201 202def ast_expr(ast, env, edt): 203 if ast[0] == "not": 204 return not ast_expr(ast[1], env, edt) 205 elif ast[0] == "or": 206 return ast_expr(ast[1], env, edt) or ast_expr(ast[2], env, edt) 207 elif ast[0] == "and": 208 return ast_expr(ast[1], env, edt) and ast_expr(ast[2], env, edt) 209 elif ast[0] == "==": 210 return ast_sym(ast[1], env) == ast[2] 211 elif ast[0] == "!=": 212 return ast_sym(ast[1], env) != ast[2] 213 elif ast[0] == ">": 214 return ast_sym_int(ast[1], env) > int(ast[2]) 215 elif ast[0] == "<": 216 return ast_sym_int(ast[1], env) < int(ast[2]) 217 elif ast[0] == ">=": 218 return ast_sym_int(ast[1], env) >= int(ast[2]) 219 elif ast[0] == "<=": 220 return ast_sym_int(ast[1], env) <= int(ast[2]) 221 elif ast[0] == "in": 222 return ast_sym(ast[1], env) in ast[2] 223 elif ast[0] == "exists": 224 return bool(ast_sym(ast[1], env)) 225 elif ast[0] == ":": 226 return bool(re.match(ast[2], ast_sym(ast[1], env))) 227 elif ast[0] == "dt_compat_enabled": 228 compat = ast[1][0] 229 for node in edt.nodes: 230 if (node.matching_compat == compat or compat in node.compats) and node.status == "okay": 231 return True 232 return False 233 elif ast[0] == "dt_alias_exists": 234 alias = ast[1][0] 235 for node in edt.nodes: 236 if alias in node.aliases and node.status == "okay": 237 return True 238 return False 239 elif ast[0] == "dt_enabled_alias_with_parent_compat": 240 # Checks if the DT has an enabled alias node whose parent has 241 # a given compatible. For matching things like gpio-leds child 242 # nodes, which do not have compatibles themselves. 243 244 alias = ast[1][0] 245 compat = ast[1][1] 246 for node in edt.nodes: 247 parent = node.parent 248 if parent is None: 249 continue 250 if node.status == "okay" and alias in node.aliases and \ 251 (parent.matching_compat == compat or compat in parent.compats): 252 return True 253 return False 254 elif ast[0] == "dt_label_with_parent_compat_enabled": 255 compat = ast[1][1] 256 label = ast[1][0] 257 node = edt.label2node.get(label) 258 if node is not None: 259 parent = node.parent 260 else: 261 return False 262 return parent is not None and parent.status == 'okay' and \ 263 (parent.matching_compat == compat or compat in parent.compats) 264 elif ast[0] == "dt_chosen_enabled": 265 chosen = ast[1][0] 266 node = edt.chosen_node(chosen) 267 if node and node.status == "okay": 268 return True 269 return False 270 elif ast[0] == "dt_nodelabel_enabled": 271 label = ast[1][0] 272 node = edt.label2node.get(label) 273 if node and node.status == "okay": 274 return True 275 return False 276 elif ast[0] == "dt_node_prop_enabled": 277 label = ast[1][0] 278 node = edt.label2node.get(label) 279 prop = ast[1][1] 280 if node and prop in node.props and node.props[prop].val: 281 return True 282 return False 283 284 285mutex = threading.Lock() 286 287def parse(expr_text, env, edt): 288 """Given a text representation of an expression in our language, 289 use the provided environment to determine whether the expression 290 is true or false""" 291 292 # Like it's C counterpart, state machine is not thread-safe 293 mutex.acquire() 294 try: 295 ast = parser.parse(expr_text) 296 finally: 297 mutex.release() 298 299 return ast_expr(ast, env, edt) 300 301# Just some test code 302if __name__ == "__main__": 303 304 local_env = { 305 "A" : "1", 306 "C" : "foo", 307 "D" : "20", 308 "E" : 0x100, 309 "F" : "baz" 310 } 311 312 313 for line in open(sys.argv[1]).readlines(): 314 lex.input(line) 315 for tok in iter(lex.token, None): 316 print(tok.type, tok.value) 317 318 parser = yacc.yacc() 319 print(parser.parse(line)) 320 321 print(parse(line, local_env, None)) 322