diff options
author | Mathieu Duponchelle <mathieu@centricular.com> | 2018-04-28 01:56:56 +0200 |
---|---|---|
committer | Mathieu Duponchelle <mathieu@centricular.com> | 2018-05-20 21:19:44 +0200 |
commit | ecb88380827e33161a29c5ca6f3448a6cdd557a5 (patch) | |
tree | e31280e1cf6dee24fccd716c41ccef08d437164a /mesonbuild | |
parent | 7e8c099387ffcdfbdc55e3ef550cf7e8ab0e848d (diff) | |
download | meson-ecb88380827e33161a29c5ca6f3448a6cdd557a5.zip meson-ecb88380827e33161a29c5ca6f3448a6cdd557a5.tar.gz meson-ecb88380827e33161a29c5ca6f3448a6cdd557a5.tar.bz2 |
Add new built-in type, dict
For now dicts are immutable, and do not expose any methods,
they however support "native" syntax such as [] lookup,
and foreach iterating, and can be printed.
Diffstat (limited to 'mesonbuild')
-rw-r--r-- | mesonbuild/interpreter.py | 4 | ||||
-rw-r--r-- | mesonbuild/interpreterbase.py | 60 | ||||
-rw-r--r-- | mesonbuild/mparser.py | 57 |
3 files changed, 102 insertions, 19 deletions
diff --git a/mesonbuild/interpreter.py b/mesonbuild/interpreter.py index 49e9381..4118a96 100644 --- a/mesonbuild/interpreter.py +++ b/mesonbuild/interpreter.py @@ -45,6 +45,8 @@ permitted_method_kwargs = { def stringifyUserArguments(args): if isinstance(args, list): return '[%s]' % ', '.join([stringifyUserArguments(x) for x in args]) + elif isinstance(args, dict): + return '{%s}' % ', '.join(['%s : %s' % (stringifyUserArguments(k), stringifyUserArguments(v)) for k, v in args.items()]) elif isinstance(args, int): return str(args) elif isinstance(args, str): @@ -2273,6 +2275,8 @@ to directly access options of other subprojects.''') arg = posargs[0] if isinstance(arg, list): argstr = stringifyUserArguments(arg) + elif isinstance(arg, dict): + argstr = stringifyUserArguments(arg) elif isinstance(arg, str): argstr = arg elif isinstance(arg, int): diff --git a/mesonbuild/interpreterbase.py b/mesonbuild/interpreterbase.py index 60b0465..edc399d 100644 --- a/mesonbuild/interpreterbase.py +++ b/mesonbuild/interpreterbase.py @@ -265,6 +265,8 @@ class InterpreterBase: return self.evaluate_comparison(cur) elif isinstance(cur, mparser.ArrayNode): return self.evaluate_arraystatement(cur) + elif isinstance(cur, mparser.DictNode): + return self.evaluate_dictstatement(cur) elif isinstance(cur, mparser.NumberNode): return cur.value elif isinstance(cur, mparser.AndNode): @@ -296,6 +298,12 @@ class InterpreterBase: raise InvalidCode('Keyword arguments are invalid in array construction.') return arguments + def evaluate_dictstatement(self, cur): + (arguments, kwargs) = self.reduce_arguments(cur.args) + if len(arguments) > 0: + raise InvalidCode('Only key:value pairs are valid in dict construction.') + return kwargs + def evaluate_notstatement(self, cur): v = self.evaluate_statement(cur.value) if not isinstance(v, bool): @@ -444,15 +452,28 @@ The result of this is undefined and will become a hard error in a future Meson r def evaluate_foreach(self, node): assert(isinstance(node, mparser.ForeachClauseNode)) - varname = node.varname.value items = self.evaluate_statement(node.items) - if is_disabler(items): - return items - if not isinstance(items, list): - raise InvalidArguments('Items of foreach loop is not an array') - for item in items: - self.set_variable(varname, item) - self.evaluate_codeblock(node.block) + + if isinstance(items, list): + if len(node.varnames) != 1: + raise InvalidArguments('Foreach on array does not unpack') + varname = node.varnames[0].value + if is_disabler(items): + return items + for item in items: + self.set_variable(varname, item) + self.evaluate_codeblock(node.block) + elif isinstance(items, dict): + if len(node.varnames) != 2: + raise InvalidArguments('Foreach on dict unpacks key and value') + if is_disabler(items): + return items + for key, value in items.items(): + self.set_variable(node.varnames[0].value, key) + self.set_variable(node.varnames[1].value, value) + self.evaluate_codeblock(node.block) + else: + raise InvalidArguments('Items of foreach loop must be an array or a dict') def evaluate_plusassign(self, node): assert(isinstance(node, mparser.PlusAssignmentNode)) @@ -491,12 +512,21 @@ The result of this is undefined and will become a hard error in a future Meson r raise InterpreterException( 'Tried to index an object that doesn\'t support indexing.') index = self.evaluate_statement(node.index) - if not isinstance(index, int): - raise InterpreterException('Index value is not an integer.') - try: - return iobject[index] - except IndexError: - raise InterpreterException('Index %d out of bounds of array of size %d.' % (index, len(iobject))) + + if isinstance(iobject, dict): + if not isinstance(index, str): + raise InterpreterException('Key is not a string') + try: + return iobject[index] + except KeyError: + raise InterpreterException('Key %s is not in dict' % index) + else: + if not isinstance(index, int): + raise InterpreterException('Index value is not an integer.') + try: + return iobject[index] + except IndexError: + raise InterpreterException('Index %d out of bounds of array of size %d.' % (index, len(iobject))) def function_call(self, node): func_name = node.func_name @@ -741,7 +771,7 @@ To specify a keyword argument, use : instead of =.''') def is_assignable(self, value): return isinstance(value, (InterpreterObject, dependencies.Dependency, - str, int, list, mesonlib.File)) + str, int, list, dict, mesonlib.File)) def is_elementary_type(self, v): return isinstance(v, (int, float, str, bool, list)) diff --git a/mesonbuild/mparser.py b/mesonbuild/mparser.py index 8cef377..fb1058c 100644 --- a/mesonbuild/mparser.py +++ b/mesonbuild/mparser.py @@ -104,6 +104,8 @@ class Lexer: ('rparen', re.compile(r'\)')), ('lbracket', re.compile(r'\[')), ('rbracket', re.compile(r'\]')), + ('lcurl', re.compile(r'\{')), + ('rcurl', re.compile(r'\}')), ('dblquote', re.compile(r'"')), ('string', re.compile(r"'([^'\\]|(\\.))*'")), ('comma', re.compile(r',')), @@ -134,6 +136,7 @@ class Lexer: loc = 0 par_count = 0 bracket_count = 0 + curl_count = 0 col = 0 while loc < len(self.code): matched = False @@ -160,6 +163,10 @@ class Lexer: bracket_count += 1 elif tid == 'rbracket': bracket_count -= 1 + elif tid == 'lcurl': + curl_count += 1 + elif tid == 'rcurl': + curl_count -= 1 elif tid == 'dblquote': raise ParseException('Double quotes are not supported. Use single quotes.', self.getline(line_start), lineno, col) elif tid == 'string': @@ -187,7 +194,7 @@ This will become a hard error in a future Meson release.""", self.getline(line_s elif tid == 'eol' or tid == 'eol_cont': lineno += 1 line_start = loc - if par_count > 0 or bracket_count > 0: + if par_count > 0 or bracket_count > 0 or curl_count > 0: break elif tid == 'id': if match_text in self.keywords: @@ -241,6 +248,13 @@ class ArrayNode: self.colno = args.colno self.args = args +class DictNode: + def __init__(self, args): + self.subdir = args.subdir + self.lineno = args.lineno + self.colno = args.colno + self.args = args + class EmptyNode: def __init__(self, lineno, colno): self.subdir = '' @@ -340,10 +354,10 @@ class PlusAssignmentNode: self.value = value class ForeachClauseNode: - def __init__(self, lineno, colno, varname, items, block): + def __init__(self, lineno, colno, varnames, items, block): self.lineno = lineno self.colno = colno - self.varname = varname + self.varnames = varnames self.items = items self.block = block @@ -601,6 +615,10 @@ class Parser: args = self.args() self.block_expect('rbracket', block_start) return ArrayNode(args) + elif self.accept('lcurl'): + key_values = self.key_values() + self.block_expect('rcurl', block_start) + return DictNode(key_values) else: return self.e9() @@ -618,6 +636,30 @@ class Parser: return StringNode(t) return EmptyNode(self.current.lineno, self.current.colno) + def key_values(self): + s = self.statement() + a = ArgumentNode(s) + + while not isinstance(s, EmptyNode): + potential = self.current + if self.accept('comma'): + a.commas.append(potential) + a.append(s) + elif self.accept('colon'): + if not isinstance(s, StringNode): + raise ParseException('Key must be a string.', + self.getline(), s.lineno, s.colno) + a.set_kwarg(s.value, self.statement()) + potential = self.current + if not self.accept('comma'): + return a + a.commas.append(potential) + else: + a.append(s) + return a + s = self.statement() + return a + def args(self): s = self.statement() a = ArgumentNode(s) @@ -664,10 +706,17 @@ class Parser: t = self.current self.expect('id') varname = t + varnames = [t] + + if (self.accept('comma')): + t = self.current + self.expect('id') + varnames.append(t) + self.expect('colon') items = self.statement() block = self.codeblock() - return ForeachClauseNode(varname.lineno, varname.colno, varname, items, block) + return ForeachClauseNode(varname.lineno, varname.colno, varnames, items, block) def ifblock(self): condition = self.statement() |