Skip to content

Commit

Permalink
Minor fixes for map key/value assignments.
Browse files Browse the repository at this point in the history
  • Loading branch information
Alexhuszagh committed Oct 17, 2022
1 parent 3bddc3a commit e5970d6
Show file tree
Hide file tree
Showing 5 changed files with 94 additions and 19 deletions.
61 changes: 54 additions & 7 deletions docker/android/android/soong.py
Original file line number Diff line number Diff line change
Expand Up @@ -196,9 +196,9 @@ def pairs(self, prod):
def pairs(self, prod):
return [prod.pair]

@_('ident COLON expr')
@_('ident COLON expr', 'ident EQUALS expr')
def pair(self, prod):
return (prod.ident, prod.expr)
return (prod.ident, MapValue(prod[1], prod.expr))

@_('ident', 'binary_operator', 'map', 'list', 'string', 'integer', 'bool')
def expr(self, prod):
Expand Down Expand Up @@ -272,6 +272,9 @@ def is_map(self):
def is_list(self):
return False

def is_map_value(self):
return False

def is_ident(self):
return False

Expand Down Expand Up @@ -390,6 +393,12 @@ def to_str(self, pretty=True, indent=4, depth=0):
def is_binary_operator(self):
return True

def str_op(self, cmp):
return (
(self.lhs.is_string() and self.lhs.str_op(cmp))
or (self.rhs.is_string() and self.rhs.str_op(cmp))
)

def __eq__(self, other):
return (self.lhs, self.op, self.rhs) == (other.lhs, other.op, other.rhs)

Expand Down Expand Up @@ -434,7 +443,7 @@ def __str__(self):
def to_str(self, pretty=True, indent=4, depth=0):
fmt = lambda x: x.to_str(pretty, indent, depth + 1)
result = '{'
pairs = [f'{fmt(k)}: {fmt(v)}' for k, v in self.items()]
pairs = [f'{fmt(k)}{fmt(v)}' for k, v in self.items()]
if len(self) == 0:
result += '}'
elif pretty:
Expand All @@ -454,13 +463,13 @@ def is_test(self):
name = self.get('name')
if name is None:
return False
return 'test' in name.lower()
return 'test' in name.value.lower()

def is_benchmark(self):
name = self.get('name')
if name is None:
return False
return 'benchmark' in name.lower()
return 'benchmark' in name.value.lower()

def is_dev(self):
return self.is_test() or self.is_benchmark()
Expand All @@ -475,8 +484,8 @@ def recurse(self, max_depth=-1, depth=0):
if depth != max_depth:
for key, value in self.items():
yield (key, value, depth + 1, self)
if value.is_map():
yield from value.recurse(max_depth, depth + 1)
if value.value.is_map():
yield from value.value.recurse(max_depth, depth + 1)


class List(list, Node):
Expand Down Expand Up @@ -507,6 +516,41 @@ def filter(self, op):
self[:] = [i for i in self if op(i)]


class MapValue(Node):
def __init__(self, delimiter, value):
# map key/value separators can be `:` or `=`.
assert delimiter in (':', '=')
self.delimiter = delimiter
self.value = value

def __repr__(self):
return f'MapValue({str(self)})'

def __str__(self):
return self.to_str(False)

def __eq__(self, other):
# delimiter doesn't matter for equality comparison
if isinstance(other, MapValue):
return self.value == other.value
return self.value == other

def __len__(self):
return len(self.value)

def to_str(self, pretty=True, indent=4, depth=0):
value = self.value.to_str(pretty, indent, depth)
if self.delimiter == '=':
return f' = {value}'
return f': {value}'

def is_map_value(self):
return True

def filter(self, op):
self.value.filter(op)


class Ident(str, Node):
def __repr__(self):
return f'Ident({str(self)})'
Expand All @@ -528,6 +572,9 @@ def __repr__(self):
def to_str(self, *_, **__):
return f'{super().__str__()}'

def str_op(self, cmp):
return cmp(self)

def __str__(self):
# `"target"` should be shown as `'target'`, not `'"target"'`
return super().__str__()[1:-1]
Expand Down
9 changes: 5 additions & 4 deletions docker/android/scripts/build-system.py
Original file line number Diff line number Diff line change
Expand Up @@ -50,10 +50,11 @@ def remove_soong_tests(path, args, *_):
# or those with `{name: "test"}`, etc.
ast.filter(lambda x: not (x.is_scope() and x.is_dev()))
# need to remove test and benchmark subdirs
test_names = ('test', 'benchmark')
subdirs = [i for i in ast if i.name == 'subdirs']
for subdir in subdirs:
assert type(subdir.expr) is android.soong.List
subdir.expr.filter(lambda x: not any(i in x.lower() for i in ('test', 'benchmark')))
for sub in subdirs:
assert type(sub.expr) is android.soong.List
sub.expr.filter(lambda x: x.str_op(lambda y: not any(i in y.lower() for i in test_names)))
# remove gtest dependencies from regular targets.
for node in ast:
map = None
Expand All @@ -65,7 +66,7 @@ def remove_soong_tests(path, args, *_):
map = node.expr
for key, value, *_ in map.recurse():
if value.is_list():
value.filter(lambda x: 'libgtest' not in x.lower())
value.filter(lambda x: x.str_op(lambda y: 'libgtest' not in y.lower()))

with open(path, 'w') as file:
ast.dump(file)
Expand Down
4 changes: 4 additions & 0 deletions docker/android/tests/Android.bp
Original file line number Diff line number Diff line change
Expand Up @@ -64,4 +64,8 @@ cc_test_host {
"-l" + "z",
],
shared_libs: [],
cflags = [
"-Wall",
"-fstrict-aliasing",
],
}
32 changes: 26 additions & 6 deletions docker/android/tests/test_soong.py
Original file line number Diff line number Diff line change
Expand Up @@ -133,6 +133,11 @@ def test_addition():
assert ast[26].expr[2].rhs == 'test'
assert ast[26].expr[2].rhs.is_ident()

# test a few binops, just in case
binop = ast[26].expr[1]
assert binop.str_op(lambda x: 'test' in x.lower())
assert binop.lhs.str_op(lambda x: 'test' in x.lower())


def test_empty():
path = os.path.join(TEST_DIR, 'Empty.bp')
Expand All @@ -143,19 +148,20 @@ def test_empty():
def test_ast():
array = soong.List([soong.String('"value1"'), soong.String('"value2"')])
assignment = soong.Assignment(soong.Ident('name'), array)
map = soong.Map({soong.Ident('key'): soong.String('"value"')})
value = soong.MapValue('=', soong.String('"value"'))
map = soong.Map({soong.Ident('key'): value})
scope = soong.Scope(soong.Ident('name'), map)
ast = soong.Ast([assignment, scope])
assert repr(ast) == '''Ast(name = ["value1", "value2"]
name {key: "value"})'''
name {key = "value"})'''
assert str(ast) == '''name = ["value1", "value2"]
name {key: "value"}'''
name {key = "value"}'''
assert ast.to_str() == '''name = [
"value1",
"value2",
]
name {
key: "value",
key = "value",
}'''


Expand Down Expand Up @@ -196,7 +202,8 @@ def test_binary_operator():


def test_scope():
map = soong.Map({soong.Ident('key'): soong.String('"value"')})
value = soong.MapValue(':', soong.String('"value"'))
map = soong.Map({soong.Ident('key'): value})
scope = soong.Scope(soong.Ident('name'), map)
assert repr(scope) == 'Scope(name {key: "value"})'
assert str(scope) == 'name {key: "value"}'
Expand All @@ -210,7 +217,8 @@ def test_scope():


def test_map():
map = soong.Map({soong.Ident('key'): soong.String('"value"')})
value = soong.MapValue(':', soong.String('"value"'))
map = soong.Map({soong.Ident('key'): value})
assert repr(map) == 'Map({key: "value"})'
assert str(map) == '{key: "value"}'
assert map.to_str(pretty=False) == '{key: "value"}'
Expand Down Expand Up @@ -266,6 +274,18 @@ def test_list():
assert sequence.to_str() == '[]'


def test_map_value():
value = soong.MapValue(':', soong.String('"value"'))
assert repr(value) == 'MapValue(: "value")'
assert str(value) == ': "value"'
assert value.to_str() == ': "value"'

value = soong.MapValue('=', soong.String('"value"'))
assert repr(value) == 'MapValue( = "value")'
assert str(value) == ' = "value"'
assert value.to_str() == ' = "value"'


def test_ident():
ident = soong.Ident('name')
assert repr(ident) == 'Ident(name)'
Expand Down
7 changes: 5 additions & 2 deletions docker/android/tox.ini
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,11 @@ passenv =

[flake8]
max-line-length = 100
# we use lambdas for short, one-line conditions and formatters
ignore = E731
ignore =
# we use lambdas for short, one-line conditions and formatters
E731
# opt-in to new behavior with operators after line breaks
W503
per-file-ignores =
# the sly grammar uses variables before they are defined via a metaclass
# likewise, it uses redefinitions to extend parsers via SLR grammar
Expand Down

0 comments on commit e5970d6

Please sign in to comment.