Initial import
diff --git a/plyproto/__init__.py b/plyproto/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/plyproto/__init__.py
diff --git a/plyproto/model.py b/plyproto/model.py
new file mode 100644
index 0000000..7e39ae9
--- /dev/null
+++ b/plyproto/model.py
@@ -0,0 +1,218 @@
+__author__ = "Dusan (Ph4r05) Klinec"
+__copyright__ = "Copyright (C) 2014 Dusan (ph4r05) Klinec"
+__license__ = "Apache License, Version 2.0"
+__version__ = "1.0"
+
+# Base node
+class SourceElement(object):
+ '''
+ A SourceElement is the base class for all elements that occur in a Java
+ file parsed by plyj.
+ '''
+ def __init__(self):
+ super(SourceElement, self).__init__()
+ self._fields = []
+
+ def __repr__(self):
+ equals = ("{0}={1!r}".format(k, getattr(self, k))
+ for k in self._fields)
+ args = ", ".join(equals)
+ return "{0}({1})".format(self.__class__.__name__, args)
+
+ def __eq__(self, other):
+ try:
+ return self.__dict__ == other.__dict__
+ except AttributeError:
+ return False
+
+ def __ne__(self, other):
+ return not self == other
+
+ def accept(self, visitor):
+ pass
+
+class PackageDeclaration(SourceElement):
+ def __init__(self, name):
+ super(PackageDeclaration, self).__init__()
+ self._fields = ['name']
+ self.name = name
+
+ def accept(self, visitor):
+ visitor.visit_PackageDeclaration(self)
+
+class ImportStatement(SourceElement):
+ def __init__(self, name):
+ super(ImportStatement, self).__init__()
+ self._fields = ['name']
+ self.name = name
+
+ def accept(self, visitor):
+ visitor.visit_ImportStatement(self)
+
+class OptionDefinition(SourceElement):
+ def __init__(self, name, value):
+ super(OptionDefinition, self).__init__()
+ self._fields = ['name', 'value']
+ self.name = name
+ self.value = value
+
+ def accept(self, visitor):
+ visitor.visit_OptionDefinition(self)
+
+class FieldDirectiveDeclaration(SourceElement):
+ def __init__(self, name, value):
+ super(FieldDirectiveDeclaration, self).__init__()
+ self._fields = ['name', 'value']
+ self.name = name
+ self.value = value
+
+ def accept(self, visitor):
+ visitor.visit_FieldDirectiveDeclaration(self)
+
+class FieldPrimitiveType(SourceElement):
+ def __init__(self, name):
+ super(FieldPrimitiveType, self).__init__()
+ self._fields = ['name']
+ self.name = name
+
+ def accept(self, visitor):
+ visitor.visit_FieldPrimitiveType(self)
+
+class FieldDeclaration(SourceElement):
+ def __init__(self, field_modifier, ftype, name, fieldId, fieldDirective):
+ super(FieldDeclaration, self).__init__()
+ self._fields = ['field_modifier', 'ftype', 'name', 'fieldId', 'fieldDirective']
+ self.name = name
+ self.field_modifier = field_modifier
+ self.ftype = ftype
+ self.fieldId = fieldId
+ self.fieldDirective = fieldDirective
+
+ def accept(self, visitor):
+ visitor.visit_FieldDeclaration(self)
+
+class EnumFieldDeclaration(SourceElement):
+ def __init__(self, name, fieldId):
+ super(EnumFieldDeclaration, self).__init__()
+ self._fields = ['name', 'fieldId']
+ self.name = name
+ self.fieldId = fieldId
+
+ def accept(self, visitor):
+ visitor.visit_EnumFieldDeclaration(self)
+
+class EnumDeclaration(SourceElement):
+ def __init__(self, name, body):
+ super(EnumDeclaration, self).__init__()
+ self._fields = ['name', 'body']
+ self.name = name
+ self.body = body
+
+ def accept(self, visitor):
+ if visitor.visit_EnumDeclaration(self):
+ for s in self.body:
+ s.accept(visitor)
+
+class MessageDeclaration(SourceElement):
+ def __init__(self, name, body):
+ super(MessageDeclaration, self).__init__()
+ self._fields = ['name', 'body']
+ self.name = name
+ self.body = body
+
+ def accept(self, visitor):
+ if visitor.visit_MessageDeclaration(self):
+ for s in self.body:
+ s.accept(visitor)
+
+class MessageExtension(SourceElement):
+ def __init__(self, name, body):
+ super(MessageExtension, self).__init__()
+ self._fields = ['name', 'body']
+ self.name = name
+ self.body = body
+
+ def accept(self, visitor):
+ if visitor.visit_MessageExtension(self):
+ for s in self.body:
+ s.accept(visitor)
+
+class MethodDefinition(SourceElement):
+ def __init__(self, name, name2, name3):
+ super(MethodDefinition, self).__init__()
+ self._fields = ['name', 'name2', 'name3']
+ self.name = name
+ self.name2 = name2
+ self.name3 = name3
+
+ def accept(self, visitor):
+ visitor.visit_MethodDefinition(self)
+
+class ServiceDeclaration(SourceElement):
+ def __init__(self, name, body):
+ super(ServiceDeclaration, self).__init__()
+ self._fields = ['name', 'body']
+ self.name = name
+ self.body = body
+
+ def accept(self, visitor):
+ if visitor.visit_ServiceDeclaration(self):
+ for s in self.body:
+ s.accept(visitor)
+
+class ExtensionsMax(SourceElement):
+ pass
+
+class ExtensionsDirective(SourceElement):
+ def __init__(self, fromVal, toVal):
+ super(ExtensionsDirective, self).__init__()
+ self._fields = ['fromVal', 'toVal']
+ self.fromVal = fromVal
+ self.toVal = toVal
+
+ def accept(self, visitor):
+ visitor.visit_ExtensionsDirective(self)
+
+class Literal(SourceElement):
+
+ def __init__(self, value):
+ super(Literal, self).__init__()
+ self._fields = ['value']
+ self.value = value
+
+ def accept(self, visitor):
+ visitor.visit_Literal(self)
+
+class Name(SourceElement):
+
+ def __init__(self, value):
+ super(Name, self).__init__()
+ self._fields = ['value']
+ self.value = value
+
+ def append_name(self, name):
+ try:
+ self.value = self.value + '.' + name.value
+ except:
+ self.value = self.value + '.' + name
+
+ def accept(self, visitor):
+ visitor.visit_Name(self)
+
+
+class Visitor(object):
+
+ def __init__(self, verbose=False):
+ self.verbose = verbose
+
+ def __getattr__(self, name):
+ if not name.startswith('visit_'):
+ raise AttributeError('name must start with visit_ but was {}'
+ .format(name))
+
+ def f(element):
+ if self.verbose:
+ msg = 'unimplemented call to {}; ignoring ({})'
+ print(msg.format(name, element))
+ return True
+ return f
diff --git a/plyproto/parser.py b/plyproto/parser.py
new file mode 100755
index 0000000..545faee
--- /dev/null
+++ b/plyproto/parser.py
@@ -0,0 +1,335 @@
+__author__ = "Dusan (Ph4r05) Klinec"
+__copyright__ = "Copyright (C) 2014 Dusan (ph4r05) Klinec"
+__license__ = "Apache License, Version 2.0"
+__version__ = "1.0"
+
+import ply.lex as lex
+import ply.yacc as yacc
+from .model import *
+
+class ProtobufLexer(object):
+ keywords = ('double', 'float', 'int32', 'int64', 'uint32', 'uint64', 'sint32', 'sint64',
+ 'fixed32', 'fixed64', 'sfixed32', 'sfixed64', 'bool', 'string', 'bytes',
+ 'message', 'required', 'optional', 'repeated', 'enum', 'extensions', 'max', 'extends', 'extend',
+ 'to', 'package', 'service', 'rpc', 'returns', 'true', 'false', 'option', 'import')
+
+ tokens = [
+ 'NAME',
+ 'NUM',
+ 'STRING_LITERAL',
+ 'LINE_COMMENT', 'BLOCK_COMMENT',
+
+ 'LBRACE', 'RBRACE', 'LBRACK', 'RBRACK',
+ 'LPAR', 'RPAR', 'EQ', 'SEMI'
+
+ ] + [k.upper() for k in keywords]
+ literals = '()+-*/=?:,.^|&~!=[]{};<>@%'
+
+ t_NUM = r'[+-]?\d+'
+ t_STRING_LITERAL = r'\"([^\\\n]|(\\.))*?\"'
+
+ t_ignore_LINE_COMMENT = '//.*'
+ def t_BLOCK_COMMENT(self, t):
+ r'/\*(.|\n)*?\*/'
+ t.lexer.lineno += t.value.count('\n')
+
+ t_LBRACE = '{'
+ t_RBRACE = '}'
+ t_LBRACK = '\\['
+ t_RBRACK = '\\]'
+ t_LPAR = '\\('
+ t_RPAR = '\\)'
+ t_EQ = '='
+ t_SEMI = ';'
+ t_ignore = ' \t\f'
+
+ def t_NAME(self, t):
+ '[A-Za-z_$][A-Za-z0-9_$]*'
+ if t.value in ProtobufLexer.keywords:
+ t.type = t.value.upper()
+ return t
+
+ def t_newline(self, t):
+ r'\n+'
+ t.lexer.lineno += len(t.value)
+
+ def t_newline2(self, t):
+ r'(\r\n)+'
+ t.lexer.lineno += len(t.value) / 2
+
+ def t_error(self, t):
+ print("Illegal character '{}' ({}) in line {}".format(t.value[0], hex(ord(t.value[0])), t.lexer.lineno))
+ t.lexer.skip(1)
+
+class ProtobufParser(object):
+ tokens = ProtobufLexer.tokens
+
+ def p_empty(self, p):
+ '''empty :'''
+ pass
+
+ def p_field_modifier(self,p):
+ '''field_modifier : REQUIRED
+ | OPTIONAL
+ | REPEATED'''
+ p[0] = p[1]
+
+ def p_primitive_type(self, p):
+ '''primitive_type : DOUBLE
+ | FLOAT
+ | INT32
+ | INT64
+ | UINT32
+ | UINT64
+ | SINT32
+ | SINT64
+ | FIXED32
+ | FIXED64
+ | SFIXED32
+ | SFIXED64
+ | BOOL
+ | STRING
+ | BYTES'''
+ p[0] = p[1]
+
+ def p_field_id(self, p):
+ '''field_id : NUM'''
+ p[0] = p[1]
+
+ def p_rvalue(self, p):
+ '''rvalue : NUM
+ | TRUE
+ | FALSE'''
+ p[0] = p[1]
+
+ def p_rvalue2(self, p):
+ '''rvalue : NAME'''
+ p[0] = Name(p[1])
+
+ def p_field_directive(self, p):
+ '''field_directive : LBRACK NAME EQ rvalue RBRACK'''
+ p[0] = FieldDirectiveDeclaration(Name(p[2]), p[4])
+
+ def p_field_directive_times(self, p):
+ '''field_directive_times : field_directive_plus'''
+ p[0] = p[1]
+
+ def p_field_directive_times2(self, p):
+ '''field_directive_times : empty'''
+ p[0] = []
+
+ def p_field_directive_plus(self, p):
+ '''field_directive_plus : field_directive
+ | field_directive_plus field_directive'''
+ if len(p) == 2:
+ p[0] = [p[1]]
+ else:
+ p[0] = p[1] + [p[2]]
+
+ def p_field_type(self, p):
+ '''field_type : primitive_type'''
+ p[0] = FieldPrimitiveType(p[1])
+
+ def p_field_type2(self, p):
+ '''field_type : NAME'''
+ p[0] = Name(p[1])
+
+ # Root of the field declaration.
+ def p_field_definition(self, p):
+ '''field_definition : field_modifier field_type NAME EQ field_id field_directive_times SEMI'''
+ p[0] = FieldDeclaration(p[1], p[2], Name(p[3]), p[5], p[6])
+
+ # Root of the enum field declaration.
+ def p_enum_field(self, p):
+ '''enum_field : NAME EQ NUM SEMI'''
+ p[0] = EnumFieldDeclaration(Name(p[1]), p[3])
+
+ def p_enum_body_part(self, p):
+ '''enum_body_part : enum_field
+ | option_directive'''
+ p[0] = p[1]
+
+ def p_enum_body(self, p):
+ '''enum_body : enum_body_part
+ | enum_body enum_body_part'''
+ if len(p) == 2:
+ p[0] = [p[1]]
+ else:
+ p[0] = p[1] + [p[2]]
+
+ def p_enum_body_opt(self, p):
+ '''enum_body_opt : empty'''
+ p[0] = []
+
+ def p_enum_body_opt2(self, p):
+ '''enum_body_opt : enum_body'''
+ p[0] = p[1]
+
+ # Root of the enum declaration.
+ # enum_definition ::= 'enum' ident '{' { ident '=' integer ';' }* '}'
+ def p_enum_definition(self, p):
+ '''enum_definition : ENUM NAME LBRACE enum_body_opt RBRACE'''
+ p[0] = EnumDeclaration(Name(p[2]), p[4])
+
+ def p_extensions_to(self, p):
+ '''extensions_to : MAX'''
+ p[0] = ExtensionsMax()
+
+ def p_extensions_to2(self, p):
+ '''extensions_to : NUM'''
+ p[0] = p[1]
+
+ # extensions_definition ::= 'extensions' integer 'to' integer ';'
+ def p_extensions_definition(self, p):
+ '''extensions_definition : EXTENSIONS NUM TO extensions_to SEMI'''
+ p[0] = ExtensionsDirective(p[2], p[4])
+
+ # message_extension ::= 'extend' ident '{' message_body '}'
+ def p_message_extension(self, p):
+ '''message_extension : EXTEND NAME LBRACE message_body RBRACE'''
+ p[0] = MessageExtension(Name(p[2]), p[4])
+
+ def p_message_body_part(self, p):
+ '''message_body_part : field_definition
+ | enum_definition
+ | message_definition
+ | extensions_definition
+ | message_extension'''
+ p[0] = p[1]
+
+ # message_body ::= { field_definition | enum_definition | message_definition | extensions_definition | message_extension }*
+ def p_message_body(self, p):
+ '''message_body : empty'''
+ p[0] = []
+
+ # message_body ::= { field_definition | enum_definition | message_definition | extensions_definition | message_extension }*
+ def p_message_body2(self, p):
+ '''message_body : message_body_part
+ | message_body message_body_part'''
+ if len(p) == 2:
+ p[0] = [p[1]]
+ else:
+ p[0] = p[1] + [p[2]]
+
+ # Root of the message declaration.
+ # message_definition = MESSAGE_ - ident("messageId") + LBRACE + message_body("body") + RBRACE
+ def p_message_definition(self, p):
+ '''message_definition : MESSAGE NAME LBRACE message_body RBRACE'''
+ p[0] = MessageDeclaration(Name(p[2]), p[4])
+
+ # method_definition ::= 'rpc' ident '(' [ ident ] ')' 'returns' '(' [ ident ] ')' ';'
+ def p_method_definition(self, p):
+ '''method_definition : RPC NAME LPAR NAME RPAR RETURNS LPAR NAME RPAR'''
+ p[0] = MethodDefinition(Name(p[2]), Name(p[4]), Name(p[8]))
+
+ def p_method_definition_opt(self, p):
+ '''method_definition_opt : empty'''
+ p[0] = []
+
+ def p_method_definition_opt2(self, p):
+ '''method_definition_opt : method_definition
+ | method_definition_opt method_definition'''
+ if len(p) == 2:
+ p[0] = [p[1]]
+ else:
+ p[0] = p[1] + [p[2]]
+
+ # service_definition ::= 'service' ident '{' method_definition* '}'
+ # service_definition = SERVICE_ - ident("serviceName") + LBRACE + ZeroOrMore(Group(method_definition)) + RBRACE
+ def p_service_definition(self, p):
+ '''service_definition : SERVICE NAME LBRACE method_definition_opt RBRACE'''
+ p[0] = ServiceDeclaration(Name(p[2]), p[4])
+
+ # package_directive ::= 'package' ident [ '.' ident]* ';'
+ def p_package_directive(self,p):
+ '''package_directive : PACKAGE NAME SEMI'''
+ p[0] = PackageDeclaration(Name(p[2]))
+
+ # import_directive = IMPORT_ - quotedString("importFileSpec") + SEMI
+ def p_import_directive(self, p):
+ '''import_directive : IMPORT STRING_LITERAL SEMI'''
+ p[0] = ImportStatement(Literal(p[2]))
+
+ def p_option_rvalue(self, p):
+ '''option_rvalue : NUM
+ | TRUE
+ | FALSE'''
+ p[0] = p[1]
+
+ def p_option_rvalue2(self, p):
+ '''option_rvalue : STRING_LITERAL'''
+ p[0] = Literal(p[1])
+
+ # option_directive = OPTION_ - ident("optionName") + EQ + quotedString("optionValue") + SEMI
+ def p_option_directive(self, p):
+ '''option_directive : OPTION NAME EQ option_rvalue SEMI'''
+ p[0] = OptionDefinition(Name(p[2]), p[4])
+
+ # topLevelStatement = Group(message_definition | message_extension | enum_definition | service_definition | import_directive | option_directive)
+ def p_topLevel(self,p):
+ '''topLevel : message_definition
+ | message_extension
+ | enum_definition
+ | service_definition
+ | import_directive
+ | option_directive'''
+ p[0] = p[1]
+
+ def p_package_definition(self, p):
+ '''package_definition : package_directive'''
+ p[0] = p[1]
+
+ def p_packages2(self, p):
+ '''package_definition : empty'''
+ p[0] = []
+
+ def p_statements2(self, p):
+ '''statements : topLevel
+ | statements topLevel'''
+ if len(p) == 2:
+ p[0] = [p[1]]
+ else:
+ p[0] = p[1] + [p[2]]
+
+ def p_statements(self, p):
+ '''statements : empty'''
+ p[0] = []
+
+ # parser = Optional(package_directive) + ZeroOrMore(topLevelStatement)
+ def p_goal2(self, p):
+ '''goal : package_definition statements'''
+ p[0] = [p[1], p[2]]
+
+ def p_error(self, p):
+ print('error: {}'.format(p))
+
+class ProtobufAnalyzer(object):
+
+ def __init__(self):
+ self.lexer = lex.lex(module=ProtobufLexer(), optimize=1)
+ self.parser = yacc.yacc(module=ProtobufParser(), start='goal', optimize=1)
+
+ def tokenize_string(self, code):
+ self.lexer.input(code)
+ for token in self.lexer:
+ print(token)
+
+ def tokenize_file(self, _file):
+ if type(_file) == str:
+ _file = file(_file)
+ content = ''
+ for line in _file:
+ content += line
+ return self.tokenize_string(content)
+
+ def parse_string(self, code, debug=0, lineno=1, prefix=''):
+ self.lexer.lineno = lineno
+ return self.parser.parse(prefix + code, lexer=self.lexer, debug=debug)
+
+ def parse_file(self, _file, debug=0):
+ if type(_file) == str:
+ _file = file(_file)
+ content = ''
+ for line in _file:
+ content += line
+ return self.parse_string(content, debug=debug)