From b5b952688f03d69e947c144f4d928145e6e47a5f Mon Sep 17 00:00:00 2001 From: Xavier Claessens Date: Fri, 9 Jun 2023 23:20:48 -0400 Subject: [PATCH] cargo: builder: Remove all duplicated functions Keep only the Builder class, there is no point in duplicating everything. --- mesonbuild/cargo/builder.py | 218 +++++++------------------------- mesonbuild/cargo/cfg.py | 58 ++++----- mesonbuild/cargo/interpreter.py | 5 +- unittests/cargotests.py | 102 +++++++-------- 4 files changed, 123 insertions(+), 260 deletions(-) diff --git a/mesonbuild/cargo/builder.py b/mesonbuild/cargo/builder.py index 3f7f6885e..66fd00c27 100644 --- a/mesonbuild/cargo/builder.py +++ b/mesonbuild/cargo/builder.py @@ -17,175 +17,26 @@ if T.TYPE_CHECKING: import builtins -def _token(tid: str, filename: str, value: mparser.TV_TokenTypes) -> mparser.Token[mparser.TV_TokenTypes]: - """Create a Token object, but with the line numbers stubbed out. - - :param tid: the token id (such as string, number, etc) - :param filename: the filename that the token was generated from - :param value: the value of the token - :return: A Token object - """ - return mparser.Token(tid, filename, -1, -1, -1, (-1, -1), value) - - -def _symbol(filename: str, val: str) -> mparser.SymbolNode: - return mparser.SymbolNode(_token('', filename, val)) - - -def string(value: str, filename: str) -> mparser.StringNode: - """Build A StringNode - - :param value: the value of the string - :param filename: the file that the value came from - :return: A StringNode - """ - return mparser.StringNode(_token('string', filename, value)) - - -def number(value: int, filename: str) -> mparser.NumberNode: - """Build A NumberNode - - :param value: the value of the number - :param filename: the file that the value came from - :return: A NumberNode - """ - return mparser.NumberNode(_token('number', filename, str(value))) - - -def bool(value: builtins.bool, filename: str) -> mparser.BooleanNode: - """Build A BooleanNode - - :param value: the value of the boolean - :param filename: the file that the value came from - :return: A BooleanNode - """ - return mparser.BooleanNode(_token('bool', filename, value)) - - -def array(value: T.List[mparser.BaseNode], filename: str) -> mparser.ArrayNode: - """Build an Array Node - - :param value: A list of nodes to insert into the array - :param filename: The file the array is from - :return: An ArrayNode built from the arguments - """ - args = mparser.ArgumentNode(_token('array', filename, 'unused')) - args.arguments = value - return mparser.ArrayNode(_symbol(filename, '['), args, _symbol(filename, ']')) - - -def identifier(value: str, filename: str) -> mparser.IdNode: - """Build A IdNode - - :param value: the value of the boolean - :param filename: the file that the value came from - :return: A BooleanNode - """ - return mparser.IdNode(_token('id', filename, value)) - - -def method(name: str, id_: mparser.IdNode, - pos: T.Optional[T.List[mparser.BaseNode]] = None, - kw: T.Optional[T.Mapping[str, mparser.BaseNode]] = None, - ) -> mparser.MethodNode: - """Create a method call. - - :param name: the name of the method - :param id_: the object to call the method of - :param pos: a list of positional arguments, defaults to None - :param kw: a dictionary of keyword arguments, defaults to None - :return: a method call object - """ - args = mparser.ArgumentNode(_token('array', id_.filename, 'unused')) - if pos is not None: - args.arguments = pos - if kw is not None: - args.kwargs = {identifier(k, id_.filename): v for k, v in kw.items()} - return mparser.MethodNode(id_, _symbol(id_.filename, '.'), identifier(name, id_.filename), _symbol(id_.filename, '('), args, _symbol(id_.filename, ')')) - - -def function(name: str, filename: str, - pos: T.Optional[T.List[mparser.BaseNode]] = None, - kw: T.Optional[T.Mapping[str, mparser.BaseNode]] = None, - ) -> mparser.FunctionNode: - """Create a function call. - - :param name: the name of the function - :param filename: The name of the current file being evaluated - :param pos: a list of positional arguments, defaults to None - :param kw: a dictionary of keyword arguments, defaults to None - :return: a method call object - """ - args = mparser.ArgumentNode(_token('array', filename, 'unused')) - if pos is not None: - args.arguments = pos - if kw is not None: - args.kwargs = {identifier(k, filename): v for k, v in kw.items()} - return mparser.FunctionNode(identifier(name, filename), _symbol(filename, '('), args, _symbol(filename, ')')) - - -def equal(lhs: mparser.BaseNode, rhs: mparser.BaseNode) -> mparser.ComparisonNode: - """Create an equality operation - - :param lhs: The left hand side of the equal - :param rhs: the right hand side of the equal - :return: A compraison node - """ - return mparser.ComparisonNode('==', lhs, _symbol(lhs.filename, '=='), rhs) - - -def or_(lhs: mparser.BaseNode, rhs: mparser.BaseNode) -> mparser.OrNode: - """Create and OrNode - - :param lhs: The Left of the Node - :param rhs: The Right of the Node - :return: The OrNode - """ - return mparser.OrNode(lhs, _symbol(lhs.filename, 'or'), rhs) - - -def and_(lhs: mparser.BaseNode, rhs: mparser.BaseNode) -> mparser.AndNode: - """Create an AndNode - - :param lhs: The left of the And - :param rhs: The right of the And - :return: The AndNode - """ - return mparser.AndNode(lhs, _symbol(lhs.filename, 'and'), rhs) - - -def not_(value: mparser.BaseNode, filename: str) -> mparser.NotNode: - """Create a not node - - :param value: The value to negate - :param filename: the string filename - :return: The NotNode - """ - return mparser.NotNode(_token('not', filename, ''), _symbol(filename, 'not'), value) - - -def assign(value: mparser.BaseNode, varname: str, filename: str) -> mparser.AssignmentNode: - """Create an AssignmentNode - - :param value: The rvalue - :param varname: The lvalue - :param filename: The filename - :return: An AssignmentNode - """ - return mparser.AssignmentNode(identifier(varname, filename), _symbol(filename, '='), value) - - -def block(filename: str) -> mparser.CodeBlockNode: - return mparser.CodeBlockNode(_token('node', filename, '')) - - @dataclasses.dataclass class Builder: filename: str + def _token(self, tid: str, value: mparser.TV_TokenTypes) -> mparser.Token[mparser.TV_TokenTypes]: + """Create a Token object, but with the line numbers stubbed out. + + :param tid: the token id (such as string, number, etc) + :param filename: the filename that the token was generated from + :param value: the value of the token + :return: A Token object + """ + return mparser.Token(tid, self.filename, -1, -1, -1, (-1, -1), value) + + def _symbol(self, val: str) -> mparser.SymbolNode: + return mparser.SymbolNode(self._token('', val)) + def assign(self, value: mparser.BaseNode, varname: str) -> mparser.AssignmentNode: - return assign(value, varname, self.filename) + return mparser.AssignmentNode(self.identifier(varname), self._symbol('='), value) def string(self, value: str) -> mparser.StringNode: """Build A StringNode @@ -193,7 +44,7 @@ class Builder: :param value: the value of the string :return: A StringNode """ - return string(value, self.filename) + return mparser.StringNode(self._token('string', value)) def number(self, value: int) -> mparser.NumberNode: """Build A NumberNode @@ -201,7 +52,7 @@ class Builder: :param value: the value of the number :return: A NumberNode """ - return number(value, self.filename) + return mparser.NumberNode(self._token('number', str(value))) def bool(self, value: builtins.bool) -> mparser.BooleanNode: """Build A BooleanNode @@ -209,7 +60,7 @@ class Builder: :param value: the value of the boolean :return: A BooleanNode """ - return bool(value, self.filename) + return mparser.BooleanNode(self._token('bool', value)) def array(self, value: T.List[mparser.BaseNode]) -> mparser.ArrayNode: """Build an Array Node @@ -217,7 +68,9 @@ class Builder: :param value: A list of nodes to insert into the array :return: An ArrayNode built from the arguments """ - return array(value, self.filename) + args = mparser.ArgumentNode(self._token('array', 'unused')) + args.arguments = value + return mparser.ArrayNode(self._symbol('['), args, self._symbol(']')) def identifier(self, value: str) -> mparser.IdNode: """Build A IdNode @@ -225,7 +78,7 @@ class Builder: :param value: the value of the boolean :return: A BooleanNode """ - return identifier(value, self.filename) + return mparser.IdNode(self._token('id', value)) def method(self, name: str, id_: mparser.IdNode, pos: T.Optional[T.List[mparser.BaseNode]] = None, @@ -239,7 +92,12 @@ class Builder: :param kw: a dictionary of keyword arguments, defaults to None :return: a method call object """ - return method(name, id_, pos or [], kw or {}) + args = mparser.ArgumentNode(self._token('array', 'unused')) + if pos is not None: + args.arguments = pos + if kw is not None: + args.kwargs = {self.identifier(k): v for k, v in kw.items()} + return mparser.MethodNode(id_, self._symbol('.'), self.identifier(name), self._symbol('('), args, self._symbol(')')) def function(self, name: str, pos: T.Optional[T.List[mparser.BaseNode]] = None, @@ -252,7 +110,12 @@ class Builder: :param kw: a dictionary of keyword arguments, defaults to None :return: a method call object """ - return function(name, self.filename, pos or [], kw or {}) + args = mparser.ArgumentNode(self._token('array', 'unused')) + if pos is not None: + args.arguments = pos + if kw is not None: + args.kwargs = {self.identifier(k): v for k, v in kw.items()} + return mparser.FunctionNode(self.identifier(name), self._symbol('('), args, self._symbol(')')) def equal(self, lhs: mparser.BaseNode, rhs: mparser.BaseNode) -> mparser.ComparisonNode: """Create an equality operation @@ -261,7 +124,7 @@ class Builder: :param rhs: the right hand side of the equal :return: A compraison node """ - return equal(lhs, rhs) + return mparser.ComparisonNode('==', lhs, self._symbol('=='), rhs) def or_(self, lhs: mparser.BaseNode, rhs: mparser.BaseNode) -> mparser.OrNode: """Create and OrNode @@ -270,7 +133,7 @@ class Builder: :param rhs: The Right of the Node :return: The OrNode """ - return or_(lhs, rhs) + return mparser.OrNode(lhs, self._symbol('or'), rhs) def and_(self, lhs: mparser.BaseNode, rhs: mparser.BaseNode) -> mparser.AndNode: """Create an AndNode @@ -279,12 +142,17 @@ class Builder: :param rhs: The right of the And :return: The AndNode """ - return and_(lhs, rhs) + return mparser.AndNode(lhs, self._symbol('and'), rhs) - def not_(self, value: mparser.BaseNode, filename: str) -> mparser.NotNode: + def not_(self, value: mparser.BaseNode) -> mparser.NotNode: """Create a not node :param value: The value to negate :return: The NotNode """ - return not_(value, self.filename) + return mparser.NotNode(self._token('not', ''), self._symbol('not'), value) + + def block(self, lines: T.List[mparser.BaseNode]) -> mparser.CodeBlockNode: + block = mparser.CodeBlockNode(self._token('node', '')) + block.lines = lines + return block diff --git a/mesonbuild/cargo/cfg.py b/mesonbuild/cargo/cfg.py index ed6fd53d6..0d49527cc 100644 --- a/mesonbuild/cargo/cfg.py +++ b/mesonbuild/cargo/cfg.py @@ -130,7 +130,6 @@ class IR: """Base IR node for Cargo CFG.""" - filename: str @dataclasses.dataclass class String(IR): @@ -169,7 +168,7 @@ class Not(IR): value: IR -def _parse(ast: _LEX_STREAM_AH, filename: str) -> IR: +def _parse(ast: _LEX_STREAM_AH) -> IR: (token, value), n_stream = next(ast) if n_stream is not None: ntoken, _ = n_stream @@ -179,12 +178,12 @@ def _parse(ast: _LEX_STREAM_AH, filename: str) -> IR: stream: T.List[_LEX_TOKEN] if token is TokenType.IDENTIFIER: if ntoken is TokenType.EQUAL: - return Equal(filename, Identifier(filename, value), _parse(ast, filename)) + return Equal(Identifier(value), _parse(ast)) if token is TokenType.STRING: - return String(filename, value) + return String(value) if token is TokenType.EQUAL: # In this case the previous caller already has handled the equal - return _parse(ast, filename) + return _parse(ast) if token in {TokenType.ANY, TokenType.ALL}: type_ = All if token is TokenType.ALL else Any assert ntoken is TokenType.LPAREN @@ -194,13 +193,13 @@ def _parse(ast: _LEX_STREAM_AH, filename: str) -> IR: while token is not TokenType.RPAREN: (token, value), _ = next(ast) if token is TokenType.COMMA: - args.append(_parse(lookahead(iter(stream)), filename)) + args.append(_parse(lookahead(iter(stream)))) stream.clear() else: stream.append((token, value)) if stream: - args.append(_parse(lookahead(iter(stream)), filename)) - return type_(filename, args) + args.append(_parse(lookahead(iter(stream)))) + return type_(args) if token is TokenType.NOT: next(ast) # advance the iterator to get rid of the LPAREN stream = [] @@ -208,69 +207,68 @@ def _parse(ast: _LEX_STREAM_AH, filename: str) -> IR: while token is not TokenType.RPAREN: # type: ignore (token, value), _ = next(ast) stream.append((token, value)) - return Not(filename, _parse(lookahead(iter(stream)), filename)) + return Not(_parse(lookahead(iter(stream)))) raise MesonBugException(f'Unhandled Cargo token: {token}') -def parse(ast: _LEX_STREAM, filename: str) -> IR: +def parse(ast: _LEX_STREAM) -> IR: """Parse the tokenized list into Meson AST. :param ast: An iterable of Tokens - :param filename: The name of the file being parsed :return: An mparser Node to be used as a conditional """ ast_i: _LEX_STREAM_AH = lookahead(iter(ast)) - return _parse(ast_i, filename) + return _parse(ast_i) @functools.singledispatch -def ir_to_meson(ir: T.Any) -> mparser.BaseNode: +def ir_to_meson(ir: T.Any, build: builder.Builder) -> mparser.BaseNode: raise NotImplementedError @ir_to_meson.register -def _(ir: String) -> mparser.BaseNode: - return builder.string(ir.value, ir.filename) +def _(ir: String, build: builder.Builder) -> mparser.BaseNode: + return build.string(ir.value) @ir_to_meson.register -def _(ir: Identifier) -> mparser.BaseNode: - host_machine = builder.identifier('host_machine', ir.filename) +def _(ir: Identifier, build: builder.Builder) -> mparser.BaseNode: + host_machine = build.identifier('host_machine') if ir.value == "target_arch": - return builder.method('cpu_family', host_machine) + return build.method('cpu_family', host_machine) elif ir.value in {"target_os", "target_family"}: - return builder.method('system', host_machine) + return build.method('system', host_machine) elif ir.value == "target_endian": - return builder.method('endian', host_machine) + return build.method('endian', host_machine) raise MesonBugException(f"Unhandled Cargo identifier: {ir.value}") @ir_to_meson.register -def _(ir: Equal) -> mparser.BaseNode: - return builder.equal(ir_to_meson(ir.lhs), ir_to_meson(ir.rhs)) +def _(ir: Equal, build: builder.Builder) -> mparser.BaseNode: + return build.equal(ir_to_meson(ir.lhs, build), ir_to_meson(ir.rhs, build)) @ir_to_meson.register -def _(ir: Not) -> mparser.BaseNode: - return builder.not_(ir_to_meson(ir.value), ir.filename) +def _(ir: Not, build: builder.Builder) -> mparser.BaseNode: + return build.not_(ir_to_meson(ir.value, build)) @ir_to_meson.register -def _(ir: Any) -> mparser.BaseNode: +def _(ir: Any, build: builder.Builder) -> mparser.BaseNode: args = iter(reversed(ir.args)) last = next(args) - cur = builder.or_(ir_to_meson(next(args)), ir_to_meson(last)) + cur = build.or_(ir_to_meson(next(args), build), ir_to_meson(last, build)) for a in args: - cur = builder.or_(ir_to_meson(a), cur) + cur = build.or_(ir_to_meson(a, build), cur) return cur @ir_to_meson.register -def _(ir: All) -> mparser.BaseNode: +def _(ir: All, build: builder.Builder) -> mparser.BaseNode: args = iter(reversed(ir.args)) last = next(args) - cur = builder.and_(ir_to_meson(next(args)), ir_to_meson(last)) + cur = build.and_(ir_to_meson(next(args), build), ir_to_meson(last, build)) for a in args: - cur = builder.and_(ir_to_meson(a), cur) + cur = build.and_(ir_to_meson(a, build), cur) return cur diff --git a/mesonbuild/cargo/interpreter.py b/mesonbuild/cargo/interpreter.py index 8848a46a8..d0cdbaac5 100644 --- a/mesonbuild/cargo/interpreter.py +++ b/mesonbuild/cargo/interpreter.py @@ -445,7 +445,4 @@ def interpret(cargo: Manifest, env: Environment) -> mparser.CodeBlockNode: if os.path.exists(os.path.join(env.source_dir, cargo.subdir, cargo.path, 'src', 'lib.rs')): ast.extend(_create_lib(cargo, build)) - # XXX: make this not awful - block = builder.block(filename) - block.lines = ast - return block + return build.block(ast) diff --git a/unittests/cargotests.py b/unittests/cargotests.py index 61b64b170..e1bce1c89 100644 --- a/unittests/cargotests.py +++ b/unittests/cargotests.py @@ -104,83 +104,83 @@ class CargoCfgTest(unittest.TestCase): def test_parse(self) -> None: cases = [ - ('target_os = "windows"', cfg.Equal('', cfg.Identifier('', "target_os"), cfg.String('', "windows"))), - ('target_arch = "x86"', cfg.Equal('', cfg.Identifier('', "target_arch"), cfg.String('', "x86"))), - ('target_family = "unix"', cfg.Equal('', cfg.Identifier('', "target_family"), cfg.String('', "unix"))), + ('target_os = "windows"', cfg.Equal(cfg.Identifier("target_os"), cfg.String("windows"))), + ('target_arch = "x86"', cfg.Equal(cfg.Identifier("target_arch"), cfg.String("x86"))), + ('target_family = "unix"', cfg.Equal(cfg.Identifier("target_family"), cfg.String("unix"))), ('any(target_arch = "x86", target_arch = "x86_64")', cfg.Any( - '', [ - cfg.Equal('', cfg.Identifier('', "target_arch"), cfg.String('', "x86")), - cfg.Equal('', cfg.Identifier('', "target_arch"), cfg.String('', "x86_64")), + [ + cfg.Equal(cfg.Identifier("target_arch"), cfg.String("x86")), + cfg.Equal(cfg.Identifier("target_arch"), cfg.String("x86_64")), ])), ('all(target_arch = "x86", target_os = "linux")', cfg.All( - '', [ - cfg.Equal('', cfg.Identifier('', "target_arch"), cfg.String('', "x86")), - cfg.Equal('', cfg.Identifier('', "target_os"), cfg.String('', "linux")), + [ + cfg.Equal(cfg.Identifier("target_arch"), cfg.String("x86")), + cfg.Equal(cfg.Identifier("target_os"), cfg.String("linux")), ])), ('not(all(target_arch = "x86", target_os = "linux"))', cfg.Not( - '', cfg.All( - '', [ - cfg.Equal('', cfg.Identifier('', "target_arch"), cfg.String('', "x86")), - cfg.Equal('', cfg.Identifier('', "target_os"), cfg.String('', "linux")), + [ + cfg.Equal(cfg.Identifier("target_arch"), cfg.String("x86")), + cfg.Equal(cfg.Identifier("target_os"), cfg.String("linux")), ]))), ] for data, expected in cases: with self.subTest(): - self.assertEqual(cfg.parse(iter(cfg.lexer(data)), ''), expected) + self.assertEqual(cfg.parse(iter(cfg.lexer(data))), expected) def test_ir_to_meson(self) -> None: - HOST_MACHINE = builder.identifier('host_machine', '') + build = builder.Builder('') + HOST_MACHINE = build.identifier('host_machine') cases = [ ('target_os = "windows"', - builder.equal(builder.method('system', HOST_MACHINE), - builder.string('windows', ''))), + build.equal(build.method('system', HOST_MACHINE), + build.string('windows'))), ('target_arch = "x86"', - builder.equal(builder.method('cpu_family', HOST_MACHINE), - builder.string('x86', ''))), + build.equal(build.method('cpu_family', HOST_MACHINE), + build.string('x86'))), ('target_family = "unix"', - builder.equal(builder.method('system', HOST_MACHINE), - builder.string('unix', ''))), + build.equal(build.method('system', HOST_MACHINE), + build.string('unix'))), ('not(target_arch = "x86")', - builder.not_(builder.equal( - builder.method('cpu_family', HOST_MACHINE), - builder.string('x86', '')), '')), + build.not_(build.equal( + build.method('cpu_family', HOST_MACHINE), + build.string('x86')))), ('any(target_arch = "x86", target_arch = "x86_64")', - builder.or_( - builder.equal(builder.method('cpu_family', HOST_MACHINE), - builder.string('x86', '')), - builder.equal(builder.method('cpu_family', HOST_MACHINE), - builder.string('x86_64', '')))), + build.or_( + build.equal(build.method('cpu_family', HOST_MACHINE), + build.string('x86')), + build.equal(build.method('cpu_family', HOST_MACHINE), + build.string('x86_64')))), ('any(target_arch = "x86", target_arch = "x86_64", target_arch = "aarch64")', - builder.or_( - builder.equal(builder.method('cpu_family', HOST_MACHINE), - builder.string('x86', '')), - builder.or_( - builder.equal(builder.method('cpu_family', HOST_MACHINE), - builder.string('x86_64', '')), - builder.equal(builder.method('cpu_family', HOST_MACHINE), - builder.string('aarch64', ''))))), + build.or_( + build.equal(build.method('cpu_family', HOST_MACHINE), + build.string('x86')), + build.or_( + build.equal(build.method('cpu_family', HOST_MACHINE), + build.string('x86_64')), + build.equal(build.method('cpu_family', HOST_MACHINE), + build.string('aarch64'))))), ('all(target_arch = "x86", target_arch = "x86_64")', - builder.and_( - builder.equal(builder.method('cpu_family', HOST_MACHINE), - builder.string('x86', '')), - builder.equal(builder.method('cpu_family', HOST_MACHINE), - builder.string('x86_64', '')))), + build.and_( + build.equal(build.method('cpu_family', HOST_MACHINE), + build.string('x86')), + build.equal(build.method('cpu_family', HOST_MACHINE), + build.string('x86_64')))), ('all(target_arch = "x86", target_arch = "x86_64", target_arch = "aarch64")', - builder.and_( - builder.equal(builder.method('cpu_family', HOST_MACHINE), - builder.string('x86', '')), - builder.and_( - builder.equal(builder.method('cpu_family', HOST_MACHINE), - builder.string('x86_64', '')), - builder.equal(builder.method('cpu_family', HOST_MACHINE), - builder.string('aarch64', ''))))), + build.and_( + build.equal(build.method('cpu_family', HOST_MACHINE), + build.string('x86')), + build.and_( + build.equal(build.method('cpu_family', HOST_MACHINE), + build.string('x86_64')), + build.equal(build.method('cpu_family', HOST_MACHINE), + build.string('aarch64'))))), ] for data, expected in cases: with self.subTest(): - value = cfg.ir_to_meson(cfg.parse(iter(cfg.lexer(data)), '')) + value = cfg.ir_to_meson(cfg.parse(iter(cfg.lexer(data))), build) self.assertEqual(value, expected)