You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
717 lines
20 KiB
717 lines
20 KiB
/***************************************************************************
|
|
begin : Wed Jan 22 2003
|
|
copyright : (C) 2003 by David Hugh-Jones
|
|
(C) 2004-2006 Umbrello UML Modeller Authors <uml-devel@uml.sf.net>
|
|
email : hughjonesd@yahoo.co.uk
|
|
***************************************************************************/
|
|
|
|
/***************************************************************************
|
|
* *
|
|
* This program is free software; you can redistribute it and/or modify *
|
|
* it under the terms of the GNU General Public License as published by *
|
|
* the Free Software Foundation; either version 2 of the License, or *
|
|
* (at your option) any later version. *
|
|
* *
|
|
***************************************************************************/
|
|
|
|
#include "perlwriter.h"
|
|
#include "../classifier.h"
|
|
#include "../operation.h"
|
|
#include "../umldoc.h"
|
|
#include "../association.h"
|
|
#include "../attribute.h"
|
|
#include "../uml.h"
|
|
|
|
#include <kdebug.h>
|
|
#include <tqregexp.h>
|
|
#include <tqstring.h>
|
|
#include <tqdir.h>
|
|
#include <tqdatetime.h>
|
|
#include <tqtextstream.h>
|
|
|
|
PerlWriter::PerlWriter()
|
|
{
|
|
}
|
|
|
|
PerlWriter::~PerlWriter() {}
|
|
|
|
bool PerlWriter::GetUseStatements(UMLClassifier *c, TQString &Ret,
|
|
TQString &ThisPkgName){
|
|
|
|
if(!c){
|
|
return(false);
|
|
}
|
|
|
|
UMLPackageList includes;
|
|
findObjectsRelated(c,includes);
|
|
UMLPackage *conc;
|
|
TQString AV = "@";
|
|
TQString SV = "$";
|
|
TQString HV = "%";
|
|
for(conc = includes.first(); conc ;conc = includes.next()) {
|
|
if (conc->getBaseType() == Uml::ot_Datatype)
|
|
continue;
|
|
TQString neatName = cleanName(conc->getName());
|
|
if (neatName != AV && neatName != SV && neatName != HV) {
|
|
TQString OtherPkgName = conc->getPackage(".");
|
|
OtherPkgName.replace(TQRegExp("\\."),"::");
|
|
TQString OtherName = OtherPkgName + "::" + cleanName(conc->getName());
|
|
|
|
// Only print out the use statement if the other package isn't the
|
|
// same as the one we are working on. (This happens for the
|
|
// "Singleton" design pattern.)
|
|
if(OtherName != ThisPkgName){
|
|
Ret += "use ";
|
|
Ret += OtherName;
|
|
Ret += ';';
|
|
Ret += m_endl;
|
|
}
|
|
}
|
|
}
|
|
UMLClassifierList superclasses = c->getSuperClasses();
|
|
if (superclasses.count()) {
|
|
Ret += m_endl;
|
|
Ret += "use base qw( ";
|
|
for (UMLClassifier *obj = superclasses.first();
|
|
obj; obj = superclasses.next()) {
|
|
TQString packageName = obj->getPackage(".");
|
|
packageName.replace(TQRegExp("\\."),"::");
|
|
|
|
Ret += packageName + "::" + cleanName(obj->getName()) + ' ';
|
|
}
|
|
Ret += ");" + m_endl;
|
|
}
|
|
|
|
return(true);
|
|
}
|
|
|
|
void PerlWriter::writeClass(UMLClassifier *c) {
|
|
|
|
/* if(!c) {
|
|
kDebug()<<"Cannot write class of NULL concept!" << endl;
|
|
return;
|
|
}
|
|
*/
|
|
TQString classname = cleanName(c->getName());// this is fine: cleanName is "::-clean"
|
|
TQString packageName = c->getPackage(".");
|
|
TQString fileName;
|
|
|
|
// Replace all white spaces with blanks
|
|
packageName.simplifyWhiteSpace();
|
|
|
|
// Replace all blanks with underscore
|
|
packageName.replace(TQRegExp(" "), "_");
|
|
|
|
// Replace all dots (".") with double colon scope resolution operators
|
|
// ("::")
|
|
packageName.replace(TQRegExp("\\."),"::");
|
|
|
|
// Store complete package name
|
|
TQString ThisPkgName = packageName + "::" + classname;
|
|
|
|
fileName = findFileName(c, ".pm");
|
|
// the above lower-cases my nice class names. That is bad.
|
|
// correct solution: refactor,
|
|
// split massive findFileName up, reimplement
|
|
// parts here
|
|
// actual solution: shameful ".pm" hack in codegenerator
|
|
|
|
CodeGenerationPolicy *pol = UMLApp::app()->getCommonPolicy();
|
|
TQString curDir = pol->getOutputDirectory().absPath();
|
|
if (fileName.contains("::")) {
|
|
// create new directories for each level
|
|
TQString newDir;
|
|
newDir = curDir;
|
|
TQString fragment = fileName;
|
|
TQDir* existing = new TQDir (curDir);
|
|
TQRegExp regEx("(.*)(::)");
|
|
regEx.setMinimal(true);
|
|
while (regEx.search(fragment) > -1) {
|
|
newDir = regEx.cap(1);
|
|
fragment.remove(0, (regEx.pos(2) + 2)); // get round strange minimal matching bug
|
|
existing->setPath(curDir + '/' + newDir);
|
|
if (! existing->exists()) {
|
|
existing->setPath(curDir);
|
|
if (! existing->mkdir(newDir)) {
|
|
emit codeGenerated(c, false);
|
|
return;
|
|
}
|
|
}
|
|
curDir += '/' + newDir;
|
|
}
|
|
fileName = fragment + ".pm";
|
|
}
|
|
if (fileName.isEmpty()) {
|
|
emit codeGenerated(c, false);
|
|
return;
|
|
}
|
|
TQString oldDir = pol->getOutputDirectory().absPath();
|
|
pol->setOutputDirectory(curDir);
|
|
TQFile fileperl;
|
|
if(!openFile(fileperl, fileName)) {
|
|
emit codeGenerated(c, false);
|
|
return;
|
|
}
|
|
TQTextStream perl(&fileperl);
|
|
pol->setOutputDirectory(oldDir);
|
|
|
|
//======================================================================
|
|
// Start generating the code!!
|
|
//======================================================================
|
|
|
|
// try to find a heading file (license, comments, etc)
|
|
TQString str;
|
|
bool bPackageDeclared = false;
|
|
bool bUseStmsWritten = false;
|
|
|
|
str = getHeadingFile(".pm"); // what this mean?
|
|
if(!str.isEmpty()) {
|
|
str.replace(TQRegExp("%filename%"),fileName);
|
|
str.replace(TQRegExp("%filepath%"),fileperl.name());
|
|
str.replace(TQRegExp("%year%"),TQDate::currentDate().toString("yyyy"));
|
|
str.replace(TQRegExp("%date%"),TQDate::currentDate().toString());
|
|
str.replace(TQRegExp("%time%"),TQTime::currentTime().toString());
|
|
str.replace(TQRegExp("%package-name%"),ThisPkgName);
|
|
if(str.find(TQRegExp("%PACKAGE-DECLARE%"))){
|
|
str.replace(TQRegExp("%PACKAGE-DECLARE%"),
|
|
"package " + ThisPkgName + ';'
|
|
+ m_endl + m_endl
|
|
+ "#UML_MODELER_BEGIN_PERSONAL_VARS_" + classname
|
|
+ m_endl + m_endl
|
|
+ "#UML_MODELER_END_PERSONAL_VARS_" + classname
|
|
+ m_endl
|
|
);
|
|
bPackageDeclared = true;
|
|
}
|
|
|
|
if(str.find(TQRegExp("%USE-STATEMENTS%"))){
|
|
TQString UseStms;
|
|
if(GetUseStatements(c,UseStms,ThisPkgName)){
|
|
str.replace(TQRegExp("%USE-STATEMENTS%"), UseStms);
|
|
bUseStmsWritten = true;
|
|
}
|
|
}
|
|
|
|
perl<<str<<m_endl;
|
|
}
|
|
|
|
// if the package wasn't declared above during keyword substitution,
|
|
// add it now. (At the end of the file.)
|
|
if(! bPackageDeclared){
|
|
perl << m_endl << m_endl << "package " <<ThisPkgName << ";" << m_endl
|
|
<< m_endl;
|
|
//write includes
|
|
perl << m_endl << "#UML_MODELER_BEGIN_PERSONAL_VARS_" << classname
|
|
<< m_endl ;
|
|
perl << m_endl << "#UML_MODELER_END_PERSONAL_VARS_" << classname
|
|
<< m_endl << m_endl ;
|
|
}
|
|
|
|
if(! bUseStmsWritten){
|
|
TQString UseStms;
|
|
if(GetUseStatements(c,UseStms,ThisPkgName)){
|
|
perl<<UseStms<<m_endl;
|
|
}
|
|
}
|
|
|
|
perl << m_endl;
|
|
|
|
// Do we really need these for anything???
|
|
UMLAssociationList aggregations = c->getAggregations();
|
|
UMLAssociationList compositions = c->getCompositions();
|
|
|
|
//Write class Documentation
|
|
if(forceDoc() || !c->getDoc().isEmpty()) {
|
|
perl << m_endl << "=head1";
|
|
perl << " " << classname.upper() << m_endl << m_endl;
|
|
perl << c->getDoc();
|
|
perl << m_endl << m_endl << "=cut" << m_endl << m_endl;
|
|
}
|
|
|
|
//check if class is abstract and / or has abstract methods
|
|
if(c->getAbstract())
|
|
perl << "=head1 ABSTRACT CLASS" << m_endl << m_endl << "=cut" << m_endl;
|
|
|
|
//attributes
|
|
if (! c->isInterface())
|
|
writeAttributes(c, perl); // keep for documentation's sake
|
|
|
|
//operations
|
|
writeOperations(c,perl);
|
|
|
|
perl << m_endl;
|
|
|
|
//finish file
|
|
//perl << m_endl << m_endl << "=cut" << m_endl;
|
|
perl << m_endl << m_endl << "return 1;" << m_endl;
|
|
|
|
//close files and notify we are done
|
|
fileperl.close();
|
|
emit codeGenerated(c, true);
|
|
}
|
|
|
|
/**
|
|
* returns "Perl"
|
|
*/
|
|
Uml::Programming_Language PerlWriter::getLanguage() {
|
|
return Uml::pl_Perl;
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////////////////////
|
|
// Helper Methods
|
|
|
|
void PerlWriter::writeOperations(UMLClassifier *c, TQTextStream &perl) {
|
|
|
|
//Lists to store operations sorted by scope
|
|
UMLOperationList oppub,opprot,oppriv;
|
|
|
|
oppub.setAutoDelete(false);
|
|
opprot.setAutoDelete(false);
|
|
oppriv.setAutoDelete(false);
|
|
|
|
//sort operations by scope first and see if there are abstract methods
|
|
//keep this for documentation only!
|
|
UMLOperationList opl(c->getOpList());
|
|
for(UMLOperation *op = opl.first(); op ; op = opl.next()) {
|
|
switch(op->getVisibility()) {
|
|
case Uml::Visibility::Public:
|
|
oppub.append(op);
|
|
break;
|
|
case Uml::Visibility::Protected:
|
|
opprot.append(op);
|
|
break;
|
|
case Uml::Visibility::Private:
|
|
oppriv.append(op);
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
|
|
TQString classname(cleanName(c->getName()));
|
|
|
|
//write operations to file
|
|
if(forceSections() || !oppub.isEmpty()) {
|
|
perl << m_endl << "=head1 PUBLIC METHODS" << m_endl << m_endl ;
|
|
writeOperations(classname,oppub,perl);
|
|
perl << m_endl << m_endl << "=cut" << m_endl << m_endl;
|
|
}
|
|
|
|
if(forceSections() || !opprot.isEmpty()) {
|
|
perl << m_endl << "=head1 METHODS FOR SUBCLASSING" << m_endl << m_endl ;
|
|
//perl << "=pod " << m_endl << m_endl << "=head3 " ;
|
|
writeOperations(classname,opprot,perl);
|
|
perl << m_endl << m_endl << "=cut" << m_endl << m_endl;
|
|
}
|
|
|
|
if(forceSections() || !oppriv.isEmpty()) {
|
|
perl << m_endl << "=head1 PRIVATE METHODS" << m_endl << m_endl ;
|
|
//perl << "=pod " << m_endl << m_endl << "=head3 " ;
|
|
writeOperations(classname,oppriv,perl);
|
|
perl << m_endl << m_endl << "=cut" << m_endl << m_endl;
|
|
}
|
|
|
|
// moved here for perl
|
|
if (!c->isInterface() && hasDefaultValueAttr(c)) {
|
|
UMLAttributeList atl = c->getAttributeList();
|
|
|
|
perl << m_endl;
|
|
perl << m_endl << "=head2 _init" << m_endl << m_endl << m_endl;
|
|
perl << "_init sets all " + classname + " attributes to their default values unless already set" << m_endl << m_endl << "=cut" << m_endl << m_endl;
|
|
perl << "sub _init {" << m_endl << m_indentation << "my $self = shift;" << m_endl<<m_endl;
|
|
|
|
for(UMLAttribute *at = atl.first(); at ; at = atl.next()) {
|
|
if(!at->getInitialValue().isEmpty())
|
|
perl << m_indentation << "defined $self->{" << cleanName(at->getName())<<"}"
|
|
<< " or $self->{" << cleanName(at->getName()) << "} = "
|
|
<< at->getInitialValue() << ";" << m_endl;
|
|
}
|
|
|
|
perl << " }" << m_endl;
|
|
}
|
|
|
|
perl << m_endl << m_endl;
|
|
}
|
|
|
|
void PerlWriter::writeOperations(const TQString &/* classname */, UMLOperationList &opList, TQTextStream &perl) {
|
|
UMLOperation *op;
|
|
UMLAttribute *at;
|
|
|
|
for(op=opList.first(); op ; op=opList.next())
|
|
{
|
|
UMLAttributeList atl = op->getParmList();
|
|
//write method doc if we have doc || if at least one of the params has doc
|
|
bool writeDoc = forceDoc() || !op->getDoc().isEmpty();
|
|
for (at = atl.first(); at ; at = atl.next())
|
|
writeDoc |= !at->getDoc().isEmpty();
|
|
|
|
if( writeDoc ) //write method documentation
|
|
{
|
|
perl << "=pod " << m_endl << m_endl << "=head3 " ;
|
|
perl << cleanName(op->getName()) << m_endl << m_endl;
|
|
|
|
perl << " Parameters :" << m_endl ;
|
|
//write parameter documentation
|
|
for (at = atl.first(); at ; at = atl.next()) {
|
|
if(forceDoc() || !at->getDoc().isEmpty()) {
|
|
perl << " "
|
|
<< cleanName(at->getName()) << " : "
|
|
<< at->getTypeName() << " : "
|
|
<< at->getDoc()
|
|
<< m_endl;
|
|
}
|
|
}//end for : write parameter documentation
|
|
|
|
perl << m_endl;
|
|
perl << " Return : " << m_endl;
|
|
perl << " " << op->getTypeName();
|
|
perl << m_endl << m_endl;
|
|
perl << " Description : " << m_endl;
|
|
perl << " " << op->getDoc();
|
|
perl << m_endl << m_endl << "=cut" << m_endl << m_endl;
|
|
}//end if : write method documentation
|
|
|
|
perl << "sub " << cleanName(op->getName()) << m_endl << "{" << m_endl;
|
|
perl << " my($self";
|
|
|
|
bool bStartPrinted = false;
|
|
//write parameters
|
|
for (at = atl.first(); at; at = atl.next()) {
|
|
if (!bStartPrinted) {
|
|
bStartPrinted = true;
|
|
perl << "," << m_endl;
|
|
}
|
|
perl << " $"<< cleanName(at->getName()) << ", # "
|
|
<< at->getTypeName() << " : " << at->getDoc() << m_endl;
|
|
}
|
|
|
|
perl << " ) = @_;" << m_endl;
|
|
|
|
perl << "#UML_MODELER_BEGIN_PERSONAL_CODE_" << cleanName(op->getName());
|
|
perl << m_endl << "#UML_MODELER_END_PERSONAL_CODE_" << cleanName(op->getName()) << m_endl;
|
|
perl << "}" << m_endl;
|
|
perl << m_endl << m_endl;
|
|
}//end for
|
|
}
|
|
|
|
|
|
void PerlWriter::writeAttributes(UMLClassifier *c, TQTextStream &perl) {
|
|
UMLAttributeList atpub, atprot, atpriv, atdefval;
|
|
atpub.setAutoDelete(false);
|
|
atprot.setAutoDelete(false);
|
|
atpriv.setAutoDelete(false);
|
|
atdefval.setAutoDelete(false);
|
|
|
|
//sort attributes by scope and see if they have a default value
|
|
UMLAttributeList atl = c->getAttributeList();
|
|
UMLAttribute *at;
|
|
for(at = atl.first(); at ; at = atl.next()) {
|
|
if(!at->getInitialValue().isEmpty())
|
|
atdefval.append(at);
|
|
switch(at->getVisibility()) {
|
|
case Uml::Visibility::Public:
|
|
atpub.append(at);
|
|
break;
|
|
case Uml::Visibility::Protected:
|
|
atprot.append(at);
|
|
break;
|
|
case Uml::Visibility::Private:
|
|
atpriv.append(at);
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
|
|
|
|
if(forceSections() || atpub.count()) {
|
|
writeAttributes(atpub,perl);
|
|
}
|
|
/* not needed as writeAttributes only writes documentation
|
|
if(forceSections() || atprot.count()) {
|
|
writeAttributes(atprot,perl);
|
|
}
|
|
|
|
if(forceSections() || atpriv.count()) {
|
|
writeAttributes(atpriv,perl);
|
|
}
|
|
*/
|
|
}
|
|
|
|
|
|
void PerlWriter::writeAttributes(UMLAttributeList &atList, TQTextStream &perl)
|
|
{
|
|
perl << m_endl << "=head1 PUBLIC ATTRIBUTES" << m_endl << m_endl;
|
|
perl << "=pod " << m_endl << m_endl ;
|
|
for (UMLAttribute *at = atList.first(); at ; at = atList.next())
|
|
{
|
|
if (forceDoc() || !at->getDoc().isEmpty())
|
|
{
|
|
perl << "=head3 " << cleanName(at->getName()) << m_endl << m_endl ;
|
|
perl << " Description : " << at->getDoc() << m_endl << m_endl;
|
|
}
|
|
} // end for
|
|
perl << m_endl << m_endl << "=cut" << m_endl << m_endl;
|
|
return;
|
|
}
|
|
|
|
TQStringList PerlWriter::defaultDatatypes() {
|
|
TQStringList l;
|
|
l.append("$");
|
|
l.append("@");
|
|
l.append("%");
|
|
return l;
|
|
}
|
|
|
|
const TQStringList PerlWriter::reservedKeywords() const {
|
|
|
|
static TQStringList keywords;
|
|
|
|
if (keywords.isEmpty()) {
|
|
keywords << "abs"
|
|
<< "accept"
|
|
<< "alarm"
|
|
<< "and"
|
|
<< "atan2"
|
|
<< "BEGIN"
|
|
<< "bind"
|
|
<< "binmode"
|
|
<< "bless"
|
|
<< "byte"
|
|
<< "caller"
|
|
<< "carp"
|
|
<< "chdir"
|
|
<< "chmod"
|
|
<< "chomp"
|
|
<< "chop"
|
|
<< "chown"
|
|
<< "chr"
|
|
<< "chroot"
|
|
<< "close"
|
|
<< "closedir"
|
|
<< "cmp"
|
|
<< "confess"
|
|
<< "connect"
|
|
<< "continue"
|
|
<< "cos"
|
|
<< "croak"
|
|
<< "crypt"
|
|
<< "dbmclose"
|
|
<< "dbmopen"
|
|
<< "defined"
|
|
<< "delete"
|
|
<< "die"
|
|
<< "do"
|
|
<< "dump"
|
|
<< "each"
|
|
<< "else"
|
|
<< "elsif"
|
|
<< "END"
|
|
<< "endgrent"
|
|
<< "endhostent"
|
|
<< "endnetent"
|
|
<< "endprotoent"
|
|
<< "endpwent"
|
|
<< "endservent"
|
|
<< "eof"
|
|
<< "eq"
|
|
<< "eval"
|
|
<< "exec"
|
|
<< "exists"
|
|
<< "exit"
|
|
<< "exp"
|
|
<< "fcntl"
|
|
<< "fileno"
|
|
<< "flock"
|
|
<< "for"
|
|
<< "foreach"
|
|
<< "fork"
|
|
<< "format"
|
|
<< "formline"
|
|
<< "ge"
|
|
<< "getc"
|
|
<< "getgrent"
|
|
<< "getgrgid"
|
|
<< "getgrnam"
|
|
<< "gethostbyaddr"
|
|
<< "gethostbyname"
|
|
<< "gethostent"
|
|
<< "getlogin"
|
|
<< "getnetbyaddr"
|
|
<< "getnetbyname"
|
|
<< "getnetent"
|
|
<< "getpeername"
|
|
<< "getpgrp"
|
|
<< "getppid"
|
|
<< "getpriority"
|
|
<< "getprotobyname"
|
|
<< "getprotobynumber"
|
|
<< "getprotoent"
|
|
<< "getpwent"
|
|
<< "getpwnam"
|
|
<< "getpwuid"
|
|
<< "getservbyname"
|
|
<< "getservbyport"
|
|
<< "getservent"
|
|
<< "getsockname"
|
|
<< "getsockopt"
|
|
<< "glob"
|
|
<< "gmtime"
|
|
<< "goto"
|
|
<< "grep"
|
|
<< "gt"
|
|
<< "hex"
|
|
<< "if"
|
|
<< "import"
|
|
<< "index"
|
|
<< "int"
|
|
<< "integer"
|
|
<< "ioctl"
|
|
<< "join"
|
|
<< "keys"
|
|
<< "kill"
|
|
<< "last"
|
|
<< "lc"
|
|
<< "lcfirst"
|
|
<< "le"
|
|
<< "length"
|
|
<< "lib"
|
|
<< "link"
|
|
<< "listen"
|
|
<< "local"
|
|
<< "localtime"
|
|
<< "lock"
|
|
<< "log"
|
|
<< "lstat"
|
|
<< "lt"
|
|
<< "map"
|
|
<< "mkdir"
|
|
<< "msgctl"
|
|
<< "msgget"
|
|
<< "msgrcv"
|
|
<< "msgsnd"
|
|
<< "my"
|
|
<< "ne"
|
|
<< "new"
|
|
<< "next"
|
|
<< "no"
|
|
<< "not"
|
|
<< "oct"
|
|
<< "open"
|
|
<< "opendir"
|
|
<< "or"
|
|
<< "ord"
|
|
<< "our"
|
|
<< "pack"
|
|
<< "package"
|
|
<< "pipe"
|
|
<< "pop"
|
|
<< "pos"
|
|
<< "print"
|
|
<< "printf"
|
|
<< "prototype"
|
|
<< "push"
|
|
<< "quotemeta"
|
|
<< "rand"
|
|
<< "read"
|
|
<< "readdir"
|
|
<< "readline"
|
|
<< "readlink"
|
|
<< "readpipe"
|
|
<< "recv"
|
|
<< "redo"
|
|
<< "ref"
|
|
<< "rename"
|
|
<< "require"
|
|
<< "reset"
|
|
<< "return"
|
|
<< "reverse"
|
|
<< "rewinddir"
|
|
<< "rindex"
|
|
<< "rmdir"
|
|
<< "scalar"
|
|
<< "seek"
|
|
<< "seekdir"
|
|
<< "select"
|
|
<< "semctl"
|
|
<< "semget"
|
|
<< "semop"
|
|
<< "send"
|
|
<< "setgrent"
|
|
<< "sethostent"
|
|
<< "setnetent"
|
|
<< "setpgrp"
|
|
<< "setpriority"
|
|
<< "setprotoent"
|
|
<< "setpwent"
|
|
<< "setservent"
|
|
<< "setsockopt"
|
|
<< "shift"
|
|
<< "shmctl"
|
|
<< "shmget"
|
|
<< "shmread"
|
|
<< "shmwrite"
|
|
<< "shutdown"
|
|
<< "sigtrap"
|
|
<< "sin"
|
|
<< "sleep"
|
|
<< "socket"
|
|
<< "socketpair"
|
|
<< "sort"
|
|
<< "splice"
|
|
<< "split"
|
|
<< "sprintf"
|
|
<< "sqrt"
|
|
<< "srand"
|
|
<< "stat"
|
|
<< "strict"
|
|
<< "study"
|
|
<< "sub"
|
|
<< "subs"
|
|
<< "substr"
|
|
<< "switch"
|
|
<< "symlink"
|
|
<< "syscall"
|
|
<< "sysopen"
|
|
<< "sysread"
|
|
<< "sysseek"
|
|
<< "system"
|
|
<< "syswrite"
|
|
<< "tell"
|
|
<< "telldir"
|
|
<< "tie"
|
|
<< "tied"
|
|
<< "time"
|
|
<< "times"
|
|
<< "truncate"
|
|
<< "uc"
|
|
<< "ucfirst"
|
|
<< "umask"
|
|
<< "undef"
|
|
<< "unless"
|
|
<< "unlink"
|
|
<< "unpack"
|
|
<< "unshift"
|
|
<< "untie"
|
|
<< "until"
|
|
<< "use"
|
|
<< "utf8"
|
|
<< "utime"
|
|
<< "values"
|
|
<< "vars"
|
|
<< "vec"
|
|
<< "wait"
|
|
<< "waitpid"
|
|
<< "wantarray"
|
|
<< "warn"
|
|
<< "warnings"
|
|
<< "while"
|
|
<< "write"
|
|
<< "xor";
|
|
}
|
|
|
|
return keywords;
|
|
}
|
|
|
|
#include "perlwriter.moc"
|