1# This Source Code Form is subject to the terms of the Mozilla Public 2# License, v. 2.0. If a copy of the MPL was not distributed with this 3# file, You can obtain one at http://mozilla.org/MPL/2.0/. 4 5from __future__ import absolute_import 6from __future__ import unicode_literals 7import re 8 9from .base import ( 10 CAN_COPY, 11 Entry, OffsetComment, Junk, Whitespace, 12 Parser 13) 14 15 16class DefinesInstruction(Entry): 17 '''Entity-like object representing processing instructions in inc files 18 ''' 19 def __init__(self, ctx, span, val_span): 20 self.ctx = ctx 21 self.span = span 22 self.key_span = self.val_span = val_span 23 24 def __repr__(self): 25 return self.raw_val 26 27 28class DefinesParser(Parser): 29 # can't merge, #unfilter needs to be the last item, which we don't support 30 capabilities = CAN_COPY 31 reWhitespace = re.compile('\n+', re.M) 32 33 EMPTY_LINES = 1 << 0 34 35 class Comment(OffsetComment): 36 comment_offset = 2 37 38 class Context(Parser.Context): 39 def __init__(self, contents): 40 super(DefinesParser.Context, self).__init__(contents) 41 self.filter_empty_lines = False 42 43 def __init__(self): 44 self.reComment = re.compile('(?:^# .*?\n)*(?:^# [^\n]*)', re.M) 45 # corresponds to 46 # https://hg.mozilla.org/mozilla-central/file/72ee4800d4156931c89b58bd807af4a3083702bb/python/mozbuild/mozbuild/preprocessor.py#l561 # noqa 47 self.reKey = re.compile( 48 r'#define[ \t]+(?P<key>\w+)(?:[ \t](?P<val>[^\n]*))?', re.M) 49 self.rePI = re.compile(r'#(?P<val>\w+[ \t]+[^\n]+)', re.M) 50 Parser.__init__(self) 51 52 def getNext(self, ctx, offset): 53 junk_offset = offset 54 contents = ctx.contents 55 56 m = self.reComment.match(ctx.contents, offset) 57 if m: 58 current_comment = self.Comment(ctx, m.span()) 59 offset = m.end() 60 else: 61 current_comment = None 62 63 m = self.reWhitespace.match(contents, offset) 64 if m: 65 # blank lines outside of filter_empty_lines or 66 # leading whitespace are bad 67 if ( 68 offset == 0 or 69 not (len(m.group()) == 1 or ctx.filter_empty_lines) 70 ): 71 if current_comment: 72 return current_comment 73 return Junk(ctx, m.span()) 74 white_space = Whitespace(ctx, m.span()) 75 offset = m.end() 76 if ( 77 current_comment is not None 78 and white_space.raw_val.count('\n') > 1 79 ): 80 # standalone comment 81 # return the comment, and reparse the whitespace next time 82 return current_comment 83 if current_comment is None: 84 return white_space 85 else: 86 white_space = None 87 88 m = self.reKey.match(contents, offset) 89 if m: 90 return self.createEntity(ctx, m, current_comment, white_space) 91 # defines instructions don't have comments 92 # Any pending commment is standalone 93 if current_comment: 94 return current_comment 95 if white_space: 96 return white_space 97 m = self.rePI.match(contents, offset) 98 if m: 99 instr = DefinesInstruction(ctx, m.span(), m.span('val')) 100 if instr.val == 'filter emptyLines': 101 ctx.filter_empty_lines = True 102 if instr.val == 'unfilter emptyLines': 103 ctx.filter_empty_lines = False 104 return instr 105 return self.getJunk( 106 ctx, junk_offset, self.reComment, self.reKey, self.rePI) 107