blob: 5f7813f7e12e4d75f965778ec96c701e689e7140 [file] [log] [blame]
Andrew Boieb4efd542016-03-24 13:45:46 -07001#!/usr/bin/env python3
2#
3# Copyright (c) 2016 Intel Corporation.
4#
David B. Kinderac74d8b2017-01-18 17:01:01 -08005# SPDX-License-Identifier: Apache-2.0
Andrew Boieb4efd542016-03-24 13:45:46 -07006
Andrew Boieb4efd542016-03-24 13:45:46 -07007import copy
Martí Bolívar51f55b42021-02-26 07:50:27 -08008import logging
Martí Bolívard802fc32021-02-26 08:09:22 -08009import os
Andrew Boie7a87f9f2016-06-02 12:27:54 -070010import re
Martí Bolívard802fc32021-02-26 08:09:22 -080011import sys
12import threading
Andrew Boieb4efd542016-03-24 13:45:46 -070013
14try:
15 import ply.lex as lex
16 import ply.yacc as yacc
17except ImportError:
Ulf Magnusson50b9b122019-09-07 14:41:01 +020018 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.")
Andrew Boieb4efd542016-03-24 13:45:46 -070021
Martí Bolívar51f55b42021-02-26 07:50:27 -080022_logger = logging.getLogger('twister')
23
Andrew Boieb4efd542016-03-24 13:45:46 -070024reserved = {
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",
Andrew Boie7a87f9f2016-06-02 12:27:54 -070047 "COLON",
Andrew Boieb4efd542016-03-24 13:45:46 -070048] + 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):
Andrew Boie5aaf5f12016-06-02 12:21:41 -070061 r'\"([^\\\n]|(\\.))*?\"|\'([^\\\n]|(\\.))*?\''
Andrew Boieb4efd542016-03-24 13:45:46 -070062 # 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
Andrew Boie7a87f9f2016-06-02 12:27:54 -070088t_COLON = ":"
89
Andrew Boieb4efd542016-03-24 13:45:46 -070090def 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'),
Ulf Magnusson0d39a102019-09-06 11:13:19 +0200106 ('nonassoc', 'EQUALS', 'NOTEQUALS', 'GT', 'LT', 'GTEQ', 'LTEQ', 'IN'),
Andrew Boieb4efd542016-03-24 13:45:46 -0700107)
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
Andrew Boie7a87f9f2016-06-02 12:27:54 -0700132 | SYMBOL IN list
133 | SYMBOL COLON STR"""
Andrew Boieb4efd542016-03-24 13:45:46 -0700134 p[0] = (p[2], p[1], p[3])
135
136def p_expr_single(p):
137 """expr : SYMBOL"""
138 p[0] = ("exists", p[1])
139
Kumar Gala7733b942019-09-12 17:08:43 -0500140def 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
Andrew Boieb4efd542016-03-24 13:45:46 -0700154def 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
Anas Nashif9c974b92017-12-06 10:10:55 -0500183if "PARSETAB_DIR" not in os.environ:
184 parser = yacc.yacc(debug=0)
185else:
186 parser = yacc.yacc(debug=0, outputdir=os.environ["PARSETAB_DIR"])
Andrew Boieb4efd542016-03-24 13:45:46 -0700187
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:
Andrew Boie31e772d2017-04-18 11:22:50 -0700195 v = env[ast]
196 if v.startswith("0x") or v.startswith("0X"):
197 return int(v, 16)
198 else:
199 return int(v, 10)
Andrew Boieb4efd542016-03-24 13:45:46 -0700200 return 0
201
Kumar Gala7733b942019-09-12 17:08:43 -0500202def ast_expr(ast, env, edt):
Andrew Boieb4efd542016-03-24 13:45:46 -0700203 if ast[0] == "not":
Kumar Gala7733b942019-09-12 17:08:43 -0500204 return not ast_expr(ast[1], env, edt)
Andrew Boieb4efd542016-03-24 13:45:46 -0700205 elif ast[0] == "or":
Kumar Gala7733b942019-09-12 17:08:43 -0500206 return ast_expr(ast[1], env, edt) or ast_expr(ast[2], env, edt)
Andrew Boieb4efd542016-03-24 13:45:46 -0700207 elif ast[0] == "and":
Kumar Gala7733b942019-09-12 17:08:43 -0500208 return ast_expr(ast[1], env, edt) and ast_expr(ast[2], env, edt)
Andrew Boieb4efd542016-03-24 13:45:46 -0700209 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":
Ulf Magnusson47ef9ba2019-09-05 18:50:18 +0200224 return bool(ast_sym(ast[1], env))
Andrew Boie7a87f9f2016-06-02 12:27:54 -0700225 elif ast[0] == ":":
Ulf Magnusson47ef9ba2019-09-05 18:50:18 +0200226 return bool(re.match(ast[2], ast_sym(ast[1], env)))
Kumar Gala7733b942019-09-12 17:08:43 -0500227 elif ast[0] == "dt_compat_enabled":
228 compat = ast[1][0]
229 for node in edt.nodes:
Martí Bolívar81650082020-10-05 20:02:13 -0700230 if compat in node.compats and node.status == "okay":
Kumar Gala7733b942019-09-12 17:08:43 -0500231 return True
232 return False
233 elif ast[0] == "dt_alias_exists":
234 alias = ast[1][0]
235 for node in edt.nodes:
Martí Bolívar81650082020-10-05 20:02:13 -0700236 if alias in node.aliases and node.status == "okay":
Kumar Gala7733b942019-09-12 17:08:43 -0500237 return True
238 return False
Martí Bolívar51f55b42021-02-26 07:50:27 -0800239 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 # The legacy "dt_compat_enabled_with_alias" form is still
245 # accepted but is now deprecated and causes a warning. This is
246 # meant to give downstream users some time to notice and
247 # adjust. Its argument order only made sense under the (bad)
248 # assumption that the gpio-leds child node has the same compatible
249
250 alias = ast[1][0]
251 compat = ast[1][1]
252
253 return ast_handle_dt_enabled_alias_with_parent_compat(edt, alias,
254 compat)
Kumar Gala7733b942019-09-12 17:08:43 -0500255 elif ast[0] == "dt_compat_enabled_with_alias":
256 compat = ast[1][0]
257 alias = ast[1][1]
Martí Bolívar51f55b42021-02-26 07:50:27 -0800258
259 _logger.warning('dt_compat_enabled_with_alias("%s", "%s"): '
260 'this is deprecated, use '
261 'dt_enabled_alias_with_parent_compat("%s", "%s") '
262 'instead',
263 compat, alias, alias, compat)
264
265 return ast_handle_dt_enabled_alias_with_parent_compat(edt, alias,
266 compat)
Johann Fischerbb474972020-12-03 21:19:53 +0100267 elif ast[0] == "dt_compat_enabled_with_label":
268 compat = ast[1][0]
269 label = ast[1][1]
Johann Fischer44a02c52021-03-16 14:20:46 +0100270 node = edt.label2node.get(label)
271 return node is not None and node.status == 'okay' and node.matching_compat == compat
Henrik Brix Andersenc08d1e02020-11-20 17:31:57 +0100272 elif ast[0] == "dt_chosen_enabled":
273 chosen = ast[1][0]
274 node = edt.chosen_node(chosen)
275 if node and node.status == "okay":
276 return True
277 return False
Andrew Boieb4efd542016-03-24 13:45:46 -0700278
Martí Bolívar51f55b42021-02-26 07:50:27 -0800279def ast_handle_dt_enabled_alias_with_parent_compat(edt, alias, compat):
280 # Helper shared with the now deprecated
281 # dt_compat_enabled_with_alias version.
282
283 for node in edt.nodes:
284 parent = node.parent
285 if parent is None:
286 continue
287 if (node.status == "okay" and alias in node.aliases and
288 parent.matching_compat == compat):
289 return True
290
291 return False
292
Andrew Boie99c0f642016-06-02 12:22:41 -0700293mutex = threading.Lock()
Andrew Boieb4efd542016-03-24 13:45:46 -0700294
Kumar Gala7733b942019-09-12 17:08:43 -0500295def parse(expr_text, env, edt):
Andrew Boieb4efd542016-03-24 13:45:46 -0700296 """Given a text representation of an expression in our language,
297 use the provided environment to determine whether the expression
298 is true or false"""
Andrew Boieb4efd542016-03-24 13:45:46 -0700299
Andrew Boie99c0f642016-06-02 12:22:41 -0700300 # Like it's C counterpart, state machine is not thread-safe
301 mutex.acquire()
302 try:
303 ast = parser.parse(expr_text)
304 finally:
305 mutex.release()
306
Kumar Gala7733b942019-09-12 17:08:43 -0500307 return ast_expr(ast, env, edt)
Andrew Boieb4efd542016-03-24 13:45:46 -0700308
309# Just some test code
310if __name__ == "__main__":
311
312 local_env = {
313 "A" : "1",
314 "C" : "foo",
315 "D" : "20",
316 "E" : 0x100,
317 "F" : "baz"
318 }
319
320
321 for line in open(sys.argv[1]).readlines():
322 lex.input(line)
323 for tok in iter(lex.token, None):
324 print(tok.type, tok.value)
325
326 parser = yacc.yacc()
327 print(parser.parse(line))
328
Kumar Gala7733b942019-09-12 17:08:43 -0500329 print(parse(line, local_env, None))