зеркало из https://github.com/mozilla/pjs.git
Import IPDL from cjones' working repo, revision 282b4211d881. New IPDL work will take place in electrolysis.
This commit is contained in:
Родитель
dc5af222a3
Коммит
dc5be22981
|
@ -0,0 +1,7 @@
|
|||
.PHONY: clean
|
||||
|
||||
clean:
|
||||
rm -f cxx/*~ *~ *.pyc cxx/*.pyc parser.out ipdl_lextab.py ipdl_yacctab.py
|
||||
|
||||
check:
|
||||
@echo "Implement me! With tests/!"
|
|
@ -0,0 +1,58 @@
|
|||
# ***** BEGIN LICENSE BLOCK *****
|
||||
# Version: MPL 1.1/GPL 2.0/LGPL 2.1
|
||||
#
|
||||
# The contents of this file are subject to the Mozilla Public License Version
|
||||
# 1.1 (the "License"); you may not use this file except in compliance with
|
||||
# the License. You may obtain a copy of the License at
|
||||
# http://www.mozilla.org/MPL/
|
||||
#
|
||||
# Software distributed under the License is distributed on an "AS IS" basis,
|
||||
# WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
|
||||
# for the specific language governing rights and limitations under the
|
||||
# License.
|
||||
#
|
||||
# The Original Code is mozilla.org code.
|
||||
#
|
||||
# Contributor(s):
|
||||
# Chris Jones <jones.chris.g@gmail.com>
|
||||
#
|
||||
# Alternatively, the contents of this file may be used under the terms of
|
||||
# either of the GNU General Public License Version 2 or later (the "GPL"),
|
||||
# or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
|
||||
# in which case the provisions of the GPL or the LGPL are applicable instead
|
||||
# of those above. If you wish to allow use of your version of this file only
|
||||
# under the terms of either the GPL or the LGPL, and not to allow others to
|
||||
# use your version of this file under the terms of the MPL, indicate your
|
||||
# decision by deleting the provisions above and replace them with the notice
|
||||
# and other provisions required by the GPL or the LGPL. If you do not delete
|
||||
# the provisions above, a recipient may use your version of this file under
|
||||
# the terms of any one of the MPL, the GPL or the LGPL.
|
||||
#
|
||||
# ***** END LICENSE BLOCK *****
|
||||
|
||||
__all__ = [ 'gencxx', 'genipdl', 'parse', 'typecheck' ]
|
||||
|
||||
import os, sys
|
||||
|
||||
from ipdl.cgen import IPDLCodeGen
|
||||
from ipdl.lower import LowerToCxx
|
||||
from ipdl.parser import Parser
|
||||
from ipdl.type import TypeCheck
|
||||
|
||||
from ipdl.cxx.cgen import CxxCodeGen
|
||||
|
||||
def parse(specstring, filename='<stdin>'):
|
||||
return Parser().parse(specstring, filename)
|
||||
|
||||
def typecheck(ast, errout=sys.stderr):
|
||||
'''Returns True iff |ast| is well typed. Print errors to |errout| if
|
||||
it is not.'''
|
||||
return TypeCheck().check(ast, errout)
|
||||
|
||||
def gencxx(ast, outdir):
|
||||
for hdr in LowerToCxx().lower(ast):
|
||||
path = os.path.join(outdir, hdr.filename)
|
||||
CxxCodeGen(outf=open(path, 'w')).cgen(hdr)
|
||||
|
||||
def genipdl(ast, outdir):
|
||||
return IPDLCodeGen().cgen(ast)
|
|
@ -0,0 +1,280 @@
|
|||
# ***** BEGIN LICENSE BLOCK *****
|
||||
# Version: MPL 1.1/GPL 2.0/LGPL 2.1
|
||||
#
|
||||
# The contents of this file are subject to the Mozilla Public License Version
|
||||
# 1.1 (the "License"); you may not use this file except in compliance with
|
||||
# the License. You may obtain a copy of the License at
|
||||
# http://www.mozilla.org/MPL/
|
||||
#
|
||||
# Software distributed under the License is distributed on an "AS IS" basis,
|
||||
# WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
|
||||
# for the specific language governing rights and limitations under the
|
||||
# License.
|
||||
#
|
||||
# The Original Code is mozilla.org code.
|
||||
#
|
||||
# Contributor(s):
|
||||
# Chris Jones <jones.chris.g@gmail.com>
|
||||
#
|
||||
# Alternatively, the contents of this file may be used under the terms of
|
||||
# either of the GNU General Public License Version 2 or later (the "GPL"),
|
||||
# or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
|
||||
# in which case the provisions of the GPL or the LGPL are applicable instead
|
||||
# of those above. If you wish to allow use of your version of this file only
|
||||
# under the terms of either the GPL or the LGPL, and not to allow others to
|
||||
# use your version of this file under the terms of the MPL, indicate your
|
||||
# decision by deleting the provisions above and replace them with the notice
|
||||
# and other provisions required by the GPL or the LGPL. If you do not delete
|
||||
# the provisions above, a recipient may use your version of this file under
|
||||
# the terms of any one of the MPL, the GPL or the LGPL.
|
||||
#
|
||||
# ***** END LICENSE BLOCK *****
|
||||
|
||||
import sys
|
||||
|
||||
class Visitor:
|
||||
def defaultVisit(self, node):
|
||||
raise Exception, "INTERNAL ERROR: no visitor for node type `%s'"% (
|
||||
node.__class__.__name__)
|
||||
|
||||
def visitTranslationUnit(self, tu):
|
||||
for cxxInc in tu.cxxIncludes:
|
||||
cxxInc.accept(self)
|
||||
for protoInc in tu.protocolIncludes:
|
||||
protoInc.accept(self)
|
||||
for using in tu.using:
|
||||
using.accept(self)
|
||||
tu.protocol.accept(self)
|
||||
|
||||
def visitCxxInclude(self, inc):
|
||||
pass
|
||||
|
||||
def visitProtocolInclude(self, inc):
|
||||
# Note: we don't visit the child AST here, because that needs delicate
|
||||
# and pass-specific handling
|
||||
pass
|
||||
|
||||
def visitUsingStmt(self, using):
|
||||
pass
|
||||
|
||||
def visitProtocol(self, p):
|
||||
for namespace in p.namespaces:
|
||||
namespace.accept(self)
|
||||
if p.manager is not None:
|
||||
p.manager.accept(self)
|
||||
for managed in p.managesStmts:
|
||||
managed.accept(self)
|
||||
for msgDecl in p.messageDecls:
|
||||
msgDecl.accept(self)
|
||||
for transitionStmt in p.transitionStmts:
|
||||
transitionStmt.accept(self)
|
||||
|
||||
def visitNamespace(self, ns):
|
||||
pass
|
||||
|
||||
def visitManagerStmt(self, mgr):
|
||||
pass
|
||||
|
||||
def visitManagesStmt(self, mgs):
|
||||
pass
|
||||
|
||||
def visitMessageDecl(self, md):
|
||||
for inParam in md.inParams:
|
||||
inParam.accept(self)
|
||||
for outParam in md.outParams:
|
||||
outParam.accept(self)
|
||||
|
||||
def visitParam(self, decl):
|
||||
pass
|
||||
|
||||
def visitTypeSpec(self, ts):
|
||||
pass
|
||||
|
||||
def visitDecl(self, d):
|
||||
pass
|
||||
|
||||
class Loc:
|
||||
def __init__(self, filename='<??>', lineno=0):
|
||||
assert filename
|
||||
self.filename = filename
|
||||
self.lineno = lineno
|
||||
def __repr__(self):
|
||||
return '%r:%r'% (self.filename, self.lineno)
|
||||
def __str__(self):
|
||||
return '%s:%s'% (self.filename, self.lineno)
|
||||
|
||||
Loc.NONE = Loc(filename='<??>', lineno=0)
|
||||
|
||||
class _struct():
|
||||
pass
|
||||
|
||||
class Node:
|
||||
def __init__(self, loc=Loc.NONE):
|
||||
self.loc = loc
|
||||
|
||||
def accept(self, visitor):
|
||||
visit = getattr(visitor, 'visit'+ self.__class__.__name__, None)
|
||||
if visit is None:
|
||||
return getattr(visitor, 'defaultVisit')(self)
|
||||
return visit(self)
|
||||
|
||||
def addAttrs(self, attrsName):
|
||||
if not hasattr(self, attrsName):
|
||||
setattr(self, attrsName, _struct())
|
||||
|
||||
class TranslationUnit(Node):
|
||||
def __init__(self):
|
||||
Node.__init__(self)
|
||||
self.filename = None
|
||||
self.cxxIncludes = [ ]
|
||||
self.protocolIncludes = [ ]
|
||||
self.using = [ ]
|
||||
self.protocol = None
|
||||
|
||||
def addCxxInclude(self, cxxInclude): self.cxxIncludes.append(cxxInclude)
|
||||
def addProtocolInclude(self, pInc): self.protocolIncludes.append(pInc)
|
||||
def addUsingStmt(self, using): self.using.append(using)
|
||||
|
||||
def setProtocol(self, protocol): self.protocol = protocol
|
||||
|
||||
class CxxInclude(Node):
|
||||
def __init__(self, loc, cxxFile):
|
||||
Node.__init__(self, loc)
|
||||
self.file = cxxFile
|
||||
|
||||
class ProtocolInclude(Node):
|
||||
def __init__(self, loc, protocolFile):
|
||||
Node.__init__(self, loc)
|
||||
self.file = protocolFile
|
||||
|
||||
class UsingStmt(Node):
|
||||
def __init__(self, loc, cxxTypeSpec):
|
||||
Node.__init__(self, loc)
|
||||
self.type = cxxTypeSpec
|
||||
|
||||
# "singletons"
|
||||
class ASYNC:
|
||||
pretty = 'Async'
|
||||
class RPC:
|
||||
pretty = 'Rpc'
|
||||
class SYNC:
|
||||
pretty = 'Sync'
|
||||
|
||||
class INOUT:
|
||||
pretty = 'InOut'
|
||||
class IN:
|
||||
@staticmethod
|
||||
def pretty(ss): return _prettyTable['In'][ss.pretty]
|
||||
class OUT:
|
||||
@staticmethod
|
||||
def pretty(ss): return _prettyTable['Out'][ss.pretty]
|
||||
|
||||
_prettyTable = {
|
||||
'In' : { 'Async': 'AsyncRecv',
|
||||
'Sync': 'SyncRecv',
|
||||
'Rpc': 'RpcAnswer' },
|
||||
'Out' : { 'Async': 'AsyncSend',
|
||||
'Sync': 'SyncSend',
|
||||
'Rpc': 'RpcCall' }
|
||||
# inout doesn't make sense here
|
||||
}
|
||||
|
||||
|
||||
class Protocol(Node):
|
||||
def __init__(self, loc):
|
||||
Node.__init__(self, loc)
|
||||
self.name = None
|
||||
self.namespaces = [ ]
|
||||
self.sendSemantics = ASYNC
|
||||
self.managesStmts = [ ]
|
||||
self.messageDecls = [ ]
|
||||
self.transitionStmts = [ ]
|
||||
|
||||
def addOuterNamespace(self, namespace):
|
||||
self.namespaces.insert(0, namespace)
|
||||
|
||||
def addManagesStmts(self, managesStmts):
|
||||
self.managesStmts += managesStmts
|
||||
|
||||
def addMessageDecls(self, messageDecls):
|
||||
self.messageDecls += messageDecls
|
||||
|
||||
def addTransitionStmts(self, transStmts):
|
||||
self.transitionStmts += transStmts
|
||||
|
||||
class Namespace(Node):
|
||||
def __init__(self, loc, namespace):
|
||||
Node.__init__(self, loc)
|
||||
self.namespace = namespace
|
||||
|
||||
class ManagerStmt(Node):
|
||||
def __init__(self, loc, managerName):
|
||||
Node.__init__(self, loc)
|
||||
self.name = managerName
|
||||
|
||||
class ManagesStmt(Node):
|
||||
def __init__(self, loc, managedName):
|
||||
Node.__init__(self, loc)
|
||||
self.name = managedName
|
||||
|
||||
class MessageDecl(Node):
|
||||
def __init__(self, loc):
|
||||
Node.__init__(self, loc)
|
||||
self.name = None
|
||||
self.sendSemantics = ASYNC
|
||||
self.direction = None
|
||||
self.inParams = [ ]
|
||||
self.outParams = [ ]
|
||||
|
||||
def addInParams(self, inParamsList):
|
||||
self.inParams += inParamsList
|
||||
|
||||
def addOutParams(self, outParamsList):
|
||||
self.outParams += outParamsList
|
||||
|
||||
def hasReply(self):
|
||||
return self.sendSemantics is SYNC or self.sendSemantics is RPC
|
||||
|
||||
class Param(Node):
|
||||
def __init__(self, loc, typespec, name):
|
||||
Node.__init__(self, loc)
|
||||
self.name = name
|
||||
self.typespec = typespec
|
||||
|
||||
class TypeSpec(Node):
|
||||
def __init__(self, loc, spec):
|
||||
Node.__init__(self, loc)
|
||||
self.spec = spec
|
||||
|
||||
def basename(self):
|
||||
return self.spec.baseid
|
||||
|
||||
def __str__(self): return str(self.spec)
|
||||
|
||||
class QualifiedId: # FIXME inherit from node?
|
||||
def __init__(self, loc, baseid, quals=[ ]):
|
||||
self.loc = loc
|
||||
self.baseid = baseid
|
||||
self.quals = quals
|
||||
|
||||
def qualify(self, id):
|
||||
self.quals.append(self.baseid)
|
||||
self.baseid = id
|
||||
|
||||
def __str__(self):
|
||||
if 0 == len(self.quals):
|
||||
return self.baseid
|
||||
return '::'.join(self.quals) +'::'+ self.baseid
|
||||
|
||||
# added by type checking passes
|
||||
class Decl(Node):
|
||||
def __init__(self, loc):
|
||||
Node.__init__(self, loc)
|
||||
self.progname = None # what the programmer typed, if relevant
|
||||
self.shortname = None # shortest way to refer to this decl
|
||||
self.fullname = None # full way to refer to this decl
|
||||
self.loc = loc
|
||||
self.type = None
|
||||
self.scope = None
|
||||
|
||||
|
|
@ -0,0 +1,68 @@
|
|||
# ***** BEGIN LICENSE BLOCK *****
|
||||
# Version: MPL 1.1/GPL 2.0/LGPL 2.1
|
||||
#
|
||||
# The contents of this file are subject to the Mozilla Public License Version
|
||||
# 1.1 (the "License"); you may not use this file except in compliance with
|
||||
# the License. You may obtain a copy of the License at
|
||||
# http://www.mozilla.org/MPL/
|
||||
#
|
||||
# Software distributed under the License is distributed on an "AS IS" basis,
|
||||
# WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
|
||||
# for the specific language governing rights and limitations under the
|
||||
# License.
|
||||
#
|
||||
# The Original Code is mozilla.org code.
|
||||
#
|
||||
# Contributor(s):
|
||||
# Chris Jones <jones.chris.g@gmail.com>
|
||||
#
|
||||
# Alternatively, the contents of this file may be used under the terms of
|
||||
# either of the GNU General Public License Version 2 or later (the "GPL"),
|
||||
# or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
|
||||
# in which case the provisions of the GPL or the LGPL are applicable instead
|
||||
# of those above. If you wish to allow use of your version of this file only
|
||||
# under the terms of either the GPL or the LGPL, and not to allow others to
|
||||
# use your version of this file under the terms of the MPL, indicate your
|
||||
# decision by deleting the provisions above and replace them with the notice
|
||||
# and other provisions required by the GPL or the LGPL. If you do not delete
|
||||
# the provisions above, a recipient may use your version of this file under
|
||||
# the terms of any one of the MPL, the GPL or the LGPL.
|
||||
#
|
||||
# ***** END LICENSE BLOCK *****
|
||||
|
||||
# WARNING: the syntax of the builtin types is not checked, so please
|
||||
# don't add something syntactically invalid. It will not be fun to
|
||||
# track down the bug.
|
||||
|
||||
Types = (
|
||||
# C types
|
||||
'char',
|
||||
'short',
|
||||
'int',
|
||||
'long',
|
||||
'float',
|
||||
'double',
|
||||
|
||||
# stdint types
|
||||
'int8_t',
|
||||
'uint8_t',
|
||||
'int16_t',
|
||||
'uint16_t',
|
||||
'int32_t',
|
||||
'uint32_t',
|
||||
'int64_t',
|
||||
'uint64_t',
|
||||
'intptr_t',
|
||||
'uintptr_t',
|
||||
|
||||
# Mozilla types: "less" standard things we know how serialize/deserialize
|
||||
'mozilla::ipc::String',
|
||||
'mozilla::ipc::StringArray',
|
||||
)
|
||||
|
||||
|
||||
Includes = (
|
||||
'nscore.h',
|
||||
'IPC/IPCMessageUtils.h',
|
||||
'mozilla/ipc/MessageTypes.h',
|
||||
)
|
|
@ -0,0 +1,129 @@
|
|||
# ***** BEGIN LICENSE BLOCK *****
|
||||
# Version: MPL 1.1/GPL 2.0/LGPL 2.1
|
||||
#
|
||||
# The contents of this file are subject to the Mozilla Public License Version
|
||||
# 1.1 (the "License"); you may not use this file except in compliance with
|
||||
# the License. You may obtain a copy of the License at
|
||||
# http://www.mozilla.org/MPL/
|
||||
#
|
||||
# Software distributed under the License is distributed on an "AS IS" basis,
|
||||
# WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
|
||||
# for the specific language governing rights and limitations under the
|
||||
# License.
|
||||
#
|
||||
# The Original Code is mozilla.org code.
|
||||
#
|
||||
# Contributor(s):
|
||||
# Chris Jones <jones.chris.g@gmail.com>
|
||||
#
|
||||
# Alternatively, the contents of this file may be used under the terms of
|
||||
# either of the GNU General Public License Version 2 or later (the "GPL"),
|
||||
# or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
|
||||
# in which case the provisions of the GPL or the LGPL are applicable instead
|
||||
# of those above. If you wish to allow use of your version of this file only
|
||||
# under the terms of either the GPL or the LGPL, and not to allow others to
|
||||
# use your version of this file under the terms of the MPL, indicate your
|
||||
# decision by deleting the provisions above and replace them with the notice
|
||||
# and other provisions required by the GPL or the LGPL. If you do not delete
|
||||
# the provisions above, a recipient may use your version of this file under
|
||||
# the terms of any one of the MPL, the GPL or the LGPL.
|
||||
#
|
||||
# ***** END LICENSE BLOCK *****
|
||||
|
||||
import os, sys
|
||||
|
||||
from ipdl.ast import Visitor
|
||||
from ipdl.ast import IN, OUT, INOUT, ASYNC, SYNC, RPC
|
||||
|
||||
class CodePrinter:
|
||||
def __init__(self, outf=sys.stdout, indentCols=4):
|
||||
self.outf = outf
|
||||
self.col = 0
|
||||
self.indentCols = indentCols
|
||||
|
||||
def write(self, str):
|
||||
self.outf.write(str)
|
||||
|
||||
def printdent(self, str=''):
|
||||
self.write((' '* self.col) + str)
|
||||
|
||||
def println(self, str=''):
|
||||
self.write(str +'\n')
|
||||
|
||||
def printdentln(self, str):
|
||||
self.write((' '* self.col) + str +'\n')
|
||||
|
||||
def indent(self): self.col += self.indentCols
|
||||
def dedent(self): self.col -= self.indentCols
|
||||
|
||||
|
||||
##-----------------------------------------------------------------------------
|
||||
class IPDLCodeGen(CodePrinter, Visitor):
|
||||
'''Spits back out equivalent IPDL to the code that generated this.
|
||||
Also known as pretty-printing.'''
|
||||
|
||||
def __init__(self, outf=sys.stdout, indentCols=4, printed=set()):
|
||||
CodePrinter.__init__(self, outf, indentCols)
|
||||
self.printed = printed
|
||||
|
||||
def visitTranslationUnit(self, tu):
|
||||
self.printed.add(tu.filename)
|
||||
self.println('//\n// Automatically generated by ipdlc\n//')
|
||||
CodeGen.visitTranslationUnit(self, tu)
|
||||
|
||||
def visitCxxInclude(self, inc):
|
||||
self.println('include "'+ inc.file +'";')
|
||||
|
||||
def visitProtocolInclude(self, inc):
|
||||
self.println('include protocol "'+ inc.file +'";')
|
||||
if inc.tu.filename not in self.printed:
|
||||
self.println('/* Included file:')
|
||||
IPDLCodeGen(outf=self.outf, indentCols=self.indentCols,
|
||||
printed=self.printed).visitTranslationUnit(inc.tu)
|
||||
|
||||
self.println('*/')
|
||||
|
||||
def visitProtocol(self, p):
|
||||
self.println()
|
||||
for namespace in p.namespaces: namespace.accept(self)
|
||||
|
||||
self.println('%s protocol %s\n{'% (p.sendSemantics[0], p.name))
|
||||
self.indent()
|
||||
|
||||
for mgs in p.managesStmts:
|
||||
mgs.accept(self)
|
||||
if len(p.managesStmts): self.println()
|
||||
|
||||
for msgDecl in p.messageDecls: msgDecl.accept(self)
|
||||
self.println()
|
||||
|
||||
for transStmt in p.transitionStmts: transStmt.accept(self)
|
||||
|
||||
self.dedent()
|
||||
self.println('}')
|
||||
self.write('}\n'* len(p.namespaces))
|
||||
|
||||
def visitManagerStmt(self, mgr):
|
||||
self.printdentln('manager '+ mgr.name +';')
|
||||
|
||||
def visitManagesStmt(self, mgs):
|
||||
self.printdentln('manages '+ mgs.name +';')
|
||||
|
||||
def visitMessageDecl(self, msg):
|
||||
self.printdent('%s %s %s('% (msg.sendSemantics[0], msg.direction[0], msg.name))
|
||||
for i, inp in enumerate(msg.inParams):
|
||||
inp.accept(self)
|
||||
if i != (len(msg.inParams) - 1): self.write(', ')
|
||||
self.write(')')
|
||||
if 0 == len(msg.outParams):
|
||||
self.println(';')
|
||||
return
|
||||
|
||||
self.println()
|
||||
self.indent()
|
||||
self.printdent('returns (')
|
||||
for i, outp in enumerate(msg.outParams):
|
||||
outp.accept(self)
|
||||
if i != (len(msg.outParams) - 1): self.write(', ')
|
||||
self.println(');')
|
||||
self.dedent()
|
|
@ -0,0 +1,34 @@
|
|||
# ***** BEGIN LICENSE BLOCK *****
|
||||
# Version: MPL 1.1/GPL 2.0/LGPL 2.1
|
||||
#
|
||||
# The contents of this file are subject to the Mozilla Public License Version
|
||||
# 1.1 (the "License"); you may not use this file except in compliance with
|
||||
# the License. You may obtain a copy of the License at
|
||||
# http://www.mozilla.org/MPL/
|
||||
#
|
||||
# Software distributed under the License is distributed on an "AS IS" basis,
|
||||
# WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
|
||||
# for the specific language governing rights and limitations under the
|
||||
# License.
|
||||
#
|
||||
# The Original Code is mozilla.org code.
|
||||
#
|
||||
# Contributor(s):
|
||||
# Chris Jones <jones.chris.g@gmail.com>
|
||||
#
|
||||
# Alternatively, the contents of this file may be used under the terms of
|
||||
# either of the GNU General Public License Version 2 or later (the "GPL"),
|
||||
# or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
|
||||
# in which case the provisions of the GPL or the LGPL are applicable instead
|
||||
# of those above. If you wish to allow use of your version of this file only
|
||||
# under the terms of either the GPL or the LGPL, and not to allow others to
|
||||
# use your version of this file under the terms of the MPL, indicate your
|
||||
# decision by deleting the provisions above and replace them with the notice
|
||||
# and other provisions required by the GPL or the LGPL. If you do not delete
|
||||
# the provisions above, a recipient may use your version of this file under
|
||||
# the terms of any one of the MPL, the GPL or the LGPL.
|
||||
#
|
||||
# ***** END LICENSE BLOCK *****
|
||||
|
||||
import ipdl.cxx.ast
|
||||
import ipdl.cxx.cgen
|
|
@ -0,0 +1,426 @@
|
|||
# ***** BEGIN LICENSE BLOCK *****
|
||||
# Version: MPL 1.1/GPL 2.0/LGPL 2.1
|
||||
#
|
||||
# The contents of this file are subject to the Mozilla Public License Version
|
||||
# 1.1 (the "License"); you may not use this file except in compliance with
|
||||
# the License. You may obtain a copy of the License at
|
||||
# http://www.mozilla.org/MPL/
|
||||
#
|
||||
# Software distributed under the License is distributed on an "AS IS" basis,
|
||||
# WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
|
||||
# for the specific language governing rights and limitations under the
|
||||
# License.
|
||||
#
|
||||
# The Original Code is mozilla.org code.
|
||||
#
|
||||
# Contributor(s):
|
||||
# Chris Jones <jones.chris.g@gmail.com>
|
||||
#
|
||||
# Alternatively, the contents of this file may be used under the terms of
|
||||
# either of the GNU General Public License Version 2 or later (the "GPL"),
|
||||
# or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
|
||||
# in which case the provisions of the GPL or the LGPL are applicable instead
|
||||
# of those above. If you wish to allow use of your version of this file only
|
||||
# under the terms of either the GPL or the LGPL, and not to allow others to
|
||||
# use your version of this file under the terms of the MPL, indicate your
|
||||
# decision by deleting the provisions above and replace them with the notice
|
||||
# and other provisions required by the GPL or the LGPL. If you do not delete
|
||||
# the provisions above, a recipient may use your version of this file under
|
||||
# the terms of any one of the MPL, the GPL or the LGPL.
|
||||
#
|
||||
# ***** END LICENSE BLOCK *****
|
||||
|
||||
import copy, sys
|
||||
|
||||
class Visitor:
|
||||
def defaultVisit(self, node):
|
||||
raise Exception, "INTERNAL ERROR: no visitor for node type `%s'"% (
|
||||
node.__class__.__name__)
|
||||
|
||||
def visitWhitespace(self, ws):
|
||||
pass
|
||||
|
||||
def visitFile(self, f):
|
||||
for thing in f.stuff:
|
||||
thing.accept(self)
|
||||
|
||||
def visitCppDirective(self, ppd):
|
||||
pass
|
||||
|
||||
def visitBlock(self, block):
|
||||
for stmt in block.stmts:
|
||||
stmt.accept(self)
|
||||
|
||||
def visitNamespace(self, ns):
|
||||
self.visitBlock(ns)
|
||||
|
||||
def visitType(self, type):
|
||||
pass
|
||||
|
||||
def visitTypeEnum(self, enum):
|
||||
pass
|
||||
|
||||
def visitTypedef(self, tdef):
|
||||
tdef.fromtype.accept(self)
|
||||
tdef.tottype.accept(self)
|
||||
|
||||
def visitDecl(self, decl):
|
||||
decl.type.accept(self)
|
||||
|
||||
def visitClass(self, cls):
|
||||
for viz, parent in cls.inherits:
|
||||
parent.accept(self)
|
||||
self.visitBlock(cls)
|
||||
|
||||
def visitInherit(self, inh):
|
||||
pass
|
||||
|
||||
def visitMethodDecl(self, meth):
|
||||
for param in meth.params:
|
||||
param.accept(self)
|
||||
if meth.ret is not None:
|
||||
meth.ret.accept(self)
|
||||
|
||||
def visitMethodDefn(self, meth):
|
||||
meth.decl.accept(self)
|
||||
self.visitBlock(meth)
|
||||
|
||||
def visitConstructorDecl(self, ctor):
|
||||
self.visitMethodDecl(ctor)
|
||||
|
||||
def visitConstructorDefn(self, cd):
|
||||
cd.decl.accept(self)
|
||||
for init in cd.memberinits:
|
||||
init.accept(self)
|
||||
self.visitBlock(cd)
|
||||
|
||||
def visitDestructorDecl(self, dtor):
|
||||
self.visitMethodDecl(dtor)
|
||||
|
||||
def visitDestructorDefn(self, dd):
|
||||
dd.decl.accept(self)
|
||||
self.visitBlock(dd)
|
||||
|
||||
def visitExprVar(self, v):
|
||||
pass
|
||||
|
||||
def visitExprPrefixUnop(self, e):
|
||||
e.expr.accept(self)
|
||||
|
||||
def visitExprAddrOf(self, eao):
|
||||
self.visitExprPrefixUnop(eao)
|
||||
|
||||
def visitExprDeref(self, ed):
|
||||
self.visitExprPrefixUnop(ed)
|
||||
|
||||
def visitExprSelect(self, es):
|
||||
es.obj.accept(self)
|
||||
|
||||
def visitExprAssn(self, ea):
|
||||
ea.lhs.accept(self)
|
||||
ea.rhs.accept(self)
|
||||
|
||||
def visitExprCall(self, ec):
|
||||
ec.func.accept(self)
|
||||
for arg in ec.args:
|
||||
arg.accept(self)
|
||||
|
||||
def visitExprNew(self, en):
|
||||
self.visitExprCall(en)
|
||||
|
||||
def visitExprDelete(self, ed):
|
||||
ed.obj.accept(self)
|
||||
|
||||
def visitExprMemberInit(self, minit):
|
||||
self.visitExprCall(minit)
|
||||
|
||||
def visitStmtBlock(self, sb):
|
||||
self.visitBlock(sb)
|
||||
|
||||
def visitStmtDecl(self, sd):
|
||||
sd.decl.accept(self)
|
||||
|
||||
def visitLabel(self, label):
|
||||
pass
|
||||
|
||||
def visitCaseLabel(self, case):
|
||||
pass
|
||||
|
||||
def visitDefaultLabel(self, dl):
|
||||
pass
|
||||
|
||||
def visitStmtSwitch(self, ss):
|
||||
ss.expr.accept(self)
|
||||
self.visitBlock(ss)
|
||||
|
||||
def visitStmtExpr(self, se):
|
||||
se.expr.accept(self)
|
||||
|
||||
def visitStmtReturn(self, sr):
|
||||
if sr.expr is not None:
|
||||
sr.expr.accept(self)
|
||||
|
||||
##------------------------------
|
||||
class Node:
|
||||
def __init__(self):
|
||||
pass
|
||||
|
||||
def accept(self, visitor):
|
||||
visit = getattr(visitor, 'visit'+ self.__class__.__name__, None)
|
||||
if visit is None:
|
||||
return getattr(visitor, 'defaultVisit')(self)
|
||||
return visit(self)
|
||||
|
||||
class Whitespace(Node):
|
||||
# yes, this is silly. but we need to stick comments in the
|
||||
# generated code without resorting to more serious hacks
|
||||
def __init__(self, ws):
|
||||
Node.__init__(self)
|
||||
self.ws = ws
|
||||
Whitespace.NL = Whitespace('\n')
|
||||
|
||||
class File(Node):
|
||||
def __init__(self, filename):
|
||||
Node.__init__(self)
|
||||
self.filename = filename
|
||||
# array of stuff in the file --- stmts and preprocessor thingies
|
||||
self.stuff = [ ]
|
||||
|
||||
def addthing(self, thing):
|
||||
self.stuff.append(thing)
|
||||
|
||||
# "look like" a Block so code doesn't have to care whether they're
|
||||
# in global scope or not
|
||||
def addstmt(self, stmt):
|
||||
self.stuff.append(stmt)
|
||||
|
||||
class CppDirective(Node):
|
||||
'''represents |#[directive] [rest]|, where |rest| is any string'''
|
||||
def __init__(self, directive, rest):
|
||||
Node.__init__(self)
|
||||
self.directive = directive
|
||||
self.rest = rest
|
||||
|
||||
class Block(Node):
|
||||
def __init__(self):
|
||||
Node.__init__(self)
|
||||
self.stmts = [ ]
|
||||
|
||||
def addstmt(self, stmt):
|
||||
self.stmts.append(stmt)
|
||||
|
||||
##------------------------------
|
||||
# type and decl thingies
|
||||
class Namespace(Block):
|
||||
def __init__(self, name):
|
||||
Block.__init__(self)
|
||||
self.name = name
|
||||
|
||||
class Type(Node):
|
||||
def __init__(self, name, const=False, ptr=False, ref=False):
|
||||
Node.__init__(self)
|
||||
self.name = name
|
||||
self.const = const
|
||||
self.ptr = ptr
|
||||
self.ref = ref
|
||||
# XXX could get serious here with recursive types, but shouldn't
|
||||
# need that for this codegen
|
||||
def __deepcopy__(self, memo):
|
||||
return Type(self.name, self.const, self.ptr, self.ref)
|
||||
|
||||
class TypeEnum(Node):
|
||||
def __init__(self, name=None):
|
||||
'''name can be None'''
|
||||
Node.__init__(self)
|
||||
self.name = name
|
||||
self.idnums = [ ] # pairs of ('Foo', [num]) or ('Foo', None)
|
||||
|
||||
def addId(self, id, num=None):
|
||||
self.idnums.append((id, num))
|
||||
|
||||
class Typedef(Node):
|
||||
def __init__(self, fromtype, totype):
|
||||
Node.__init__(self)
|
||||
self.fromtype = fromtype
|
||||
self.totype = totype
|
||||
|
||||
class Decl(Node):
|
||||
'''represents |Foo bar|, e.g. in a function signature'''
|
||||
def __init__(self, type, name):
|
||||
Node.__init__(self)
|
||||
self.type = type
|
||||
self.name = name
|
||||
def __deepcopy__(self, memo):
|
||||
return Decl(copy.deepcopy(self.type, memo), self.name)
|
||||
|
||||
##------------------------------
|
||||
# class stuff
|
||||
class Class(Block):
|
||||
def __init__(self, name, inherits=[ ],
|
||||
interface=False, final=False):
|
||||
assert not (interface and final)
|
||||
|
||||
Block.__init__(self)
|
||||
self.name = name
|
||||
self.inherits = inherits # array of (viz, Type) pairs
|
||||
self.interface = interface
|
||||
self.final = final
|
||||
|
||||
class Inherit(Node):
|
||||
def __init__(self, name, viz='public'):
|
||||
Node.__init__(self)
|
||||
self.name = name
|
||||
self.viz = viz
|
||||
|
||||
class MethodDecl(Node):
|
||||
def __init__(self, name, params=[ ], ret=Type('void'),
|
||||
virtual=False, const=False, pure=False, static=False):
|
||||
assert not (virtual and static)
|
||||
assert not pure or virtual # pure => virtual
|
||||
|
||||
Node.__init__(self)
|
||||
self.name = name
|
||||
self.params = params
|
||||
self.ret = ret
|
||||
self.virtual = virtual
|
||||
self.const = const
|
||||
self.pure = pure
|
||||
self.static = static
|
||||
def __deepcopy__(self, memo):
|
||||
return MethodDecl(self.name,
|
||||
copy.deepcopy(self.params, memo),
|
||||
copy.deepcopy(self.ret, memo),
|
||||
self.virtual,
|
||||
self.const,
|
||||
self.pure)
|
||||
|
||||
class MethodDefn(Block):
|
||||
def __init__(self, decl):
|
||||
Block.__init__(self)
|
||||
self.decl = decl
|
||||
|
||||
class ConstructorDecl(MethodDecl):
|
||||
def __init__(self, name, params=[ ]):
|
||||
MethodDecl.__init__(self, name, params=params, ret=None)
|
||||
|
||||
class ConstructorDefn(MethodDefn):
|
||||
def __init__(self, decl, memberinits=[ ]):
|
||||
MethodDefn.__init__(self, decl)
|
||||
self.memberinits = memberinits
|
||||
|
||||
class DestructorDecl(MethodDecl):
|
||||
def __init__(self, name, virtual=False):
|
||||
MethodDecl.__init__(self, name, params=[ ], ret=None,
|
||||
virtual=virtual)
|
||||
class DestructorDefn(MethodDefn):
|
||||
def __init__(self, decl): MethodDefn.__init__(self, decl)
|
||||
|
||||
##------------------------------
|
||||
# expressions
|
||||
class ExprVar(Node):
|
||||
def __init__(self, name):
|
||||
Node.__init__(self)
|
||||
self.name = name
|
||||
|
||||
class ExprPrefixUnop(Node):
|
||||
def __init__(self, expr, op):
|
||||
self.expr = expr
|
||||
self.op = op
|
||||
|
||||
class ExprAddrOf(ExprPrefixUnop):
|
||||
def __init__(self, expr):
|
||||
ExprPrefixUnop.__init__(self, expr, '&')
|
||||
|
||||
class ExprDeref(ExprPrefixUnop):
|
||||
def __init__(self, expr):
|
||||
ExprPrefixUnop.__init__(self, expr, '*')
|
||||
|
||||
class ExprSelect(Node):
|
||||
def __init__(self, obj, op, field):
|
||||
Node.__init__(self)
|
||||
self.obj = obj
|
||||
self.op = op
|
||||
self.field = field
|
||||
|
||||
class ExprAssn(Node):
|
||||
def __init__(self, lhs, rhs):
|
||||
Node.__init__(self)
|
||||
self.lhs = lhs
|
||||
self.rhs = rhs
|
||||
|
||||
class ExprCall(Node):
|
||||
def __init__(self, func, args=[ ]):
|
||||
Node.__init__(self)
|
||||
self.func = func
|
||||
self.args = args
|
||||
|
||||
class ExprNew(ExprCall):
|
||||
# XXX taking some poetic license ...
|
||||
def __init__(self, type, args=[ ]):
|
||||
ExprCall.__init__(self, ExprVar(type.name), args)
|
||||
|
||||
class ExprDelete(Node):
|
||||
def __init__(self, obj):
|
||||
Node.__init__(self)
|
||||
self.obj = obj
|
||||
|
||||
class ExprMemberInit(ExprCall):
|
||||
def __init__(self, member, args=[ ]):
|
||||
ExprCall.__init__(self, member, args)
|
||||
|
||||
##------------------------------
|
||||
# statements etc.
|
||||
class StmtBlock(Block):
|
||||
def __init__(self):
|
||||
Block.__init__(self)
|
||||
|
||||
class StmtDecl(Node):
|
||||
def __init__(self, decl):
|
||||
Node.__init__(self)
|
||||
self.decl = decl
|
||||
|
||||
class Label(Node):
|
||||
def __init__(self, name):
|
||||
Node.__init__(self)
|
||||
self.name = name
|
||||
|
||||
class CaseLabel(Node):
|
||||
def __init__(self, name):
|
||||
Node.__init__(self)
|
||||
self.name = name
|
||||
|
||||
class DefaultLabel(Node):
|
||||
def __init__(self):
|
||||
Node.__init__(self)
|
||||
|
||||
class StmtIf(Node):
|
||||
def __init__(self, cond):
|
||||
Node.__init__(self)
|
||||
self.cond = cond
|
||||
self.ifb = Block()
|
||||
self.elseb = None
|
||||
def addifstmt(self, stmt):
|
||||
self.ifb.addstmt(stmt)
|
||||
def addelsestmt(self, stmt):
|
||||
if self.elseb is None: self.elseb = Block()
|
||||
self.elseb.addstmt(stmt)
|
||||
|
||||
class StmtSwitch(Block):
|
||||
def __init__(self, expr):
|
||||
Block.__init__(self)
|
||||
self.expr = expr
|
||||
|
||||
def addcase(self, case, block):
|
||||
'''NOTE: |case| is not checked for uniqueness'''
|
||||
self.addstmt(case)
|
||||
self.addstmt(block)
|
||||
|
||||
class StmtExpr(Node):
|
||||
def __init__(self, expr):
|
||||
Node.__init__(self)
|
||||
self.expr = expr
|
||||
|
||||
class StmtReturn(Node):
|
||||
def __init__(self, expr=None):
|
||||
Node.__init__(self)
|
||||
self.expr = expr
|
|
@ -0,0 +1,337 @@
|
|||
# ***** BEGIN LICENSE BLOCK *****
|
||||
# Version: MPL 1.1/GPL 2.0/LGPL 2.1
|
||||
#
|
||||
# The contents of this file are subject to the Mozilla Public License Version
|
||||
# 1.1 (the "License"); you may not use this file except in compliance with
|
||||
# the License. You may obtain a copy of the License at
|
||||
# http://www.mozilla.org/MPL/
|
||||
#
|
||||
# Software distributed under the License is distributed on an "AS IS" basis,
|
||||
# WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
|
||||
# for the specific language governing rights and limitations under the
|
||||
# License.
|
||||
#
|
||||
# The Original Code is mozilla.org code.
|
||||
#
|
||||
# Contributor(s):
|
||||
# Chris Jones <jones.chris.g@gmail.com>
|
||||
#
|
||||
# Alternatively, the contents of this file may be used under the terms of
|
||||
# either of the GNU General Public License Version 2 or later (the "GPL"),
|
||||
# or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
|
||||
# in which case the provisions of the GPL or the LGPL are applicable instead
|
||||
# of those above. If you wish to allow use of your version of this file only
|
||||
# under the terms of either the GPL or the LGPL, and not to allow others to
|
||||
# use your version of this file under the terms of the MPL, indicate your
|
||||
# decision by deleting the provisions above and replace them with the notice
|
||||
# and other provisions required by the GPL or the LGPL. If you do not delete
|
||||
# the provisions above, a recipient may use your version of this file under
|
||||
# the terms of any one of the MPL, the GPL or the LGPL.
|
||||
#
|
||||
# ***** END LICENSE BLOCK *****
|
||||
|
||||
import sys
|
||||
|
||||
from ipdl.cgen import CodePrinter
|
||||
from ipdl.cxx.ast import Visitor
|
||||
|
||||
class CxxCodeGen(CodePrinter, Visitor):
|
||||
def __init__(self, outf=sys.stdout, indentCols=4):
|
||||
CodePrinter.__init__(self, outf, indentCols)
|
||||
|
||||
def cgen(self, cxxfile):
|
||||
cxxfile.accept(self)
|
||||
|
||||
def visitWhitespace(self, ws):
|
||||
self.write(ws.ws)
|
||||
|
||||
def visitCppDirective(self, cd):
|
||||
self.println('#%s %s'% (cd.directive, cd.rest))
|
||||
|
||||
def visitNamespace(self, ns):
|
||||
self.println('namespace '+ ns.name +' {')
|
||||
self.visitBlock(ns)
|
||||
self.println('} // namespace '+ ns.name)
|
||||
|
||||
def visitType(self, t):
|
||||
ts = ''
|
||||
if t.const: ts += 'const '
|
||||
ts += t.name
|
||||
if t.ptr: ts += '*'
|
||||
if t.ref: ts += '&'
|
||||
self.write(ts)
|
||||
|
||||
def visitTypeEnum(self, te):
|
||||
self.write('enum')
|
||||
if te.name:
|
||||
self.write(' '+ te.name)
|
||||
self.println(' {')
|
||||
|
||||
self.indent()
|
||||
nids = len(te.idnums)
|
||||
for i, (id, num) in enumerate(te.idnums):
|
||||
self.printdent(id)
|
||||
if num:
|
||||
self.write(' = '+ str(num))
|
||||
if i != (nids-1):
|
||||
self.write(',')
|
||||
self.println()
|
||||
self.dedent()
|
||||
self.printdent('}')
|
||||
|
||||
|
||||
def visitTypedef(self, td):
|
||||
self.printdent('typedef ')
|
||||
td.fromtype.accept(self)
|
||||
self.write(' ')
|
||||
td.totype.accept(self)
|
||||
self.println(';')
|
||||
|
||||
def visitDecl(self, d):
|
||||
d.type.accept(self)
|
||||
if d.name:
|
||||
self.write(' '+ d.name)
|
||||
|
||||
def visitClass(self, c):
|
||||
self.printdent('class')
|
||||
if c.interface:
|
||||
# FIXME/cjones: turn this "on" when we get the analysis
|
||||
self.write(' /*NS_INTERFACE_CLASS*/')
|
||||
if c.final:
|
||||
self.write(' /*NS_FINAL_CLASS*/')
|
||||
self.write(' '+ c.name)
|
||||
|
||||
ninh = len(c.inherits)
|
||||
if 0 < ninh:
|
||||
self.println(' :')
|
||||
self.indent()
|
||||
for i, inherit in enumerate(c.inherits):
|
||||
self.printdent()
|
||||
inherit.accept(self)
|
||||
if i != (ninh - 1):
|
||||
self.println(',')
|
||||
self.dedent()
|
||||
self.println()
|
||||
|
||||
self.printdentln('{')
|
||||
self.indent()
|
||||
|
||||
self.visitBlock(c)
|
||||
|
||||
self.dedent()
|
||||
self.printdentln('};')
|
||||
|
||||
def visitInherit(self, inh):
|
||||
self.write(inh.viz +' '+ inh.name)
|
||||
|
||||
|
||||
def visitMethodDecl(self, md):
|
||||
assert not (md.static and md.virtual)
|
||||
if md.static:
|
||||
self.write('static ')
|
||||
if md.virtual:
|
||||
self.write('virtual ')
|
||||
if md.ret:
|
||||
md.ret.accept(self)
|
||||
self.write(' ')
|
||||
self.write(md.name +'(')
|
||||
self.writeDeclList(md.params)
|
||||
self.write(')')
|
||||
if md.const:
|
||||
self.write(' const')
|
||||
if md.pure:
|
||||
self.write(' = 0')
|
||||
|
||||
def visitMethodDefn(self, md):
|
||||
self.printdent()
|
||||
md.decl.accept(self)
|
||||
self.println()
|
||||
|
||||
self.printdentln('{')
|
||||
self.indent()
|
||||
self.visitBlock(md)
|
||||
self.dedent()
|
||||
self.printdentln('}')
|
||||
|
||||
|
||||
def visitConstructorDecl(self, cd):
|
||||
# FIXME/cjones: explicit when possible
|
||||
self.visitMethodDecl(cd)
|
||||
|
||||
def visitConstructorDefn(self, cd):
|
||||
self.printdent()
|
||||
cd.decl.accept(self)
|
||||
if len(cd.memberinits):
|
||||
self.println(' :')
|
||||
self.indent()
|
||||
ninits = len(cd.memberinits)
|
||||
for i, init in enumerate(cd.memberinits):
|
||||
self.printdent()
|
||||
init.accept(self)
|
||||
if i != (ninits-1):
|
||||
self.println(',')
|
||||
self.dedent()
|
||||
self.println()
|
||||
|
||||
self.printdentln('{')
|
||||
self.indent()
|
||||
|
||||
self.visitBlock(cd)
|
||||
|
||||
self.dedent()
|
||||
self.printdentln('}')
|
||||
|
||||
|
||||
def visitDestructorDecl(self, dd):
|
||||
if dd.virtual:
|
||||
self.write('virtual ')
|
||||
self.write('~'+ dd.name +'()')
|
||||
|
||||
def visitDestructorDefn(self, dd):
|
||||
self.printdent()
|
||||
dd.decl.accept(self)
|
||||
self.println()
|
||||
|
||||
self.printdentln('{')
|
||||
self.indent()
|
||||
|
||||
self.visitBlock(dd)
|
||||
|
||||
self.dedent()
|
||||
self.printdentln('}')
|
||||
|
||||
|
||||
def visitExprVar(self, ev):
|
||||
self.write(ev.name)
|
||||
|
||||
def visitExprPrefixUnop(self, e):
|
||||
self.write(e.op)
|
||||
self.write('(')
|
||||
e.expr.accept(self)
|
||||
self.write(')')
|
||||
|
||||
def visitExprSelect(self, es):
|
||||
es.obj.accept(self)
|
||||
self.write(es.op + es.field)
|
||||
|
||||
def visitExprAssn(self, ea):
|
||||
ea.lhs.accept(self)
|
||||
self.write(' = ')
|
||||
ea.rhs.accept(self)
|
||||
|
||||
def visitExprCall(self, ec):
|
||||
ec.func.accept(self)
|
||||
self.write('(')
|
||||
self.writeExprList(ec.args)
|
||||
self.write(')')
|
||||
|
||||
def visitExprNew(self, en):
|
||||
self.write('new ')
|
||||
self.visitExprCall(en)
|
||||
|
||||
def visitExprDelete(self, ed):
|
||||
self.write('delete ')
|
||||
ed.accept(self)
|
||||
|
||||
|
||||
def visitStmtBlock(self, b):
|
||||
self.printdentln('{')
|
||||
self.indent()
|
||||
self.visitBlock(b)
|
||||
self.dedent()
|
||||
self.printdentln('}')
|
||||
|
||||
def visitLabel(self, label):
|
||||
self.dedent() # better not be at global scope ...
|
||||
self.printdentln(label.name +':')
|
||||
self.indent()
|
||||
|
||||
def visitCaseLabel(self, cl):
|
||||
self.dedent()
|
||||
self.printdentln('case '+ cl.name +':')
|
||||
self.indent()
|
||||
|
||||
def visitDefaultLabel(self, dl):
|
||||
self.dedent()
|
||||
self.printdentln('default:')
|
||||
self.indent()
|
||||
|
||||
|
||||
def visitStmtIf(self, si):
|
||||
self.printdent('if (')
|
||||
si.cond.accept(self)
|
||||
self.println(') {')
|
||||
self.indent()
|
||||
si.ifb.accept(self)
|
||||
self.dedent()
|
||||
self.printdentln('}')
|
||||
|
||||
if si.elseb is not None:
|
||||
self.printdentln('else {')
|
||||
self.indent()
|
||||
si.elseb.accept(self)
|
||||
self.dedent()
|
||||
self.println('}')
|
||||
|
||||
|
||||
def visitStmtSwitch(self, sw):
|
||||
self.printdent('switch (')
|
||||
sw.expr.accept(self)
|
||||
self.println(') {')
|
||||
self.indent()
|
||||
self.visitBlock(sw)
|
||||
self.dedent()
|
||||
self.printdentln('}')
|
||||
|
||||
|
||||
def visitStmtDecl(self, sd):
|
||||
self.printdent()
|
||||
sd.decl.accept(self)
|
||||
self.println(';')
|
||||
|
||||
|
||||
def visitStmtExpr(self, se):
|
||||
self.printdent()
|
||||
se.expr.accept(self)
|
||||
self.println(';')
|
||||
|
||||
|
||||
def visitStmtReturn(self, sr):
|
||||
self.printdent('return')
|
||||
if sr.expr:
|
||||
self.write (' ')
|
||||
sr.expr.accept(self)
|
||||
self.println(';')
|
||||
|
||||
|
||||
def writeDeclList(self, decls):
|
||||
# FIXME/cjones: try to do nice formatting of these guys
|
||||
|
||||
ndecls = len(decls)
|
||||
if 0 == ndecls:
|
||||
return
|
||||
elif 1 == ndecls:
|
||||
decls[0].accept(self)
|
||||
return
|
||||
|
||||
self.indent()
|
||||
self.indent()
|
||||
self.indent()
|
||||
for i, decl in enumerate(decls):
|
||||
self.println()
|
||||
self.printdent()
|
||||
decl.accept(self)
|
||||
if i != (ndecls-1):
|
||||
self.write(',')
|
||||
self.dedent()
|
||||
self.dedent()
|
||||
self.dedent()
|
||||
|
||||
def writeExprList(self, exprs):
|
||||
# FIXME/cjones: try to do nice formatting and share code with
|
||||
# writeDeclList()
|
||||
nexprs = len(exprs)
|
||||
for i, expr in enumerate(exprs):
|
||||
expr.accept(self)
|
||||
if i != (nexprs-1):
|
||||
self.write(', ')
|
|
@ -0,0 +1,729 @@
|
|||
# ***** BEGIN LICENSE BLOCK *****
|
||||
# Version: MPL 1.1/GPL 2.0/LGPL 2.1
|
||||
#
|
||||
# The contents of this file are subject to the Mozilla Public License Version
|
||||
# 1.1 (the "License"); you may not use this file except in compliance with
|
||||
# the License. You may obtain a copy of the License at
|
||||
# http://www.mozilla.org/MPL/
|
||||
#
|
||||
# Software distributed under the License is distributed on an "AS IS" basis,
|
||||
# WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
|
||||
# for the specific language governing rights and limitations under the
|
||||
# License.
|
||||
#
|
||||
# The Original Code is mozilla.org code.
|
||||
#
|
||||
# Contributor(s):
|
||||
# Chris Jones <jones.chris.g@gmail.com>
|
||||
#
|
||||
# Alternatively, the contents of this file may be used under the terms of
|
||||
# either of the GNU General Public License Version 2 or later (the "GPL"),
|
||||
# or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
|
||||
# in which case the provisions of the GPL or the LGPL are applicable instead
|
||||
# of those above. If you wish to allow use of your version of this file only
|
||||
# under the terms of either the GPL or the LGPL, and not to allow others to
|
||||
# use your version of this file under the terms of the MPL, indicate your
|
||||
# decision by deleting the provisions above and replace them with the notice
|
||||
# and other provisions required by the GPL or the LGPL. If you do not delete
|
||||
# the provisions above, a recipient may use your version of this file under
|
||||
# the terms of any one of the MPL, the GPL or the LGPL.
|
||||
#
|
||||
# ***** END LICENSE BLOCK *****
|
||||
|
||||
import os
|
||||
from copy import deepcopy
|
||||
|
||||
from ipdl.ast import Visitor, ASYNC, SYNC, RPC, IN, OUT, INOUT
|
||||
import ipdl.cxx.ast as cxx
|
||||
|
||||
|
||||
def _joinProtocolNamespacesName(sep, p, pname):
|
||||
return sep.join([ ns.namespace for ns in p.namespaces ] + [ pname ])
|
||||
|
||||
def _protocolIncludeGuard(p, pname):
|
||||
return _joinProtocolNamespacesName('_', p, pname) +'_h'
|
||||
|
||||
def _protocolHeaderFilename(p, pname):
|
||||
return _joinProtocolNamespacesName(os.path.sep, p, pname)+ '.h'
|
||||
|
||||
def _protocolHeaderName(pname):
|
||||
return pname +'Protocol'
|
||||
|
||||
|
||||
class _struct: pass
|
||||
|
||||
class LowerToCxx:
|
||||
def lower(self, tu):
|
||||
'''returns a list of cxx.File representing the lowered form of |tu|'''
|
||||
pname = _protocolHeaderName(tu.protocol.name)
|
||||
pheader = cxx.File(pname +'.h')
|
||||
GenerateProtocolHeader().lower(tu, pname, pheader)
|
||||
|
||||
parentname = pname +'Parent'
|
||||
parentheader = cxx.File(parentname +'.h')
|
||||
GenerateProtocolParentHeader().lower(
|
||||
tu, pname, parentname, parentheader)
|
||||
|
||||
childname = pname +'Child'
|
||||
childheader = cxx.File(childname +'.h')
|
||||
GenerateProtocolChildHeader().lower(
|
||||
tu, pname, childname, childheader)
|
||||
|
||||
return pheader, parentheader, childheader
|
||||
|
||||
|
||||
##-----------------------------------------------------------------------------
|
||||
class GenerateProtocolHeader(Visitor):
|
||||
'''creates a "generic" protocol header from an IPDL AST'''
|
||||
def __init__(self):
|
||||
self.pname = None
|
||||
self.file = None
|
||||
self.ns = None # the namespace we toss all this stuff into
|
||||
# list of typedefs for the protocol namespace. these are
|
||||
# produced from various places in the AST and collected here
|
||||
self.typedefs = [ ]
|
||||
|
||||
def lower(self, tu, protocol, outcxxfile):
|
||||
self.pname = protocol
|
||||
self.file = outcxxfile
|
||||
tu.accept(self)
|
||||
|
||||
|
||||
def typedef(self, t, name):
|
||||
self.typedefs.append(cxx.Typedef(cxx.Type(t), cxx.Type(name)))
|
||||
|
||||
def injectTypedefs(self, scope):
|
||||
for tdef in self.typedefs:
|
||||
scope.addstmt(tdef)
|
||||
|
||||
|
||||
def visitTranslationUnit(self, tu):
|
||||
f = self.file
|
||||
|
||||
f.addthing(cxx.Whitespace('''//
|
||||
// Automatically generated by ipdlc.
|
||||
// Edit at your own risk
|
||||
//
|
||||
|
||||
'''))
|
||||
includeguard = _protocolIncludeGuard(tu.protocol, self.pname)
|
||||
f.addthing(cxx.CppDirective('ifndef', includeguard))
|
||||
f.addthing(cxx.CppDirective('define', includeguard))
|
||||
f.addthing(cxx.Whitespace.NL)
|
||||
|
||||
Visitor.visitTranslationUnit(self, tu)
|
||||
|
||||
f.addthing(cxx.Whitespace.NL)
|
||||
f.addthing(cxx.CppDirective('endif', '// ifndef '+ includeguard))
|
||||
|
||||
|
||||
def visitCxxInclude(self, inc):
|
||||
self.file.addthing(cxx.CppDirective('include', '"'+ inc.file +'"'))
|
||||
|
||||
|
||||
def visitProtocolInclude(self, inc):
|
||||
p = inc.tu.protocol
|
||||
self.file.addthing(
|
||||
cxx.CppDirective(
|
||||
'include',
|
||||
'"'+ _protocolHeaderFilename(p, _protocolHeaderName(p.name)) +'"'))
|
||||
# FIXME/cjones: not clear what types we need from other header
|
||||
# if p.decl.fullname is not None:
|
||||
# self.typedef(p.decl.fullname, p.decl.shortname)
|
||||
|
||||
|
||||
def visitUsingStmt(self, using):
|
||||
if using.decl.fullname is not None:
|
||||
self.typedef(using.decl.fullname, using.decl.shortname)
|
||||
|
||||
|
||||
def visitProtocol(self, p):
|
||||
self.file.addthing(cxx.Whitespace.NL)
|
||||
|
||||
# construct the namespace into which we'll stick all our decls
|
||||
if 0 == len(p.namespaces):
|
||||
self.ns = self.file
|
||||
else:
|
||||
innernamespaces = p.namespaces[1:]
|
||||
self.ns = cxx.Namespace(p.namespaces[0].namespace)
|
||||
self.file.addthing(self.ns)
|
||||
|
||||
for ns in innernamespaces:
|
||||
innerns = cxx.Namespace(ns.namespace)
|
||||
self.ns.addstmt(innerns)
|
||||
self.ns = innerns
|
||||
|
||||
ns = cxx.Namespace(self.pname)
|
||||
self.ns.addstmt(ns)
|
||||
self.ns = ns
|
||||
ns.addstmt(cxx.Whitespace.NL)
|
||||
ns.addstmt(cxx.Whitespace.NL)
|
||||
|
||||
# previsit the messages and stash away some common info used
|
||||
# several times later
|
||||
for md in p.messageDecls:
|
||||
md.accept(self)
|
||||
|
||||
# generate parent and child interfaces
|
||||
iparent = cxx.Class('IParent', interface=True)
|
||||
iparent.addstmt(cxx.Label('protected'))
|
||||
self.injectTypedefs(iparent)
|
||||
iparent.addstmt(cxx.Whitespace.NL)
|
||||
|
||||
p.accept(GenerateParentInterface(iparent))
|
||||
ns.addstmt(iparent)
|
||||
ns.addstmt(cxx.Whitespace.NL)
|
||||
|
||||
ns.addstmt(cxx.Whitespace.NL)
|
||||
ichild = cxx.Class('IChild', interface=True)
|
||||
ichild.addstmt(cxx.Label('protected'))
|
||||
self.injectTypedefs(ichild)
|
||||
ichild.addstmt(cxx.Whitespace.NL)
|
||||
|
||||
p.accept(GenerateChildInterface(ichild))
|
||||
ns.addstmt(ichild)
|
||||
ns.addstmt(cxx.Whitespace.NL)
|
||||
|
||||
# TODO
|
||||
for ts in p.transitionStmts:
|
||||
ts.accept(self)
|
||||
ns.addstmt(cxx.StmtDecl(cxx.Decl(cxx.TypeEnum('State'), '')))
|
||||
ns.addstmt(cxx.Whitespace.NL)
|
||||
|
||||
# spit out message type enum and classes
|
||||
msgstart = self.pname +'MsgStart << 12'
|
||||
msgenum = cxx.TypeEnum(self.pname +'MsgType')
|
||||
msgenum.addId(self.pname +'Start', msgstart)
|
||||
msgenum.addId(self.pname +'PreStart', '('+ msgstart +') - 1')
|
||||
|
||||
for md in p.messageDecls:
|
||||
msgenum.addId(md._cxx.id +'__ID')
|
||||
if md.decl.type.hasReply():
|
||||
msgenum.addId(md._cxx.replyid +'__ID')
|
||||
|
||||
msgenum.addId(self.pname +'End')
|
||||
ns.addstmt(cxx.StmtDecl(cxx.Decl(msgenum, '')))
|
||||
|
||||
for md in p.messageDecls:
|
||||
ns.addstmt(generateMessageClass(md, self.injectTypedefs))
|
||||
if md.decl.type.hasReply():
|
||||
ns.addstmt(generateReplyClass(md, self.injectTypedefs))
|
||||
|
||||
ns.addstmt(cxx.Whitespace.NL)
|
||||
ns.addstmt(cxx.Whitespace.NL)
|
||||
|
||||
|
||||
def visitMessageDecl(self, md):
|
||||
# where we squirrel away some common information
|
||||
setattr(md, '_cxx', _struct())
|
||||
|
||||
md._cxx.params = [ ]
|
||||
for param in md.inParams:
|
||||
md._cxx.params.append(cxx.Decl(cxx.Type(param.type.name()),
|
||||
param.progname))
|
||||
md._cxx.returns = [ ]
|
||||
for param in md.outParams:
|
||||
md._cxx.returns.append(cxx.Decl(cxx.Type(param.type.name()),
|
||||
param.progname))
|
||||
|
||||
# generate C++ interface to message sending/handling
|
||||
method = cxx.MethodDecl(
|
||||
name=md.decl.progname,
|
||||
params=[ ],
|
||||
ret=cxx.Type('nsresult'),
|
||||
virtual=True)
|
||||
for param in md._cxx.params:
|
||||
pcopy = deepcopy(param)
|
||||
pcopy.type.const = True
|
||||
pcopy.type.ref = True
|
||||
method.params.append(pcopy)
|
||||
for ret in md._cxx.returns:
|
||||
rcopy = deepcopy(ret)
|
||||
rcopy.type.ptr = True
|
||||
method.params.append(rcopy)
|
||||
md._cxx.method = method
|
||||
|
||||
# the ID is used by the IPC layer only
|
||||
md._cxx.id = 'Msg_%s'% (md.decl.progname)
|
||||
md._cxx.nsid = '%s::%s'% (self.pname, md._cxx.id)
|
||||
if md.decl.type.hasReply():
|
||||
md._cxx.replyid = 'Reply_%s'% (md.decl.progname)
|
||||
md._cxx.nsreplyid = '%s::%s'% (self.pname, md._cxx.replyid)
|
||||
|
||||
|
||||
class GenerateInterface(Visitor):
|
||||
def __init__(self, iface, name):
|
||||
self.iface = iface
|
||||
self.name = name
|
||||
|
||||
def visitProtocol(self, p):
|
||||
ifc = self.iface
|
||||
n = self.name
|
||||
|
||||
ifc.addstmt(cxx.Label('public'))
|
||||
nmsgs = len(p.messageDecls)
|
||||
for i, msgdecl in enumerate(p.messageDecls):
|
||||
msgdecl.accept(self)
|
||||
|
||||
ifc.addstmt(cxx.Whitespace.NL)
|
||||
ifc.addstmt(cxx.Label('protected'))
|
||||
ifc.addstmt(cxx.ConstructorDefn(
|
||||
cxx.ConstructorDecl(n)))
|
||||
ifc.addstmt(cxx.DestructorDefn(
|
||||
cxx.DestructorDecl(n, virtual=True)))
|
||||
|
||||
# disable unwanted ctors/operators
|
||||
ifc.addstmt(cxx.Whitespace.NL)
|
||||
ifc.addstmt(cxx.Label('private'))
|
||||
ref = cxx.Type(n, ref=True)
|
||||
constref = cxx.Type(n, const=True, ref=True)
|
||||
ifc.addstmt(cxx.StmtDecl(
|
||||
cxx.ConstructorDecl(n,
|
||||
params=[ cxx.Decl(constref, '')])))
|
||||
ifc.addstmt(cxx.StmtDecl(
|
||||
cxx.MethodDecl('operator=',
|
||||
params= [ cxx.Decl(constref, '') ],
|
||||
ret=ref)))
|
||||
|
||||
def visitMessageDecl(self, md):
|
||||
if self.msgComesIn(md.decl.type):
|
||||
method = deepcopy(md._cxx.method)
|
||||
method.pure = True
|
||||
self.iface.addstmt(cxx.StmtDecl(method))
|
||||
self.iface.addstmt(cxx.Whitespace.NL)
|
||||
|
||||
|
||||
class GenerateParentInterface(GenerateInterface):
|
||||
def __init__(self, iparent):
|
||||
GenerateInterface.__init__(self, iparent, iparent.name)
|
||||
def msgComesIn(self, mtype): return mtype.isIn() or mtype.isInout()
|
||||
|
||||
class GenerateChildInterface(GenerateInterface):
|
||||
def __init__(self, iparent):
|
||||
GenerateInterface.__init__(self, iparent, iparent.name)
|
||||
def msgComesIn(self, mtype): return mtype.isOut() or mtype.isInout()
|
||||
|
||||
|
||||
def generateMsgClass(clsname, params, typedefInjector):
|
||||
cls = cxx.Class(name=clsname,
|
||||
inherits=[ cxx.Inherit('IPC::Message') ])
|
||||
cls.addstmt(cxx.Label('private'))
|
||||
typedefInjector(cls)
|
||||
cls.addstmt(cxx.Whitespace.NL)
|
||||
|
||||
cls.addstmt(cxx.Label('public'))
|
||||
|
||||
idenum = cxx.TypeEnum()
|
||||
idenum.addId('ID', clsname +'__ID')
|
||||
cls.addstmt(cxx.StmtDecl(cxx.Decl(idenum, '')))
|
||||
|
||||
# FIXME/cjones: need to handle "managed" messages
|
||||
|
||||
constparams = deepcopy(params)
|
||||
writestmts = [ ]
|
||||
for cparam in constparams:
|
||||
cparam.type.const = True
|
||||
cparam.type.ref = True
|
||||
writestmts.append(
|
||||
cxx.StmtExpr(cxx.ExprCall(cxx.ExprVar('IPC::WriteParam'),
|
||||
[ cxx.ExprVar('this'),
|
||||
cxx.ExprVar(cparam.name) ])))
|
||||
|
||||
# make the message constructor (serializer)
|
||||
ctordecl = cxx.ConstructorDecl(clsname, params=constparams)
|
||||
|
||||
superinit = cxx.ExprMemberInit(
|
||||
cxx.ExprVar('IPC::Message'),
|
||||
[ cxx.ExprVar('MSG_ROUTING_CONTROL'),
|
||||
cxx.ExprVar('ID'),
|
||||
cxx.ExprVar('PRIORITY_NORMAL') ])
|
||||
|
||||
ctor = cxx.ConstructorDefn(ctordecl, [ superinit ])
|
||||
for writestmt in writestmts:
|
||||
ctor.addstmt(writestmt)
|
||||
cls.addstmt(ctor)
|
||||
|
||||
cls.addstmt(cxx.Whitespace.NL)
|
||||
|
||||
# make the message deserializer
|
||||
outparams = deepcopy(params)
|
||||
for oparam in outparams:
|
||||
oparam.type.ptr = True
|
||||
|
||||
reader = cxx.MethodDefn(
|
||||
cxx.MethodDecl(
|
||||
'Read',
|
||||
params=([ cxx.Decl(cxx.Type('Message', ptr=True, const=True),
|
||||
'msg') ]
|
||||
+ outparams),
|
||||
ret=cxx.Type('bool'),
|
||||
static=True))
|
||||
|
||||
# avoid generating an unused variable when we don't deserialize
|
||||
# anything. why generate the method anyway? it keeps other code
|
||||
# consistent, and we might do some checking in here eventually
|
||||
if len(outparams):
|
||||
# hack
|
||||
reader.addstmt(
|
||||
cxx.StmtDecl(cxx.Decl(cxx.Type('void', ptr=True), 'iter = 0')))
|
||||
reader.addstmt(cxx.Whitespace.NL)
|
||||
|
||||
for oparam in outparams:
|
||||
cond = cxx.ExprPrefixUnop(
|
||||
cxx.ExprCall(cxx.ExprVar('IPC::ReadParam'),
|
||||
[ cxx.ExprVar('msg'),
|
||||
cxx.ExprAddrOf(cxx.ExprVar('iter')),
|
||||
cxx.ExprVar(oparam.name) ]),
|
||||
'!')
|
||||
ifstmt = cxx.StmtIf(cond)
|
||||
# false isn't a var
|
||||
ifstmt.addifstmt(cxx.StmtReturn(cxx.ExprVar('false')))
|
||||
reader.addstmt(ifstmt)
|
||||
reader.addstmt(cxx.Whitespace.NL)
|
||||
|
||||
# false isn't a var
|
||||
reader.addstmt(cxx.StmtReturn(cxx.ExprVar('true')))
|
||||
|
||||
cls.addstmt(reader)
|
||||
|
||||
return cls
|
||||
|
||||
def generateMessageClass(md, typedefInjector):
|
||||
return generateMsgClass(md._cxx.id, md._cxx.params, typedefInjector)
|
||||
|
||||
def generateReplyClass(md, typedefInjector):
|
||||
return generateMsgClass(md._cxx.replyid, md._cxx.returns, typedefInjector)
|
||||
|
||||
|
||||
##-----------------------------------------------------------------------------
|
||||
_channelTable = {
|
||||
'Async': [ 'mozilla', 'ipc', 'AsyncChannel' ],
|
||||
'Sync': [ 'mozilla', 'ipc', 'SyncChannel' ],
|
||||
'Rpc': [ 'mozilla', 'ipc', 'RPCChannel' ]
|
||||
}
|
||||
|
||||
|
||||
class GenerateProtocolActorHeader(Visitor):
|
||||
def __init__(self, thisiface, thatiface):
|
||||
self.thisiface = thisiface
|
||||
self.thatiface = thatiface
|
||||
self.clsname = None
|
||||
self.pname = None
|
||||
self.file = None
|
||||
self.ns = None
|
||||
|
||||
def lower(self, tu, pname, clsname, cxxHeaderFile):
|
||||
self.pname = pname
|
||||
self.clsname = clsname
|
||||
self.file = cxxHeaderFile
|
||||
tu.accept(self)
|
||||
|
||||
def visitTranslationUnit(self, tu):
|
||||
f = self.file
|
||||
|
||||
f.addthing(cxx.Whitespace('''//
|
||||
// Automatically generated by ipdlc.
|
||||
// Edit at your own risk
|
||||
//
|
||||
|
||||
'''))
|
||||
includeguard = _protocolIncludeGuard(tu.protocol, self.clsname)
|
||||
f.addthing(cxx.CppDirective('ifndef', includeguard))
|
||||
f.addthing(cxx.CppDirective('define', includeguard))
|
||||
f.addthing(cxx.Whitespace.NL)
|
||||
|
||||
mainheader = _protocolHeaderFilename(tu.protocol, self.pname)
|
||||
f.addthing(cxx.CppDirective('include', '"'+ mainheader +'"'))
|
||||
|
||||
tu.protocol.accept(self)
|
||||
|
||||
f.addthing(cxx.Whitespace.NL)
|
||||
f.addthing(cxx.CppDirective('endif', '// ifndef '+ includeguard))
|
||||
|
||||
|
||||
def visitProtocol(self, p):
|
||||
channel = _channelTable[p.decl.type.sendSemantics.pretty]
|
||||
channelname = '::'.join(channel)
|
||||
channelfile = '/'.join(channel) +'.h'
|
||||
if p.decl.type.isToplevel():
|
||||
self.channelsel = '.'
|
||||
else:
|
||||
self.channelsel = '->'
|
||||
|
||||
self.file.addthing(cxx.CppDirective('include', '"'+ channelfile +'"'))
|
||||
self.file.addthing(cxx.Whitespace.NL)
|
||||
|
||||
# construct the namespace into which we'll stick all our decls
|
||||
if 0 == len(p.namespaces):
|
||||
self.ns = self.file
|
||||
else:
|
||||
innernamespaces = p.namespaces[1:]
|
||||
self.ns = cxx.Namespace(p.namespaces[0].namespace)
|
||||
self.file.addthing(self.ns)
|
||||
|
||||
for ns in innernamespaces:
|
||||
innerns = cxx.Namespace(ns.namespace)
|
||||
self.ns.addstmt(innerns)
|
||||
self.ns = innerns
|
||||
|
||||
self.ns.addstmt(cxx.Whitespace.NL)
|
||||
self.ns.addstmt(cxx.Whitespace.NL)
|
||||
|
||||
iface = p.decl.fullname +'Protocol::'+ self.thatiface
|
||||
cls = cxx.Class(self.clsname,
|
||||
inherits=[ cxx.Inherit(iface),
|
||||
cxx.Inherit(channelname +'::Listener') ],
|
||||
final=True)
|
||||
|
||||
cls.addstmt(cxx.Label('private'))
|
||||
impliface = p.decl.fullname +'Protocol::'+ self.thisiface
|
||||
cls.addstmt(cxx.Typedef(cxx.Type('IPC::Message'),
|
||||
cxx.Type('Message')))
|
||||
cls.addstmt(cxx.Typedef(cxx.Type(channelname),
|
||||
cxx.Type('Channel')))
|
||||
cls.addstmt(cxx.Typedef(cxx.Type(impliface),
|
||||
cxx.Type(self.thisiface)))
|
||||
cls.addstmt(cxx.Whitespace.NL)
|
||||
|
||||
# TODO manager param to constructor, when protocol is managed
|
||||
|
||||
cls.addstmt(cxx.Label('public'))
|
||||
ctor = cxx.ConstructorDefn(
|
||||
cxx.ConstructorDecl(
|
||||
self.clsname,
|
||||
[ cxx.Decl(cxx.Type(self.thisiface, ptr=True), 'aImpl') ]),
|
||||
[ cxx.ExprMemberInit(cxx.ExprVar('mImpl'),
|
||||
[ cxx.ExprVar('aImpl') ]) ])
|
||||
if p.decl.type.isToplevel():
|
||||
ctor.memberinits.append(
|
||||
cxx.ExprMemberInit(cxx.ExprVar('mChannel'),
|
||||
[ cxx.ExprVar('this') ]))
|
||||
cls.addstmt(ctor)
|
||||
cls.addstmt(cxx.Whitespace.NL)
|
||||
|
||||
dtor = cxx.DestructorDefn(
|
||||
cxx.DestructorDecl(self.clsname, virtual=True))
|
||||
cls.addstmt(dtor)
|
||||
cls.addstmt(cxx.Whitespace.NL)
|
||||
|
||||
if p.decl.type.isToplevel():
|
||||
# open
|
||||
openmeth = cxx.MethodDefn(
|
||||
cxx.MethodDecl(
|
||||
'Open',
|
||||
params=[ cxx.Decl(cxx.Type('IPC::Channel', ptr=True),
|
||||
'aChannel'),
|
||||
cxx.Decl(cxx.Type('MessageLoop', ptr=True),
|
||||
'aThread = 0') ],
|
||||
ret=cxx.Type('bool')))
|
||||
openmeth.addstmt(cxx.StmtReturn(
|
||||
cxx.ExprCall(
|
||||
cxx.ExprSelect(cxx.ExprVar('mChannel'), '.', 'Open'),
|
||||
[ cxx.ExprVar('aChannel'), cxx.ExprVar('aThread') ])))
|
||||
cls.addstmt(openmeth)
|
||||
cls.addstmt(cxx.Whitespace.NL)
|
||||
|
||||
# close
|
||||
closemeth = cxx.MethodDefn(cxx.MethodDecl('Close'))
|
||||
closemeth.addstmt(cxx.StmtExpr(
|
||||
cxx.ExprCall(
|
||||
cxx.ExprSelect(cxx.ExprVar('mChannel'), '.', 'Close'))))
|
||||
cls.addstmt(closemeth)
|
||||
cls.addstmt(cxx.Whitespace.NL)
|
||||
|
||||
# incoming message dispatchers
|
||||
self.asyncswitch = cxx.StmtSwitch(
|
||||
cxx.ExprCall(cxx.ExprSelect(cxx.ExprVar('msg'), '.', 'type'), [ ]))
|
||||
if p.decl.type.talksSync():
|
||||
self.syncswitch = deepcopy(self.asyncswitch)
|
||||
if p.decl.type.talksRpc():
|
||||
self.rpcswitch = deepcopy(self.syncswitch)
|
||||
|
||||
# implement child iface and add handlers to message switches
|
||||
self.cls = cls
|
||||
for md in p.messageDecls:
|
||||
self.visitMessageDecl(md)
|
||||
|
||||
# add default cases
|
||||
default = cxx.StmtBlock()
|
||||
default.addstmt(cxx.StmtReturn(cxx.ExprVar('MsgNotKnown')))
|
||||
|
||||
self.asyncswitch.addcase(cxx.DefaultLabel(), default)
|
||||
if p.decl.type.talksSync():
|
||||
self.syncswitch.addcase(cxx.DefaultLabel(), default)
|
||||
if p.decl.type.talksRpc():
|
||||
self.rpcswitch.addcase(cxx.DefaultLabel(), default)
|
||||
|
||||
asynchandler = cxx.MethodDefn(
|
||||
cxx.MethodDecl(
|
||||
'OnMessageReceived', virtual=True,
|
||||
params=[ cxx.Decl(cxx.Type('Message', const=1, ref=1),'msg') ],
|
||||
ret=cxx.Type('Result')))
|
||||
|
||||
if p.decl.type.talksSync():
|
||||
synchandler = deepcopy(asynchandler)
|
||||
synchandler.decl.params.append(cxx.Decl(
|
||||
cxx.Type('Message', ref=1, ptr=1), 'reply'))
|
||||
|
||||
if p.decl.type.talksRpc():
|
||||
rpchandler = deepcopy(synchandler)
|
||||
rpchandler.decl.name = 'OnCallReceived'
|
||||
|
||||
asynchandler.addstmt(self.asyncswitch)
|
||||
cls.addstmt(asynchandler)
|
||||
cls.addstmt(cxx.Whitespace.NL)
|
||||
|
||||
if p.decl.type.talksSync():
|
||||
synchandler.addstmt(self.syncswitch)
|
||||
cls.addstmt(synchandler)
|
||||
cls.addstmt(cxx.Whitespace.NL)
|
||||
|
||||
if p.decl.type.talksRpc():
|
||||
rpchandler.addstmt(self.rpcswitch)
|
||||
cls.addstmt(rpchandler)
|
||||
cls.addstmt(cxx.Whitespace.NL)
|
||||
|
||||
# private members and methods
|
||||
|
||||
# TODO handle manager stuff: lookups, routing
|
||||
|
||||
cls.addstmt(cxx.Label('private'))
|
||||
cls.addstmt(cxx.StmtDecl(cxx.Decl(cxx.Type(self.thisiface, ptr=True),
|
||||
'mImpl')))
|
||||
channeltype = cxx.Type('Channel')
|
||||
if p.decl.type.isManaged():
|
||||
channeltype.ptr = True # subprotocols inherit this
|
||||
cls.addstmt(cxx.StmtDecl(cxx.Decl(channeltype, 'mChannel')))
|
||||
|
||||
self.ns.addstmt(cls)
|
||||
self.ns.addstmt(cxx.Whitespace.NL)
|
||||
self.ns.addstmt(cxx.Whitespace.NL)
|
||||
|
||||
|
||||
def visitMessageDecl(self, md):
|
||||
# TODO special handling of constructor messages
|
||||
|
||||
# create method for "that" interface
|
||||
if self.sendsMessage(md):
|
||||
impl = cxx.MethodDefn(md._cxx.method)
|
||||
|
||||
impl.addstmt(cxx.StmtDecl(cxx.Decl(cxx.Type('nsresult'), '__rv')))
|
||||
rv = cxx.ExprVar('__rv')
|
||||
failif = cxx.StmtIf(rv)
|
||||
failif.ifb.addstmt(cxx.StmtReturn(rv))
|
||||
|
||||
hasreply = md.decl.type.hasReply()
|
||||
if hasreply:
|
||||
impl.addstmt(cxx.StmtDecl(
|
||||
cxx.Decl(cxx.Type('Message'), 'reply')))
|
||||
reply = cxx.ExprVar('reply')
|
||||
impl.addstmt(cxx.Whitespace.NL)
|
||||
|
||||
sendcall = cxx.ExprCall(
|
||||
cxx.ExprSelect(
|
||||
cxx.ExprVar('mChannel'), self.channelsel, 'Call'),
|
||||
[ cxx.ExprNew(cxx.Type(md._cxx.nsid),
|
||||
[ cxx.ExprVar(p.name)
|
||||
for p in md._cxx.params ]) ])
|
||||
if hasreply:
|
||||
sendcall.args.append(cxx.ExprAddrOf(reply))
|
||||
|
||||
# TODO special handling of actor handles
|
||||
|
||||
impl.addstmt(cxx.StmtExpr(cxx.ExprAssn(rv, sendcall)))
|
||||
if not hasreply:
|
||||
impl.addstmt(cxx.StmtReturn(rv))
|
||||
self.cls.addstmt(impl)
|
||||
self.cls.addstmt(cxx.Whitespace.NL)
|
||||
else:
|
||||
impl.addstmt(failif)
|
||||
|
||||
unpack = cxx.ExprCall(cxx.ExprVar(md._cxx.nsreplyid +'::Read'),
|
||||
[ cxx.ExprAddrOf(cxx.ExprVar('reply')) ]
|
||||
+ [ cxx.ExprVar(r.name)
|
||||
for r in md._cxx.returns ])
|
||||
errhandle = cxx.StmtIf(cxx.ExprPrefixUnop(unpack, '!'))
|
||||
errhandle.ifb.addstmt(cxx.StmtReturn(
|
||||
cxx.ExprVar('MsgPayloadError')))
|
||||
impl.addstmt(errhandle)
|
||||
|
||||
# TODO special handling of actor handles
|
||||
|
||||
impl.addstmt(cxx.StmtReturn(cxx.ExprVar('NS_OK')))
|
||||
|
||||
self.cls.addstmt(impl)
|
||||
self.cls.addstmt(cxx.Whitespace.NL)
|
||||
|
||||
|
||||
# create case for this message in the big handler switch statement
|
||||
if self.receivesMessage(md):
|
||||
case = cxx.CaseLabel(md._cxx.nsid +'__ID')
|
||||
block = cxx.StmtBlock()
|
||||
|
||||
rv = cxx.ExprVar('__rv')
|
||||
for param in md._cxx.params:
|
||||
block.addstmt(cxx.StmtDecl(param))
|
||||
for ret in md._cxx.returns:
|
||||
block.addstmt(cxx.StmtDecl(ret))
|
||||
block.addstmt(cxx.Whitespace.NL)
|
||||
|
||||
unpack = cxx.ExprCall(cxx.ExprVar(md._cxx.nsid +'::Read'),
|
||||
[ cxx.ExprAddrOf(cxx.ExprVar('msg')) ]
|
||||
+ [ cxx.ExprAddrOf(cxx.ExprVar(p.name))
|
||||
for p in md._cxx.params ])
|
||||
errhandle = cxx.StmtIf(cxx.ExprPrefixUnop(unpack, '!'))
|
||||
errhandle.ifb.addstmt(cxx.StmtReturn(
|
||||
cxx.ExprVar('MsgPayloadError')))
|
||||
block.addstmt(errhandle)
|
||||
|
||||
# TODO special handling of actor handles
|
||||
|
||||
callimpl = cxx.ExprCall(
|
||||
cxx.ExprSelect(cxx.ExprVar('mImpl'), '->',
|
||||
md.decl.progname), [ ])
|
||||
callimpl.args += [ cxx.ExprVar(p.name) for p in md._cxx.params ]
|
||||
callimpl.args += [ cxx.ExprAddrOf(cxx.ExprVar(r.name))
|
||||
for r in md._cxx.returns ]
|
||||
errhandle = cxx.StmtIf(callimpl)
|
||||
errhandle.ifb.addstmt(cxx.StmtReturn(
|
||||
cxx.ExprVar('MsgValueError')))
|
||||
block.addstmt(errhandle)
|
||||
|
||||
# TODO special handling of actor handles
|
||||
|
||||
if md.decl.type.hasReply():
|
||||
replymsg = cxx.ExprNew(
|
||||
cxx.Type(md._cxx.nsreplyid),
|
||||
[ cxx.ExprVar(r.name) for r in md._cxx.returns ])
|
||||
block.addstmt(cxx.StmtExpr(cxx.ExprAssn(cxx.ExprVar('reply'),
|
||||
replymsg)))
|
||||
|
||||
block.addstmt(cxx.StmtReturn(cxx.ExprVar('MsgProcessed')))
|
||||
|
||||
if md.decl.type.isAsync():
|
||||
self.asyncswitch.addcase(case, block)
|
||||
elif md.decl.type.isSync():
|
||||
self.syncswitch.addcase(case, block)
|
||||
else:
|
||||
self.rpcswitch.addcase(case, block)
|
||||
|
||||
|
||||
class GenerateProtocolParentHeader(GenerateProtocolActorHeader):
|
||||
def __init__(self):
|
||||
GenerateProtocolActorHeader.__init__(self, 'IParent', 'IChild')
|
||||
|
||||
def sendsMessage(self, md):
|
||||
return not md.decl.type.isIn()
|
||||
|
||||
def receivesMessage(self, md):
|
||||
return md.decl.type.isInout() or md.decl.type.isIn()
|
||||
|
||||
class GenerateProtocolChildHeader(GenerateProtocolActorHeader):
|
||||
def __init__(self):
|
||||
GenerateProtocolActorHeader.__init__(self, 'IChild', 'IParent')
|
||||
|
||||
def sendsMessage(self, md):
|
||||
return not md.decl.type.isOut()
|
||||
|
||||
def receivesMessage(self, md):
|
||||
return md.decl.type.isInout() or md.decl.type.isOut()
|
|
@ -0,0 +1,381 @@
|
|||
# ***** BEGIN LICENSE BLOCK *****
|
||||
# Version: MPL 1.1/GPL 2.0/LGPL 2.1
|
||||
#
|
||||
# The contents of this file are subject to the Mozilla Public License Version
|
||||
# 1.1 (the "License"); you may not use this file except in compliance with
|
||||
# the License. You may obtain a copy of the License at
|
||||
# http://www.mozilla.org/MPL/
|
||||
#
|
||||
# Software distributed under the License is distributed on an "AS IS" basis,
|
||||
# WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
|
||||
# for the specific language governing rights and limitations under the
|
||||
# License.
|
||||
#
|
||||
# The Original Code is mozilla.org code.
|
||||
#
|
||||
# Contributor(s):
|
||||
# Chris Jones <jones.chris.g@gmail.com>
|
||||
#
|
||||
# Alternatively, the contents of this file may be used under the terms of
|
||||
# either of the GNU General Public License Version 2 or later (the "GPL"),
|
||||
# or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
|
||||
# in which case the provisions of the GPL or the LGPL are applicable instead
|
||||
# of those above. If you wish to allow use of your version of this file only
|
||||
# under the terms of either the GPL or the LGPL, and not to allow others to
|
||||
# use your version of this file under the terms of the MPL, indicate your
|
||||
# decision by deleting the provisions above and replace them with the notice
|
||||
# and other provisions required by the GPL or the LGPL. If you do not delete
|
||||
# the provisions above, a recipient may use your version of this file under
|
||||
# the terms of any one of the MPL, the GPL or the LGPL.
|
||||
#
|
||||
# ***** END LICENSE BLOCK *****
|
||||
|
||||
import os, sys
|
||||
from ply import lex, yacc
|
||||
|
||||
from ipdl.ast import *
|
||||
|
||||
def _getcallerpath():
|
||||
'''Return the absolute path of the file containing the code that
|
||||
**CALLED** this function.'''
|
||||
return os.path.abspath(sys._getframe(1).f_code.co_filename)
|
||||
|
||||
# we want PLY to generate its output in the module directory, not wherever
|
||||
# the user chooses to run ipdlc from
|
||||
_thisdir, _ = os.path.split(_getcallerpath())
|
||||
|
||||
##-----------------------------------------------------------------------------
|
||||
|
||||
class Parser:
|
||||
# when we reach an |include protocol "foo.ipdl";| statement, we need to
|
||||
# save the current parser state and create a new one. this "stack" is
|
||||
# where that state is saved
|
||||
#
|
||||
# there is one Parser per file
|
||||
current = None
|
||||
parseStack = [ ]
|
||||
parsed = { }
|
||||
|
||||
def __init__(self, debug=0):
|
||||
self.debug = debug
|
||||
self.filename = None
|
||||
self.loc = None # not always up to date
|
||||
self.lexer = None
|
||||
self.parser = None
|
||||
self.tu = TranslationUnit()
|
||||
|
||||
def parse(self, input, filename):
|
||||
realpath = os.path.abspath(filename)
|
||||
if realpath in Parser.parsed:
|
||||
return Parser.parsed[realpath].tu
|
||||
|
||||
self.lexer = lex.lex(debug=self.debug,
|
||||
optimize=not self.debug,
|
||||
lextab="ipdl_lextab",
|
||||
outputdir=_thisdir)
|
||||
self.parser = yacc.yacc(debug=self.debug,
|
||||
optimize=not self.debug,
|
||||
tabmodule="ipdl_yacctab",
|
||||
outputdir=_thisdir)
|
||||
self.filename = filename
|
||||
self.tu.filename = realpath
|
||||
|
||||
Parser.parsed[realpath] = self
|
||||
Parser.parseStack.append(Parser.current)
|
||||
Parser.current = self
|
||||
|
||||
ast = self.parser.parse(input=input, lexer=self.lexer,
|
||||
debug=self.debug)
|
||||
|
||||
Parser.current = Parser.parseStack.pop()
|
||||
return ast
|
||||
|
||||
# returns a GCC-style string representation of the include stack.
|
||||
# e.g.,
|
||||
# in file included from 'foo.ipdl', line 120:
|
||||
# in file included from 'bar.ipd', line 12:
|
||||
# which can be printed above a proper error message or warning
|
||||
@staticmethod
|
||||
def includeStackString():
|
||||
s = ''
|
||||
for parse in Parser.parseStack[1:]:
|
||||
s += " in file included from `%s', line %d:\n"% (
|
||||
parse.loc.filename, parse.loc.lineno)
|
||||
return s
|
||||
|
||||
def locFromTok(p, num):
|
||||
return Loc(Parser.current.filename, p.lineno(num))
|
||||
|
||||
|
||||
##-----------------------------------------------------------------------------
|
||||
|
||||
reserved = set((
|
||||
'answer',
|
||||
'async',
|
||||
'call',
|
||||
'goto',
|
||||
'in',
|
||||
'include',
|
||||
'inout',
|
||||
'manager',
|
||||
'manages',
|
||||
'namespace',
|
||||
'out',
|
||||
'protocol',
|
||||
'recv',
|
||||
'returns',
|
||||
'rpc',
|
||||
'send',
|
||||
'share',
|
||||
'sync',
|
||||
'transfer',
|
||||
'using'))
|
||||
tokens = [
|
||||
'COLONCOLON', 'ID', 'STRING'
|
||||
] + [ r.upper() for r in reserved ]
|
||||
|
||||
t_COLONCOLON = '::'
|
||||
|
||||
literals = '(){}[];,~'
|
||||
t_ignore = ' \f\t\v'
|
||||
|
||||
def t_linecomment(t):
|
||||
r'//[^\n]*'
|
||||
|
||||
def t_multilinecomment(t):
|
||||
r'/\*(\n|.)*?\*/'
|
||||
t.lexer.lineno += t.value.count('\n')
|
||||
|
||||
def t_NL(t):
|
||||
r'\n+'
|
||||
t.lexer.lineno += len(t.value)
|
||||
|
||||
def t_ID(t):
|
||||
r'[a-zA-Z_][a-zA-Z0-9_]*'
|
||||
if t.value in reserved:
|
||||
t.type = t.value.upper()
|
||||
return t
|
||||
|
||||
def t_STRING(t):
|
||||
r'"[^"\n]*"'
|
||||
t.value = t.value[1:-1]
|
||||
return t
|
||||
|
||||
def t_error(t):
|
||||
includeStackStr = Parser.includeStackString()
|
||||
raise Exception, '%s%s: lexically invalid characters %s'% (
|
||||
includeStackStr, Loc(Parser.current.filename, t.lineno), str(t))
|
||||
|
||||
##-----------------------------------------------------------------------------
|
||||
|
||||
def p_TranslationUnit(p):
|
||||
"""TranslationUnit : Preamble NamespacedProtocolDefn"""
|
||||
tu = Parser.current.tu
|
||||
for stmt in p[1]:
|
||||
if isinstance(stmt, CxxInclude): tu.addCxxInclude(stmt)
|
||||
elif isinstance(stmt, ProtocolInclude): tu.addProtocolInclude(stmt)
|
||||
elif isinstance(stmt, UsingStmt): tu.addUsingStmt(stmt)
|
||||
else:
|
||||
assert 0
|
||||
tu.protocol = p[2]
|
||||
p[0] = tu
|
||||
|
||||
##--------------------
|
||||
## Preamble
|
||||
def p_Preamble(p):
|
||||
"""Preamble : Preamble PreambleStmt ';'
|
||||
|"""
|
||||
if 1 == len(p):
|
||||
p[0] = [ ]
|
||||
else:
|
||||
p[1].append(p[2])
|
||||
p[0] = p[1]
|
||||
|
||||
def p_PreambleStmt(p):
|
||||
"""PreambleStmt : CxxIncludeStmt
|
||||
| ProtocolIncludeStmt
|
||||
| UsingStmt"""
|
||||
p[0] = p[1]
|
||||
|
||||
def p_CxxIncludeStmt(p):
|
||||
"""CxxIncludeStmt : INCLUDE STRING"""
|
||||
p[0] = CxxInclude(locFromTok(p, 1), p[2])
|
||||
|
||||
def p_ProtocolIncludeStmt(p):
|
||||
"""ProtocolIncludeStmt : INCLUDE PROTOCOL STRING"""
|
||||
loc = locFromTok(p, 1)
|
||||
Parser.current.loc = loc
|
||||
|
||||
inc = ProtocolInclude(loc, p[3])
|
||||
inc.tu = Parser().parse(open(inc.file).read(), inc.file)
|
||||
p[0] = inc
|
||||
|
||||
def p_UsingStmt(p):
|
||||
"""UsingStmt : USING CxxType"""
|
||||
p[0] = UsingStmt(locFromTok(p, 1), p[2])
|
||||
|
||||
##--------------------
|
||||
## Protocol definition
|
||||
def p_NamespacedProtocolDefn(p):
|
||||
"""NamespacedProtocolDefn : NAMESPACE ID '{' NamespacedProtocolDefn '}'
|
||||
| ProtocolDefn"""
|
||||
if 2 == len(p):
|
||||
p[0] = p[1]
|
||||
else:
|
||||
protocol = p[4]
|
||||
protocol.addOuterNamespace(Namespace(locFromTok(p, 1), p[2]))
|
||||
p[0] = protocol
|
||||
|
||||
def p_ProtocolDefn(p):
|
||||
"""ProtocolDefn : SendSemanticsQual PROTOCOL ID '{' ManagerStmtOpt ManagesStmts MessageDecls TransitionStmts '}' ';'"""
|
||||
protocol = Protocol(locFromTok(p, 2))
|
||||
protocol.name = p[3]
|
||||
protocol.sendSemantics = p[1]
|
||||
protocol.manager = p[5]
|
||||
protocol.addManagesStmts(p[6])
|
||||
protocol.addMessageDecls(p[7])
|
||||
protocol.addTransitionStmts(p[8])
|
||||
p[0] = protocol
|
||||
|
||||
def p_ManagesStmts(p):
|
||||
"""ManagesStmts : ManagesStmts ManagesStmt
|
||||
| """
|
||||
if 1 == len(p):
|
||||
p[0] = [ ]
|
||||
else:
|
||||
p[1].append(p[2])
|
||||
p[0] = p[1]
|
||||
|
||||
def p_ManagerStmtOpt(p):
|
||||
"""ManagerStmtOpt : MANAGER ID ';'
|
||||
| """
|
||||
if 1 == len(p):
|
||||
p[0] = None
|
||||
else:
|
||||
p[0] = ManagerStmt(locFromTok(p, 1), p[2])
|
||||
|
||||
def p_ManagesStmt(p):
|
||||
"""ManagesStmt : MANAGES ID ';'"""
|
||||
p[0] = ManagesStmt(locFromTok(p, 1), p[2])
|
||||
|
||||
def p_MessageDecls(p):
|
||||
"""MessageDecls : MessageDecls MessageDecl ';'
|
||||
| MessageDecl ';'"""
|
||||
if 3 == len(p):
|
||||
p[0] = [ p[1] ]
|
||||
else:
|
||||
p[1].append(p[2])
|
||||
p[0] = p[1]
|
||||
|
||||
def p_MessageDecl(p):
|
||||
"""MessageDecl : SendSemanticsQual DirectionQual MessageBody"""
|
||||
msg = p[3]
|
||||
msg.sendSemantics = p[1]
|
||||
msg.direction = p[2]
|
||||
p[0] = msg
|
||||
|
||||
def p_MessageBody(p):
|
||||
"""MessageBody : MessageId MessageInParams MessageOutParams"""
|
||||
# FIXME/cjones: need better loc info: use one of the quals
|
||||
msg = MessageDecl(locFromTok(p, 1))
|
||||
msg.name = p[1]
|
||||
msg.addInParams(p[2])
|
||||
msg.addOutParams(p[3])
|
||||
p[0] = msg
|
||||
|
||||
def p_MessageId(p):
|
||||
"""MessageId : ID
|
||||
| '~' ID"""
|
||||
if 3 == len(p):
|
||||
p[1] += p[2] # munge dtor name to "~Foo". handled later
|
||||
p[0] = p[1]
|
||||
|
||||
def p_MessageInParams(p):
|
||||
"""MessageInParams : '(' ParamList ')'"""
|
||||
p[0] = p[2]
|
||||
|
||||
def p_MessageOutParams(p):
|
||||
"""MessageOutParams : RETURNS '(' ParamList ')'
|
||||
| """
|
||||
if 1 == len(p):
|
||||
p[0] = [ ]
|
||||
else:
|
||||
p[0] = p[3]
|
||||
|
||||
def p_TransitionStmts(p):
|
||||
"""TransitionStmts : """
|
||||
# FIXME/cjones: impl
|
||||
p[0] = [ ]
|
||||
|
||||
##--------------------
|
||||
## Minor stuff
|
||||
def p_SendSemanticsQual(p):
|
||||
"""SendSemanticsQual : ASYNC
|
||||
| RPC
|
||||
| SYNC
|
||||
| """
|
||||
if 1 == len(p):
|
||||
p[0] = ASYNC
|
||||
return
|
||||
|
||||
s = p[1]
|
||||
if 'async' == s: p[0] = ASYNC
|
||||
elif 'rpc' == s: p[0] = RPC
|
||||
elif 'sync'== s: p[0] = SYNC
|
||||
else:
|
||||
assert 0
|
||||
|
||||
def p_DirectionQual(p):
|
||||
"""DirectionQual : IN
|
||||
| INOUT
|
||||
| OUT"""
|
||||
s = p[1]
|
||||
if 'in' == s: p[0] = IN
|
||||
elif 'inout' == s: p[0] = INOUT
|
||||
elif 'out' == s: p[0] = OUT
|
||||
else:
|
||||
assert 0
|
||||
|
||||
def p_ParamList(p):
|
||||
"""ParamList : ParamList ',' Param
|
||||
| Param
|
||||
| """
|
||||
if 1 == len(p):
|
||||
p[0] = [ ]
|
||||
elif 2 == len(p):
|
||||
p[0] = [ p[1] ]
|
||||
else:
|
||||
p[1].append(p[3])
|
||||
p[0] = p[1]
|
||||
|
||||
def p_Param(p):
|
||||
"""Param : ID ID"""
|
||||
loc = locFromTok(p, 1)
|
||||
p[0] = Param(loc,
|
||||
TypeSpec(loc, QualifiedId(loc, p[1])),
|
||||
p[2])
|
||||
|
||||
##--------------------
|
||||
## C++ stuff
|
||||
def p_CxxType(p):
|
||||
"""CxxType : QualifiedID
|
||||
| ID"""
|
||||
if isinstance(p[0], QualifiedId):
|
||||
p[0] = TypeSpec(p[1].loc, p[1])
|
||||
else:
|
||||
loc = locFromTok(p, 1)
|
||||
p[0] = TypeSpec(loc, QualifiedId(loc, p[1]))
|
||||
|
||||
def p_QualifiedID(p):
|
||||
"""QualifiedID : QualifiedID COLONCOLON ID
|
||||
| ID COLONCOLON ID"""
|
||||
if isinstance(p[1], QualifiedId):
|
||||
p[1].qualify(p[3])
|
||||
p[0] = p[1]
|
||||
else:
|
||||
p[0] = QualifiedId(locFromTok(p, 1), p[3])
|
||||
|
||||
def p_error(t):
|
||||
includeStackStr = Parser.includeStackString()
|
||||
raise Exception, '%s%s: syntax error near "%s"'% (
|
||||
includeStackStr, Loc(Parser.current.filename, t.lineno), t.value)
|
|
@ -0,0 +1,679 @@
|
|||
# ***** BEGIN LICENSE BLOCK *****
|
||||
# Version: MPL 1.1/GPL 2.0/LGPL 2.1
|
||||
#
|
||||
# The contents of this file are subject to the Mozilla Public License Version
|
||||
# 1.1 (the "License"); you may not use this file except in compliance with
|
||||
# the License. You may obtain a copy of the License at
|
||||
# http://www.mozilla.org/MPL/
|
||||
#
|
||||
# Software distributed under the License is distributed on an "AS IS" basis,
|
||||
# WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
|
||||
# for the specific language governing rights and limitations under the
|
||||
# License.
|
||||
#
|
||||
# The Original Code is mozilla.org code.
|
||||
#
|
||||
# Contributor(s):
|
||||
# Chris Jones <jones.chris.g@gmail.com>
|
||||
#
|
||||
# Alternatively, the contents of this file may be used under the terms of
|
||||
# either of the GNU General Public License Version 2 or later (the "GPL"),
|
||||
# or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
|
||||
# in which case the provisions of the GPL or the LGPL are applicable instead
|
||||
# of those above. If you wish to allow use of your version of this file only
|
||||
# under the terms of either the GPL or the LGPL, and not to allow others to
|
||||
# use your version of this file under the terms of the MPL, indicate your
|
||||
# decision by deleting the provisions above and replace them with the notice
|
||||
# and other provisions required by the GPL or the LGPL. If you do not delete
|
||||
# the provisions above, a recipient may use your version of this file under
|
||||
# the terms of any one of the MPL, the GPL or the LGPL.
|
||||
#
|
||||
# ***** END LICENSE BLOCK *****
|
||||
|
||||
import sys
|
||||
|
||||
from ipdl.ast import CxxInclude, Decl, Loc, QualifiedId, TypeSpec, UsingStmt, Visitor, ASYNC, SYNC, RPC, IN, OUT, INOUT
|
||||
import ipdl.builtin as builtin
|
||||
|
||||
class Type:
|
||||
# Is this a C++ type?
|
||||
def isCxx():
|
||||
return False
|
||||
# Is this an IPDL type?
|
||||
def isIPDL():
|
||||
return False
|
||||
# Can this type appear in IPDL programs?
|
||||
def isVisible():
|
||||
return False
|
||||
def isVoid(self):
|
||||
return False
|
||||
def typename(self):
|
||||
return self.__class__.__name__
|
||||
|
||||
def name(self): raise Exception, 'NYI'
|
||||
def fullname(self): raise Exception, 'NYI'
|
||||
|
||||
class VoidType(Type):
|
||||
# the following are not a type-o's (hah): void is both a Cxx and IPDL type
|
||||
def isCxx():
|
||||
return True
|
||||
def isIPDL():
|
||||
return True
|
||||
def isVisible(self):
|
||||
return True
|
||||
def isVoid(self):
|
||||
return True
|
||||
|
||||
def name(self): return 'void'
|
||||
def fullname(self): return 'void'
|
||||
|
||||
VOID = VoidType()
|
||||
|
||||
##--------------------
|
||||
class CxxType(Type):
|
||||
def isCxx(self):
|
||||
return True
|
||||
def isBuiltin(self):
|
||||
return False
|
||||
def isImported(self):
|
||||
return False
|
||||
def isGenerated(self):
|
||||
return False
|
||||
def isVisible(self):
|
||||
return True
|
||||
|
||||
class BuiltinCxxType(CxxType):
|
||||
def __init__(self, qname):
|
||||
assert isinstance(qname, QualifiedId)
|
||||
self.loc = qname.loc
|
||||
self.qname = qname
|
||||
def isBuiltin(self): return True
|
||||
|
||||
def name(self):
|
||||
return self.qname.baseid
|
||||
def fullname(self):
|
||||
return str(self.qname)
|
||||
|
||||
class ImportedCxxType(CxxType):
|
||||
def __init__(self, qname):
|
||||
assert isinstance(qname, QualifiedId)
|
||||
self.loc = qname.loc
|
||||
self.qname = qname
|
||||
def isImported(self): return True
|
||||
|
||||
def name(self):
|
||||
return self.qname.baseid
|
||||
def fullname(self):
|
||||
return str(self.qname)
|
||||
|
||||
class GeneratedCxxType(CxxType):
|
||||
def isGenerated(self): return True
|
||||
def isVisible(self): return False
|
||||
|
||||
##--------------------
|
||||
class IPDLType(Type):
|
||||
def isIPDL(self): return True
|
||||
def isVisible(self): return True
|
||||
|
||||
def isAsync(self): return self.sendSemantics is ASYNC
|
||||
def isSync(self): return self.sendSemantics is SYNC
|
||||
def isRpc(self): return self.sendSemantics is RPC
|
||||
|
||||
def talksAsync(self): return True
|
||||
def talksSync(self): return self.isSync() or self.isRpc()
|
||||
def talksRpc(self): return self.isRpc()
|
||||
|
||||
def hasReply(self): return self.isSync() or self.isRpc()
|
||||
|
||||
def needsMoreJuiceThan(self, o):
|
||||
return (o.isAsync() and not self.isAsync()
|
||||
or o.isSync() and self.isRpc())
|
||||
|
||||
class MessageType(IPDLType):
|
||||
def __init__(self, sendSemantics, direction,
|
||||
ctor=False, dtor=False, cdtype=None):
|
||||
assert not (ctor and dtor)
|
||||
assert not (ctor or dtor) or type is not None
|
||||
|
||||
self.sendSemantics = sendSemantics
|
||||
self.direction = direction
|
||||
self.params = [ ]
|
||||
self.returns = [ ]
|
||||
self.ctor = ctor
|
||||
self.dtor = dtor
|
||||
self.cdtype = cdtype
|
||||
def isMessage(self): return True
|
||||
|
||||
def isCtor(self): return self.ctor
|
||||
def isDtor(self): return self.dtor
|
||||
def constructedType(self): return self.cdtype
|
||||
|
||||
def isIn(self): return self.direction is IN
|
||||
def isOut(self): return self.direction is OUT
|
||||
def isInout(self): return self.direction is INOUT
|
||||
|
||||
class ProtocolType(IPDLType):
|
||||
def __init__(self, qname, sendSemantics):
|
||||
self.qname = qname
|
||||
self.sendSemantics = sendSemantics
|
||||
self.manager = None
|
||||
self.manages = [ ]
|
||||
def isProtocol(self): return True
|
||||
|
||||
def name(self):
|
||||
return self.qname.baseid
|
||||
def fullname(self):
|
||||
return str(self.qname)
|
||||
|
||||
def managedBy(self, mgr):
|
||||
self.manager = mgr
|
||||
|
||||
def isManager(self, pt):
|
||||
for managed in self.manages:
|
||||
if pt is managed:
|
||||
return True
|
||||
return False
|
||||
|
||||
def isManaged(self):
|
||||
return self.manager is not None
|
||||
def isToplevel(self):
|
||||
return not self.isManaged()
|
||||
|
||||
##--------------------
|
||||
_builtinloc = Loc('<builtin>', 0)
|
||||
def makeBuiltinUsing(tname):
|
||||
quals = tname.split('::')
|
||||
base = quals.pop()
|
||||
quals = quals[0:]
|
||||
return UsingStmt(_builtinloc,
|
||||
TypeSpec(_builtinloc,
|
||||
QualifiedId(_builtinloc, base, quals)))
|
||||
|
||||
builtinUsing = [ makeBuiltinUsing(t) for t in builtin.Types ]
|
||||
builtinIncludes = [ CxxInclude(_builtinloc, f) for f in builtin.Includes ]
|
||||
|
||||
##--------------------
|
||||
def errormsg(loc, fmt, *args):
|
||||
while not isinstance(loc, Loc):
|
||||
if loc is None: loc = Loc.NONE
|
||||
else: loc = loc.loc
|
||||
return '%s: error: %s'% (str(loc), fmt % args)
|
||||
|
||||
|
||||
class SymbolTable:
|
||||
def __init__(self, errors):
|
||||
self.errors = errors
|
||||
self.scopes = [ { } ] # stack({})
|
||||
self.globalScope = self.scopes[0]
|
||||
self.currentScope = self.globalScope
|
||||
|
||||
def enterScope(self, node):
|
||||
assert (isinstance(self.scopes[0], dict)
|
||||
and self.globalScope is self.scopes[0])
|
||||
assert (isinstance(self.currentScope, dict))
|
||||
|
||||
if not hasattr(node, 'symtab'):
|
||||
node.symtab = { }
|
||||
|
||||
self.scopes.append(node.symtab)
|
||||
self.currentScope = self.scopes[-1]
|
||||
|
||||
def exitScope(self, node):
|
||||
symtab = self.scopes.pop()
|
||||
assert self.currentScope is symtab
|
||||
|
||||
self.currentScope = self.scopes[-1]
|
||||
|
||||
assert (isinstance(self.scopes[0], dict)
|
||||
and self.globalScope is self.scopes[0])
|
||||
assert isinstance(self.currentScope, dict)
|
||||
|
||||
def lookup(self, sym):
|
||||
# NB: since IPDL doesn't allow any aliased names of different types,
|
||||
# it doesn't matter in which order we walk the scope chain to resolve
|
||||
# |sym|
|
||||
for scope in self.scopes:
|
||||
decl = scope.get(sym, None)
|
||||
if decl is not None: return decl
|
||||
return None
|
||||
|
||||
def declare(self, decl):
|
||||
assert decl.progname or decl.shortname or decl.fullname
|
||||
assert decl.loc
|
||||
assert decl.type
|
||||
|
||||
def tryadd(name):
|
||||
olddecl = self.lookup(name)
|
||||
if olddecl is not None:
|
||||
self.errors.append(errormsg(
|
||||
decl.loc,
|
||||
"redeclaration of symbol `%s', first declared at %s",
|
||||
name, olddecl.loc))
|
||||
return
|
||||
self.currentScope[name] = decl
|
||||
decl.scope = self.currentScope
|
||||
|
||||
if decl.progname: tryadd(decl.progname)
|
||||
if decl.shortname: tryadd(decl.shortname)
|
||||
if decl.fullname: tryadd(decl.fullname)
|
||||
|
||||
|
||||
class TypeCheck(Visitor):
|
||||
'''This pass sets the .type attribute of every AST node. For some
|
||||
nodes, the type is meaningless and it is set to "VOID." This pass
|
||||
also sets the .decl attribute of AST nodes for which that is relevant;
|
||||
a decl says where, with what type, and under what name(s) a node was
|
||||
declared.
|
||||
|
||||
With this information, it finally type checks the AST.'''
|
||||
|
||||
def __init__(self):
|
||||
# NB: no IPDL compile will EVER print a warning. A program has
|
||||
# one of two attributes: it is either well typed, or not well typed.
|
||||
self.errors = [ ] # [ string ]
|
||||
self.symtab = SymbolTable(self.errors)
|
||||
|
||||
def check(self, tu, errout=sys.stderr):
|
||||
tu.cxxIncludes = builtinIncludes + tu.cxxIncludes
|
||||
|
||||
# tag each relevant node with "decl" information, giving type, name,
|
||||
# and location of declaration
|
||||
tu.accept(GatherDecls(builtinUsing, self.symtab, self.errors))
|
||||
if len(self.errors):
|
||||
# no point in checking types if we couldn't even resolve symbols
|
||||
self.reportErrors(errout)
|
||||
return False
|
||||
|
||||
# now that the nodes have decls, type checking is much easier.
|
||||
tu.accept(CheckTypes(self.symtab, self.errors))
|
||||
if (len(self.errors)):
|
||||
# no point in later passes if type checking fails
|
||||
self.reportErrors(errout)
|
||||
return False
|
||||
|
||||
return True
|
||||
|
||||
def reportErrors(self, errout):
|
||||
for error in self.errors:
|
||||
print >>errout, error
|
||||
|
||||
|
||||
class GatherDecls(Visitor):
|
||||
def __init__(self, builtinUsing, symtab, errors):
|
||||
self.builtinUsing = builtinUsing
|
||||
self.symtab = symtab
|
||||
self.errors = errors
|
||||
self.visited = set() # set(filename)
|
||||
self.depth = 0
|
||||
|
||||
def visitTranslationUnit(self, tu):
|
||||
# all TranslationUnits declare symbols in global scope
|
||||
if tu.filename in self.visited:
|
||||
return
|
||||
self.visited.add(tu.filename)
|
||||
self.depth += 1
|
||||
|
||||
# bit of a hack here --- we want the builtin |using|
|
||||
# statements to be added to the symbol table before anything
|
||||
# else, but we also want them in the top-level translation
|
||||
# unit's list of using stmts so that we can use them later
|
||||
# down the pipe. so we add them to the symbol table before
|
||||
# anything else, and prepend them to the top-level TU after
|
||||
# it's visited all its |using| decls
|
||||
if 1 == self.depth:
|
||||
for using in self.builtinUsing:
|
||||
udecl = Decl(using.loc)
|
||||
udecl.shortname = using.type.basename()
|
||||
fullname = str(using.type)
|
||||
if udecl.shortname != fullname:
|
||||
udecl.fullname = fullname
|
||||
udecl.type = BuiltinCxxType(using.type.spec)
|
||||
self.symtab.declare(udecl)
|
||||
using.decl = udecl
|
||||
|
||||
p = tu.protocol
|
||||
|
||||
# FIXME/cjones: it's a little weird and counterintuitive to put
|
||||
# both the namespace and non-namespaced name in the global scope.
|
||||
# try to figure out something better; maybe a type-neutral |using|
|
||||
# that works for C++ and protocol types?
|
||||
pdecl = Decl(p.loc)
|
||||
pdecl.shortname = p.name
|
||||
|
||||
fullname = QualifiedId(p.loc, p.name,
|
||||
[ ns.namespace for ns in p.namespaces ])
|
||||
if len(fullname.quals):
|
||||
pdecl.fullname = str(fullname)
|
||||
|
||||
pdecl.type = ProtocolType(fullname, p.sendSemantics)
|
||||
pdecl.body = p
|
||||
self.symtab.declare(pdecl)
|
||||
|
||||
p.decl = pdecl
|
||||
p.type = pdecl.type
|
||||
|
||||
# make sure we have decls for all dependent protocols
|
||||
for pinc in tu.protocolIncludes:
|
||||
pinc.accept(self)
|
||||
|
||||
# each protocol has its own scope for types brought in through |using|
|
||||
self.symtab.enterScope(tu)
|
||||
|
||||
# and for all imported C++ types
|
||||
for using in tu.using:
|
||||
using.accept(self)
|
||||
|
||||
# (see long comment above)
|
||||
if 1 == self.depth:
|
||||
tu.using = self.builtinUsing + tu.using
|
||||
|
||||
# grab symbols in the protocol itself
|
||||
p.accept(self)
|
||||
|
||||
self.symtab.exitScope(tu)
|
||||
|
||||
tu.type = VOID
|
||||
self.depth -= 1
|
||||
|
||||
def visitProtocolInclude(self, pi):
|
||||
pi.tu.accept(self)
|
||||
|
||||
def visitUsingStmt(self, using):
|
||||
decl = Decl(using.loc)
|
||||
decl.shortname = using.type.basename()
|
||||
fullname = str(using.type)
|
||||
if decl.shortname != fullname:
|
||||
decl.fullname = fullname
|
||||
decl.type = ImportedCxxType(using.type.spec)
|
||||
self.symtab.declare(decl)
|
||||
using.decl = decl
|
||||
|
||||
|
||||
def visitProtocol(self, p):
|
||||
# protocol scope
|
||||
self.symtab.enterScope(p)
|
||||
|
||||
if p.manager is not None:
|
||||
p.manager.of = p
|
||||
p.manager.accept(self)
|
||||
|
||||
for managed in p.managesStmts:
|
||||
managed.manager = p
|
||||
managed.accept(self)
|
||||
|
||||
setattr(self, 'currentProtocolDecl', p.decl)
|
||||
for msg in p.messageDecls:
|
||||
msg.accept(self)
|
||||
del self.currentProtocolDecl
|
||||
|
||||
for trans in p.transitionStmts:
|
||||
trans.accept(self)
|
||||
|
||||
# declare all the little C++ thingies that will be generated.
|
||||
|
||||
# they're not relevant to IPDL itself, but those ("invisible")
|
||||
# symbols can clash with others in the IPDL spec, and we'd like
|
||||
# to catch those before C++ compilers are allowed to obfuscate
|
||||
# the error
|
||||
|
||||
self.symtab.exitScope(p)
|
||||
|
||||
|
||||
def visitManagerStmt(self, mgr):
|
||||
mgrdecl = self.symtab.lookup(mgr.name)
|
||||
pdecl = mgr.of.decl
|
||||
assert pdecl
|
||||
|
||||
pname, mgrname = pdecl.shortname, mgr.name
|
||||
loc = mgr.loc
|
||||
|
||||
if mgrdecl is None:
|
||||
self.errors.append(
|
||||
errmsg(
|
||||
loc,
|
||||
"protocol `%s' referenced as |manager| of `%s' has not been declared",
|
||||
mgrname, pname))
|
||||
elif not isinstance(mgrdecl.type, ProtocolType):
|
||||
self.errors.append(
|
||||
errmsg(
|
||||
loc,
|
||||
"entity `%s' referenced as |manager| of `%s' is not of `protocol' type; instead it is of type `%s'",
|
||||
mgrname, pname, mgrdecl.type.typename()))
|
||||
else:
|
||||
assert pdecl.type.manager is None
|
||||
mgr.decl = mgrdecl
|
||||
pdecl.type.manager = mgrdecl.type
|
||||
|
||||
|
||||
def visitManagesStmt(self, mgs):
|
||||
mgsdecl = self.symtab.lookup(mgs.name)
|
||||
pdecl = mgs.manager.decl
|
||||
assert pdecl
|
||||
|
||||
pname, mgsname = pdecl.shortname, mgs.name
|
||||
loc = mgs.loc
|
||||
|
||||
if mgsdecl is None:
|
||||
self.errors.append(
|
||||
errormsg(
|
||||
loc,
|
||||
"protocol `%s', managed by `%s', has not been declared",
|
||||
mgsname, pdeclname))
|
||||
elif not isinstance(mgsdecl.type, ProtocolType):
|
||||
self.errors.append(
|
||||
errormsg(
|
||||
loc,
|
||||
"%s declares itself managing a non-`protocol' entity `%s' of type `%s'",
|
||||
pdeclname, mgsname, mgsdecl.type.typename()))
|
||||
else:
|
||||
mgs.decl = mgsdecl
|
||||
pdecl.type.manages.append(mgsdecl.type)
|
||||
|
||||
|
||||
def visitMessageDecl(self, md):
|
||||
msgname = md.name
|
||||
loc = md.loc
|
||||
|
||||
isctor = False
|
||||
isdtor = False
|
||||
cdtype = None
|
||||
|
||||
if '~' == msgname[0]:
|
||||
# it's a destructor. look up the constructed type
|
||||
msgname = msgname[1:]
|
||||
|
||||
decl = self.symtab.lookup(msgname)
|
||||
if decl is None:
|
||||
self.errors.append(
|
||||
errormsg(
|
||||
loc,
|
||||
"type `%s' has not been declared",
|
||||
msgname))
|
||||
elif not decl.type.isProtocol():
|
||||
self.errors.append(
|
||||
errormsg(
|
||||
loc,
|
||||
"destructor for non-protocol type `%s'",
|
||||
msgname))
|
||||
else:
|
||||
msgname += 'Destructor'
|
||||
isdtor = True
|
||||
cdtype = decl.type
|
||||
|
||||
decl = self.symtab.lookup(msgname)
|
||||
|
||||
if decl is not None and decl.type.isProtocol():
|
||||
# probably a ctor. we'll check validity later.
|
||||
msgname += 'Constructor'
|
||||
isctor = True
|
||||
cdtype = decl.type
|
||||
elif decl is not None:
|
||||
self.errors.append(
|
||||
errormsg(
|
||||
loc,
|
||||
"message name `%s' already declared as `%s'",
|
||||
msgname, decl.type.typename()))
|
||||
# if we error here, no big deal; move on to find more
|
||||
decl = None
|
||||
|
||||
# enter message scope
|
||||
self.symtab.enterScope(md)
|
||||
|
||||
msgtype = MessageType(md.sendSemantics, md.direction,
|
||||
ctor=isctor, dtor=isdtor, cdtype=cdtype)
|
||||
|
||||
# replace inparam Param nodes with proper Decls
|
||||
for i, inparam in enumerate(md.inParams):
|
||||
inptname = inparam.typespec.basename()
|
||||
inploc = inparam.typespec.loc
|
||||
|
||||
inptdecl = self.symtab.lookup(inptname)
|
||||
|
||||
if inptdecl is None:
|
||||
self.errors.append(
|
||||
errormsg(
|
||||
inploc,
|
||||
"inparam typename `%s' of message `%s' has not been declared",
|
||||
inptname, msgname))
|
||||
else:
|
||||
inpdecl = Decl(inploc)
|
||||
inpdecl.progname = inparam.name
|
||||
inpdecl.type = inptdecl.type
|
||||
|
||||
self.symtab.declare(inpdecl)
|
||||
|
||||
msgtype.params.append(inpdecl.type)
|
||||
md.inParams[i] = inpdecl
|
||||
|
||||
# replace outparam Param with proper Decls
|
||||
for i, outparam in enumerate(md.outParams):
|
||||
outptname = outparam.typespec.basename()
|
||||
outploc = outparam.typespec.loc
|
||||
|
||||
outptdecl = self.symtab.lookup(outptname)
|
||||
|
||||
if outptdecl is None:
|
||||
self.errors.append(
|
||||
errormsg(
|
||||
outploc,
|
||||
"outparam typename `%s' of message `%s' has not been declared",
|
||||
outptname, msgname))
|
||||
else:
|
||||
outpdecl = Decl(outploc)
|
||||
outpdecl.progname = outparam.name
|
||||
outpdecl.type = outptdecl.type
|
||||
|
||||
self.symtab.declare(outpdecl)
|
||||
|
||||
msgtype.returns.append(outpdecl.type)
|
||||
md.outParams[i] = outpdecl
|
||||
|
||||
self.symtab.exitScope(md)
|
||||
|
||||
decl = Decl(loc)
|
||||
decl.progname = msgname
|
||||
decl.type = msgtype
|
||||
|
||||
self.symtab.declare(decl)
|
||||
md.decl = decl
|
||||
md.protocolDecl = self.currentProtocolDecl
|
||||
|
||||
|
||||
class CheckTypes(Visitor):
|
||||
def __init__(self, symtab, errors):
|
||||
self.symtab = symtab
|
||||
self.errors = errors
|
||||
self.visited = set()
|
||||
|
||||
def visitProtocolInclude(self, inc):
|
||||
if inc.tu.filename in self.visited:
|
||||
return
|
||||
self.visited.add(inc.tu.filename)
|
||||
inc.tu.protocol.accept(self)
|
||||
|
||||
|
||||
def visitProtocol(self, p):
|
||||
# check that we require no more "power" than our manager protocol
|
||||
ptype, pname = p.decl.type, p.decl.shortname
|
||||
mgrtype = ptype.manager
|
||||
if mgrtype is not None and ptype.needsMoreJuiceThan(mgrtype):
|
||||
mgrname = p.manager.decl.shortname
|
||||
self.errors.append(errormsg(
|
||||
p.decl.loc,
|
||||
"protocol `%s' requires more powerful send semantics than its manager `%s' provides",
|
||||
pname,
|
||||
mgrname))
|
||||
|
||||
return Visitor.visitProtocol(self, p)
|
||||
|
||||
|
||||
def visitManagesStmt(self, mgs):
|
||||
pdecl = mgs.manager.decl
|
||||
ptype, pname = pdecl.type, pdecl.shortname
|
||||
|
||||
mgsdecl = mgs.decl
|
||||
mgstype, mgsname = mgsdecl.type, mgsdecl.shortname
|
||||
|
||||
loc = mgs.loc
|
||||
|
||||
# we added this information; sanity check it
|
||||
for managed in ptype.manages:
|
||||
if managed is mgstype:
|
||||
break
|
||||
else:
|
||||
assert False
|
||||
|
||||
# check that the "managed" protocol agrees
|
||||
if mgstype.manager is not ptype:
|
||||
self.errors.append(errormsg(
|
||||
loc,
|
||||
"|manages| declaration in protocol `%s' does not match any |manager| declaration in protocol `%s'",
|
||||
pname, mgsname))
|
||||
|
||||
|
||||
def visitManagerStmt(self, mgr):
|
||||
pdecl = mgr.of.decl
|
||||
ptype, pname = pdecl.type, pdecl.shortname
|
||||
|
||||
mgrdecl = mgr.decl
|
||||
mgrtype, mgrname = mgrdecl.type, mgrdecl.shortname
|
||||
|
||||
# we added this information; sanity check it
|
||||
assert ptype.manager is mgrtype
|
||||
|
||||
loc = mgr.loc
|
||||
|
||||
# check that the "manager" protocol agrees
|
||||
if not mgrtype.isManager(ptype):
|
||||
self.errors.append(errormsg(
|
||||
loc,
|
||||
"|manager| declaration in protocol `%s' does not match any |manages| declaration in protocol `%s'",
|
||||
pname, mgrname))
|
||||
|
||||
|
||||
def visitMessageDecl(self, md):
|
||||
mtype, mname = md.decl.type, md.decl.progname
|
||||
ptype, pname = md.protocolDecl.type, md.protocolDecl.shortname
|
||||
|
||||
loc = md.decl.loc
|
||||
|
||||
if mtype.needsMoreJuiceThan(ptype):
|
||||
self.errors.append(errormsg(
|
||||
loc,
|
||||
"message `%s' requires more powerful send semantics than its protocol `%s' provides",
|
||||
mname,
|
||||
pname))
|
||||
|
||||
if mtype.isAsync() and len(mtype.returns):
|
||||
# XXX/cjones could modify grammar to disallow this ...
|
||||
self.errors.append(errormsg(
|
||||
loc,
|
||||
"asynchronous message `%s' requests returned values",
|
||||
mname))
|
||||
|
||||
if (mtype.isCtor() or mtype.isDtor()) and not ptype.isManager(mtype.constructedType()):
|
||||
self.errors.append(errormsg(
|
||||
loc,
|
||||
"ctor/dtor for protocol `%s', which is not managed by protocol `%s'",
|
||||
mname[:-len('constructor')],
|
||||
pname))
|
|
@ -0,0 +1,141 @@
|
|||
#!/usr/bin/env python
|
||||
|
||||
# ***** BEGIN LICENSE BLOCK *****
|
||||
# Version: MPL 1.1/GPL 2.0/LGPL 2.1
|
||||
#
|
||||
# The contents of this file are subject to the Mozilla Public License Version
|
||||
# 1.1 (the "License"); you may not use this file except in compliance with
|
||||
# the License. You may obtain a copy of the License at
|
||||
# http://www.mozilla.org/MPL/
|
||||
#
|
||||
# Software distributed under the License is distributed on an "AS IS" basis,
|
||||
# WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
|
||||
# for the specific language governing rights and limitations under the
|
||||
# License.
|
||||
#
|
||||
# The Original Code is mozilla.org code.
|
||||
#
|
||||
# Contributor(s):
|
||||
# Chris Jones <jones.chris.g@gmail.com>
|
||||
#
|
||||
# Alternatively, the contents of this file may be used under the terms of
|
||||
# either of the GNU General Public License Version 2 or later (the "GPL"),
|
||||
# or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
|
||||
# in which case the provisions of the GPL or the LGPL are applicable instead
|
||||
# of those above. If you wish to allow use of your version of this file only
|
||||
# under the terms of either the GPL or the LGPL, and not to allow others to
|
||||
# use your version of this file under the terms of the MPL, indicate your
|
||||
# decision by deleting the provisions above and replace them with the notice
|
||||
# and other provisions required by the GPL or the LGPL. If you do not delete
|
||||
# the provisions above, a recipient may use your version of this file under
|
||||
# the terms of any one of the MPL, the GPL or the LGPL.
|
||||
#
|
||||
# ***** END LICENSE BLOCK *****
|
||||
|
||||
import getopt, os, re, sys
|
||||
|
||||
_verbosity = 1
|
||||
def log(minv, fmt, *args):
|
||||
if _verbosity >= minv:
|
||||
print >>sys.stderr, fmt % args
|
||||
|
||||
def getcallerpath():
|
||||
'''Return the absolute path of the file containing the code that
|
||||
**CALLED** this function.'''
|
||||
return os.path.abspath(sys._getframe(1).f_code.co_filename)
|
||||
|
||||
# auto-add the compiler modules to the pythonpath
|
||||
installdir, _ = os.path.split(getcallerpath())
|
||||
sys.path.append(installdir)
|
||||
|
||||
import ipdl
|
||||
|
||||
# process command line
|
||||
|
||||
def usage(err):
|
||||
if err:
|
||||
out, rv = (sys.stderr, 1)
|
||||
else:
|
||||
out, rv = (sys.stdout, 0)
|
||||
print >>out, '''
|
||||
Usage: ipdlc [OPTIONS...] SPECIFICATION
|
||||
|
||||
where OPTIONS are zero or more of
|
||||
-h, --help
|
||||
: print this message and exit
|
||||
-d DIR, --output-dir=DIR
|
||||
: directory in which to put generated code.
|
||||
Created if it doesn't exist.
|
||||
default: use current working directory.
|
||||
-v N, --verbosity=N
|
||||
: be as verbose as N. set to 0 for no output, and higher than 1
|
||||
for more output.
|
||||
default: -v 1
|
||||
|
||||
and SPECIFICATION is a single IPDL specification file. '-' = read from stdin.
|
||||
default : read specification from stdin
|
||||
'''
|
||||
sys.exit(rv)
|
||||
|
||||
try:
|
||||
opts, args = getopt.gnu_getopt(
|
||||
sys.argv[1:],
|
||||
'd:hv:',
|
||||
['help',
|
||||
'output-dir=',
|
||||
'verbosity='])
|
||||
except getopt.GetoptError, err:
|
||||
print >>sys.stderr, str(err), '\n'
|
||||
usage(err=True)
|
||||
|
||||
codedir = os.getcwd()
|
||||
spec = '-'
|
||||
|
||||
for o, a in opts:
|
||||
if o in ('-h', '--help'):
|
||||
usage(err=False)
|
||||
elif o in ('-d', '--output-dir'):
|
||||
codedir = a
|
||||
elif o in ('-v', '--verbosity'):
|
||||
_verbosity = int(a)
|
||||
|
||||
if 1 < len(args):
|
||||
usage(err=True)
|
||||
if 1 == len(args):
|
||||
spec = args[0]
|
||||
|
||||
specstring = None
|
||||
specfilename = None
|
||||
if spec != '-':
|
||||
log (1, 'using specification %s', spec)
|
||||
specstring = open(spec, 'r').read()
|
||||
specfilename = spec
|
||||
else:
|
||||
log(1, 'reading specification from stdin')
|
||||
specstring = sys.stdin.read()
|
||||
specfilename = '<stdin>'
|
||||
|
||||
log(3, ' specification:\n%s', specstring)
|
||||
|
||||
log(1, 'parsing specification')
|
||||
ast = ipdl.parse(specstring, specfilename)
|
||||
|
||||
log(1, 'checking types')
|
||||
if not ipdl.typecheck(ast):
|
||||
print >>sys.stderr, 'Specification is not well typed.'
|
||||
sys.exit(1)
|
||||
|
||||
if _verbosity >= 3:
|
||||
log(3, ' pretty printed code:')
|
||||
ipdl.genipdl(ast, codedir)
|
||||
|
||||
log(1, 'generating code to "%s"', codedir)
|
||||
ipdl.gencxx(ast, codedir)
|
||||
|
||||
log(1, '''\nIMPORTANT: remember to add the new enum value
|
||||
|
||||
%sMsgStart,
|
||||
|
||||
to the |IPCMessageStart| enum in "ipc/glue/IPCMessageUtils.h".
|
||||
Your code will not compile until this value is added.
|
||||
'''% (ast.protocol.name))
|
Загрузка…
Ссылка в новой задаче