blob: e0fbf1768e8ff2bc6a321415b1815ff977d045a0 [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
7import sys
8import os
9import copy
Andrew Boie99c0f642016-06-02 12:22:41 -070010import threading
Andrew Boie7a87f9f2016-06-02 12:27:54 -070011import re
Andrew Boieb4efd542016-03-24 13:45:46 -070012
13try:
14 import ply.lex as lex
15 import ply.yacc as yacc
16except ImportError:
17 print("PLY library for Python 3 not installed.")
18 print("Please install the python3-ply package using your workstation's")
19 print("package manager or the 'pip' tool.")
20 sys.exit(1)
21
22reserved = {
23 'and' : 'AND',
24 'or' : 'OR',
25 'not' : 'NOT',
26 'in' : 'IN',
27}
28
29tokens = [
30 "HEX",
31 "STR",
32 "INTEGER",
33 "EQUALS",
34 "NOTEQUALS",
35 "LT",
36 "GT",
37 "LTEQ",
38 "GTEQ",
39 "OPAREN",
40 "CPAREN",
41 "OBRACKET",
42 "CBRACKET",
43 "COMMA",
44 "SYMBOL",
Andrew Boie7a87f9f2016-06-02 12:27:54 -070045 "COLON",
Andrew Boieb4efd542016-03-24 13:45:46 -070046] + list(reserved.values())
47
48def t_HEX(t):
49 r"0x[0-9a-fA-F]+"
50 t.value = str(int(t.value, 16))
51 return t
52
53def t_INTEGER(t):
54 r"\d+"
55 t.value = str(int(t.value))
56 return t
57
58def t_STR(t):
Andrew Boie5aaf5f12016-06-02 12:21:41 -070059 r'\"([^\\\n]|(\\.))*?\"|\'([^\\\n]|(\\.))*?\''
Andrew Boieb4efd542016-03-24 13:45:46 -070060 # nip off the quotation marks
61 t.value = t.value[1:-1]
62 return t
63
64t_EQUALS = r"=="
65
66t_NOTEQUALS = r"!="
67
68t_LT = r"<"
69
70t_GT = r">"
71
72t_LTEQ = r"<="
73
74t_GTEQ = r">="
75
76t_OPAREN = r"[(]"
77
78t_CPAREN = r"[)]"
79
80t_OBRACKET = r"\["
81
82t_CBRACKET = r"\]"
83
84t_COMMA = r","
85
Andrew Boie7a87f9f2016-06-02 12:27:54 -070086t_COLON = ":"
87
Andrew Boieb4efd542016-03-24 13:45:46 -070088def t_SYMBOL(t):
89 r"[A-Za-z_][0-9A-Za-z_]*"
90 t.type = reserved.get(t.value, "SYMBOL")
91 return t
92
93t_ignore = " \t\n"
94
95def t_error(t):
96 raise SyntaxError("Unexpected token '%s'" % t.value)
97
98lex.lex()
99
100precedence = (
101 ('left', 'OR'),
102 ('left', 'AND'),
103 ('right', 'NOT'),
104 ('nonassoc' , 'EQUALS', 'NOTEQUALS', 'GT', 'LT', 'GTEQ', 'LTEQ', 'IN'),
105)
106
107def p_expr_or(p):
108 'expr : expr OR expr'
109 p[0] = ("or", p[1], p[3])
110
111def p_expr_and(p):
112 'expr : expr AND expr'
113 p[0] = ("and", p[1], p[3])
114
115def p_expr_not(p):
116 'expr : NOT expr'
117 p[0] = ("not", p[2])
118
119def p_expr_parens(p):
120 'expr : OPAREN expr CPAREN'
121 p[0] = p[2]
122
123def p_expr_eval(p):
124 """expr : SYMBOL EQUALS const
125 | SYMBOL NOTEQUALS const
126 | SYMBOL GT number
127 | SYMBOL LT number
128 | SYMBOL GTEQ number
129 | SYMBOL LTEQ number
Andrew Boie7a87f9f2016-06-02 12:27:54 -0700130 | SYMBOL IN list
131 | SYMBOL COLON STR"""
Andrew Boieb4efd542016-03-24 13:45:46 -0700132 p[0] = (p[2], p[1], p[3])
133
134def p_expr_single(p):
135 """expr : SYMBOL"""
136 p[0] = ("exists", p[1])
137
138def p_list(p):
139 """list : OBRACKET list_intr CBRACKET"""
140 p[0] = p[2]
141
142def p_list_intr_single(p):
143 """list_intr : const"""
144 p[0] = [p[1]]
145
146def p_list_intr_mult(p):
147 """list_intr : list_intr COMMA const"""
148 p[0] = copy.copy(p[1])
149 p[0].append(p[3])
150
151def p_const(p):
152 """const : STR
153 | number"""
154 p[0] = p[1]
155
156def p_number(p):
157 """number : INTEGER
158 | HEX"""
159 p[0] = p[1]
160
161def p_error(p):
162 if p:
163 raise SyntaxError("Unexpected token '%s'" % p.value)
164 else:
165 raise SyntaxError("Unexpected end of expression")
166
167parser = yacc.yacc()
168
169def ast_sym(ast, env):
170 if ast in env:
171 return str(env[ast])
172 return ""
173
174def ast_sym_int(ast, env):
175 if ast in env:
Andrew Boie31e772d2017-04-18 11:22:50 -0700176 v = env[ast]
177 if v.startswith("0x") or v.startswith("0X"):
178 return int(v, 16)
179 else:
180 return int(v, 10)
Andrew Boieb4efd542016-03-24 13:45:46 -0700181 return 0
182
183def ast_expr(ast, env):
184 if ast[0] == "not":
185 return not ast_expr(ast[1], env)
186 elif ast[0] == "or":
187 return ast_expr(ast[1], env) or ast_expr(ast[2], env)
188 elif ast[0] == "and":
189 return ast_expr(ast[1], env) and ast_expr(ast[2], env)
190 elif ast[0] == "==":
191 return ast_sym(ast[1], env) == ast[2]
192 elif ast[0] == "!=":
193 return ast_sym(ast[1], env) != ast[2]
194 elif ast[0] == ">":
195 return ast_sym_int(ast[1], env) > int(ast[2])
196 elif ast[0] == "<":
197 return ast_sym_int(ast[1], env) < int(ast[2])
198 elif ast[0] == ">=":
199 return ast_sym_int(ast[1], env) >= int(ast[2])
200 elif ast[0] == "<=":
201 return ast_sym_int(ast[1], env) <= int(ast[2])
202 elif ast[0] == "in":
203 return ast_sym(ast[1], env) in ast[2]
204 elif ast[0] == "exists":
205 return True if ast_sym(ast[1], env) else False
Andrew Boie7a87f9f2016-06-02 12:27:54 -0700206 elif ast[0] == ":":
207 return True if re.compile(ast[2]).match(ast_sym(ast[1], env)) else False
Andrew Boieb4efd542016-03-24 13:45:46 -0700208
Andrew Boie99c0f642016-06-02 12:22:41 -0700209mutex = threading.Lock()
Andrew Boieb4efd542016-03-24 13:45:46 -0700210
211def parse(expr_text, env):
212 """Given a text representation of an expression in our language,
213 use the provided environment to determine whether the expression
214 is true or false"""
Andrew Boieb4efd542016-03-24 13:45:46 -0700215
Andrew Boie99c0f642016-06-02 12:22:41 -0700216 # Like it's C counterpart, state machine is not thread-safe
217 mutex.acquire()
218 try:
219 ast = parser.parse(expr_text)
220 finally:
221 mutex.release()
222
223 return ast_expr(ast, env)
Andrew Boieb4efd542016-03-24 13:45:46 -0700224
225# Just some test code
226if __name__ == "__main__":
227
228 local_env = {
229 "A" : "1",
230 "C" : "foo",
231 "D" : "20",
232 "E" : 0x100,
233 "F" : "baz"
234 }
235
236
237 for line in open(sys.argv[1]).readlines():
238 lex.input(line)
239 for tok in iter(lex.token, None):
240 print(tok.type, tok.value)
241
242 parser = yacc.yacc()
243 print(parser.parse(line))
244
245 print(parse(line, local_env))
246
247
248
249