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.
505 lines
12 KiB
505 lines
12 KiB
/* ANTLR Translator Generator
|
|
* Project led by Terence Parr at http://www.jGuru.com
|
|
* Software rights: http://www.antlr.org/license.html
|
|
*
|
|
* $Id$
|
|
*/
|
|
|
|
#include "antlr/CommonAST.h"
|
|
#include "antlr/ANTLRException.h"
|
|
#include "antlr/IOException.h"
|
|
#include "antlr/ASTFactory.h"
|
|
#include "antlr/ANTLRUtil.h"
|
|
|
|
#include <iostream>
|
|
#include <istream>
|
|
|
|
using namespace std;
|
|
|
|
#ifdef ANTLR_CXX_SUPPORTS_NAMESPACE
|
|
namespace antlr {
|
|
#endif
|
|
|
|
/** AST Support code shared by TreeParser and Parser.
|
|
* We use delegation to share code (and have only one
|
|
* bit of code to maintain) rather than subclassing
|
|
* or superclassing (forces AST support code to be
|
|
* loaded even when you don't want to do AST stuff).
|
|
*
|
|
* This class collects all factories of AST types used inside the code.
|
|
* New AST node types are registered with the registerFactory method.
|
|
* On creation of an ASTFactory object a default AST node factory may be
|
|
* specified.
|
|
*
|
|
* When registering types gaps between different types are filled with entries
|
|
* for the default factory.
|
|
*/
|
|
|
|
/// Initialize factory
|
|
ASTFactory::ASTFactory()
|
|
: default_factory_descriptor(ANTLR_USE_NAMESPACE(std)make_pair(CommonAST::TYPE_NAME,&CommonAST::factory))
|
|
{
|
|
nodeFactories.resize( Token::MIN_USER_TYPE, &default_factory_descriptor );
|
|
}
|
|
|
|
/** Initialize factory with a non default node type.
|
|
* factory_node_name should be the name of the AST node type the factory
|
|
* generates. (should exist during the existance of this ASTFactory instance)
|
|
*/
|
|
ASTFactory::ASTFactory( const char* factory_node_name, factory_type fact )
|
|
: default_factory_descriptor(ANTLR_USE_NAMESPACE(std)make_pair(factory_node_name, fact))
|
|
{
|
|
nodeFactories.resize( Token::MIN_USER_TYPE, &default_factory_descriptor );
|
|
}
|
|
|
|
/// Delete ASTFactory
|
|
ASTFactory::~ASTFactory()
|
|
{
|
|
factory_descriptor_list::iterator i = nodeFactories.begin();
|
|
|
|
while( i != nodeFactories.end() )
|
|
{
|
|
if( *i != &default_factory_descriptor )
|
|
delete *i;
|
|
i++;
|
|
}
|
|
}
|
|
|
|
/// Register a factory for a given AST type
|
|
void ASTFactory::registerFactory( int type, const char* ast_name, factory_type factory )
|
|
{
|
|
// check validity of arguments...
|
|
if( type < Token::MIN_USER_TYPE )
|
|
throw ANTLRException("Internal parser error invalid type passed to RegisterFactory");
|
|
if( factory == 0 )
|
|
throw ANTLRException("Internal parser error 0 factory passed to RegisterFactory");
|
|
|
|
// resize up to and including 'type' and initalize any gaps to default
|
|
// factory.
|
|
if( nodeFactories.size() < (static_cast<unsigned int>(type)+1) )
|
|
nodeFactories.resize( type+1, &default_factory_descriptor );
|
|
|
|
// And add new thing..
|
|
nodeFactories[type] = new ANTLR_USE_NAMESPACE(std)pair<const char*, factory_type>( ast_name, factory );
|
|
}
|
|
|
|
void ASTFactory::setMaxNodeType( int type )
|
|
{
|
|
if( nodeFactories.size() < (static_cast<unsigned int>(type)+1) )
|
|
nodeFactories.resize( type+1, &default_factory_descriptor );
|
|
}
|
|
|
|
/** Create a new empty AST node; if the user did not specify
|
|
* an AST node type, then create a default one: CommonAST.
|
|
*/
|
|
RefAST ASTFactory::create()
|
|
{
|
|
RefAST node = nodeFactories[0]->second();
|
|
node->setType(Token::INVALID_TYPE);
|
|
return node;
|
|
}
|
|
|
|
RefAST ASTFactory::create(int type)
|
|
{
|
|
RefAST t = nodeFactories[type]->second();
|
|
t->initialize(type,"");
|
|
return t;
|
|
}
|
|
|
|
RefAST ASTFactory::create(int type, const ANTLR_USE_NAMESPACE(std)string& txt)
|
|
{
|
|
RefAST t = nodeFactories[type]->second();
|
|
t->initialize(type,txt);
|
|
return t;
|
|
}
|
|
|
|
#ifdef ANTLR_SUPPORT_XML
|
|
RefAST ASTFactory::create(const ANTLR_USE_NAMESPACE(std)string& type_name, ANTLR_USE_NAMESPACE(std)istream& infile )
|
|
{
|
|
factory_descriptor_list::iterator fact = nodeFactories.begin();
|
|
|
|
while( fact != nodeFactories.end() )
|
|
{
|
|
if( type_name == (*fact)->first )
|
|
{
|
|
RefAST t = (*fact)->second();
|
|
t->initialize(infile);
|
|
return t;
|
|
}
|
|
fact++;
|
|
}
|
|
|
|
string error = "ASTFactory::create: Unknown AST type '" + type_name + "'";
|
|
throw ANTLRException(error);
|
|
}
|
|
#endif
|
|
|
|
/** Create a new empty AST node; if the user did not specify
|
|
* an AST node type, then create a default one: CommonAST.
|
|
*/
|
|
RefAST ASTFactory::create(RefAST tr)
|
|
{
|
|
if (!tr)
|
|
return nullAST;
|
|
|
|
// cout << "create(tr)" << endl;
|
|
|
|
RefAST t = nodeFactories[tr->getType()]->second();
|
|
t->initialize(tr);
|
|
return t;
|
|
}
|
|
|
|
RefAST ASTFactory::create(RefToken tok)
|
|
{
|
|
// cout << "create( tok="<< tok->getType() << ", " << tok->getText() << ")" << nodeFactories.size() << endl;
|
|
RefAST t = nodeFactories[tok->getType()]->second();
|
|
t->initialize(tok);
|
|
return t;
|
|
}
|
|
|
|
/** Add a child to the current AST */
|
|
void ASTFactory::addASTChild(ASTPair& currentAST, RefAST child)
|
|
{
|
|
if (child)
|
|
{
|
|
if (!currentAST.root)
|
|
{
|
|
// Make new child the current root
|
|
currentAST.root = child;
|
|
}
|
|
else
|
|
{
|
|
if (!currentAST.child)
|
|
{
|
|
// Add new child to current root
|
|
currentAST.root->setFirstChild(child);
|
|
}
|
|
else
|
|
{
|
|
currentAST.child->setNextSibling(child);
|
|
}
|
|
}
|
|
// Make new child the current child
|
|
currentAST.child = child;
|
|
currentAST.advanceChildToEnd();
|
|
}
|
|
}
|
|
|
|
/** Deep copy a single node. This function the new clone() methods in the AST
|
|
* interface. Returns nullAST if t is null.
|
|
*/
|
|
RefAST ASTFactory::dup(RefAST t)
|
|
{
|
|
if( t )
|
|
return t->clone();
|
|
else
|
|
return RefAST(nullASTptr);
|
|
}
|
|
|
|
/** Duplicate tree including siblings of root. */
|
|
RefAST ASTFactory::dupList(RefAST t)
|
|
{
|
|
RefAST result = dupTree(t); // if t == null, then result==null
|
|
RefAST nt = result;
|
|
|
|
while( t )
|
|
{ // for each sibling of the root
|
|
t = t->getNextSibling();
|
|
nt->setNextSibling(dupTree(t)); // dup each subtree, building new tree
|
|
nt = nt->getNextSibling();
|
|
}
|
|
return result;
|
|
}
|
|
|
|
/** Duplicate a tree, assuming this is a root node of a tree
|
|
* duplicate that node and what's below; ignore siblings of root node.
|
|
*/
|
|
RefAST ASTFactory::dupTree(RefAST t)
|
|
{
|
|
RefAST result = dup(t); // make copy of root
|
|
// copy all children of root.
|
|
if( t )
|
|
result->setFirstChild( dupList(t->getFirstChild()) );
|
|
return result;
|
|
}
|
|
|
|
/** Make a tree from a list of nodes. The first element in the
|
|
* array is the root. If the root is null, then the tree is
|
|
* a simple list not a tree. Handles null children nodes correctly.
|
|
* For example, make(a, b, null, c) yields tree (a b c). make(null,a,b)
|
|
* yields tree (nil a b).
|
|
*/
|
|
RefAST ASTFactory::make(ANTLR_USE_NAMESPACE(std)vector<RefAST>& nodes)
|
|
{
|
|
if ( nodes.size() == 0 )
|
|
return RefAST(nullASTptr);
|
|
|
|
RefAST root = nodes[0];
|
|
RefAST tail = RefAST(nullASTptr);
|
|
|
|
if( root )
|
|
root->setFirstChild(RefAST(nullASTptr)); // don't leave any old pointers set
|
|
|
|
// link in children;
|
|
for( unsigned int i = 1; i < nodes.size(); i++ )
|
|
{
|
|
if ( nodes[i] == 0 ) // ignore null nodes
|
|
continue;
|
|
|
|
if ( root == 0 ) // Set the root and set it up for a flat list
|
|
root = tail = nodes[i];
|
|
else if ( tail == 0 )
|
|
{
|
|
root->setFirstChild(nodes[i]);
|
|
tail = root->getFirstChild();
|
|
}
|
|
else
|
|
{
|
|
tail->setNextSibling(nodes[i]);
|
|
tail = tail->getNextSibling();
|
|
}
|
|
|
|
if( tail ) // RK: I cannot fathom why this missing check didn't bite anyone else...
|
|
{
|
|
// Chase tail to last sibling
|
|
while (tail->getNextSibling())
|
|
tail = tail->getNextSibling();
|
|
}
|
|
}
|
|
|
|
return root;
|
|
}
|
|
|
|
/** Make a tree from a list of nodes, where the nodes are contained
|
|
* in an ASTArray object
|
|
*/
|
|
RefAST ASTFactory::make(ASTArray* nodes)
|
|
{
|
|
RefAST ret = make(nodes->array);
|
|
delete nodes;
|
|
return ret;
|
|
}
|
|
|
|
/// Make an AST the root of current AST
|
|
void ASTFactory::makeASTRoot( ASTPair& currentAST, RefAST root )
|
|
{
|
|
if (root)
|
|
{
|
|
// Add the current root as a child of new root
|
|
root->addChild(currentAST.root);
|
|
// The new current child is the last sibling of the old root
|
|
currentAST.child = currentAST.root;
|
|
currentAST.advanceChildToEnd();
|
|
// Set the new root
|
|
currentAST.root = root;
|
|
}
|
|
}
|
|
|
|
void ASTFactory::setASTNodeFactory( const char* factory_node_name,
|
|
factory_type factory )
|
|
{
|
|
default_factory_descriptor.first = factory_node_name;
|
|
default_factory_descriptor.second = factory;
|
|
}
|
|
|
|
#ifdef ANTLR_SUPPORT_XML
|
|
bool ASTFactory::checkCloseTag( ANTLR_USE_NAMESPACE(std)istream& in )
|
|
{
|
|
char ch;
|
|
|
|
if( in.get(ch) )
|
|
{
|
|
if( ch == '<' )
|
|
{
|
|
char ch2;
|
|
if( in.get(ch2) )
|
|
{
|
|
if( ch2 == '/' )
|
|
{
|
|
in.putback(ch2);
|
|
in.putback(ch);
|
|
return true;
|
|
}
|
|
in.putback(ch2);
|
|
in.putback(ch);
|
|
return false;
|
|
}
|
|
}
|
|
in.putback(ch);
|
|
return false;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
void ASTFactory::loadChildren( ANTLR_USE_NAMESPACE(std)istream& infile,
|
|
RefAST current )
|
|
{
|
|
char ch;
|
|
|
|
for(;;) // for all children of this node....
|
|
{
|
|
eatwhite(infile);
|
|
|
|
infile.get(ch); // '<'
|
|
if( ch != '<' )
|
|
{
|
|
string error = "Invalid XML file... no '<' found (";
|
|
error += ch + ")";
|
|
throw IOException(error);
|
|
}
|
|
|
|
infile.get(ch); // / or text....
|
|
|
|
if( ch == '/' ) // check for close tag...
|
|
{
|
|
string temp;
|
|
|
|
// read until '>' and see if it matches the open tag... if not trouble
|
|
temp = read_identifier( infile );
|
|
|
|
if( strcmp(temp.c_str(), current->typeName() ) != 0 )
|
|
{
|
|
string error = "Invalid XML file... close tag does not match start tag: ";
|
|
error += current->typeName();
|
|
error += " closed by " + temp;
|
|
throw IOException(error);
|
|
}
|
|
|
|
infile.get(ch); // must be a '>'
|
|
|
|
if( ch != '>' )
|
|
{
|
|
string error = "Invalid XML file... no '>' found (";
|
|
error += ch + ")";
|
|
throw IOException(error);
|
|
}
|
|
// close tag => exit loop
|
|
break;
|
|
}
|
|
|
|
// put our 'look ahead' back where it came from
|
|
infile.putback(ch);
|
|
infile.putback('<');
|
|
|
|
// and recurse into the tree...
|
|
RefAST child = LoadAST(infile);
|
|
|
|
current->addChild( child );
|
|
}
|
|
}
|
|
|
|
void ASTFactory::loadSiblings(ANTLR_USE_NAMESPACE(std)istream& infile,
|
|
RefAST current )
|
|
{
|
|
for(;;)
|
|
{
|
|
eatwhite(infile);
|
|
|
|
if( infile.eof() )
|
|
break;
|
|
|
|
if( checkCloseTag(infile) )
|
|
break;
|
|
|
|
RefAST sibling = LoadAST(infile);
|
|
current->setNextSibling(sibling);
|
|
}
|
|
}
|
|
|
|
RefAST ASTFactory::LoadAST( ANTLR_USE_NAMESPACE(std)istream& infile )
|
|
{
|
|
RefAST current = nullAST;
|
|
char ch;
|
|
|
|
eatwhite(infile);
|
|
|
|
if( !infile.get(ch) )
|
|
return nullAST;
|
|
|
|
if( ch != '<' )
|
|
{
|
|
string error = "Invalid XML file... no '<' found (";
|
|
error += ch + ")";
|
|
throw IOException(error);
|
|
}
|
|
|
|
string ast_type = read_identifier(infile);
|
|
|
|
// create the ast of type 'ast_type'
|
|
current = create( ast_type, infile );
|
|
if( current == nullAST )
|
|
{
|
|
string error = "Unsuported AST type: " + ast_type;
|
|
throw IOException(error);
|
|
}
|
|
|
|
eatwhite(infile);
|
|
|
|
infile.get(ch);
|
|
|
|
// now if we have a '/' here it's a single node. If it's a '>' we get
|
|
// a tree with children
|
|
|
|
if( ch == '/' )
|
|
{
|
|
infile.get(ch); // get the closing '>'
|
|
if( ch != '>' )
|
|
{
|
|
string error = "Invalid XML file... no '>' found after '/' (";
|
|
error += ch + ")";
|
|
throw IOException(error);
|
|
}
|
|
|
|
// get the rest on this level
|
|
loadSiblings( infile, current );
|
|
|
|
return current;
|
|
}
|
|
|
|
// and finaly see if we got the close tag...
|
|
if( ch != '>' )
|
|
{
|
|
string error = "Invalid XML file... no '>' found (";
|
|
error += ch + ")";
|
|
throw IOException(error);
|
|
}
|
|
|
|
// handle the ones below this level..
|
|
loadChildren( infile, current );
|
|
|
|
// load the rest on this level...
|
|
loadSiblings( infile, current );
|
|
|
|
return current;
|
|
}
|
|
#endif // ANTLR_SUPPORT_XML
|
|
|
|
#ifdef ANTLR_CXX_SUPPORTS_NAMESPACE
|
|
}
|
|
#endif
|
|
|
|
/* Heterogeneous AST/XML-I/O ramblings...
|
|
*
|
|
* So there is some heterogeneous AST support....
|
|
* basically in the code generators a new custom ast is generated without
|
|
* going throug the factory. It also expects the RefXAST to be defined.
|
|
*
|
|
* Is it maybe better to register all AST types with the ASTFactory class
|
|
* together with the respective factory methods.
|
|
*
|
|
* More and more I get the impression that hetero ast was a kindoff hack
|
|
* on top of ANTLR's normal AST system.
|
|
*
|
|
* The heteroast stuff will generate trouble for all astFactory.create( ... )
|
|
* invocations. Most of this is handled via getASTCreateString methods in the
|
|
* codegenerator. At the moment getASTCreateString(GrammarAtom, String) has
|
|
* slightly to little info to do it's job (ok the hack that is in now
|
|
* works, but it's an ugly hack)
|
|
*
|
|
* An extra caveat is the 'nice' action.g thing. Which also judiciously calls
|
|
* getASTCreateString methods because it handles the #( ... ) syntax.
|
|
* And converts that to ASTFactory calls.
|
|
*
|
|
*
|
|
*/
|