mirror of
https://github.com/facebook/proxygen.git
synced 2025-08-08 18:02:05 +03:00
Summary: Add support for overriding os, distro and distro version to command line when inspecting system packages so one can requested see ubuntu 18.04 package from other OS. Makes testing easier Used shlex to shell unquote the value to be tested in the getdeps expression evaluator. getdeps expression parser didn't tolerate 18.04 as . is special char to getdeps expressions, needed to be "18.04" Reviewed By: quark-zju Differential Revision: D33741323 fbshipit-source-id: d83397c7fb5180a4d985d0d8ae7b3ff33b72f828
185 lines
4.8 KiB
Python
185 lines
4.8 KiB
Python
# Copyright (c) Meta Platforms, Inc. and affiliates.
|
|
#
|
|
# This source code is licensed under the MIT license found in the
|
|
# LICENSE file in the root directory of this source tree.
|
|
|
|
import re
|
|
import shlex
|
|
|
|
|
|
def parse_expr(expr_text, valid_variables):
|
|
"""parses the simple criteria expression syntax used in
|
|
dependency specifications.
|
|
Returns an ExprNode instance that can be evaluated like this:
|
|
|
|
```
|
|
expr = parse_expr("os=windows")
|
|
ok = expr.eval({
|
|
"os": "windows"
|
|
})
|
|
```
|
|
|
|
Whitespace is allowed between tokens. The following terms
|
|
are recognized:
|
|
|
|
KEY = VALUE # Evaluates to True if ctx[KEY] == VALUE
|
|
not(EXPR) # Evaluates to True if EXPR evaluates to False
|
|
# and vice versa
|
|
all(EXPR1, EXPR2, ...) # Evaluates True if all of the supplied
|
|
# EXPR's also evaluate True
|
|
any(EXPR1, EXPR2, ...) # Evaluates True if any of the supplied
|
|
# EXPR's also evaluate True, False if
|
|
# none of them evaluated true.
|
|
"""
|
|
|
|
p = Parser(expr_text, valid_variables)
|
|
return p.parse()
|
|
|
|
|
|
class ExprNode(object):
|
|
def eval(self, ctx):
|
|
return False
|
|
|
|
|
|
class TrueExpr(ExprNode):
|
|
def eval(self, ctx):
|
|
return True
|
|
|
|
def __str__(self):
|
|
return "true"
|
|
|
|
|
|
class NotExpr(ExprNode):
|
|
def __init__(self, node):
|
|
self._node = node
|
|
|
|
def eval(self, ctx):
|
|
return not self._node.eval(ctx)
|
|
|
|
def __str__(self):
|
|
return "not(%s)" % self._node
|
|
|
|
|
|
class AllExpr(ExprNode):
|
|
def __init__(self, nodes):
|
|
self._nodes = nodes
|
|
|
|
def eval(self, ctx):
|
|
for node in self._nodes:
|
|
if not node.eval(ctx):
|
|
return False
|
|
return True
|
|
|
|
def __str__(self):
|
|
items = []
|
|
for node in self._nodes:
|
|
items.append(str(node))
|
|
return "all(%s)" % ",".join(items)
|
|
|
|
|
|
class AnyExpr(ExprNode):
|
|
def __init__(self, nodes):
|
|
self._nodes = nodes
|
|
|
|
def eval(self, ctx):
|
|
for node in self._nodes:
|
|
if node.eval(ctx):
|
|
return True
|
|
return False
|
|
|
|
def __str__(self):
|
|
items = []
|
|
for node in self._nodes:
|
|
items.append(str(node))
|
|
return "any(%s)" % ",".join(items)
|
|
|
|
|
|
class EqualExpr(ExprNode):
|
|
def __init__(self, key, value):
|
|
self._key = key
|
|
self._value = value
|
|
|
|
def eval(self, ctx):
|
|
return ctx.get(self._key) == self._value
|
|
|
|
def __str__(self):
|
|
return "%s=%s" % (self._key, self._value)
|
|
|
|
|
|
class Parser(object):
|
|
def __init__(self, text, valid_variables):
|
|
self.text = text
|
|
self.lex = shlex.shlex(text)
|
|
self.valid_variables = valid_variables
|
|
|
|
def parse(self):
|
|
expr = self.top()
|
|
garbage = self.lex.get_token()
|
|
if garbage != "":
|
|
raise Exception(
|
|
"Unexpected token %s after EqualExpr in %s" % (garbage, self.text)
|
|
)
|
|
return expr
|
|
|
|
def top(self):
|
|
name = self.ident()
|
|
op = self.lex.get_token()
|
|
|
|
if op == "(":
|
|
parsers = {
|
|
"not": self.parse_not,
|
|
"any": self.parse_any,
|
|
"all": self.parse_all,
|
|
}
|
|
func = parsers.get(name)
|
|
if not func:
|
|
raise Exception("invalid term %s in %s" % (name, self.text))
|
|
return func()
|
|
|
|
if op == "=":
|
|
if name not in self.valid_variables:
|
|
raise Exception("unknown variable %r in expression" % (name,))
|
|
# remove shell quote from value so can test things with period in them, e.g "18.04"
|
|
unquoted = " ".join(shlex.split(self.lex.get_token()))
|
|
return EqualExpr(name, unquoted)
|
|
|
|
raise Exception(
|
|
"Unexpected token sequence '%s %s' in %s" % (name, op, self.text)
|
|
)
|
|
|
|
def ident(self):
|
|
ident = self.lex.get_token()
|
|
if not re.match("[a-zA-Z]+", ident):
|
|
raise Exception("expected identifier found %s" % ident)
|
|
return ident
|
|
|
|
def parse_not(self):
|
|
node = self.top()
|
|
expr = NotExpr(node)
|
|
tok = self.lex.get_token()
|
|
if tok != ")":
|
|
raise Exception("expected ')' found %s" % tok)
|
|
return expr
|
|
|
|
def parse_any(self):
|
|
nodes = []
|
|
while True:
|
|
nodes.append(self.top())
|
|
tok = self.lex.get_token()
|
|
if tok == ")":
|
|
break
|
|
if tok != ",":
|
|
raise Exception("expected ',' or ')' but found %s" % tok)
|
|
return AnyExpr(nodes)
|
|
|
|
def parse_all(self):
|
|
nodes = []
|
|
while True:
|
|
nodes.append(self.top())
|
|
tok = self.lex.get_token()
|
|
if tok == ")":
|
|
break
|
|
if tok != ",":
|
|
raise Exception("expected ',' or ')' but found %s" % tok)
|
|
return AllExpr(nodes)
|