/usr/lib/python2.7/dist-packages/syslogng/debuggercli/completerlang.py is in syslog-ng-mod-python 3.8.1-10.
This file is owned by root:root, with mode 0o644.
The actual contents of the file can be viewed below.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 | #############################################################################
# Copyright (c) 2015-2016 Balabit
#
# This program is free software; you can redistribute it and/or modify it
# under the terms of the GNU General Public License version 2 as published
# by the Free Software Foundation, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
#
# As an additional exemption you are allowed to compile & link against the
# OpenSSL libraries as published by the OpenSSL project. See the file
# COPYING for details.
#
#############################################################################
from __future__ import print_function, absolute_import
import sys
from abc import abstractmethod, ABCMeta
import ply.yacc as yacc
from .tablexer import TabLexer
class CompleterLang(object):
"""Class encapsulating a language (or grammar) used by tab completion
Derived classes should define their ply.yacc rules in their body, which is
then translated at instantiation time.
"""
__metaclass__ = ABCMeta
def __init__(self):
self._initialize_rules()
self._parser = yacc.yacc(module=self, write_tables=False, debug=False, errorlog=yacc.NullLogger())
self._lexer = TabLexer(self._construct_lexer())
self._expected_tokens = []
self._token_position = -1
@abstractmethod
def _construct_lexer(self):
raise NotImplementedError
def _initialize_rules(self):
pass
def get_expected_tokens(self, text, drop_last_token):
self._lexer.set_drop_last_token(drop_last_token)
self._parser.parse(text, lexer=self._lexer)
return self._expected_tokens, self._lexer.get_replaced_token(), self._token_position
def p_error(self, p):
if p is None:
# EOF
return None
elif p.type == 'TAB':
# We look up the current grammar state from the local variables of the caller,
# as it doesn't publish this information in self.
#
# This is somewhat fragile, however I don't really expect this variable to change
# too frequently.
#
# I don't want to hide this call within the _handle_injected_tab_token() call, as
# this is the very function that is close enough to the caller, moving this information
# deeper would probably improve readability at the cost of increasing fragility.
# pylint: disable=protected-access
if 'state' in sys._getframe(1).f_locals:
parser_state = sys._getframe(1).f_locals['state']
elif 'state' in sys._getframe(2).f_locals:
parser_state = sys._getframe(2).f_locals['state']
else:
return None
# now handle the error that the TAB token caused
self._token_position = p.lexpos
self._collect_expected_tokens_and_productions(parser_state)
self._parser.errok()
def _collect_expected_tokens_and_productions(self, parser_state):
self._expected_tokens = []
self._collect_expected_tokens_for_a_given_state(parser_state)
def _collect_expected_tokens_for_a_given_state(self, state):
for token, next_state in self._iter_parser_actions(state):
if token != '$end':
self._expected_tokens.append(token)
next_state = self._shift_production_if_needed(next_state, token)
# negative next_state value means one of two things:
#
# 1) there's a syntax error and the current token does not match
# the rule we are evaluating right now
#
# 2) this token would trigger another rule shift, which we don't
# support right now.
#
if next_state >= 0:
self._collect_expected_productions(next_state)
def _collect_expected_productions(self, state):
for _, next_state in self._iter_parser_actions(state):
if next_state < 0:
# production shift, we care about production shifts which would translate the
# next_state token
production = self._lookup_production(next_state)
if production.len and production.name not in self._expected_tokens:
self._expected_tokens.append(production.name)
def _iter_parser_actions(self, parser_state):
return self._parser.action[parser_state].items()
@staticmethod
def _are_we_shifting_a_production(state):
return state < 0
def _shift_production(self, production, token):
translated_state = self._get_target_state_after_shifting_production(production)
try:
# this might be negative (e.g. indicate a production shift).
return self._parser.action[self._parser.goto[translated_state][production.name]][token]
except KeyError:
# this indicates a syntax error, the current token
# does not match any rules where this production
# is referenced from
return -1
def _shift_production_if_needed(self, state, token):
if self._are_we_shifting_a_production(state):
production = self._lookup_production(state)
return self._shift_production(production, token)
else:
return state
def _lookup_production(self, state):
return self._parser.productions[-state]
def _get_target_state_after_shifting_production(self, production):
return self._parser.statestack[-production.len - 1]
|