[Top][All Lists]
[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
[certi-cvs] certi/scripts GenerateMessages.py TestMessageSp...
From: |
certi-cvs |
Subject: |
[certi-cvs] certi/scripts GenerateMessages.py TestMessageSp... |
Date: |
Wed, 15 Jul 2009 17:12:02 +0000 |
CVSROOT: /sources/certi
Module name: certi
Changes by: Eric NOULARD <erk> 09/07/15 17:12:02
Modified files:
scripts : GenerateMessages.py TestMessageSpec.msg
Log message:
Parser is now fine including checking step
and appropriate error reporting pointing to
accurate line numbers in the file.
Every ASTElement now has a linespan.
CVSWeb URLs:
http://cvs.savannah.gnu.org/viewcvs/certi/scripts/GenerateMessages.py?cvsroot=certi&r1=1.7&r2=1.8
http://cvs.savannah.gnu.org/viewcvs/certi/scripts/TestMessageSpec.msg?cvsroot=certi&r1=1.5&r2=1.6
Patches:
Index: GenerateMessages.py
===================================================================
RCS file: /sources/certi/certi/scripts/GenerateMessages.py,v
retrieving revision 1.7
retrieving revision 1.8
diff -u -b -r1.7 -r1.8
--- GenerateMessages.py 14 Jul 2009 20:54:35 -0000 1.7
+++ GenerateMessages.py 15 Jul 2009 17:12:02 -0000 1.8
@@ -1,13 +1,39 @@
#!/usr/bin/env python
+## ----------------------------------------------------------------------------
+## CERTI - HLA RunTime Infrastructure
+## Copyright (C) 2002-2005 ONERA
+##
+## This program is free software ; you can redistribute it and/or
+## modify it under the terms of the GNU Lesser General Public License
+## as published by the Free Software Foundation ; either version 2 of
+## the License, 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
+## Lesser General Public License for more details.
+##
+## You should have received a copy of the GNU Lesser General Public
+## License along with this program ; if not, write to the Free Software
+## Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+## USA
+##
+## $Id: GenerateMessages.py,v 1.8 2009/07/15 17:12:02 erk Exp $
+## ----------------------------------------------------------------------------
+
+"""
+The CERTI Message Generator
+"""
+
import os
import getopt, sys
-import shutil
# We use PLY in order to parse CERTI message specification files
# PLY is there: http://www.dabeaz.com/ply/
import ply.yacc
import ply.lex
# We use logging for ... logging :-)
+# see http://docs.python.org/library/logging.html
import logging
# Build some logger related objects
@@ -49,7 +75,6 @@
if o in ("-t", "--type"):
gentype=a
if o in ("-o", "--output"):
- mainlogger.info("output send to file: <%s>" % a)
output=open(a)
if o in ("-v", "--verbose"):
verbose=True
@@ -58,11 +83,15 @@
usage()
sys.exit(0)
+mainlogger.info("Reading message specifications from: <%s>" % messagefile)
+mainlogger.info("output send to: <%s>" % repr(output))
+mainlogger.info("Generating for language: <%s>" % language)
+
# Lexer+Parser specification begins here
# reserved keywords
reserved = {
'package' : 'PACKAGE',
- 'native_message' : 'NATIVE_MESSAGE',
+ 'native' : 'NATIVE',
'message' : 'MESSAGE',
'merge' : 'MERGE',
'enum' : 'ENUM',
@@ -169,80 +198,145 @@
lexer = ply.lex.lex(debug=False)
lexer.logger = lexerlogger
-# Named Class
class ASTElement(object):
+ """
+ The base class for all Abstract Syntax Tree element.
+
+ @param name: the name of the element
+ @type name: C{string}
+ @param comment: the comment attached to this element.
+ may be C{None}
+ @type comment: C{CommentBlock}
+ """
def __init__(self,name):
self.__name = name
self.__comment = None
+ self.__linespan = None
self.logger = logging.Logger("ASTElement")
self.logger.setLevel(logging.ERROR)
self.logger.addHandler(stdoutHandler)
- def getName(self):
+ def __getName(self):
return self.__name
- def setName(self,name):
+ def __setName(self,name):
self.__name = name
# pythonic getter/setter using properties
- name = property(fget=getName,fset=setName,fdel=None, doc=None)
+ name = property(fget=__getName,fset=__setName,fdel=None, doc="The name of
the C{ASTElement}")
def hasComment(self):
return self.__comment!=None
- def getComment(self):
+ def __getComment(self):
return self.__comment
- def setComment(self,comment):
+ def __setComment(self,comment):
if isinstance(comment,type("")):
pass
else:
self.logger.info("Adding comment %s to element %s" %
(comment.lines,self.name))
self.__comment = comment
# pythonic getter/setter using properties
- comment = property(fget=getComment,fset=setComment,fdel=None, doc=None)
+ comment = property(fget=__getComment,fset=__setComment,fdel=None, doc="The
comment block attached to the C{ASTElement}")
+
+ def __getLinespan(self):
+ return self.__linespan
+ def __setLinespan(self,linespan):
+ self.__linespan = linespan
+ # pythonic getter/setter using properties
+ linespan = property(fget=__getLinespan,fset=__setLinespan,fdel=None,
doc="The line span of this C{ASTElement} in the original file")
+
-# Message set
class MessageAST(ASTElement):
+ """
+ Message Abstract Syntax Tree root class.
+
+ This class represents the abstraction of the message description
+ language including all the features represented by corresponding
+ class field. Object instance of C{MessageAST} may be used by a
+ language generator in order to generate code for the target language.
+
+ @param package: the package which the generated message will belong
+ this will be used by the specific AST generator
+ @type package: C{Package}
+ @param nativeMessages: the set of native messages described in this
+ C{MessageAST}
+ @type nativeMessages: C{set} of C{NativeType}
+ @param messages: the set of messages described in this C{MessageAST}
+ @type messages: C{set} of C{MessageType}
+
+ """
def __init__(self,name):
super(MessageAST,self).__init__(name=name)
- self.__nativeMessageTypeSet = set()
+ self.__package = None
+ self.__NativeTypeSet = set()
self.__messageTypeSet = set()
self.__enumTypeSet = set()
- self.__package = None
- self.__types = dict()
+ # The types dictionary is initialized with builtin types
+ self.__types = {'bool' : ASTElement("bool"),
+ 'string' : ASTElement("string"),
+ 'byte' : ASTElement("byte"),
+ 'int8' : ASTElement("int8"),
+ 'uint8' : ASTElement("uint8"),
+ 'int16' : ASTElement("int16"),
+ 'uint16' : ASTElement("uint16"),
+ 'int32' : ASTElement("int32"),
+ 'uint32' : ASTElement("uint32"),
+ 'int64' : ASTElement("int64"),
+ 'uint64' : ASTElement("uint64"),
+ 'float' : ASTElement("float"),
+ 'double' : ASTElement("double")}
self.__ultimateElement = None
self.logger = logging.Logger("MessageAST")
self.logger.setLevel(logging.ERROR)
self.logger.addHandler(stdoutHandler)
- def __getNativeMessageTypeSet(self):
- return self.__nativeMessageTypeSet
- nativeMessages =
property(fget=__getNativeMessageTypeSet,fset=None,fdel=None,doc=None)
+ def hasPackage(self):
+ return self.__package != None
+ def __getPackage(self):
+ return self.__package
+ def __setPackage(self,package):
+ self.__package = package
+ # pythonic getter/setter using properties
+ package = property(fget=__getPackage,fset=__setPackage,fdel=None,doc=None)
+
+ def __getNativeTypeSet(self):
+ return self.__NativeTypeSet
+ # pythonic getter/setter using properties
+ nativeMessages =
property(fget=__getNativeTypeSet,fset=None,fdel=None,doc=None)
def __getMessageTypeSet(self):
return self.__messageTypeSet
+ # pythonic getter/setter using properties
messages = property(fget=__getMessageTypeSet,fset=None,fdel=None,doc=None)
def __getEnumTypeSet(self):
return self.__enumTypeSet
+ # pythonic getter/setter using properties
enums = property(fget=__getEnumTypeSet,fset=None,fdel=None,doc=None)
- def hasPackage(self):
- return self.__package != None
- def __getPackage(self):
- return self.__package
- def __setPackage(self,package):
- self.__package = package
- package = property(fget=__getPackage,fset=__setPackage,fdel=None,doc=None)
def add(self,any):
- """ Add an ASTElement to the AST """
+ """
+ Add an ASTElement to the AST.
+
+ The parser will call this method as soons as it has
+ built the appropriate ASTElement sub-class.
+
+ @param any: the object to be added to the tree
+ @type any: some sub-class of C{ASTElement}, see: G{ASTElement}
+
+ """
if any == None:
self.logger.error("<None> given to AST some rule aren't finished")
else:
self.logger.debug("Add %s %s" % (type(any).__name__,any.name))
- if isinstance(any,EnumType):
+ # Typename must be unique
+ if self.isDefined(any.name):
+ self.logger.error("%s already defined in the AST" % any.name)
+ self.logger.error(" --> Check lines (%d,%d) " % any.linespan +
"and (%d,%d)" % self.getType(any.name).linespan+ " of <%s>" % self.name)
+ elif isinstance(any,EnumType):
self.addEnumType(any)
- elif isinstance(any,NativeMessageType):
- self.addNativeMessageType(any)
+ elif isinstance(any,NativeType):
+ self.addNativeType(any)
elif isinstance(any,MessageType):
self.addMessageType(any)
elif isinstance(any,Package):
@@ -262,58 +356,111 @@
self.__ultimateElement = any
def addMessageType(self,message):
+ """
+ Add a message type to the AST.
+
+ @param message: The message type to be added
+ @type message: C{MessageType}
+ """
self.__messageTypeSet.add(message)
self.__types[message.name] = message
def addEnumType(self,enumType):
+ """
+ Add an enum type to the AST.
+
+ @param enumType: The enum type to be added
+ @type enumType: C{EnumType}
+ """
self.__enumTypeSet.add(enumType)
self.__types[enumType.name] = enumType
- def addNativeMessageType(self,message):
- self.__nativeMessageTypeSet.add(message)
- self.__types[message.name] = message
+ def addNativeType(self,native):
+ """
+ Add a native type to the AST.
+
+ @param native: The message type to be added
+ @type native: C{NativeType}
+ """
+ self.__NativeTypeSet.add(native)
+ self.__types[native.name] = native
def isDefined(self,typename):
- self.logger.debug("%s" % self.__types.keys())
+ """
+ Return true if the typename is know in this AST.
+
+ @param typename: the name of the type
+ @type typename: C{string}
+ """
+ return self.getType(typename)!=None
+
+ def getType(self,typename):
+ if isinstance(typename,type("")):
if typename in self.__types.keys():
- return True
+ return self.__types[typename]
else:
- return False
+ return None
+ else:
+ return typename
def __repr__(self):
- res = "AST with:\n <%d> native messages type,\n <%d> message type,\n
<%d> enum type\n" %
(len(self.nativeMessages),len(self.messages),len(self.enums))
- res = res + " will be in package <%s>\n" % self.package
+ res = "AST with <%d> native message, <%d> enum, <%d> message type(s)"
% (len(self.nativeMessages),len(self.enums),len(self.messages))
+ res = res + " in package <%s>" % self.package
return res
class CommentBlock(ASTElement):
+ """
+ Represents a block of comment
+
+ A C{CommentBlock} has lines which is a list of C{string}.
+ @param lines: the comments lines
+ @type lines: C{list} of C{string}
+ """
def __init__(self,content,optComment):
+ """
+ C{CommentBlock} constructor
+ """
super(CommentBlock,self).__init__(name="ANY Comment Block")
self.lines=[content]
self.__optComment=optComment
class Package(ASTElement):
- """Represents a package"""
+ """
+ Represents a package.
+
+ A C{Package} is a simple C{ASTElement} whose
+ name is a C{string} containing a
+ dot-separated IDs like: "fr.onera.certi"
+ """
def __init__(self,name):
super(Package,self).__init__(name)
def __repr__(self):
return "package %s" % self.name
-class NativeMessageType(ASTElement):
- """ Represents a native message type
+class NativeType(ASTElement):
+ """
+ Represents a native message type.
+
+ A C{NaptiveMessageType} is a simple C{ASTElement} whose
+ name is the name the native type.
"""
def __init__(self,name):
- super(NativeMessageType,self).__init__(name=name)
+ super(NativeType,self).__init__(name=name)
def __repr__(self):
- return "native_message %s" % self.name
+ return "native %s" % self.name
class MessageType(ASTElement):
- """ Represents a message type
"""
- def __init__(self,name,field_list,merge):
+ Represents a message type.
+
+ @param fields: the fields of this C{MessageType}
+ @type fields: C{list} of C{MessageType.MessageField}
+ """
+ def __init__(self,name,fields,merge):
super(MessageType,self).__init__(name=name)
- self.field_list = field_list
+ self.fields = fields
self.merge = merge
def __repr__(self):
@@ -376,7 +523,7 @@
'''statement : comment_block
| package
| message
- | native_message
+ | native
| enum'''
p[0]=p[1]
@@ -392,6 +539,7 @@
def p_package(p):
'''package : PACKAGE package_id'''
p[0]=Package(p[2])
+ p[0].linespan = (p.linespan(1)[0],p.linespan(2)[1])
def p_package_id(p):
'''package_id : ID
@@ -416,10 +564,12 @@
elif len(p)==9:
p[7].reverse()
p[0] = MessageType(p[2],p[7],p[5])
+ p[0].linespan = (p.linespan(1)[0],p.linespan(len(p)-1)[1])
-def p_native_message(p):
- 'native_message : NATIVE_MESSAGE ID'
- p[0]=NativeMessageType(p[2])
+def p_native(p):
+ 'native : NATIVE ID'
+ p[0]=NativeType(p[2])
+ p[0].linespan = p.linespan(1)
def p_enum(p):
'enum : ENUM ID LBRACE enum_value_list RBRACE'
@@ -427,6 +577,7 @@
# because the parse build it the other way around (recursive way)
p[4].reverse()
p[0] = EnumType(p[2],p[4])
+ p[0].linespan = (p.linespan(1)[0],p.linespan(5)[1])
def p_empty(p):
'empty :'
@@ -438,6 +589,7 @@
# we may store the comment text for future use
if len(p) > 1 and isinstance(p[1],type("")) :
p[0] = CommentBlock(p[1].strip('/'),optComment=True)
+ p[0].linespan = p.linespan(1)
else:
p[0] = ""
@@ -464,8 +616,10 @@
# value may be None
if len(p)>3:
p[0] = EnumType.EnumValue(p[1],p[3])
+ p[0].linespan = (p.linespan(1)[0],p.linespan(3)[1])
else:
p[0] = EnumType.EnumValue(p[1],None)
+ p[0].linespan = p.linespan(1)
def p_field_list(p):
'''field_list : field_spec optional_comment
@@ -482,9 +636,11 @@
if len(p)==5:
p[0] = MessageType.MessageField(p[1],p[2],p[3],None)
p[0].comment = p[4]
+ p[0].linespan = (p.linespan(1)[0],p.linespan(4)[1])
else:
p[0] = MessageType.MessageField(p[1],p[2],p[3],p[7])
p[0].comment = p[8]
+ p[0].linespan = (p.linespan(1)[0],p.linespan(8)[1])
def p_qualifier(p):
'''qualifier : REQUIRED
@@ -536,8 +692,62 @@
def p_error(p):
print "Syntax error at '%s' on line %d column %d (token type is '%s')" %
(p.value,p.lineno,find_column(p.lexer.lexdata, p),p.type)
+class ASTChecker(object):
+ """
+ The Purpose of this class is to check AST properties.
+
+ """
+ def __init__(self):
+ self.logger = logging.Logger("ASTChecker")
+ self.logger.setLevel(logging.ERROR)
+ self.logger.addHandler(stdoutHandler)
+ pass
+
+ def check(self,AST):
+ """
+ Check the AST.
+
+ @param AST: the AST to be checked
+ @type AST: C{MessageAST}
+ """
+
+ # check if the supplied object has appropriate super type
+ # @todo: note that we may just require to have the appropriate
+ # fields and not being sub-class of MesssageAST.
+ # this could be done with introspection.
+ # see: http://docs.python.org/library/inspect.html
+ if not isinstance(AST, MessageAST):
+ self.logger.error("The supplied object is not an instance of
MessageAST: <%s>" % type(AST))
+ AST.checked = False
+ return
+
+ # check if all field used in message have known types
+ for msg in AST.messages:
+ for f in msg.fields:
+ if not AST.isDefined(f.typeid):
+ self.logger.fatal("The type <%s> used for field <%s.%s> is
unknown (not a builtin, nor native, nor message)" % (f.typeid,msg.name,f.name))
+ self.logger.fatal(" --> Check lines (%d,%d)" %
(f.linespan) + " of <%s>" % AST.name)
+ AST.checked = False
+ return
+ else:
+ f.typeid = AST.getType(f.typeid)
+
+ # check if merger are either native or message
+ for msg in AST.messages:
+ if msg.hasMerge():
+ if not AST.isDefined(msg.merge):
+ self.logger.fatal("The merge target <%s> of message <%s>
is unknown (not a builtin, nor native, nor message)" % (msg.merge,msg.name))
+ self.logger.fatal(" --> Check lines (%d,%d)" %
(msg.linespan) + " of <%s>" % AST.name )
+ AST.checked = False
+ return
+ else:
+ msg.merge = AST.getType(msg.merge)
+ AST.checked = True
+
class TextGenerator(object):
- """This is a text generator"""
+ """
+ This is a text generator.
+ """
def __init__(self,MessageAST):
self.AST = MessageAST
@@ -571,18 +781,18 @@
# Generate native message
for native in self.AST.nativeMessages:
self.writeComment(stream, native)
- stream.write("native_message %s\n" % native.name)
+ stream.write("native %s\n" % native.name)
# Generate message type
for msg in self.AST.messages:
self.writeComment(stream, msg)
stream.write("message %s"%msg.name)
if msg.hasMerge():
- stream.write(" : merge %s {\n" % msg.merge)
+ stream.write(" : merge %s {\n" % msg.merge.name)
else:
stream.write(" {\n")
- for field in msg.field_list:
- stream.write(" %s %s %s " %
(field.qualifier,field.typeid,field.name))
+ for field in msg.fields:
+ stream.write(" %s %s %s " %
(field.qualifier,field.typeid.name,field.name))
if field.hasDefaultValue():
stream.write("[default=%s] " % field.defaultValue)
self.writeComment(stream, field)
@@ -646,13 +856,21 @@
parser = ply.yacc.yacc(debug=True)
parser.logger = parserlogger
-mainlogger.info("Trying to parse...")
+mainlogger.info("Parsing message file specifications...")
msgFile = open(messagefile,'r')
lexer.lineno = 1
parser.AST = MessageAST(messagefile)
parser.parse(msgFile.read(),lexer=lexer)
msgFile.close()
-mainlogger.info("Parse succeeded AST = %s" % (parser.AST))
+mainlogger.info("Parse succeeded %s" % (parser.AST))
+
+mainlogger.info("Checking AST properties....")
+checker = ASTChecker()
+checker.check(parser.AST)
+if parser.AST.checked:
+ mainlogger.info("AST properties checked Ok.")
+else:
+ mainlogger.error("AST has error, generation step may produce invalid
files!!!")
mainlogger.info("Generate %s from AST,..."%language)
if language=="Text":
Index: TestMessageSpec.msg
===================================================================
RCS file: /sources/certi/certi/scripts/TestMessageSpec.msg,v
retrieving revision 1.5
retrieving revision 1.6
diff -u -b -r1.5 -r1.6
--- TestMessageSpec.msg 14 Jul 2009 20:23:42 -0000 1.5
+++ TestMessageSpec.msg 15 Jul 2009 17:12:02 -0000 1.6
@@ -5,12 +5,12 @@
//
// This is a multiline
-// comment
+// comment which may contains markup
+// @desc here comes the EType
//
-
enum EType {
FIRST_VAL = 0, // one may specify the value of the first enum symbol
- REAL_VAL1 ,
+ REAL_VAL1 , //! the real value 1
REAL_VAL2 ,
UNUSED
}
@@ -18,9 +18,11 @@
message MyMessage {}
// blah
-native_message ANativeMessageType
+native ANativeType
+
+native AnotherNativeType
-native_message ANotherativeMessageType
+native MessageType
message MyMessage2 {
repeated int32 whatever
@@ -28,6 +30,6 @@
message MyOtherMessage : merge MyMessage {
required bool constrained // constrain
- required MessageType type
+ optional MessageType mytype
required uint32 a [ default=1 ]
}
\ No newline at end of file