zephyr/scripts/sanity_chk/expr_parser.py
Martí Bolívar 8165008f44 dts: remove legacy macro support
The legacy macros were first deprecated in Zephyr v2.3. Now that
Zephyr v2.4 has been released, that makes two releases where these
macros have been deprecated, so it's OK to remove them.

This leaves support for legacy binding syntax in place. Removing that
is left to future work.

We need to update various pieces of documentation related to flash
partitions that never got updated when the new API was introduced.
Consolidate this information in the flash_map.h API reference page,
since that's really where users will run into it. This also gives us
the opportunity to improve this documentation.

Adjust a couple of kconfigfunctions.py and sanitycheck bits to use
non-legacy edtlib APIs.

Signed-off-by: Martí Bolívar <marti.bolivar@nordicsemi.no>
2020-10-09 08:45:38 -05:00

281 lines
6.0 KiB
Python

#!/usr/bin/env python3
#
# Copyright (c) 2016 Intel Corporation.
#
# SPDX-License-Identifier: Apache-2.0
import sys
import os
import copy
import threading
import re
try:
import ply.lex as lex
import ply.yacc as yacc
except ImportError:
sys.exit("PLY library for Python 3 not installed.\n"
"Please install the ply package using your workstation's\n"
"package manager or the 'pip' tool.")
reserved = {
'and' : 'AND',
'or' : 'OR',
'not' : 'NOT',
'in' : 'IN',
}
tokens = [
"HEX",
"STR",
"INTEGER",
"EQUALS",
"NOTEQUALS",
"LT",
"GT",
"LTEQ",
"GTEQ",
"OPAREN",
"CPAREN",
"OBRACKET",
"CBRACKET",
"COMMA",
"SYMBOL",
"COLON",
] + list(reserved.values())
def t_HEX(t):
r"0x[0-9a-fA-F]+"
t.value = str(int(t.value, 16))
return t
def t_INTEGER(t):
r"\d+"
t.value = str(int(t.value))
return t
def t_STR(t):
r'\"([^\\\n]|(\\.))*?\"|\'([^\\\n]|(\\.))*?\''
# nip off the quotation marks
t.value = t.value[1:-1]
return t
t_EQUALS = r"=="
t_NOTEQUALS = r"!="
t_LT = r"<"
t_GT = r">"
t_LTEQ = r"<="
t_GTEQ = r">="
t_OPAREN = r"[(]"
t_CPAREN = r"[)]"
t_OBRACKET = r"\["
t_CBRACKET = r"\]"
t_COMMA = r","
t_COLON = ":"
def t_SYMBOL(t):
r"[A-Za-z_][0-9A-Za-z_]*"
t.type = reserved.get(t.value, "SYMBOL")
return t
t_ignore = " \t\n"
def t_error(t):
raise SyntaxError("Unexpected token '%s'" % t.value)
lex.lex()
precedence = (
('left', 'OR'),
('left', 'AND'),
('right', 'NOT'),
('nonassoc', 'EQUALS', 'NOTEQUALS', 'GT', 'LT', 'GTEQ', 'LTEQ', 'IN'),
)
def p_expr_or(p):
'expr : expr OR expr'
p[0] = ("or", p[1], p[3])
def p_expr_and(p):
'expr : expr AND expr'
p[0] = ("and", p[1], p[3])
def p_expr_not(p):
'expr : NOT expr'
p[0] = ("not", p[2])
def p_expr_parens(p):
'expr : OPAREN expr CPAREN'
p[0] = p[2]
def p_expr_eval(p):
"""expr : SYMBOL EQUALS const
| SYMBOL NOTEQUALS const
| SYMBOL GT number
| SYMBOL LT number
| SYMBOL GTEQ number
| SYMBOL LTEQ number
| SYMBOL IN list
| SYMBOL COLON STR"""
p[0] = (p[2], p[1], p[3])
def p_expr_single(p):
"""expr : SYMBOL"""
p[0] = ("exists", p[1])
def p_func(p):
"""expr : SYMBOL OPAREN arg_intr CPAREN"""
p[0] = [p[1]]
p[0].append(p[3])
def p_arg_intr_single(p):
"""arg_intr : const"""
p[0] = [p[1]]
def p_arg_intr_mult(p):
"""arg_intr : arg_intr COMMA const"""
p[0] = copy.copy(p[1])
p[0].append(p[3])
def p_list(p):
"""list : OBRACKET list_intr CBRACKET"""
p[0] = p[2]
def p_list_intr_single(p):
"""list_intr : const"""
p[0] = [p[1]]
def p_list_intr_mult(p):
"""list_intr : list_intr COMMA const"""
p[0] = copy.copy(p[1])
p[0].append(p[3])
def p_const(p):
"""const : STR
| number"""
p[0] = p[1]
def p_number(p):
"""number : INTEGER
| HEX"""
p[0] = p[1]
def p_error(p):
if p:
raise SyntaxError("Unexpected token '%s'" % p.value)
else:
raise SyntaxError("Unexpected end of expression")
if "PARSETAB_DIR" not in os.environ:
parser = yacc.yacc(debug=0)
else:
parser = yacc.yacc(debug=0, outputdir=os.environ["PARSETAB_DIR"])
def ast_sym(ast, env):
if ast in env:
return str(env[ast])
return ""
def ast_sym_int(ast, env):
if ast in env:
v = env[ast]
if v.startswith("0x") or v.startswith("0X"):
return int(v, 16)
else:
return int(v, 10)
return 0
def ast_expr(ast, env, edt):
if ast[0] == "not":
return not ast_expr(ast[1], env, edt)
elif ast[0] == "or":
return ast_expr(ast[1], env, edt) or ast_expr(ast[2], env, edt)
elif ast[0] == "and":
return ast_expr(ast[1], env, edt) and ast_expr(ast[2], env, edt)
elif ast[0] == "==":
return ast_sym(ast[1], env) == ast[2]
elif ast[0] == "!=":
return ast_sym(ast[1], env) != ast[2]
elif ast[0] == ">":
return ast_sym_int(ast[1], env) > int(ast[2])
elif ast[0] == "<":
return ast_sym_int(ast[1], env) < int(ast[2])
elif ast[0] == ">=":
return ast_sym_int(ast[1], env) >= int(ast[2])
elif ast[0] == "<=":
return ast_sym_int(ast[1], env) <= int(ast[2])
elif ast[0] == "in":
return ast_sym(ast[1], env) in ast[2]
elif ast[0] == "exists":
return bool(ast_sym(ast[1], env))
elif ast[0] == ":":
return bool(re.match(ast[2], ast_sym(ast[1], env)))
elif ast[0] == "dt_compat_enabled":
compat = ast[1][0]
for node in edt.nodes:
if compat in node.compats and node.status == "okay":
return True
return False
elif ast[0] == "dt_alias_exists":
alias = ast[1][0]
for node in edt.nodes:
if alias in node.aliases and node.status == "okay":
return True
return False
elif ast[0] == "dt_compat_enabled_with_alias":
compat = ast[1][0]
alias = ast[1][1]
for node in edt.nodes:
if node.status == "okay" and alias in node.aliases and node.matching_compat == compat:
return True
return False
mutex = threading.Lock()
def parse(expr_text, env, edt):
"""Given a text representation of an expression in our language,
use the provided environment to determine whether the expression
is true or false"""
# Like it's C counterpart, state machine is not thread-safe
mutex.acquire()
try:
ast = parser.parse(expr_text)
finally:
mutex.release()
return ast_expr(ast, env, edt)
# Just some test code
if __name__ == "__main__":
local_env = {
"A" : "1",
"C" : "foo",
"D" : "20",
"E" : 0x100,
"F" : "baz"
}
for line in open(sys.argv[1]).readlines():
lex.input(line)
for tok in iter(lex.token, None):
print(tok.type, tok.value)
parser = yacc.yacc()
print(parser.parse(line))
print(parse(line, local_env, None))