dict: Fully evaluate keys

The only restriction is keys must be string after evaluation. This fix
various inconsistencies.
pull/6289/head
Xavier Claessens 5 years ago committed by Xavier Claessens
parent b1c8f765fa
commit 9b1a857473
  1. 12
      docs/markdown/Syntax.md
  2. 20
      docs/markdown/snippets/add_dictionary_variable_key.md
  3. 25
      mesonbuild/interpreterbase.py
  4. 14
      mesonbuild/mparser.py
  5. 36
      test cases/common/193 dict/meson.build
  6. 21
      test cases/common/228 add dict variable key/meson.build

@ -324,8 +324,8 @@ Dictionaries
--
Dictionaries are delimited by curly braces. A dictionary can contain an
arbitrary number of key value pairs. Keys are required to be literal
strings, values can be objects of any type.
arbitrary number of key value pairs. Keys are required to be strings, values can
be objects of any type. Prior to *0.53.0* keys were required to be literal strings.
```meson
my_dict = {'foo': 42, 'bar': 'baz'}
@ -359,6 +359,14 @@ if 'foo' not in my_dict
endif
```
*Since 0.53.0* Keys can be any expression evaluating to a string value, not limited
to string literals any more.
```meson
d = {'a' + 'b' : 42}
k = 'cd'
d += {k : 43}
```
Function calls
--

@ -1,17 +1,9 @@
## Adding dictionary entry using string variable as key
New dictionary entry can now be added using string variable as key,
in addition to using string literal as key.
## Dictionary entry using string variable as key
Keys can now be any expression evaluating to a string value, not limited
to string literals any more.
```meson
dict = {}
# A variable to be used as a key
key = 'myKey'
# Add new entry using the variable
dict += {key : 'myValue'}
# Test that the stored value is correct
assert(dict[key] == 'myValue', 'Incorrect value retrieved from dictionary')
d = {'a' + 'b' : 42}
k = 'cd'
d += {k : 43}
```

@ -496,7 +496,19 @@ class InterpreterBase:
def evaluate_dictstatement(self, cur):
(arguments, kwargs) = self.reduce_arguments(cur.args)
assert (not arguments)
return kwargs
result = {}
self.argument_depth += 1
for key, value in kwargs.items():
if not isinstance(key, mparser.StringNode):
FeatureNew('Dictionary entry using non literal key', '0.53.0').use(self.subproject)
key = self.evaluate_statement(key)
if not isinstance(key, str):
raise InvalidArguments('Key must be a string')
if key in result:
raise InvalidArguments('Duplicate dictionary key: {}'.format(key))
result[key] = value
self.argument_depth -= 1
return result
def evaluate_notstatement(self, cur):
v = self.evaluate_statement(cur.value)
@ -731,16 +743,7 @@ The result of this is undefined and will become a hard error in a future Meson r
elif isinstance(old_variable, dict):
if not isinstance(addition, dict):
raise InvalidArguments('The += operator requires a dict on the right hand side if the variable on the left is a dict')
new_addition = {}
for (key, value) in addition.items():
if isinstance(key, str):
new_addition[key] = value
elif isinstance(key, mparser.IdNode) and isinstance(self.get_variable(key.value), str):
FeatureNew('Adding dictionary entry using string variable as key', '0.53.0').use(self.subproject)
new_addition[self.get_variable(key.value)] = value
else:
raise InvalidArguments('Dictionary key must be a string or string variable')
new_value = {**old_variable, **new_addition}
new_value = {**old_variable, **addition}
# Add other data types here.
else:
raise InvalidArguments('The += operator currently only works with arrays, dicts, strings or ints ')

@ -676,19 +676,7 @@ class Parser:
while not isinstance(s, EmptyNode):
potential = self.current
if self.accept('colon'):
key_value = self.statement()
if isinstance(s, StringNode):
if s.value in a.kwargs:
# + 1 to colno to point to the actual string, not the opening quote
raise ParseException('Duplicate dictionary key: {}'.format(s.value), self.getline(), s.lineno, s.colno + 1)
a.set_kwarg(s.value, key_value)
elif isinstance(s, IdNode) and isinstance(s.value, str):
for key in a.kwargs:
if s.value == key.value:
raise ParseException('Duplicate dictionary variable key: {}'.format(s.value), self.getline(), s.lineno, s.colno)
a.set_kwarg(s, key_value)
else:
raise ParseException('Key must be a string or string variable', self.getline(), s.lineno, s.colno)
a.set_kwarg(s, self.statement())
potential = self.current
if not self.accept('comma'):
return a

@ -33,3 +33,39 @@ d3 = d2
d3 += {'e' : 'f'}
assert(d3 == {'a' : 'b2', 'c' : 'd', 'e' : 'f'}, 'dict plusassign is not working')
assert(d2 == {'a' : 'b2', 'c' : 'd'}, 'dict should be immutable')
dict1 = {}
# A variable to be used as a key
testkey1 = 'myKey1'
testkey2 = 'myKey2'
# Add new entry using the variable
dict1 += {testkey1 : 'myValue'}
dict1 += {testkey2 : 42}
# Test that the stored values are correct
assert(dict1[testkey1] == 'myValue',
'Incorrect string value retrieved from dictionary - variable key')
assert(dict1['myKey1'] == 'myValue',
'Incorrect string value retrieved from dictionary - literal key')
assert(dict1[testkey2] == 42,
'Incorrect int value retrieved from dictionary - variable key')
assert(dict1['myKey2'] == 42,
'Incorrect int value retrieved from dictionary - literal key')
d = {testkey1 : 1}
assert(d[testkey1] == 1,
'Incorrect int value retrieved from dictionary - variable key')
assert(d['myKey1'] == 1,
'Incorrect int value retrieved from dictionary - literal key')
d = {'1' / '2' : 1, join_paths('a', 'b') : 2}
k1 = '1' / '2'
k2 = join_paths('a', 'b')
assert(d[k1] == 1, 'Incorrect expression evaluation in dictionary key')
assert(d[k2] == 2, 'Incorrect expression evaluation in dictionary key')
d = {'a' + 'b' : 1}
assert(d['a' + 'b'] == 1, 'Incorrect expression evaluation in dictionary key')
assert(d['ab'] == 1, 'Incorrect expression evaluation in dictionary key')

@ -1,21 +0,0 @@
project('add dictionary entry using string variable as key', meson_version: '>=0.52')
dict1 = {}
# A variable to be used as a key
testkey1 = 'myKey1'
testkey2 = 'myKey2'
# Add new entry using the variable
dict1 += {testkey1 : 'myValue'}
dict1 += {testkey2 : 42}
# Test that the stored values are correct
assert(dict1[testkey1] == 'myValue',
'Incorrect string value retrieved from dictionary - variable key')
assert(dict1['myKey1'] == 'myValue',
'Incorrect string value retrieved from dictionary - literal key')
assert(dict1[testkey2] == 42,
'Incorrect int value retrieved from dictionary - variable key')
assert(dict1['myKey2'] == 42,
'Incorrect int value retrieved from dictionary - literal key')
Loading…
Cancel
Save