Mini Shell
#
# This file is part of pysmi software.
#
# Copyright (c) 2015-2019, Ilya Etingof <etingof@gmail.com>
# License: http://snmplabs.com/pysmi/license.html
#
import sys
import re
import ply.lex as lex
from pysmi.lexer.base import AbstractLexer
from pysmi import error
from pysmi import debug
UNSIGNED32_MAX = 4294967295
UNSIGNED64_MAX = 18446744073709551615
LEX_VERSION = [int(x) for x in lex.__version__.split('.')]
# Do not overload single lexer methods - overload all or none of them!
# noinspection PySingleQuotedDocstring,PyMethodMayBeStatic,PyIncorrectDocstring
class SmiV2Lexer(AbstractLexer):
reserved_words = [
'ACCESS', 'AGENT-CAPABILITIES', 'APPLICATION', 'AUGMENTS', 'BEGIN', 'BITS',
'CONTACT-INFO', 'CREATION-REQUIRES', 'Counter', 'Counter32', 'Counter64',
'DEFINITIONS', 'DEFVAL', 'DESCRIPTION', 'DISPLAY-HINT', 'END', 'ENTERPRISE',
'EXTENDS', 'FROM', 'GROUP', 'Gauge', 'Gauge32', 'IDENTIFIER', 'IMPLICIT',
'IMPLIED', 'IMPORTS', 'INCLUDES', 'INDEX', 'INSTALL-ERRORS', 'INTEGER',
'Integer32', 'IpAddress', 'LAST-UPDATED', 'MANDATORY-GROUPS',
'MAX-ACCESS', 'MIN-ACCESS', 'MODULE', 'MODULE-COMPLIANCE',
'MODULE-IDENTITY', 'NOTIFICATION-GROUP', 'NOTIFICATION-TYPE',
'NOTIFICATIONS', 'OBJECT', 'OBJECT-GROUP', 'OBJECT-IDENTITY', 'OBJECT-TYPE',
'OBJECTS', 'OCTET', 'OF', 'ORGANIZATION', 'Opaque', 'PIB-ACCESS',
'PIB-DEFINITIONS', 'PIB-INDEX', 'PIB-MIN-ACCESS', 'PIB-REFERENCES',
'PIB-TAG', 'POLICY-ACCESS', 'PRODUCT-RELEASE', 'REFERENCE', 'REVISION',
'SEQUENCE', 'SIZE', 'STATUS', 'STRING', 'SUBJECT-CATEGORIES', 'SUPPORTS',
'SYNTAX', 'TEXTUAL-CONVENTION', 'TimeTicks', 'TRAP-TYPE', 'UNIQUENESS',
'UNITS', 'UNIVERSAL', 'Unsigned32', 'VALUE', 'VARIABLES',
'VARIATION', 'WRITE-SYNTAX'
]
reserved = {}
for w in reserved_words:
reserved[w] = w.replace('-', '_').upper()
# hack to support SMIv1
if w == 'Counter':
reserved[w] = 'COUNTER32'
elif w == 'Gauge':
reserved[w] = 'GAUGE32'
forbidden_words = [
'ABSENT', 'ANY', 'BIT', 'BOOLEAN', 'BY', 'COMPONENT', 'COMPONENTS',
'DEFAULT', 'DEFINED', 'ENUMERATED', 'EXPLICIT', 'EXTERNAL', 'FALSE', 'MAX',
'MIN', 'MINUS-INFINITY', 'NULL', 'OPTIONAL', 'PLUS-INFINITY', 'PRESENT',
'PRIVATE', 'REAL', 'SET', 'TAGS', 'TRUE', 'WITH'
]
# Token names required!
tokens = list(set([
'BIN_STRING',
'CHOICE',
'COLON_COLON_EQUAL',
'DOT_DOT',
'EXPORTS',
'HEX_STRING',
'LOWERCASE_IDENTIFIER',
'MACRO',
'NEGATIVENUMBER',
'NEGATIVENUMBER64',
'NUMBER',
'NUMBER64',
'QUOTED_STRING',
'UPPERCASE_IDENTIFIER',
] + list(reserved.values())
))
states = (
('macro', 'exclusive'),
('choice', 'exclusive'),
('exports', 'exclusive'),
('comment', 'exclusive'),
)
literals = '[]{}():;,-.|'
t_DOT_DOT = r'\.\.'
t_COLON_COLON_EQUAL = r'::='
t_ignore = ' \t'
def __init__(self, tempdir=''):
self._tempdir = tempdir
self.lexer = None
self.reset()
def reset(self):
if LEX_VERSION < [3, 0]:
self.lexer = lex.lex(module=self,
reflags=re.DOTALL,
outputdir=self._tempdir,
debug=False)
else:
if debug.logger & debug.flagLexer:
logger = debug.logger.getCurrentLogger()
else:
logger = lex.NullLogger()
if debug.logger & debug.flagGrammar:
debuglogger = debug.logger.getCurrentLogger()
else:
debuglogger = None
self.lexer = lex.lex(module=self,
reflags=re.DOTALL,
outputdir=self._tempdir,
debuglog=debuglogger,
errorlog=logger)
def t_newline(self, t):
r'\r\n|\n|\r'
t.lexer.lineno += 1
# Skipping MACRO
def t_MACRO(self, t):
r'MACRO'
t.lexer.begin('macro')
return t
def t_macro_newline(self, t):
r'\r\n|\n|\r'
t.lexer.lineno += 1
def t_macro_END(self, t):
r'END'
t.lexer.begin('INITIAL')
return t
def t_macro_body(self, t):
r'.+?(?=END)'
pass
# Skipping EXPORTS
def t_EXPORTS(self, t):
r'EXPORTS'
t.lexer.begin('exports')
return t
def t_exports_newline(self, t):
r'\r\n|\n|\r'
t.lexer.lineno += 1
def t_exports_end(self, t):
r';'
t.lexer.begin('INITIAL')
def t_exports_body(self, t):
r'[^;]+'
pass
# Skipping CHOICE
def t_CHOICE(self, t):
r'CHOICE'
t.lexer.begin('choice')
return t
def t_choice_newline(self, t):
r'\r\n|\n|\r'
t.lexer.lineno += 1
def t_choice_end(self, t):
r'\}'
t.lexer.begin('INITIAL')
def t_choice_body(self, t):
r'[^\}]+'
pass
# Comment handling
def t_begin_comment(self, t):
r'--'
t.lexer.begin('comment')
def t_comment_newline(self, t):
r'\r\n|\n|\r'
t.lexer.lineno += 1
t.lexer.begin('INITIAL')
# def t_comment_end(self, t):
# r'--'
# t.lexer.begin('INITIAL')
def t_comment_body(self, t):
r'[^\r\n]+'
pass
def t_UPPERCASE_IDENTIFIER(self, t):
r'[A-Z][-a-zA-z0-9]*'
if t.value in self.forbidden_words:
raise error.PySmiLexerError("%s is forbidden" % t.value, lineno=t.lineno)
if t.value[-1] == '-':
raise error.PySmiLexerError("Identifier should not end with '-': %s" % t.value, lineno=t.lineno)
t.type = self.reserved.get(t.value, 'UPPERCASE_IDENTIFIER')
return t
def t_LOWERCASE_IDENTIFIER(self, t):
r'[0-9]*[a-z][-a-zA-z0-9]*'
if t.value[-1] == '-':
raise error.PySmiLexerError("Identifier should not end with '-': %s" % t.value, lineno=t.lineno)
return t
def t_NUMBER(self, t):
r'-?[0-9]+'
t.value = int(t.value)
neg = 0
if t.value < 0:
neg = 1
val = abs(t.value)
if val <= UNSIGNED32_MAX:
if neg:
t.type = 'NEGATIVENUMBER'
elif val <= UNSIGNED64_MAX:
if neg:
t.type = 'NEGATIVENUMBER64'
else:
t.type = 'NUMBER64'
else:
raise error.PySmiLexerError("Number %s is too big" % t.value, lineno=t.lineno)
return t
def t_BIN_STRING(self, t):
r'\'[01]*\'[bB]'
value = t.value[1:-2]
while value and value[0] == '0' and len(value) % 8:
value = value[1:]
# XXX raise in strict mode
# if len(value) % 8:
# raise error.PySmiLexerError("Number of 0s and 1s have to divide by 8 in binary string %s" % t.value, lineno=t.lineno)
return t
def t_HEX_STRING(self, t):
r'\'[0-9a-fA-F]*\'[hH]'
value = t.value[1:-2]
while value and value[0] == '0' and len(value) % 2:
value = value[1:]
# XXX raise in strict mode
# if len(value) % 2:
# raise error.PySmiLexerError("Number of symbols have to be even in hex string %s" % t.value, lineno=t.lineno)
return t
def t_QUOTED_STRING(self, t):
r'\"[^\"]*\"'
t.lexer.lineno += len(re.findall(r'\r\n|\n|\r', t.value))
return t
def t_error(self, t):
raise error.PySmiLexerError(
"Illegal character '%s', %s characters left unparsed at this stage" % (t.value[0], len(t.value) - 1),
lineno=t.lineno)
# t.lexer.skip(1)
class SupportSmiV1Keywords(object):
@staticmethod
def reserved():
reserved_words = [
'ACCESS', 'AGENT-CAPABILITIES', 'APPLICATION', 'AUGMENTS', 'BEGIN', 'BITS',
'CONTACT-INFO', 'CREATION-REQUIRES', 'Counter', 'Counter32', 'Counter64',
'DEFINITIONS', 'DEFVAL', 'DESCRIPTION', 'DISPLAY-HINT', 'END', 'ENTERPRISE',
'EXTENDS', 'FROM', 'GROUP', 'Gauge', 'Gauge32', 'IDENTIFIER', 'IMPLICIT',
'IMPLIED', 'IMPORTS', 'INCLUDES', 'INDEX', 'INSTALL-ERRORS', 'INTEGER',
'Integer32', 'IpAddress', 'LAST-UPDATED', 'MANDATORY-GROUPS',
'MAX-ACCESS', 'MIN-ACCESS', 'MODULE', 'MODULE-COMPLIANCE', 'MAX',
'MODULE-IDENTITY', 'NetworkAddress', 'NOTIFICATION-GROUP',
'NOTIFICATION-TYPE', 'NOTIFICATIONS', 'OBJECT', 'OBJECT-GROUP',
'OBJECT-IDENTITY', 'OBJECT-TYPE', 'OBJECTS', 'OCTET', 'OF', 'ORGANIZATION',
'Opaque', 'PIB-ACCESS', 'PIB-DEFINITIONS', 'PIB-INDEX', 'PIB-MIN-ACCESS',
'PIB-REFERENCES', 'PIB-TAG', 'POLICY-ACCESS', 'PRODUCT-RELEASE',
'REFERENCE', 'REVISION', 'SEQUENCE', 'SIZE', 'STATUS', 'STRING',
'SUBJECT-CATEGORIES', 'SUPPORTS', 'SYNTAX', 'TEXTUAL-CONVENTION',
'TimeTicks', 'TRAP-TYPE', 'UNIQUENESS', 'UNITS', 'UNIVERSAL', 'Unsigned32',
'VALUE', 'VARIABLES', 'VARIATION', 'WRITE-SYNTAX'
]
reserved = {}
for w in reserved_words:
reserved[w] = w.replace('-', '_').upper()
# hack to support SMIv1
if w == 'Counter':
reserved[w] = 'COUNTER32'
elif w == 'Gauge':
reserved[w] = 'GAUGE32'
return reserved
@staticmethod
def forbidden_words():
return [
'ABSENT', 'ANY', 'BIT', 'BOOLEAN', 'BY', 'COMPONENT', 'COMPONENTS',
'DEFAULT', 'DEFINED', 'ENUMERATED', 'EXPLICIT', 'EXTERNAL', 'FALSE',
'MIN', 'MINUS-INFINITY', 'NULL', 'OPTIONAL', 'PLUS-INFINITY', 'PRESENT',
'PRIVATE', 'REAL', 'SET', 'TAGS', 'TRUE', 'WITH'
]
@staticmethod
def tokens():
# Token names required!
tokens = [
'BIN_STRING',
'CHOICE',
'COLON_COLON_EQUAL',
'DOT_DOT',
'EXPORTS',
'HEX_STRING',
'LOWERCASE_IDENTIFIER',
'MACRO',
'NEGATIVENUMBER',
'NEGATIVENUMBER64',
'NUMBER',
'NUMBER64',
'QUOTED_STRING',
'UPPERCASE_IDENTIFIER',
]
tokens += list(SupportSmiV1Keywords.reserved().values())
return list(set(tokens))
relaxedGrammar = {
'supportSmiV1Keywords': [
SupportSmiV1Keywords.reserved,
SupportSmiV1Keywords.forbidden_words,
SupportSmiV1Keywords.tokens
],
'supportIndex': [],
'commaAtTheEndOfImport': [],
'commaAtTheEndOfSequence': [],
'mixOfCommasAndSpaces': [],
'uppercaseIdentifier': [],
'lowcaseIdentifier': [],
'curlyBracesAroundEnterpriseInTrap': [],
'noCells': []
}
def lexerFactory(**grammarOptions):
classAttr = {}
for option in grammarOptions:
if grammarOptions[option]:
if option not in relaxedGrammar:
raise error.PySmiError('Unknown lexer relaxation option: %s' % option)
for func in relaxedGrammar[option]:
if sys.version_info[0] > 2:
classAttr[func.__name__] = func()
else:
classAttr[func.func_name] = func()
return type('SmiLexer', (SmiV2Lexer,), classAttr)
Zerion Mini Shell 1.0