~rodsmith/checkbox/smart-crash-fix

« back to all changes in this revision

Viewing changes to plainbox/plainbox/impl/xparsers.py

  • Committer: Daniel Manrique
  • Author(s): Zygmunt Krynicki
  • Date: 2015-02-18 12:37:54 UTC
  • mfrom: (3582.1.6 launchpad/cep-8)
  • Revision ID: daniel_manrique-20150218123754-cs1v419h57dowvn6
"automatic merge of lp:~zkrynicki/checkbox/cep-8/ by tarmac [r=kissiel][bug=][author=zkrynicki]"

Show diffs side-by-side

added added

removed removed

Lines of Context:
61
61
import sre_constants
62
62
import sre_parse
63
63
 
 
64
from plainbox.i18n import gettext as _
64
65
from plainbox.impl import pod
65
66
from plainbox.impl.censoREd import PatternProxy
 
67
from plainbox.impl.xscanners import WordScanner
66
68
 
67
69
__all__ = [
68
70
    'Comment',
393
395
            if comment:
394
396
                entries.append(Comment(lineno, col_offset + cindex, comment))
395
397
        return WhiteList(initial_lineno, col_offset, entries)
 
398
 
 
399
 
 
400
class Error(Node):
 
401
    """ node representing a syntax error """
 
402
    msg = F("message", str)
 
403
 
 
404
 
 
405
class Text(Node):
 
406
    """ node representing a bit of text """
 
407
    text = F("text", str)
 
408
 
 
409
 
 
410
class FieldOverride(Node):
 
411
    """ node representing a single override statement """
 
412
 
 
413
    value = F("value to apply (override value)", Text)
 
414
    pattern = F("pattern that selects things to override", Re)
 
415
 
 
416
    @staticmethod
 
417
    def parse(
 
418
        text: str, lineno: int=1, col_offset: int=0
 
419
    ) -> "Union[FieldOverride, Error]":
 
420
        """
 
421
        Parse a single test plan field override line
 
422
 
 
423
        Using correct syntax will result in a FieldOverride node with
 
424
        appropriate data in the ``value`` and ``pattern`` fields. Note that
 
425
        ``pattern`` may be either a :class:`RePattern` or a :class:`ReFixed`.
 
426
 
 
427
            >>> FieldOverride.parse("apply new-value to pattern")
 
428
            ... # doctest: +NORMALIZE_WHITESPACE
 
429
            FieldOverride(lineno=1, col_offset=0,
 
430
                          value=Text(lineno=1, col_offset=0, text='new-value'),
 
431
                          pattern=ReFixed(lineno=1, col_offset=0,
 
432
                                          text='pattern'))
 
433
            >>> FieldOverride.parse("apply blocker to .*")
 
434
            ... # doctest: +NORMALIZE_WHITESPACE
 
435
            FieldOverride(lineno=1, col_offset=0,
 
436
                          value=Text(lineno=1, col_offset=0, text='blocker'),
 
437
                          pattern=RePattern(lineno=1, col_offset=0, text='.*',
 
438
                                            re=re.compile('.*')))
 
439
 
 
440
        Using incorrect syntax will result in a single Error node being
 
441
        returned. The message (``msg``) field contains useful information on
 
442
        the cause of the problem, as depicted below:
 
443
 
 
444
            >>> FieldOverride.parse("")
 
445
            Error(lineno=1, col_offset=0, msg="expected 'apply' near ''")
 
446
            >>> FieldOverride.parse("apply")
 
447
            Error(lineno=1, col_offset=0, msg='expected override value')
 
448
            >>> FieldOverride.parse("apply value")
 
449
            Error(lineno=1, col_offset=0, msg="expected 'to' near ''")
 
450
            >>> FieldOverride.parse("apply value to")
 
451
            Error(lineno=1, col_offset=0, msg='expected override pattern')
 
452
            >>> FieldOverride.parse("apply value to pattern junk")
 
453
            Error(lineno=1, col_offset=0, msg="unexpected garbage: 'junk'")
 
454
 
 
455
        Lastly, shell-style comments are supported. They are discarded by the
 
456
        scanner code though.
 
457
 
 
458
            >>> FieldOverride.parse("apply value to pattern # comment")
 
459
            ... # doctest: +NORMALIZE_WHITESPACE
 
460
            FieldOverride(lineno=1, col_offset=0,
 
461
                          value=Text(lineno=1, col_offset=0, text='value'),
 
462
                          pattern=ReFixed(lineno=1, col_offset=0,
 
463
                                          text='pattern'))
 
464
 
 
465
        """
 
466
        # XXX  Until our home-grown scanner is ready col_offset values below
 
467
        # are all dummy. This is not strictly critical but should be improved
 
468
        # upon later.
 
469
        scanner = WordScanner(text)
 
470
        # 'APPLY' ...
 
471
        token, lexeme = scanner.get_token()
 
472
        if token != scanner.TokenEnum.WORD or lexeme != 'apply':
 
473
            return Error(lineno, col_offset,
 
474
                         _("expected {!a} near {!r}").format('apply', lexeme))
 
475
        # 'APPLY' VALUE ...
 
476
        token, lexeme = scanner.get_token()
 
477
        if token != scanner.TokenEnum.WORD:
 
478
            return Error(lineno, col_offset, _("expected override value"))
 
479
        value = Text(lineno, col_offset, lexeme)
 
480
        # 'APPLY' VALUE 'TO' ...
 
481
        token, lexeme = scanner.get_token()
 
482
        if token != scanner.TokenEnum.WORD or lexeme != 'to':
 
483
            return Error(lineno, col_offset,
 
484
                         _("expected {!a} near {!r}").format('to', lexeme))
 
485
        # 'APPLY' VALUE 'TO' PATTERN...
 
486
        token, lexeme = scanner.get_token()
 
487
        if token != scanner.TokenEnum.WORD:
 
488
            return Error(lineno, col_offset, _("expected override pattern"))
 
489
        pattern = Re.parse(lexeme, lineno, col_offset)
 
490
        # 'APPLY' VALUE 'TO' PATTERN <EOF>
 
491
        token, lexeme = scanner.get_token()
 
492
        if token != scanner.TokenEnum.EOF:
 
493
            return Error(lineno, col_offset,
 
494
                         _("unexpected garbage: {!r}").format(lexeme))
 
495
        return FieldOverride(lineno, col_offset, value, pattern)
 
496
 
 
497
 
 
498
class OverrideFieldList(Node):
 
499
    """ node representing a whole plainbox field override list"""
 
500
 
 
501
    entries = pod.Field("a list of comments and patterns", list,
 
502
                        initial_fn=list, assign_filter_list=[
 
503
                            pod.typed, pod.typed.sequence(Node), pod.const])
 
504
 
 
505
    @staticmethod
 
506
    def parse(
 
507
        text: str, lineno: int=1, col_offset: int=0
 
508
    ) -> "OverrideFieldList":
 
509
        entries = []
 
510
        initial_lineno = lineno
 
511
        # NOTE: lineno is consciously shadowed below
 
512
        for lineno, line in enumerate(text.splitlines(), lineno):
 
513
            entries.append(FieldOverride.parse(line, lineno, col_offset))
 
514
        return OverrideFieldList(initial_lineno, col_offset, entries)