def parse_foreach_in(ctx, tokens, breakstack): """ :: foreach(loop_var IN [LISTS [<lists>]] [ITEMS [<items>]]) """ tree = ArgGroupNode() # If it is a whitespace token then put it directly in the parse tree at # the current depth while tokens and tokens[0].type in WHITESPACE_TOKENS: tree.children.append(tokens.pop(0)) continue # Consume the loop variable and any attached comments ntokens = len(tokens) pargs = PositionalGroupNode.parse(ctx, tokens, 2, ["IN"], breakstack) assert len(tokens) < ntokens tree.children.append(pargs) kwargs = {"LISTS": PositionalParser("+"), "ITEMS": PositionalParser("+")} sub_breakstack = breakstack + [KwargBreaker(list(kwargs.keys()))] while tokens: # Break if the next token belongs to a parent parser, i.e. if it # matches a keyword argument of something higher in the stack, or if # it closes a parent group. if should_break(tokens[0], breakstack): break # If it is a whitespace token then put it directly in the parse tree at # the current depth if tokens[0].type in WHITESPACE_TOKENS: tree.children.append(tokens.pop(0)) continue # If it's a comment, then add it at the current depth if tokens[0].type in (lex.TokenType.COMMENT, lex.TokenType.BRACKET_COMMENT): if not get_tag(tokens[0]) in ("sort", "sortable"): child = TreeNode(NodeType.COMMENT) tree.children.append(child) child.children.append(tokens.pop(0)) continue ntokens = len(tokens) word = get_normalized_kwarg(tokens[0]) subparser = kwargs.get(word, None) if subparser is not None: subtree = KeywordGroupNode.parse(ctx, tokens, word, subparser, sub_breakstack) else: logger.warning("Unexpected positional argument at %s", tokens[0].location) subtree = PositionalGroupNode.parse(ctx, tokens, '+', [], sub_breakstack) assert len(tokens) < ntokens tree.children.append(subtree) return tree
def parse_add_library_object(ctx, tokens, breakstack): """ :: add_library(<name> OBJECT <src>...) :see: https://cmake.org/cmake/help/latest/command/add_library.html#object-libraries """ tree = ArgGroupNode() # If it is a whitespace token then put it directly in the parse tree at # the current depth while tokens and tokens[0].type in WHITESPACE_TOKENS: tree.children.append(tokens.pop(0)) continue ntokens = len(tokens) subtree = PositionalGroupNode.parse(ctx, tokens, 2, ["OBJECT"], breakstack) assert len(tokens) < ntokens tree.children.append(subtree) while tokens: # Break if the next token belongs to a parent parser, i.e. if it # matches a keyword argument of something higher in the stack, or if # it closes a parent group. if should_break(tokens[0], breakstack): break # If it is a whitespace token then put it directly in the parse tree at # the current depth if tokens[0].type in WHITESPACE_TOKENS: tree.children.append(tokens.pop(0)) continue # If it's a comment, then add it at the current depth if tokens[0].type in (TokenType.COMMENT, TokenType.BRACKET_COMMENT): if not get_tag(tokens[0]) in ("sort", "sortable"): child = TreeNode(NodeType.COMMENT) tree.children.append(child) child.children.append(tokens.pop(0)) continue ntokens = len(tokens) subtree = PositionalGroupNode.parse(ctx, tokens, '+', [], breakstack) assert len(tokens) < ntokens tree.children.append(subtree) return tree
def parse_add_library_standard(ctx, tokens, breakstack, sortable): """ :: add_library(<name> [STATIC | SHARED | MODULE] [EXCLUDE_FROM_ALL] source1 [source2 ...]) :see: https://cmake.org/cmake/help/v3.0/command/add_library.html """ # pylint: disable=too-many-statements parsing_name = 1 parsing_type = 2 parsing_flag = 3 parsing_sources = 4 tree = ArgGroupNode() # If it is a whitespace token then put it directly in the parse tree at # the current depth while tokens and tokens[0].type in WHITESPACE_TOKENS: tree.children.append(tokens.pop(0)) continue state_ = parsing_name parg_group = None src_group = None active_depth = tree flags = ("STATIC", "SHARED", "MODULE", "OBJECT") while tokens: # This parse function breakson the first right paren, since parenthetical # groups are not allowed. A parenthesis might exist in a filename, but # if so that filename should be quoted so it wont show up as a RIGHT_PAREN # token. if tokens[0].type is TokenType.RIGHT_PAREN: break # If it is a whitespace token then put it directly in the parse tree at # the current depth if tokens[0].type in WHITESPACE_TOKENS: active_depth.children.append(tokens.pop(0)) continue # If it's a comment token not associated with an argument, then put it # directly into the parse tree at the current depth if tokens[0].type in (TokenType.COMMENT, TokenType.BRACKET_COMMENT): if state_ > parsing_name: if get_tag(tokens[0]) in ("unsort", "unsortable"): sortable = False elif get_tag(tokens[0]) in ("sort", "sortable"): sortable = True child = TreeNode(NodeType.COMMENT) active_depth.children.append(child) child.children.append(tokens.pop(0)) continue if state_ is parsing_name: token = tokens.pop(0) parg_group = PositionalGroupNode() parg_group.spec = PositionalSpec("+") active_depth = parg_group tree.children.append(parg_group) child = TreeNode(NodeType.ARGUMENT) child.children.append(token) CommentNode.consume_trailing(ctx, tokens, child) parg_group.children.append(child) state_ += 1 elif state_ is parsing_type: if get_normalized_kwarg(tokens[0]) in flags: token = tokens.pop(0) child = TreeNode(NodeType.FLAG) child.children.append(token) CommentNode.consume_trailing(ctx, tokens, child) parg_group.children.append(child) state_ += 1 elif state_ is parsing_flag: if get_normalized_kwarg(tokens[0]) == "EXCLUDE_FROM_ALL": token = tokens.pop(0) child = TreeNode(NodeType.FLAG) child.children.append(token) CommentNode.consume_trailing(ctx, tokens, child) parg_group.children.append(child) state_ += 1 src_group = PositionalGroupNode(sortable=sortable, tags=["file-list"]) src_group.spec = PositionalSpec("+") active_depth = src_group tree.children.append(src_group) elif state_ is parsing_sources: token = tokens.pop(0) child = TreeNode(NodeType.ARGUMENT) child.children.append(token) CommentNode.consume_trailing(ctx, tokens, child) src_group.children.append(child) if only_comments_and_whitespace_remain(tokens, breakstack): active_depth = tree return tree
def parse2(cls, ctx, tokens, spec, breakstack): """ Parse a continuous sequence of `npargs` positional arguments. If npargs is an integer we will consume exactly that many arguments. If it is not an integer then it is a string meaning: * "?": zero or one * "*": zero or more * "+": one or more """ tree = cls(sortable=spec.sortable, tags=spec.tags) tree.spec = spec nconsumed = 0 # Strip off any preceeding whitespace (note that in most cases this has # already been done but in some cases (such ask kwarg subparser) where # it hasn't while tokens and tokens[0].type in WHITESPACE_TOKENS: tree.children.append(tokens.pop(0)) # If the first non-whitespace token is a cmake-format tag annotating # sortability, then parse it out here and record the annotation if tokens and get_tag(tokens[0]) in ("sortable", "sort"): tree.sortable = True elif tokens and get_tag(tokens[0]) in ("unsortable", "unsort"): tree.sortable = False while tokens: # Break if we have consumed enough positional arguments if pargs_are_full(spec.nargs, nconsumed): break # Break if the next token belongs to a parent parser, i.e. if it # matches a keyword argument of something higher in the stack, or if # it closes a parent group. if should_break(tokens[0], breakstack): # NOTE(josh): if spec.nargs is an exact number of arguments, then we # shouldn't break on kwarg match from a parent parser. Instead, we # should consume the token. This is a hack to deal with # ```install(RUNTIME COMPONENT runtime)``. In this case the second # occurance of "runtime" should not match the ``RUNTIME`` keyword # and should not break the positional parser. # TODO(josh): this is kind of hacky because it will force the positional # parser to consume a right parenthesis and will lead to parse errors # in the event of a missing positional argument. Such errors will be # difficult to debug for the user. if not npargs_is_exact(spec.nargs): break if tokens[0].type == lex.TokenType.RIGHT_PAREN: break # If this is the start of a parenthetical group, then parse the group # NOTE(josh): syntatically this probably shouldn't be allowed here, but # cmake seems to accept it so we probably should too. if tokens[0].type == lex.TokenType.LEFT_PAREN: with ctx.pusharg(tree): subtree = ParenGroupNode.parse(ctx, tokens, breakstack) tree.children.append(subtree) continue # If it is a whitespace token then put it directly in the parse tree at # the current depth if tokens[0].type in WHITESPACE_TOKENS: tree.children.append(tokens.pop(0)) continue # If it's a comment token not associated with an argument, then put it # directly into the parse tree at the current depth if tokens[0].type in (lex.TokenType.COMMENT, lex.TokenType.BRACKET_COMMENT): if comment_belongs_up_tree(ctx, tokens, tree, breakstack): break child = CommentNode.consume(ctx, tokens) tree.children.append(child) continue # If it's a sentinel comment, then add it at the current depth if tokens[0].type in (lex.TokenType.FORMAT_OFF, lex.TokenType.FORMAT_ON): tree.children.append(OnOffNode.consume(ctx, tokens)) continue # Otherwise is it is a positional argument, so add it to the tree as such if get_normalized_kwarg(tokens[0]) in spec.flags: child = TreeNode(NodeType.FLAG) else: child = TreeNode(NodeType.ARGUMENT) child.children.append(tokens.pop(0)) CommentNode.consume_trailing(ctx, tokens, child) tree.children.append(child) nconsumed += 1 return tree
def parse_set(ctx, tokens, breakstack): """ :: set(<variable> <value> [[CACHE <type> <docstring> [FORCE]] | PARENT_SCOPE]) :see: https://cmake.org/cmake/help/v3.0/command/set.html? """ tree = SetFnNode() # If it is a whitespace token then put it directly in the parse tree at # the current depth while tokens and tokens[0].type in WHITESPACE_TOKENS: tree.children.append(tokens.pop(0)) continue kwargs = { "CACHE": PositionalParser('2+', flags=["FORCE"]) } flags = ["PARENT_SCOPE"] kwarg_breakstack = breakstack + [KwargBreaker(list(kwargs.keys()) + flags)] positional_breakstack = breakstack + [KwargBreaker(list(kwargs.keys()))] ntokens = len(tokens) subtree = PositionalGroupNode.parse( ctx, tokens, 1, flags, positional_breakstack) assert len(tokens) < ntokens tree.children.append(subtree) for token in subtree.get_semantic_tokens(): tree.varname = token break while tokens: # Break if the next token belongs to a parent parser, i.e. if it # matches a keyword argument of something higher in the stack, or if # it closes a parent group. if should_break(tokens[0], breakstack): break # If it is a whitespace token then put it directly in the parse tree at # the current depth if tokens[0].type in WHITESPACE_TOKENS: tree.children.append(tokens.pop(0)) continue # If it's a comment, then add it at the current depth if tokens[0].type in (lex.TokenType.COMMENT, lex.TokenType.BRACKET_COMMENT): if not get_tag(tokens[0]) in ("sort", "sortable"): child = TreeNode(NodeType.COMMENT) tree.children.append(child) child.children.append(tokens.pop(0)) continue ntokens = len(tokens) word = get_normalized_kwarg(tokens[0]) if word == "CACHE": with ctx.pusharg(tree): subtree = KeywordGroupNode.parse( ctx, tokens, word, kwargs[word], kwarg_breakstack) cache_tokens = subtree.get_semantic_tokens() cache_tokens.pop(0) cache_args = [None, None, False] for idx, token in enumerate(cache_tokens[:2]): cache_args[idx] = token.spelling if not cache_tokens: ctx.lint_ctx.record_lint( "E1120", "<variable>", location=tokens[0].get_location()) elif len(cache_tokens) < 2: ctx.lint_ctx.record_lint( "E1120", "<value>", location=tokens[0].get_location()) elif len(cache_tokens) < 3: pass elif len(cache_tokens) > 4: ctx.lint_ctx.record_lint( "E1121", location=cache_tokens[2].get_location()) else: if cache_tokens[2].spelling.upper() == "FORCE": cache_args[2] = True else: ctx.lint_ctx.record_lint( "E1121", location=cache_tokens[2].get_location()) tree.cache = CacheTuple(*cache_args) elif word == "PARENT_SCOPE": with ctx.pusharg(tree): subtree = PositionalGroupNode.parse( ctx, tokens, '+', ["PARENT_SCOPE"], positional_breakstack) tree.parent_scope = True else: with ctx.pusharg(tree): subtree = PositionalGroupNode.parse( ctx, tokens, '+', [], kwarg_breakstack) for pattern, tags in ctx.config.parse.vartags_: if pattern.match(tree.varname.spelling): subtree.tags.extend(tags) tree.value_group = subtree assert len(tokens) < ntokens tree.children.append(subtree) return tree