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.
434 lines
11 KiB
434 lines
11 KiB
/*
|
|
**************************************************************************
|
|
description
|
|
--------------------
|
|
copyright : (C) 2000-2003 by Andreas Zehender
|
|
email : zehender@kde.org
|
|
**************************************************************************
|
|
|
|
**************************************************************************
|
|
* *
|
|
* 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 "pmparser.h"
|
|
|
|
#include <tqstring.h>
|
|
#include <tqbuffer.h>
|
|
#include <klocale.h>
|
|
|
|
#include "pmpart.h"
|
|
#include "pmdeclare.h"
|
|
#include "pmerrorflags.h"
|
|
#include "pmrecursiveobjectiterator.h"
|
|
#include "pmdebug.h"
|
|
|
|
unsigned int PMParser::s_maxErrors = 30;
|
|
unsigned int PMParser::s_maxWarnings = 50;
|
|
|
|
|
|
PMParser::PMParser( PMPart* part, TQIODevice* dev )
|
|
: m_okDeclares( 101 )
|
|
{
|
|
m_pPart = part;
|
|
m_pDevice = dev;
|
|
m_bDeviceCreated = false;
|
|
|
|
init( );
|
|
}
|
|
|
|
PMParser::PMParser( PMPart* part, const TQByteArray& array )
|
|
: m_okDeclares( 101 )
|
|
{
|
|
m_pPart = part;
|
|
TQBuffer* buffer = new TQBuffer( array );
|
|
buffer->open( IO_ReadOnly );
|
|
m_pDevice = buffer;
|
|
m_bDeviceCreated = true;
|
|
|
|
init( );
|
|
}
|
|
|
|
void PMParser::init( )
|
|
{
|
|
m_okDeclares.setAutoDelete( true );
|
|
m_pLocalST.setAutoDelete( true );
|
|
|
|
m_lineNum = -1;
|
|
m_pResultList = 0;
|
|
m_errors = 0;
|
|
m_warnings = 0;
|
|
m_bFatalError = false;
|
|
m_shownMessages = 0;
|
|
m_messages.clear( );
|
|
|
|
m_pTopParent = 0;
|
|
m_renamedObjectSymbols.clear( );
|
|
m_okDeclares.clear( );
|
|
m_pNextCheckDeclare = 0;
|
|
}
|
|
|
|
PMParser::~PMParser( )
|
|
{
|
|
if( m_bDeviceCreated )
|
|
delete m_pDevice;
|
|
}
|
|
|
|
void PMParser::printMessage( const PMPMessage messageNum )
|
|
{
|
|
if( !( m_shownMessages & messageNum ) )
|
|
{
|
|
m_shownMessages |= messageNum;
|
|
|
|
switch( messageNum )
|
|
{
|
|
case PMMClockDefault:
|
|
printWarning( i18n( "Using the default value of 0.0 for clock" ) );
|
|
break;
|
|
case PMMClockDeltaDefault:
|
|
printWarning( i18n( "Using the default value of 1.0 for clock_delta" ) );
|
|
break;
|
|
case PMMSpecialRawComment:
|
|
m_messages += PMMessage( i18n( "Note: The full povray syntax is not supported yet. "
|
|
"If you want to add unsupported povray code to the "
|
|
"scene, you can put this code between the two "
|
|
"special comments \"//*PMRawBegin\" and "
|
|
"\"//*PMRawEnd\"." ) );
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
void PMParser::printMessage( const TQString& type, const TQString& msg )
|
|
{
|
|
if( m_lineNum >= 0 )
|
|
m_messages += PMMessage( i18n( "Line %1: " ).arg( m_lineNum ) + type + ": " + msg );
|
|
else
|
|
m_messages += PMMessage( type + ": " + msg );
|
|
}
|
|
|
|
void PMParser::printError( const TQString& msg )
|
|
{
|
|
if( m_errors < s_maxErrors )
|
|
{
|
|
printMessage( i18n( "Error" ), msg );
|
|
m_errors++;
|
|
}
|
|
else if( m_errors == s_maxErrors )
|
|
{
|
|
m_messages += PMMessage( i18n( "Maximum of %1 errors reached." )
|
|
.arg( s_maxErrors ) );
|
|
m_errors++;
|
|
}
|
|
}
|
|
|
|
void PMParser::printWarning( const TQString& msg )
|
|
{
|
|
if( m_warnings < s_maxWarnings )
|
|
{
|
|
printMessage( i18n( "Warning" ), msg );
|
|
m_warnings++;
|
|
}
|
|
else if( m_warnings == s_maxWarnings )
|
|
{
|
|
m_messages += PMMessage( i18n( "Maximum of %1 warnings reached." )
|
|
.arg( s_maxWarnings ) );
|
|
m_warnings++;
|
|
}
|
|
}
|
|
|
|
void PMParser::printExpected( const char c, const char* sValue )
|
|
{
|
|
printError( i18n( "'%1' expected, found token '%2' instead." )
|
|
.arg( c ).arg( sValue ) );
|
|
}
|
|
|
|
void PMParser::printExpected( const TQString& str, const char* sValue )
|
|
{
|
|
printError( i18n( "'%1' expected, found token '%2' instead." )
|
|
.arg( str ).arg( sValue ) );
|
|
}
|
|
|
|
void PMParser::printUnexpected( const TQString& str )
|
|
{
|
|
printError( i18n( "Unexpected token '%1'." ).arg( str ) );
|
|
}
|
|
|
|
void PMParser::printInfo( const TQString& msg )
|
|
{
|
|
printMessage( i18n( "Info" ), msg );
|
|
}
|
|
|
|
int PMParser::errorFlags( ) const
|
|
{
|
|
int result = 0;
|
|
if( errors( ) )
|
|
result |= PMEError;
|
|
if( warnings( ) )
|
|
result |= PMEWarning;
|
|
if( fatal( ) )
|
|
result |= PMEFatal;
|
|
return result;
|
|
}
|
|
|
|
|
|
void PMParser::parse( PMObjectList* list, PMObject* parent,
|
|
PMObject* after )
|
|
{
|
|
m_pResultList = list;
|
|
m_pTopParent = parent;
|
|
m_pAfter = after;
|
|
|
|
// find first item, that can be a declare and can be used as link
|
|
// for parsed objects.
|
|
if( parent )
|
|
{
|
|
if( parent->type( ) == "Scene" )
|
|
{
|
|
if( after )
|
|
m_pNextCheckDeclare = after;
|
|
else
|
|
m_pNextCheckDeclare = 0;
|
|
}
|
|
else
|
|
{
|
|
PMObject* obj = parent;
|
|
bool stop = false;
|
|
|
|
// go to parents, until the parent is the scene
|
|
// (declares can only be inserted as top level objects)
|
|
do
|
|
{
|
|
if( obj->parent( ) )
|
|
{
|
|
if( obj->parent( )->type( ) == "Scene" )
|
|
stop = true;
|
|
else
|
|
obj = obj->parent( );
|
|
}
|
|
else
|
|
{
|
|
obj = 0;
|
|
stop = true;
|
|
}
|
|
}
|
|
while( obj && !stop );
|
|
|
|
// now obj is the top level parent of the object, where parsed objects
|
|
// will be inserted
|
|
if( obj )
|
|
m_pNextCheckDeclare = obj->prevSibling( );
|
|
else
|
|
m_pNextCheckDeclare = 0;
|
|
}
|
|
}
|
|
|
|
topParse( );
|
|
|
|
TQPtrListIterator<PMSymbol> it( m_renamedObjectSymbols );
|
|
for( ; it.current( ); ++it )
|
|
it.current( )->setRenamedSymbol( 0 );
|
|
m_renamedObjectSymbols.clear( );
|
|
m_pLocalST.clear( );
|
|
|
|
if( ( errors( ) || warnings( ) ) && m_pResultList->isEmpty( ) )
|
|
setFatalError( );
|
|
}
|
|
|
|
bool PMParser::insertChild( PMObject* child, PMObject* parent )
|
|
{
|
|
bool inserted = false;
|
|
|
|
if( parent )
|
|
{
|
|
if( parent->canInsert( child, parent->lastChild( ) ) )
|
|
{
|
|
parent->appendChild( child );
|
|
inserted = true;
|
|
}
|
|
else
|
|
{
|
|
printError( i18n( "Can't insert %1 into %2." )
|
|
.arg( child->description( ) )
|
|
.arg( parent->description( ) ) );
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if( m_pTopParent )
|
|
{
|
|
if( m_pTopParent->canInsert( child, m_pAfter, m_pResultList ) )
|
|
{
|
|
m_pResultList->append( child );
|
|
inserted = true;
|
|
}
|
|
else
|
|
{
|
|
printError( i18n( "Can't insert %1 into %2." )
|
|
.arg( child->description( ) )
|
|
.arg( m_pTopParent->description( ) ) );
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// these lines should not be executed
|
|
// m_pTopParent may not be null
|
|
m_pResultList->append( child );
|
|
inserted = true;
|
|
}
|
|
}
|
|
|
|
if( !inserted )
|
|
{
|
|
// insert error
|
|
// remove all links
|
|
PMRecursiveObjectIterator rit( child );
|
|
PMDeclare* decl = 0;
|
|
|
|
for( ; rit.current( ); ++rit )
|
|
{
|
|
decl = rit.current( )->linkedObject( );
|
|
if( decl )
|
|
decl->removeLinkedObject( rit.current( ) );
|
|
}
|
|
}
|
|
|
|
return inserted;
|
|
}
|
|
|
|
void PMParser::checkID( PMDeclare* decl )
|
|
{
|
|
PMSymbolTable* st = m_pPart->symbolTable( );
|
|
PMSymbol* s = m_pLocalST.find( decl->id( ) );
|
|
if( !s )
|
|
s = st->find( decl->id( ) );
|
|
|
|
if( s )
|
|
{
|
|
PMSymbol* newSym = st->findNewID( s->id( ) + "_", decl );
|
|
s->setRenamedSymbol( newSym );
|
|
// Symbol can be inserted multiple times
|
|
// Faster than searching for s and inserting s
|
|
// if the list does not contain it.
|
|
m_renamedObjectSymbols.append( s );
|
|
|
|
if( m_pTopParent )
|
|
m_pLocalST.insert( decl->id( ), newSym ); // paste/drop
|
|
else
|
|
st->insert( decl->id( ), newSym ); // load file
|
|
}
|
|
else
|
|
{
|
|
s = new PMSymbol( decl->id( ), decl );
|
|
if( m_pTopParent )
|
|
m_pLocalST.insert( decl->id( ), s ); // paste/drop
|
|
else
|
|
st->insert( decl->id( ), s ); // load file
|
|
m_okDeclares.insert( decl->id( ), new bool( true ) );
|
|
}
|
|
}
|
|
|
|
void PMParser::checkID( const TQString& id, const PMValue& v )
|
|
{
|
|
PMSymbolTable* st = m_pPart->symbolTable( );
|
|
PMSymbol* s = m_pLocalST.find( id );
|
|
|
|
if( s )
|
|
{
|
|
PMSymbol* newSym = new PMSymbol( st->findNewID( id + "_" ), v );
|
|
s->setRenamedSymbol( newSym );
|
|
// Symbol can be inserted multiple times
|
|
// Faster than searching for s and inserting s
|
|
// if the list does not contain it.
|
|
m_renamedObjectSymbols.append( s );
|
|
|
|
if( m_pTopParent )
|
|
m_pLocalST.insert( id, newSym ); // paste/drop
|
|
|
|
// values are never inserted into the parts symbol table
|
|
// else
|
|
// st->insert( decl->id( ), newSym ); // load file
|
|
}
|
|
else
|
|
{
|
|
s = new PMSymbol( id, v );
|
|
if( m_pTopParent )
|
|
m_pLocalST.insert( id, s ); // paste/drop
|
|
|
|
// values are never inserted into the parts symbol table
|
|
// else
|
|
// st->insert( decl->id( ), s ); // load file
|
|
|
|
m_okDeclares.insert( id, new bool( true ) );
|
|
}
|
|
}
|
|
|
|
PMDeclare* PMParser::checkLink( const TQString& id )
|
|
{
|
|
PMSymbolTable* t = m_pPart->symbolTable( );
|
|
bool ok = false;
|
|
|
|
// is object declared?
|
|
PMSymbol* s = m_pLocalST.find( id );
|
|
if( !s )
|
|
s = t->find( id );
|
|
|
|
if( !s )
|
|
printError( i18n( "Undefined object \"%1\"." ).arg( id ) );
|
|
else if( s->type( ) != PMSymbol::Object )
|
|
printError( i18n( "Undefined object \"%1\"." ).arg( id ) );
|
|
else
|
|
{
|
|
// the object is declared
|
|
// is the id already in m_okDeclares
|
|
bool* lok = m_okDeclares.find( id );
|
|
if( lok )
|
|
ok = true;
|
|
else
|
|
{
|
|
// the id is not in m_okDeclares
|
|
PMObject* obj = s->object( );
|
|
while( m_pNextCheckDeclare && !ok )
|
|
{
|
|
if( m_pNextCheckDeclare->isA( "Declare" ) )
|
|
{
|
|
PMDeclare* decl = ( PMDeclare* ) m_pNextCheckDeclare;
|
|
m_okDeclares.insert( decl->id( ), new bool( true ) );
|
|
}
|
|
if( m_pNextCheckDeclare == obj )
|
|
ok = true;
|
|
|
|
m_pNextCheckDeclare = m_pNextCheckDeclare->prevSibling( );
|
|
}
|
|
}
|
|
|
|
if( !ok )
|
|
printError( i18n( "Object \"%1\" is undefined at that point." )
|
|
.arg( id ) );
|
|
}
|
|
|
|
if( ok )
|
|
{
|
|
while( s->renamedSymbol( ) )
|
|
s = s->renamedSymbol( );
|
|
|
|
return s->object( );
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
PMSymbol* PMParser::getSymbol( const TQString& id ) const
|
|
{
|
|
PMSymbol* s = m_pLocalST.find( id );
|
|
if( !s )
|
|
s = m_pPart->symbolTable( )->find( id );
|
|
return s;
|
|
}
|
|
|