# This Source Code Form is subject to the terms of the Mozilla Public # License, v. 2.0. If a copy of the MPL was not distributed with this # file, You can obtain one at http://mozilla.org/MPL/2.0/. import re from .base import CAN_COPY, Entry, OffsetComment, Junk, Whitespace, Parser class DefinesInstruction(Entry): """Entity-like object representing processing instructions in inc files""" def __init__(self, ctx, span, val_span): self.ctx = ctx self.span = span self.key_span = self.val_span = val_span def __repr__(self): return self.raw_val class DefinesParser(Parser): # can't merge, #unfilter needs to be the last item, which we don't support capabilities = CAN_COPY reWhitespace = re.compile("\n+", re.M) EMPTY_LINES = 1 << 0 class Comment(OffsetComment): comment_offset = 2 class Context(Parser.Context): def __init__(self, contents): super().__init__(contents) self.filter_empty_lines = False def __init__(self): self.reComment = re.compile("(?:^# .*?\n)*(?:^# [^\n]*)", re.M) # corresponds to # https://hg.mozilla.org/mozilla-central/file/72ee4800d4156931c89b58bd807af4a3083702bb/python/mozbuild/mozbuild/preprocessor.py#l561 # noqa self.reKey = re.compile( r"#define[ \t]+(?P\w+)(?:[ \t](?P[^\n]*))?", re.M ) self.rePI = re.compile(r"#(?P\w+[ \t]+[^\n]+)", re.M) Parser.__init__(self) def getNext(self, ctx, offset): junk_offset = offset contents = ctx.contents m = self.reComment.match(ctx.contents, offset) if m: current_comment = self.Comment(ctx, m.span()) offset = m.end() else: current_comment = None m = self.reWhitespace.match(contents, offset) if m: # blank lines outside of filter_empty_lines or # leading whitespace are bad if offset == 0 or not (len(m.group()) == 1 or ctx.filter_empty_lines): if current_comment: return current_comment return Junk(ctx, m.span()) white_space = Whitespace(ctx, m.span()) offset = m.end() if current_comment is not None and white_space.raw_val.count("\n") > 1: # standalone comment # return the comment, and reparse the whitespace next time return current_comment if current_comment is None: return white_space else: white_space = None m = self.reKey.match(contents, offset) if m: return self.createEntity(ctx, m, current_comment, white_space) # defines instructions don't have comments # Any pending commment is standalone if current_comment: return current_comment if white_space: return white_space m = self.rePI.match(contents, offset) if m: instr = DefinesInstruction(ctx, m.span(), m.span("val")) if instr.val == "filter emptyLines": ctx.filter_empty_lines = True if instr.val == "unfilter emptyLines": ctx.filter_empty_lines = False return instr return self.getJunk(ctx, junk_offset, self.reComment, self.reKey, self.rePI)