diff options
Diffstat (limited to 'cpp/src')
-rw-r--r-- | cpp/src/Makefile | 1 | ||||
-rw-r--r-- | cpp/src/Slice/.depend | 1 | ||||
-rw-r--r-- | cpp/src/Slice/Makefile | 3 | ||||
-rw-r--r-- | cpp/src/Slice/PythonUtil.cpp | 1219 | ||||
-rw-r--r-- | cpp/src/slice2py/.depend | 1 | ||||
-rw-r--r-- | cpp/src/slice2py/Main.cpp | 479 | ||||
-rw-r--r-- | cpp/src/slice2py/Makefile | 31 |
7 files changed, 1734 insertions, 1 deletions
diff --git a/cpp/src/Makefile b/cpp/src/Makefile index 10faab8f212..e2000fecf7f 100644 --- a/cpp/src/Makefile +++ b/cpp/src/Makefile @@ -20,6 +20,7 @@ SUBDIRS = IceUtil \ slice2freezej \ slice2docbook \ slice2java \ + slice2py \ Ice \ IceXML \ IceSSL \ diff --git a/cpp/src/Slice/.depend b/cpp/src/Slice/.depend index 74c75cda5e4..b363db342c2 100644 --- a/cpp/src/Slice/.depend +++ b/cpp/src/Slice/.depend @@ -6,3 +6,4 @@ CsUtil.o: CsUtil.cpp ../../include/Slice/CsUtil.h ../../include/Slice/Parser.h . JavaUtil.o: JavaUtil.cpp ../../include/Slice/JavaUtil.h ../../include/Slice/Parser.h ../../include/IceUtil/Shared.h ../../include/IceUtil/Config.h ../../include/IceUtil/Handle.h ../../include/IceUtil/Exception.h ../../include/IceUtil/InputUtil.h ../../include/IceUtil/OutputUtil.h ../../include/IceUtil/Functional.h Preprocessor.o: Preprocessor.cpp ../../include/Slice/Preprocessor.h ../../include/IceUtil/Config.h Checksum.o: Checksum.cpp ../../include/Slice/Checksum.h ../../include/Slice/Parser.h ../../include/IceUtil/Shared.h ../../include/IceUtil/Config.h ../../include/IceUtil/Handle.h ../../include/IceUtil/Exception.h ../../include/IceUtil/InputUtil.h ../../include/IceUtil/MD5.h +PythonUtil.o: PythonUtil.cpp ../../include/Slice/PythonUtil.h ../../include/Slice/Parser.h ../../include/IceUtil/Shared.h ../../include/IceUtil/Config.h ../../include/IceUtil/Handle.h ../../include/IceUtil/Exception.h ../../include/IceUtil/InputUtil.h ../../include/IceUtil/OutputUtil.h ../../include/IceUtil/Functional.h diff --git a/cpp/src/Slice/Makefile b/cpp/src/Slice/Makefile index 68b24389851..aa5fabce299 100644 --- a/cpp/src/Slice/Makefile +++ b/cpp/src/Slice/Makefile @@ -22,7 +22,8 @@ OBJS = Scanner.o \ CsUtil.o \ JavaUtil.o \ Preprocessor.o \ - Checksum.o + Checksum.o \ + PythonUtil.o SRCS = $(OBJS:.o=.cpp) diff --git a/cpp/src/Slice/PythonUtil.cpp b/cpp/src/Slice/PythonUtil.cpp new file mode 100644 index 00000000000..b121cd6d928 --- /dev/null +++ b/cpp/src/Slice/PythonUtil.cpp @@ -0,0 +1,1219 @@ +// ********************************************************************** +// +// Copyright (c) 2003-2004 ZeroC, Inc. All rights reserved. +// +// This copy of Ice is licensed to you under the terms described in the +// ICE_LICENSE file included in this distribution. +// +// ********************************************************************** + +#include <Slice/PythonUtil.h> +#include <IceUtil/Functional.h> + +using namespace std; +using namespace Slice; +using namespace IceUtil; + +namespace Slice +{ +namespace Python +{ + +// +// ModuleVisitor finds all of the Slice modules whose include level is greater +// than 0 and emits a statement of the following form: +// +// _M_Foo = Ice.openModule('Foo') +// +// This statement allows the code generated for this translation unit to refer +// to types residing in those included modules. +// +class ModuleVisitor : public ParserVisitor +{ +public: + + ModuleVisitor(Output&); + + virtual bool visitModuleStart(const ModulePtr&); + +private: + + Output& _out; +}; + +// +// CodeVisitor generates the Python mapping for a translation unit. +// +class CodeVisitor : public ParserVisitor +{ +public: + + CodeVisitor(IceUtil::Output&); + + virtual bool visitModuleStart(const ModulePtr&); + virtual void visitModuleEnd(const ModulePtr&); + virtual void visitClassDecl(const ClassDeclPtr&); + virtual bool visitClassDefStart(const ClassDefPtr&); + virtual bool visitExceptionStart(const ExceptionPtr&); + virtual bool visitStructStart(const StructPtr&); + virtual void visitSequence(const SequencePtr&); + virtual void visitDictionary(const DictionaryPtr&); + virtual void visitEnum(const EnumPtr&); + virtual void visitConst(const ConstPtr&); + +protected: + + // + // Return a Python symbol for the given parser element. + // + string getSymbol(const ContainedPtr&); + + // + // Emit Python code to assign the given symbol in the current module. + // + void registerName(const string&); + + // + // Emit the tuple for a Slice type. + // + void writeType(const TypePtr&); + + Output& _out; + list<string> _moduleStack; +}; + +} +} + +static string +lookupKwd(const string& name) +{ + // + // Keyword list. *Must* be kept in alphabetical order. + // + static const string keywordList[] = + { + "and", "assert", "break", "class", "continue", "def", "del", "elif", "else", "except", "exec", + "finally", "for", "from", "global", "if", "import", "in", "is", "lambda", "not", "or", "pass", + "print", "raise", "return", "try", "while", "yield" + }; + bool found = binary_search(&keywordList[0], + &keywordList[sizeof(keywordList) / sizeof(*keywordList)], + name); + return found ? "_" + name : name; +} + +// +// Split a scoped name into its components and return the components as a list of (unscoped) identifiers. +// +static vector<string> +splitScopedName(const string& scoped) +{ + assert(scoped[0] == ':'); + vector<string> ids; + string::size_type next = 0; + string::size_type pos; + while((pos = scoped.find("::", next)) != string::npos) + { + pos += 2; + if(pos != scoped.size()) + { + string::size_type endpos = scoped.find("::", pos); + if(endpos != string::npos) + { + ids.push_back(scoped.substr(pos, endpos - pos)); + } + } + next = pos; + } + if(next != scoped.size()) + { + ids.push_back(scoped.substr(next)); + } + else + { + ids.push_back(""); + } + + return ids; +} + +// +// ModuleVisitor implementation. +// +Slice::Python::ModuleVisitor::ModuleVisitor(Output& out) : + _out(out) +{ +} + +bool +Slice::Python::ModuleVisitor::visitModuleStart(const ModulePtr& p) +{ + if(p->includeLevel() > 0) + { + string name = scopedToName(p->scoped()); + _out << sp << nl << "# Included module " << name; + _out << nl << "_M_" << name << " = Ice.openModule('" << name << "')"; + } + + return true; +} + +// +// CodeVisitor implementation. +// +Slice::Python::CodeVisitor::CodeVisitor(Output& out) : + _out(out) +{ +} + +bool +Slice::Python::CodeVisitor::visitModuleStart(const ModulePtr& p) +{ + // + // As each module is opened, we emit the statement + // + // __name__ = 'Foo' + // + // This renames the current module to 'Foo' so that subsequent + // type definitions have the proper fully-qualified name. + // + // We also emit the statement + // + // _M_Foo = Ice.openModule('Foo') + // + // This allows us to create types in the module Foo. + // + string name = scopedToName(p->scoped()); + _out << sp << nl << "# Start of module " << name; + _out << nl << "__name__ = '" << name << "'"; + _out << nl << "_M_" << name << " = Ice.openModule('" << name << "')"; + _moduleStack.push_front(name); + return true; +} + +void +Slice::Python::CodeVisitor::visitModuleEnd(const ModulePtr& p) +{ + assert(!_moduleStack.empty()); + _out << sp << nl << "# End of module " << _moduleStack.front(); + _moduleStack.pop_front(); + + if(!_moduleStack.empty()) + { + _out << sp << nl << "__name__ = '" << _moduleStack.front() << "'"; + } +} + +void +Slice::Python::CodeVisitor::visitClassDecl(const ClassDeclPtr& p) +{ + if(!p->isLocal()) + { + // + // Emit forward declarations. + // + string scoped = p->scoped(); + _out << sp << nl << "IceImpl.addClass('" << scoped << "')"; + _out << nl << "IceImpl.addProxy('" << scoped << "')"; + } +} + +bool +Slice::Python::CodeVisitor::visitClassDefStart(const ClassDefPtr& p) +{ + string scoped = p->scoped(); + string fixedScoped = scopedToName(scoped); + string fixedName = fixIdent(p->name()); + ClassList bases = p->bases(); + OperationList ops = p->operations(); + OperationList::iterator oli; + + if(!p->isLocal()) + { + // + // Define the proxy class. + // + _out << sp << nl << "_M_" << fixedScoped << "Prx = Ice.createTempClass()"; + _out << nl << "class " << fixedName << "Prx("; + if(bases.empty()) + { + _out << "IceImpl.ObjectPrx"; + } + else + { + ClassList::const_iterator q = bases.begin(); + while(q != bases.end()) + { + _out << getSymbol(*q) << "Prx"; + if(++q != bases.end()) + { + _out << ", "; + } + } + } + _out << "):"; + _out.inc(); + + for(oli = ops.begin(); oli != ops.end(); ++oli) + { + string fixedOpName = fixIdent((*oli)->name()); + TypePtr ret = (*oli)->returnType(); + ParamDeclList paramList = (*oli)->parameters(); + string inParams; + + for(ParamDeclList::const_iterator q = paramList.begin(); q != paramList.end(); ++q) + { + if(!(*q)->isOutParam()) + { + if(!inParams.empty()) + { + inParams.append(", "); + } + inParams.append(fixIdent((*q)->name())); + } + } + + _out << sp << nl << "def " << fixedOpName << "(self"; + if(!inParams.empty()) + { + _out << ", " << inParams; + } + _out << ", ctx=None):"; + _out.inc(); + _out << nl << "return self.ice_operation('" << (*oli)->name() << "', '" << scoped << "', (" << inParams; + if(!inParams.empty()) + { + _out << ", "; + } + _out << "ctx"; + if(inParams.empty()) + { + _out << ','; + } + _out << "))"; + _out.dec(); + } + + _out << sp << nl << "def checkedCast(proxy, facet=''):"; + _out.inc(); + _out << nl << "return _M_" << fixedScoped << "Prx.ice_checkedCast(proxy, '" << scoped << "', facet)"; + _out.dec(); + _out << nl << "checkedCast = staticmethod(checkedCast)"; + + _out << sp << nl << "def uncheckedCast(proxy, facet=''):"; + _out.inc(); + _out << nl << "return _M_" << fixedScoped << "Prx.ice_uncheckedCast(proxy, facet)"; + _out.dec(); + _out << nl << "uncheckedCast = staticmethod(uncheckedCast)"; + + _out.dec(); + + _out << sp << nl << "IceImpl.defineProxy('" << scoped << "', " << fixedName << "Prx)"; + + registerName(fixedName + "Prx"); + } + + // + // Define the class. + // + _out << sp << nl << "_M_" << fixedScoped << " = Ice.createTempClass()"; + _out << nl << "class " << fixedName << '('; + string baseScoped; + if(bases.empty()) + { + if(p->isLocal()) + { + _out << "Ice.LocalObject"; + } + else + { + _out << "Ice.Object"; + } + } + else + { + for(ClassList::const_iterator q = bases.begin(); q != bases.end(); ++q) + { + if(q != bases.begin()) + { + _out << ", "; + } + _out << getSymbol(*q); + } + if(!bases.front()->isInterface()) + { + baseScoped = bases.front()->scoped(); + } + } + _out << "):"; + + _out.inc(); + if(!p->isLocal()) + { + // + // ice_ids + // + ClassList allBases = p->allBases(); + StringList ids; +#if defined(__IBMCPP__) && defined(NDEBUG) +// +// VisualAge C++ 6.0 does not see that ClassDef is a Contained, +// when inlining is on. The code below issues a warning: better +// than an error! +// + transform(allBases.begin(), allBases.end(), back_inserter(ids), + IceUtil::constMemFun<string,ClassDef>(&Contained::scoped)); +#else + transform(allBases.begin(), allBases.end(), back_inserter(ids), IceUtil::constMemFun(&Contained::scoped)); +#endif + StringList other; + other.push_back(scoped); + other.push_back("::Ice::Object"); + other.sort(); + ids.merge(other); + ids.unique(); + _out << sp << nl << "def ice_ids(self, current=None):"; + _out.inc(); + _out << nl << "return ("; + for(StringList::iterator q = ids.begin(); q != ids.end(); ++q) + { + if(q != ids.begin()) + { + _out << ", "; + } + _out << "'" << *q << "'"; + } + _out << ')'; + _out.dec(); + + // + // ice_id + // + _out << sp << nl << "def ice_id(self, current=None):"; + _out.inc(); + _out << nl << "return '" << scoped << "'"; + _out.dec(); + } + + if(p->isLocal() && ops.empty()) + { + _out << nl << "pass"; + } + else + { + // + // Emit a placeholder for each operation. + // + for(oli = ops.begin(); oli != ops.end(); ++oli) + { + string fixedOpName = fixIdent((*oli)->name()); + _out << sp << nl << "def " << fixedOpName << "(self"; + + ParamDeclList params = (*oli)->parameters(); + + for(ParamDeclList::iterator pli = params.begin(); pli != params.end(); ++pli) + { + if(!(*pli)->isOutParam()) + { + _out << ", " << fixIdent((*pli)->name()); + } + } + if(!p->isLocal()) + { + _out << ", current=None"; + } + _out << "):"; + _out.inc(); + _out << nl << "raise RuntimeError(\"operation `" << fixedOpName << "' not implemented\")"; + _out.dec(); + } + } + _out.dec(); + + // + // Emit the type information for a non-local class. + // + if(!p->isLocal()) + { + DataMemberList members = p->dataMembers(); + _out << sp << nl << "IceImpl.defineClass('" << scoped << "', " << fixedName << ", "; + _out << (p->isInterface() ? "True" : "False") << ", '" << baseScoped << "', ("; + // + // InterfaceIds + // + int interfaceCount = 0; + for(ClassList::const_iterator q = bases.begin(); q != bases.end(); ++q) + { + if((*q)->isInterface()) + { + if(interfaceCount > 0) + { + _out << ", "; + } + _out << "'" << (*q)->scoped() << "'"; + ++interfaceCount; + } + } + if(interfaceCount == 1) + { + _out << ','; + } + // + // Members + // + // Data members are represented as a tuple: + // + // ('MemberName', MemberType) + // + // where MemberType is either a primitive type constant (T_INT, etc.) or the id of a constructed type. + // + _out << "), ("; + if(members.size() > 1) + { + _out.inc(); + _out << nl; + } + for(DataMemberList::iterator r = members.begin(); r != members.end(); ++r) + { + if(r != members.begin()) + { + _out << ',' << nl; + } + _out << "('" << fixIdent((*r)->name()) << "', "; + writeType((*r)->type()); + _out << ')'; + } + if(members.size() == 1) + { + _out << ','; + } + else if(members.size() > 1) + { + _out.dec(); + _out << nl; + } + _out << "), {"; + // + // OperationDict + // + // Each operation in OperationDict is described as follows: + // + // 'opName': (Mode, (InParams), (OutParams), ReturnType, (Exceptions)) + // + // where InParams and OutParams are tuples of type descriptions, and Exceptions + // is a tuple of Python exception types. + // + _out.inc(); + for(OperationList::iterator s = ops.begin(); s != ops.end(); ++s) + { + ParamDeclList params = (*s)->parameters(); + ParamDeclList::iterator t; + int count; + + if(s != ops.begin()) + { + _out << ','; + } + + _out << nl << "'" << (*s)->name() << "': ("; + switch((*s)->mode()) + { + case Operation::Normal: + _out << "IceImpl.OP_NORMAL"; + break; + case Operation::Nonmutating: + _out << "IceImpl.OP_NONMUTATING"; + break; + case Operation::Idempotent: + _out << "IceImpl.OP_IDEMPOTENT"; + break; + } + _out << ", ("; + for(t = params.begin(), count = 0; t != params.end(); ++t) + { + if(!(*t)->isOutParam()) + { + if(count > 0) + { + _out << ", "; + } + writeType((*t)->type()); + ++count; + } + } + if(count == 1) + { + _out << ','; + } + _out << "), ("; + for(t = params.begin(), count = 0; t != params.end(); ++t) + { + if((*t)->isOutParam()) + { + if(count > 0) + { + _out << ", "; + } + writeType((*t)->type()); + ++count; + } + } + if(count == 1) + { + _out << ','; + } + _out << "), "; + TypePtr returnType = (*s)->returnType(); + if(returnType) + { + writeType(returnType); + } + else + { + _out << "None"; + } + _out << ", ("; + ExceptionList exceptions = (*s)->throws(); + for(ExceptionList::iterator u = exceptions.begin(); u != exceptions.end(); ++u) + { + if(u != exceptions.begin()) + { + _out << ", "; + } + _out << "'" << (*u)->scoped() << "'"; + } + if(exceptions.size() == 1) + { + _out << ','; + } + _out << "))"; + } + _out.dec(); + if(!ops.empty()) + { + _out << nl; + } + _out << "})"; + } + + registerName(fixedName); + + return false; +} + +bool +Slice::Python::CodeVisitor::visitExceptionStart(const ExceptionPtr& p) +{ + string scoped = p->scoped(); + string fixedScoped = scopedToName(scoped); + string fixedName = fixIdent(p->name()); + _out << sp << nl << "_M_" << fixedScoped << " = Ice.createTempClass()"; + _out << nl << "class " << fixedName << '('; + ExceptionPtr base = p->base(); + string baseScoped; + if(base) + { + baseScoped = base->scoped(); + _out << getSymbol(base); + } + else if(p->isLocal()) + { + _out << "Ice.LocalException"; + } + else + { + _out << "Ice.UserException"; + } + _out << "):"; + _out.inc(); + _out << nl << "def ice_id(self):"; + _out.inc(); + _out << nl << "return '" << scoped << "'"; + _out.dec(); + _out.dec(); + + // + // Emit the type information for a non-local exception. + // + if(!p->isLocal()) + { + _out << sp << nl << "IceImpl.addException('" << scoped << "', " << fixedName << ", '" << baseScoped << "', ("; + DataMemberList members = p->dataMembers(); + if(members.size() > 1) + { + _out.inc(); + _out << nl; + } + // + // Data members are represented as a tuple: + // + // ('MemberName', MemberType) + // + // where MemberType is either a primitive type constant (T_INT, etc.) or the id of a constructed type. + // + for(DataMemberList::iterator q = members.begin(); q != members.end(); ++q) + { + if(q != members.begin()) + { + _out << ',' << nl; + } + _out << "(\"" << fixIdent((*q)->name()) << "\", "; + writeType((*q)->type()); + _out << ')'; + } + if(members.size() == 1) + { + _out << ','; + } + else if(members.size() > 1) + { + _out.dec(); + _out << nl; + } + _out << "), " << (p->usesClasses() ? "True" : "False") << ")"; + } + + registerName(fixedName); + + return false; +} + +bool +Slice::Python::CodeVisitor::visitStructStart(const StructPtr& p) +{ + string scoped = p->scoped(); + string fixedScoped = scopedToName(scoped); + string fixedName = fixIdent(p->name()); + _out << sp << nl << "_M_" << fixedScoped << " = Ice.createTempClass()"; + _out << nl << "class " << fixedName << "(object):"; + _out.inc(); + _out << nl << "pass"; + _out.dec(); + + // + // Emit the type information for a non-local struct. + // + if(!p->isLocal()) + { + _out << sp << nl << "IceImpl.addStruct('" << scoped << "', " << fixedName << ", ("; + // + // Data members are represented as a tuple: + // + // ('MemberName', MemberType) + // + // where MemberType is either a primitive type constant (T_INT, etc.) or the id of a constructed type. + // + DataMemberList members = p->dataMembers(); + if(members.size() > 1) + { + _out.inc(); + _out << nl; + } + for(DataMemberList::iterator q = members.begin(); q != members.end(); ++q) + { + if(q != members.begin()) + { + _out << ',' << nl; + } + _out << "(\"" << fixIdent((*q)->name()) << "\", "; + writeType((*q)->type()); + _out << ')'; + } + if(members.size() == 1) + { + _out << ','; + } + else if(members.size() > 1) + { + _out.dec(); + _out << nl; + } + _out << "))"; + } + + registerName(fixedName); + + return false; +} + +void +Slice::Python::CodeVisitor::visitSequence(const SequencePtr& p) +{ + // + // Emit the type information for a non-local sequence. + // + if(!p->isLocal()) + { + _out << sp << nl << "IceImpl.addSequence('" << p->scoped() << "', "; + writeType(p->type()); + _out << ")"; + } +} + +void +Slice::Python::CodeVisitor::visitDictionary(const DictionaryPtr& p) +{ + // + // Emit the type information for a non-local dictionary. + // + if(!p->isLocal()) + { + _out << sp << nl << "IceImpl.addDictionary('" << p->scoped() << "', "; + writeType(p->keyType()); + _out << ", "; + writeType(p->valueType()); + _out << ")"; + } +} + +void +Slice::Python::CodeVisitor::visitEnum(const EnumPtr& p) +{ + string scoped = p->scoped(); + string fixedScoped = scopedToName(scoped); + string fixedName = fixIdent(p->name()); + EnumeratorList enums = p->getEnumerators(); + EnumeratorList::iterator q; + int i; + + _out << sp << nl << "_M_" << fixedScoped << " = Ice.createTempClass()"; + _out << nl << "class " << fixedName << "(object):"; + _out.inc(); + _out << sp << nl << "def __init__(self, val):"; + _out.inc(); + _out << nl << "self.value = val"; + _out.dec(); + _out << sp << nl << "def __str__(self):"; + _out.inc(); + for(q = enums.begin(), i = 0; q != enums.end(); ++q, ++i) + { + _out << nl; + if(q == enums.begin()) + { + _out << "if"; + } + else + { + _out << "elif"; + } + ostringstream idx; + idx << i; + _out << " self.value == " << idx.str() << ':'; + _out.inc(); + _out << nl << "return '" << (*q)->name() << "'"; + _out.dec(); + } + _out << nl << "return None"; + _out.dec(); + _out << sp << nl << "__repr__ = __str__"; + _out << sp << nl << "def __hash__(self):"; + _out.inc(); + _out << nl << "return self.value"; + _out.dec(); + _out << sp << nl << "def __cmp__(self, other):"; + _out.inc(); + _out << nl << "return cmp(self.value, other.value)"; + _out.dec(); + _out.dec(); + + _out << sp; + for(q = enums.begin(), i = 0; q != enums.end(); ++q, ++i) + { + string fixedEnum = fixIdent((*q)->name()); + ostringstream idx; + idx << i; + _out << nl << fixedName << '.' << fixedEnum << " = " << fixedName << '(' << idx.str() << ')'; + } + + // + // Emit the type information for a non-local enum. + // + if(!p->isLocal()) + { + _out << sp << nl << "IceImpl.addEnum('" << scoped << "', " << fixedName << ", ("; + for(EnumeratorList::iterator q = enums.begin(); q != enums.end(); ++q) + { + if(q != enums.begin()) + { + _out << ", "; + } + string fixedEnum = fixIdent((*q)->name()); + _out << fixedName << '.' << fixedEnum; + } + if(enums.size() == 1) + { + _out << ','; + } + _out << "))"; + } + + registerName(fixedName); +} + +void +Slice::Python::CodeVisitor::visitConst(const ConstPtr& p) +{ + Slice::TypePtr type = p->type(); + string value = p->value(); + string name = fixIdent(p->name()); + + _out << sp << nl << "_M_" << scopedToName(p->scoped()) << " = "; + + Slice::BuiltinPtr b = Slice::BuiltinPtr::dynamicCast(type); + Slice::EnumPtr en = Slice::EnumPtr::dynamicCast(type); + if(b) + { + switch(b->kind()) + { + case Slice::Builtin::KindBool: + { + _out << (value == "true" ? "True" : "False"); + break; + } + case Slice::Builtin::KindByte: + case Slice::Builtin::KindShort: + case Slice::Builtin::KindInt: + case Slice::Builtin::KindFloat: + case Slice::Builtin::KindDouble: + { + _out << value; + break; + } + case Slice::Builtin::KindLong: + { + IceUtil::Int64 l; + string::size_type pos; + IceUtil::stringToInt64(value, l, pos); + // + // The platform's 'long' type may not be 64 bits, so we store 64-bit + // values as a string. + // + if(sizeof(IceUtil::Int64) > sizeof(long) && (l < LONG_MIN || l > LONG_MAX)) + { + _out << "'" << value << "'"; + } + else + { + _out << value; + } + break; + } + + case Slice::Builtin::KindString: + { + // + // Expand strings into the basic source character set. We can't use isalpha() and the like + // here because they are sensitive to the current locale. + // + static const string basicSourceChars = "abcdefghijklmnopqrstuvwxyz" + "ABCDEFGHIJKLMNOPQRSTUVWXYZ" + "0123456789" + "_{}[]#()<>%:;,?*+=/^&|~!=, '"; + static const set<char> charSet(basicSourceChars.begin(), basicSourceChars.end()); + + _out << "\""; // Opening " + + ios_base::fmtflags originalFlags = _out.flags(); // Save stream state + streamsize originalWidth = _out.width(); + ostream::char_type originalFill = _out.fill(); + + for(string::const_iterator c = value.begin(); c != value.end(); ++c) + { + switch(*c) + { + case '"': + { + _out << "\\\""; + break; + } + case '\\': + { + _out << "\\\\"; + break; + } + case '\r': + { + _out << "\\r"; + break; + } + case '\n': + { + _out << "\\n"; + break; + } + case '\t': + { + _out << "\\t"; + break; + } + case '\b': + { + _out << "\\b"; + break; + } + case '\f': + { + _out << "\\f"; + break; + } + default: + { + if(charSet.find(*c) == charSet.end()) + { + unsigned char uc = *c; // Char may be signed, so make it positive. + _out << "\\"; // Print as octal if not in basic source character set. + _out.flags(ios_base::oct); + _out.width(3); + _out.fill('0'); + _out << static_cast<unsigned>(uc); + } + else + { + _out << *c; // Print normally if in basic source character set. + } + break; + } + } + } + + _out.fill(originalFill); // Restore stream state + _out.width(originalWidth); + _out.flags(originalFlags); + + _out << "\""; // Closing " + break; + } + + case Slice::Builtin::KindObject: + case Slice::Builtin::KindObjectProxy: + case Slice::Builtin::KindLocalObject: + assert(false); + } + } + else if(en) + { + string enumName = getSymbol(en); + string::size_type colon = value.rfind(':'); + string enumerator; + if(colon != string::npos) + { + enumerator = fixIdent(value.substr(colon + 1)); + } + else + { + enumerator = fixIdent(value); + } + _out << enumName << '.' << enumerator; + } + else + { + assert(false); // Unknown const type. + } +} + +string +Slice::Python::CodeVisitor::getSymbol(const ContainedPtr& p) +{ + // + // An explicit reference to another type must always be prefixed with "_M_". + // + return "_M_" + scopedToName(p->scoped()); +} + +void +Slice::Python::CodeVisitor::registerName(const string& name) +{ + assert(!_moduleStack.empty()); + _out << sp << nl << "_M_" << _moduleStack.front() << '.' << name << " = " << name; + _out << nl << "del " << name; +} + +void +Slice::Python::CodeVisitor::writeType(const TypePtr& p) +{ + BuiltinPtr builtin = BuiltinPtr::dynamicCast(p); + if(builtin) + { + switch(builtin->kind()) + { + case Builtin::KindBool: + { + _out << "IceImpl.T_BOOL"; + break; + } + case Builtin::KindByte: + { + _out << "IceImpl.T_BYTE"; + break; + } + case Builtin::KindShort: + { + _out << "IceImpl.T_SHORT"; + break; + } + case Builtin::KindInt: + { + _out << "IceImpl.T_INT"; + break; + } + case Builtin::KindLong: + { + _out << "IceImpl.T_LONG"; + break; + } + case Builtin::KindFloat: + { + _out << "IceImpl.T_FLOAT"; + break; + } + case Builtin::KindDouble: + { + _out << "IceImpl.T_DOUBLE"; + break; + } + case Builtin::KindString: + { + _out << "IceImpl.T_STRING"; + break; + } + case Builtin::KindObject: + { + _out << "IceImpl.T_OBJECT"; + break; + } + case Builtin::KindObjectProxy: + { + _out << "IceImpl.T_OBJECT_PROXY"; + break; + } + case Builtin::KindLocalObject: + { + assert(false); + break; + } + } + return; + } + + ProxyPtr prx = ProxyPtr::dynamicCast(p); + if(prx) + { + _out << "'" << prx->_class()->scoped() << "Prx'"; + return; + } + + ContainedPtr cont = ContainedPtr::dynamicCast(p); + assert(cont); + _out << "'" << cont->scoped() << "'"; +} + +void +Slice::Python::generate(const UnitPtr& unit, Output& out) +{ + out << nl << "import Ice, IceImpl"; + + ModuleVisitor moduleVisitor(out); + unit->visit(&moduleVisitor, true); + + CodeVisitor codeVisitor(out); + unit->visit(&codeVisitor, false); + + out << nl; // Trailing newline. +} + +bool +Slice::Python::splitString(const string& str, vector<string>& args) +{ + string delim = " \t\n\r"; + string::size_type beg; + string::size_type end = 0; + while(true) + { + beg = str.find_first_not_of(delim, end); + if(beg == string::npos) + { + break; + } + + // + // Check for quoted argument. + // + char ch = str[beg]; + if(ch == '"' || ch == '\'') + { + beg++; + end = str.find(ch, beg); + if(end == string::npos) + { + return false; + } + args.push_back(str.substr(beg, end - beg)); + end++; // Skip end quote. + } + else + { + end = str.find_first_of(delim + "'\"", beg); + if(end == string::npos) + { + end = str.length(); + } + args.push_back(str.substr(beg, end - beg)); + } + } + + return true; +} + +string +Slice::Python::scopedToName(const string& scoped) +{ + string result = fixIdent(scoped); + if(result.find("::") == 0) + { + result.erase(0, 2); + } + + string::size_type pos; + while((pos = result.find("::")) != string::npos) + { + result.replace(pos, 2, "."); + } + + return result; +} + +string +Slice::Python::fixIdent(const string& ident) +{ + if(ident[0] != ':') + { + return lookupKwd(ident); + } + vector<string> ids = splitScopedName(ident); + transform(ids.begin(), ids.end(), ids.begin(), ptr_fun(lookupKwd)); + stringstream result; + for(vector<string>::const_iterator i = ids.begin(); i != ids.end(); ++i) + { + result << "::" + *i; + } + return result.str(); +} + +void +Slice::Python::printHeader(IceUtil::Output& out) +{ + static const char* header = +"# **********************************************************************\n" +"#\n" +"# Copyright (c) 2003-2004 ZeroC, Inc. All rights reserved.\n" +"#\n" +"# This copy of Ice is licensed to you under the terms described in the\n" +"# ICE_LICENSE file included in this distribution.\n" +"#\n" +"# **********************************************************************\n" + ; + + out << header; + out << "\n# Ice version " << ICE_STRING_VERSION; +} diff --git a/cpp/src/slice2py/.depend b/cpp/src/slice2py/.depend new file mode 100644 index 00000000000..990139ff904 --- /dev/null +++ b/cpp/src/slice2py/.depend @@ -0,0 +1 @@ +Main.o: Main.cpp ../../include/Slice/Preprocessor.h ../../include/IceUtil/Config.h ../../include/Slice/PythonUtil.h ../../include/Slice/Parser.h ../../include/IceUtil/Shared.h ../../include/IceUtil/Handle.h ../../include/IceUtil/Exception.h ../../include/IceUtil/InputUtil.h ../../include/IceUtil/OutputUtil.h diff --git a/cpp/src/slice2py/Main.cpp b/cpp/src/slice2py/Main.cpp new file mode 100644 index 00000000000..028f67b5e0f --- /dev/null +++ b/cpp/src/slice2py/Main.cpp @@ -0,0 +1,479 @@ +// ********************************************************************** +// +// Copyright (c) 2003-2004 ZeroC, Inc. All rights reserved. +// +// This copy of Ice is licensed to you under the terms described in the +// ICE_LICENSE file included in this distribution. +// +// ********************************************************************** + +#include <Slice/Preprocessor.h> +#include <Slice/PythonUtil.h> + +#include <fstream> + +#include <sys/types.h> +#include <sys/stat.h> + +#ifdef _WIN32 +#include <direct.h> +#endif + +#ifndef _WIN32 +#include <unistd.h> +#endif + +using namespace std; +using namespace Slice; +using namespace Slice::Python; + +// +// For each Slice file Foo.ice we generate Foo_ice.py containing the Python +// mappings. Furthermore, for each Slice module M in Foo.ice, we create a +// Python package of the same name. This package is simply a subdirectory +// containing the special file "__init__.py" that is executed when a Python +// script executes the statement "import M". +// +// Inside __init__.py we add an import statement for Foo_ice, causing +// Foo_ice to be imported implicitly when M is imported. +// +// Of course, another Slice file Bar.ice may contain definitions for the +// same Slice module M, in which case the __init__.py file for M is modified +// to contain an additional import statement for Bar_ice. Therefore a +// Python script executing "import M" implicitly imports the definitions +// from both Foo_ice and Bar_ice. +// +// The __init__.py file also contains import statements for submodules, +// so that importing the top-level module automatically imports all of +// its submodules. +// +// The PackageVisitor class creates the directory hierarchy to mirror the +// Slice module hierarchy, and updates the __init__.py files as necessary. +// +class PackageVisitor : public ParserVisitor +{ +public: + + PackageVisitor(const string&, const string&, const string&); + + virtual bool visitModuleStart(const ModulePtr&); + virtual void visitModuleEnd(const ModulePtr&); + +private: + + enum ReadState { PreModules, InModules, InSubmodules }; + + static const char* _moduleTag; + static const char* _submoduleTag; + + bool readInit(const string&, StringList&, StringList&); + bool writeInit(const string&, const StringList&, const StringList&); + + string _name; + string _module; + StringList _pathStack; +}; + +const char* PackageVisitor::_moduleTag = "# Modules:"; +const char* PackageVisitor::_submoduleTag = "# Submodules:"; + +PackageVisitor::PackageVisitor(const string& name, const string& module, const string& dir) : + _name(name), _module(module) +{ + if(dir.empty()) + { + _pathStack.push_front("."); + } + else + { + _pathStack.push_front(dir); + } +} + +bool +PackageVisitor::visitModuleStart(const ModulePtr& p) +{ + assert(!_pathStack.empty()); + string name = fixIdent(p->name()); + string path = _pathStack.front() + "/" + name; + string parentPath = _pathStack.front(); + _pathStack.push_front(path); + + struct stat st; + int result; + result = stat(path.c_str(), &st); + if(result != 0) + { +#ifdef _WIN32 + result = _mkdir(path.c_str()); +#else + result = mkdir(path.c_str(), S_IRWXU | S_IRWXG | S_IRWXO); +#endif + + if(result != 0) + { + cerr << _name << ": unable to create `" << path << "': " << strerror(errno) << endl; + return false; + } + } + + // + // If necessary, add this file to the set of imported modules in __init__.py. + // + StringList modules, submodules; + if(readInit(path, modules, submodules)) + { + StringList::iterator p; + p = find(modules.begin(), modules.end(), _module); + if(p == modules.end()) + { + modules.push_back(_module); + writeInit(path, modules, submodules); + } + } + + // + // If this is a submodule, then modify the parent's __init__.py to import us. + // + ModulePtr mod = ModulePtr::dynamicCast(p->container()); + if(mod) + { + modules.clear(); + submodules.clear(); + if(readInit(parentPath, modules, submodules)) + { + StringList::iterator p; + p = find(submodules.begin(), submodules.end(), name); + if(p == submodules.end()) + { + submodules.push_back(name); + writeInit(parentPath, modules, submodules); + } + } + } + + return true; +} + +void +PackageVisitor::visitModuleEnd(const ModulePtr& p) +{ + assert(!_pathStack.empty()); + _pathStack.pop_front(); +} + +bool +PackageVisitor::readInit(const string& dir, StringList& modules, StringList& submodules) +{ + string initPath = dir + "/__init__.py"; + + struct stat st; + if(stat(initPath.c_str(), &st) == 0) + { + ifstream in(initPath.c_str()); + if(!in) + { + cerr << _name << ": unable to open `" << initPath << "': " << strerror(errno) << endl; + return false; + } + + ReadState state = PreModules; + char line[1024]; + while(in.getline(line, 1024)) + { + string s = line; + if(s.find(_moduleTag) == 0) + { + if(state != PreModules) + { + break; + } + state = InModules; + } + else if(s.find(_submoduleTag) == 0) + { + if(state != InModules) + { + break; + } + state = InSubmodules; + } + else if(s.find("import") == 0) + { + if(state == PreModules) + { + break; + } + + if(s.size() < 8) + { + cerr << _name << ": invalid line `" << s << "' in `" << initPath << "'" << endl; + return false; + } + + string name = s.substr(7); + if(state == InModules) + { + modules.push_back(name); + } + else + { + submodules.push_back(name); + } + } + } + + if(state != InSubmodules) + { + cerr << _name << ": invalid format in `" << initPath << "'" << endl; + return false; + } + } + + return true; +} + +bool +PackageVisitor::writeInit(const string& dir, const StringList& modules, const StringList& submodules) +{ + string initPath = dir + "/__init__.py"; + + ofstream os(initPath.c_str()); + if(!os) + { + return false; + } + + StringList::const_iterator p; + + os << "# Generated by slice2py - DO NOT EDIT!" << endl + << "#" << endl + << _moduleTag << endl; + for(p = modules.begin(); p != modules.end(); ++p) + { + os << "import " << *p << endl; + } + + os << endl; + os << _submoduleTag << endl; + for(p = submodules.begin(); p != submodules.end(); ++p) + { + os << "import " << *p << endl; + } + + return true; +} + +void +usage(const char* n) +{ + cerr << "Usage: " << n << " [options] slice-files...\n"; + cerr << + "Options:\n" + "-h, --help Show this message.\n" + "-v, --version Display the Ice version.\n" + "--header-ext EXT Use EXT instead of the default `h' extension.\n" + "--source-ext EXT Use EXT instead of the default `cpp' extension.\n" + "-DNAME Define NAME as 1.\n" + "-DNAME=DEF Define NAME as DEF.\n" + "-UNAME Remove any definition for NAME.\n" + "-IDIR Put DIR in the include file search path.\n" + "--output-dir DIR Create files in the directory DIR.\n" + "-d, --debug Print debug messages.\n" + "--ice Permit `Ice' prefix (for building Ice source code only)\n" + "--all Generate code for Slice definitions in included files.\n" + ; + // Note: --case-sensitive is intentionally not shown here! +} + +int +main(int argc, char* argv[]) +{ + string cppArgs; + string output; + bool debug = false; + bool ice = false; + bool caseSensitive = false; + bool all = false; + + int idx = 1; + while(idx < argc) + { + if(strncmp(argv[idx], "-I", 2) == 0) + { + cppArgs += ' '; + cppArgs += argv[idx]; + + for(int i = idx ; i + 1 < argc ; ++i) + { + argv[i] = argv[i + 1]; + } + --argc; + } + else if(strncmp(argv[idx], "-D", 2) == 0 || strncmp(argv[idx], "-U", 2) == 0) + { + cppArgs += ' '; + cppArgs += argv[idx]; + + for(int i = idx ; i + 1 < argc ; ++i) + { + argv[i] = argv[i + 1]; + } + --argc; + } + else if(strcmp(argv[idx], "-h") == 0 || strcmp(argv[idx], "--help") == 0) + { + usage(argv[0]); + return EXIT_SUCCESS; + } + else if(strcmp(argv[idx], "-v") == 0 || strcmp(argv[idx], "--version") == 0) + { + cout << ICE_STRING_VERSION << endl; + return EXIT_SUCCESS; + } + else if(strcmp(argv[idx], "-d") == 0 || strcmp(argv[idx], "--debug") == 0) + { + debug = true; + for(int i = idx ; i + 1 < argc ; ++i) + { + argv[i] = argv[i + 1]; + } + --argc; + } + else if(strcmp(argv[idx], "--ice") == 0) + { + ice = true; + for(int i = idx ; i + 1 < argc ; ++i) + { + argv[i] = argv[i + 1]; + } + --argc; + } + else if(strcmp(argv[idx], "--case-sensitive") == 0) + { + caseSensitive = true; + for(int i = idx ; i + 1 < argc ; ++i) + { + argv[i] = argv[i + 1]; + } + --argc; + } + else if(strcmp(argv[idx], "--all") == 0) + { + all = true; + for(int i = idx ; i + 1 < argc ; ++i) + { + argv[i] = argv[i + 1]; + } + --argc; + } + else if(strcmp(argv[idx], "--output-dir") == 0) + { + if(idx + 1 >= argc) + { + cerr << argv[0] << ": argument expected for`" << argv[idx] << "'" << endl; + usage(argv[0]); + return EXIT_FAILURE; + } + + output = argv[idx + 1]; + for(int i = idx ; i + 2 < argc ; ++i) + { + argv[i] = argv[i + 2]; + } + argc -= 2; + } + else if(argv[idx][0] == '-') + { + cerr << argv[0] << ": unknown option `" << argv[idx] << "'" << endl; + usage(argv[0]); + return EXIT_FAILURE; + } + else + { + ++idx; + } + } + + if(argc < 2) + { + cerr << argv[0] << ": no input file" << endl; + usage(argv[0]); + return EXIT_FAILURE; + } + + int status = EXIT_SUCCESS; + + for(idx = 1 ; idx < argc ; ++idx) + { + Preprocessor icecpp(argv[0], argv[idx], cppArgs); + FILE* cppHandle = icecpp.preprocess(false); + + if(cppHandle == 0) + { + return EXIT_FAILURE; + } + + UnitPtr u = Unit::createUnit(false, all, ice, caseSensitive); + int parseStatus = u->parse(cppHandle, debug); + + if(!icecpp.close()) + { + u->destroy(); + return EXIT_FAILURE; + } + + if(parseStatus == EXIT_FAILURE) + { + status = EXIT_FAILURE; + } + else + { + string base = icecpp.getBaseName(); + string::size_type pos = base.rfind('/'); + if(pos != string::npos) + { + base.erase(0, pos + 1); + } + + // + // Append the suffix "_ice" to the filename in order to avoid any conflicts + // with Slice module names. For example, if the file Test.ice defines a + // Slice module named "Test", then we couldn't create a Python package named + // "Test" and also call the generated file "Test.py". + // + string file = base + "_ice.py"; + if(!output.empty()) + { + file = output + '/' + file; + } + + IceUtil::Output out; + out.open(file.c_str()); + if(!out) + { + cerr << argv[0] << ": can't open `" << file << "' for writing" << endl; + u->destroy(); + return EXIT_FAILURE; + } + + printHeader(out); + out << "\n# Generated from file `" << base << ".ice'\n"; + + // + // Generate the Python mapping. + // + generate(u, out); + + // + // Create or update the Python package hierarchy. + // + PackageVisitor visitor(argv[0], base + "_ice", output); + u->visit(&visitor, false); + } + + u->destroy(); + } + + return status; +} diff --git a/cpp/src/slice2py/Makefile b/cpp/src/slice2py/Makefile new file mode 100644 index 00000000000..c7a1b0e06a3 --- /dev/null +++ b/cpp/src/slice2py/Makefile @@ -0,0 +1,31 @@ +# ********************************************************************** +# +# Copyright (c) 2003-2004 ZeroC, Inc. All rights reserved. +# +# This copy of Ice is licensed to you under the terms described in the +# ICE_LICENSE file included in this distribution. +# +# ********************************************************************** + +top_srcdir = ../.. + +NAME = $(top_srcdir)/bin/slice2py + +TARGETS = $(NAME) + +OBJS = Main.o + +SRCS = $(OBJS:.o=.cpp) + +include $(top_srcdir)/config/Make.rules + +CPPFLAGS := -I. $(CPPFLAGS) + +$(NAME): $(OBJS) + rm -f $@ + $(CXX) $(LDFLAGS) -o $@ $(OBJS) -lSlice $(BASELIBS) + +install:: all + $(INSTALL_PROGRAM) $(NAME) $(install_bindir) + +include .depend |