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.
tdepim/kode/kxml_compiler/parser.cpp

300 lines
8.3 KiB

/*
This file is part of KDE.
Copyright (c) 2004 Cornelius Schumacher <schumacher@kde.org>
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Library General Public
License as published by the Free Software Foundation; either
version 2 of the License, or (at your option) any later version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Library General Public License for more details.
You should have received a copy of the GNU Library General Public License
along with this library; see the file COPYING.LIB. If not, write to
the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
Boston, MA 02110-1301, USA.
*/
#include "parser.h"
#include <kode/code.h>
#include <kode/printer.h>
#include <kode/typedef.h>
#include <kaboutdata.h>
#include <kapplication.h>
#include <kdebug.h>
#include <klocale.h>
#include <kcmdlineargs.h>
#include <kglobal.h>
#include <kconfig.h>
#include <ksimpleconfig.h>
#include <kstandarddirs.h>
#include <tqfile.h>
#include <textstream.h>
#include <tqdom.h>
#include <tqregexp.h>
#include <tqmap.h>
#include <iostream>
Pattern::Pattern()
: optional( false ), zeroOrMore( false ), oneOrMore( false ),
choice( false )
{
}
bool Pattern::isEmpty()
{
return !optional && !zeroOrMore && !oneOrMore && !choice;
}
TQString Pattern::asString()
{
if ( isEmpty() ) return "";
TQString str = "( ";
if ( optional ) str += "optional ";
if ( zeroOrMore ) str += "zeroOrMore ";
if ( oneOrMore ) str += "oneOrMore ";
if ( choice ) str += "choice ";
str += ")";
return str;
}
void Pattern::merge( Pattern p )
{
if ( p.optional ) optional = true;
if ( p.zeroOrMore ) zeroOrMore = true;
if ( p.oneOrMore ) oneOrMore = true;
if ( p.choice ) choice = true;
}
Element::Element()
: hasText( false ), isEmpty( false )
{
}
Parser::Parser()
{
}
Element *Parser::parse( const TQDomElement &docElement )
{
Element *start = 0;
TQDomNode n1;
for( n1 = docElement.firstChild(); !n1.isNull(); n1 = n1.nextSibling() ) {
TQDomElement e1 = n1.toElement();
kdDebug() << "TOP LEVEL element " << e1.tagName() << endl;
if ( e1.tagName() == "define" ) {
Element *d = new Element;
d->name = e1.attribute( "name" );
parseElement( e1, d, Pattern() );
Element::List definitions;
TQMap<TQString,Element::List >::ConstIterator it;
it = mDefinitionMap.find( d->name );
if ( it != mDefinitionMap.end() ) definitions = *it;
definitions.append( d );
mDefinitionMap.replace( d->name, definitions );
} else if ( e1.tagName() == "start" ) {
start = new Element;
parseElement( e1, start, Pattern() );
} else {
kdDebug() << "parseGrammar: Unrecognized tag: " << e1.tagName() << endl;
}
}
return start;
}
Reference *Parser::parseReference( const TQDomElement &referenceElement )
{
Reference *r = new Reference;
r->name = referenceElement.attribute( "name" );
return r;
}
bool Parser::parseAttribute( const TQDomElement &attributeElement,
Attribute *a )
{
a->name = attributeElement.attribute( "name" );
return true;
}
bool Parser::parseElement( const TQDomElement &elementElement, Element *e,
Pattern pattern )
{
kdDebug() << "parseElement " << e->name << endl;
TQDomNode n1;
for( n1 = elementElement.firstChild(); !n1.isNull(); n1 = n1.nextSibling() ) {
TQDomElement e1 = n1.toElement();
if ( e1.tagName() == "element" ) {
Element *element = new Element;
element->name = e1.attribute( "name" );
element->pattern = pattern;
parseElement( e1, element, Pattern() );
e->elements.append( element );
} else if ( e1.tagName() == "attribute" ) {
Attribute *a = new Attribute;
a->name = e1.attribute( "name" );
a->pattern = pattern;
kdDebug() << "ATTRIBUTE: " << a->name << " " << a->pattern.asString()
<< endl;
parseAttribute( e1, a );
e->attributes.append( a );
} else if ( e1.tagName() == "ref" ) {
Reference *r = parseReference( e1 );
r->pattern = pattern;
e->references.append( r );
} else if ( e1.tagName() == "text" ) {
e->hasText = true;
} else if ( e1.tagName() == "empty" ) {
e->isEmpty = true;
} else {
Pattern p = pattern;
if ( e1.tagName() == "optional" ) p.optional = true;
else if ( e1.tagName() == "zeroOrMore" ) p.zeroOrMore = true;
else if ( e1.tagName() == "oneOrMore" ) p.oneOrMore = true;
else if ( e1.tagName() == "choice" ) p.choice = true;
else {
kdDebug() << "Unsupported pattern '" << e1.tagName() << "'" << endl;
}
parseElement( e1, e, p );
}
}
return true;
}
void Parser::substituteReferences( Element *s )
{
kdDebug() << "substituteReferences for '" << s->name << "'" << endl;
Reference::List::Iterator it = s->references.begin();
while( it != s->references.end() ) {
Reference *r = *it;
kdDebug() << "REF " << r->name << endl;
if ( r->name == s->name ) {
kdDebug() << "Don't resolve self reference" << endl;
return;
}
if ( r->substituted ) {
kdDebug() << "Already substituted." << endl;
++it;
continue;
} else {
r->substituted = true;
}
TQMap<TQString,Element::List >::ConstIterator it1;
it1 = mDefinitionMap.find( r->name );
if ( it1 != mDefinitionMap.end() ) {
Element::List elements = *it1;
Element::List::ConstIterator it4;
for( it4 = elements.begin(); it4 != elements.end(); ++it4 ) {
Element *d = *it4;
substituteReferences( d );
Element::List::ConstIterator it2;
for( it2 = d->elements.begin(); it2 != d->elements.end(); ++it2 ) {
Element *e = *it2;
e->pattern.merge( r->pattern );
substituteReferences( e );
s->elements.append( e );
}
Attribute::List::ConstIterator it3;
for( it3 = d->attributes.begin(); it3 != d->attributes.end();
++it3 ) {
Attribute *a = *it3;
a->pattern.merge( r->pattern );
s->attributes.append( a );
}
}
it = s->references.erase( it );
} else {
kdDebug() << "Reference not found" << endl;
++it;
}
}
}
void Parser::doIndent( int cols )
{
for( int i = 0; i < cols; ++i ) std::cout << " ";
}
void Parser::dumpPattern( Pattern pattern )
{
std::cout << pattern.asString().utf8().data();
}
void Parser::dumpReferences( const Reference::List &references, int indent )
{
Reference::List::ConstIterator it;
for( it = references.begin(); it != references.end(); ++it ) {
Reference *r = *it;
doIndent( indent );
std::cout << "REFERENCE " << r->name.utf8().data();
dumpPattern( r->pattern );
std::cout << std::endl;
}
}
void Parser::dumpAttributes( const Attribute::List &attributes, int indent )
{
Attribute::List::ConstIterator it;
for( it = attributes.begin(); it != attributes.end(); ++it ) {
Attribute *a = *it;
doIndent( indent );
std::cout << "ATTRIBUTE " << a->name.utf8().data();
dumpPattern( a->pattern );
std::cout << std::endl;
}
}
void Parser::dumpElements( const Element::List &elements, int indent )
{
Element::List::ConstIterator it;
for( it = elements.begin(); it != elements.end(); ++it ) {
Element *e = *it;
dumpElement( e, indent );
}
}
void Parser::dumpElement( Element *e, int indent )
{
doIndent( indent );
std::cout << "ELEMENT " << e->name.utf8().data();
dumpPattern( e->pattern );
std::cout << std::endl;
if ( e->hasText ) {
doIndent( indent + 2 );
std::cout << "TEXT" << std::endl;
}
dumpAttributes( e->attributes, indent + 2 );
dumpElements( e->elements, indent + 2 );
dumpReferences( e->references, indent + 2 );
}
void Parser::dumpTree( Element *s )
{
std::cout << "START " << s->name.utf8().data() << std::endl;
dumpElements( s->elements, 2 );
dumpReferences( s->references, 2 );
}
void Parser::dumpDefinitionMap()
{
std::cout << "DEFINITION MAP" << std::endl;
TQMap<TQString,Element::List >::ConstIterator it;
for( it = mDefinitionMap.begin(); it != mDefinitionMap.end(); ++it ) {
dumpElements( *it, 2 );
}
}