291 lines
6.5 KiB
C++
291 lines
6.5 KiB
C++
|
/*
|
||
|
* This program source code file is part of KiCad, a free EDA CAD application.
|
||
|
*
|
||
|
* Copyright (C) 2012 SoftPLC Corporation, Dick Hollenbeck <dick@softplc.com>
|
||
|
* Copyright (C) 2012 KiCad Developers, see CHANGELOG.TXT for contributors.
|
||
|
*
|
||
|
* 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.
|
||
|
*
|
||
|
* This program 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 General Public License for more details.
|
||
|
*
|
||
|
* You should have received a copy of the GNU General Public License
|
||
|
* along with this program; if not, you may find one here:
|
||
|
* http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
|
||
|
* or you may search the http://www.gnu.org website for the version 2 license,
|
||
|
* or you may write to the Free Software Foundation, Inc.,
|
||
|
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
|
||
|
*/
|
||
|
|
||
|
|
||
|
// This is wanting to be an s-expression C++ parser generator. Feed it a sample
|
||
|
// file and maybe someday it will generate a C++ file which uses DSNLEXER to
|
||
|
// parse the described grammar OK.
|
||
|
|
||
|
// Until then, it is a non-specctra mode s-expression beautifier.
|
||
|
|
||
|
|
||
|
#include <assert.h>
|
||
|
#include <richio.h>
|
||
|
#include <dsnlexer.h>
|
||
|
#include <macros.h>
|
||
|
#include <boost/ptr_container/ptr_vector.hpp>
|
||
|
|
||
|
|
||
|
// http://sexpr.sourceforge.net/ see comments about graphviz
|
||
|
// http://www.codeproject.com/KB/recipes/JSON_Spirit.aspx
|
||
|
|
||
|
#define D(x) x
|
||
|
//#define D(x)
|
||
|
|
||
|
|
||
|
/**
|
||
|
* Class ELEM
|
||
|
*/
|
||
|
class ELEM
|
||
|
{
|
||
|
protected:
|
||
|
int token;
|
||
|
std::string text;
|
||
|
|
||
|
typedef boost::ptr_vector<ELEM> ELEMS;
|
||
|
typedef ELEMS::const_iterator ELEMS_CITER;
|
||
|
typedef ELEMS::iterator ELEMS_ITER;
|
||
|
|
||
|
ELEMS kids; ///< ELEM pointers
|
||
|
|
||
|
public:
|
||
|
|
||
|
// there are two constructors, one for a list, one for an atom
|
||
|
|
||
|
/// List constructor
|
||
|
ELEM( int aToken ) :
|
||
|
token( aToken ),
|
||
|
text( "" )
|
||
|
{
|
||
|
// D( printf( "ELEM%p: list\n", this ); )
|
||
|
}
|
||
|
|
||
|
/// Atom constructor
|
||
|
ELEM( const std::string& aText, int aToken ) :
|
||
|
token( aToken ),
|
||
|
text( aText )
|
||
|
{
|
||
|
// D( printf( "ELEM%p: '%s'\n", this, text.c_str() ); )
|
||
|
}
|
||
|
|
||
|
int Token() const { return token; }
|
||
|
|
||
|
const char* Text() { return text.c_str(); }
|
||
|
|
||
|
|
||
|
/**
|
||
|
* Function Format
|
||
|
* writes this object as ASCII out to an OUTPUTFORMATTER
|
||
|
* @param out The formatter to write to.
|
||
|
* @param nestLevel A multiple of the number of spaces to preceed the output with.
|
||
|
* @throw IO_ERROR if a system error writing the output, such as a full disk.
|
||
|
*/
|
||
|
void Format( OUTPUTFORMATTER* aFormatter, int aNestLevel = 0, int aControlBits = 0 );
|
||
|
|
||
|
#define CTL_OMIT_NL (1<<0)
|
||
|
|
||
|
/**
|
||
|
* Function Length
|
||
|
* returns the number of ELEMs in this ELEM.
|
||
|
* @return int - the count of children
|
||
|
*/
|
||
|
int Length() const
|
||
|
{
|
||
|
return kids.size();
|
||
|
}
|
||
|
|
||
|
void Append( ELEM* aElem )
|
||
|
{
|
||
|
kids.push_back( aElem );
|
||
|
}
|
||
|
|
||
|
ELEM* Replace( int aIndex, ELEM* aElem )
|
||
|
{
|
||
|
ELEMS::auto_type ret = kids.replace( aIndex, aElem );
|
||
|
return ret.release();
|
||
|
}
|
||
|
|
||
|
ELEM* Remove( int aIndex )
|
||
|
{
|
||
|
ELEMS::auto_type ret = kids.release( kids.begin()+aIndex );
|
||
|
return ret.release();
|
||
|
}
|
||
|
|
||
|
void Insert( int aIndex, ELEM* aElem )
|
||
|
{
|
||
|
kids.insert( kids.begin()+aIndex, aElem );
|
||
|
}
|
||
|
|
||
|
ELEM* At( int aIndex ) const
|
||
|
{
|
||
|
const ELEM& ref = kids.at( aIndex );
|
||
|
return (ELEM*) &ref;
|
||
|
}
|
||
|
|
||
|
ELEM* operator[]( int aIndex ) const
|
||
|
{
|
||
|
return At( aIndex );
|
||
|
}
|
||
|
|
||
|
void Delete( int aIndex )
|
||
|
{
|
||
|
kids.erase( kids.begin()+aIndex );
|
||
|
}
|
||
|
};
|
||
|
|
||
|
|
||
|
void ELEM::Format( OUTPUTFORMATTER* out, int nestLevel, int ctl )
|
||
|
{
|
||
|
if( token == DSN_LEFT ) // this is a list
|
||
|
{
|
||
|
out->Print( nestLevel, "(" );
|
||
|
|
||
|
const int count = Length();
|
||
|
for( int i=0; i<count; ++i )
|
||
|
{
|
||
|
ELEM* cur = At( i );
|
||
|
ELEM* next = i < count-1 ? At( i+1 ) : NULL;
|
||
|
|
||
|
if( i > 0 )
|
||
|
out->Print( 0, " " );
|
||
|
|
||
|
if( next && next->token == DSN_LEFT )
|
||
|
{
|
||
|
cur->Format( out, nestLevel+1, 0 );
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
cur->Format( out, nestLevel+1, CTL_OMIT_NL );
|
||
|
}
|
||
|
}
|
||
|
|
||
|
out->Print( 0, ")%s", ctl & CTL_OMIT_NL ? "" : "\n" );
|
||
|
}
|
||
|
else // this is an atom
|
||
|
{
|
||
|
const char* s = out->Quotes( text ).c_str();
|
||
|
out->Print( 0, "%s%s", s, ctl & CTL_OMIT_NL ? "" : "\n" );
|
||
|
}
|
||
|
}
|
||
|
|
||
|
ELEM* Scan( DSNLEXER* lex );
|
||
|
ELEM* ScanList( DSNLEXER* lex );
|
||
|
ELEM* ScanAtom( DSNLEXER* lex );
|
||
|
|
||
|
|
||
|
void usage()
|
||
|
{
|
||
|
fprintf( stderr, "Usage: parser_gen <grammar_s-expression_file>\n" );
|
||
|
exit( 1 );
|
||
|
}
|
||
|
|
||
|
|
||
|
static KEYWORD empty_keywords[] = {};
|
||
|
|
||
|
|
||
|
ELEM* Scan( DSNLEXER* lex )
|
||
|
{
|
||
|
ELEM* elem = NULL;
|
||
|
int tok = lex->CurTok();
|
||
|
|
||
|
// conditionally read first token.
|
||
|
if( tok == DSN_NONE )
|
||
|
tok = lex->NextTok();
|
||
|
|
||
|
if( tok == DSN_EOF )
|
||
|
{
|
||
|
lex->Unexpected( DSN_EOF );
|
||
|
}
|
||
|
|
||
|
if( tok == DSN_LEFT )
|
||
|
{
|
||
|
elem = ScanList( lex );
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
elem = ScanAtom( lex );
|
||
|
}
|
||
|
|
||
|
return elem;
|
||
|
}
|
||
|
|
||
|
|
||
|
/**
|
||
|
* Function ScanList
|
||
|
* reads and returns a sexpList from the input stream.
|
||
|
*/
|
||
|
ELEM* ScanList( DSNLEXER* lex )
|
||
|
{
|
||
|
int tok;
|
||
|
ELEM* list = NULL;
|
||
|
|
||
|
assert( lex->CurTok() == DSN_LEFT );
|
||
|
|
||
|
list = new ELEM( DSN_LEFT );
|
||
|
|
||
|
while( ( tok = lex->NextTok() ) != DSN_RIGHT )
|
||
|
{
|
||
|
if( tok == DSN_EOF )
|
||
|
lex->Unexpected( DSN_EOF );
|
||
|
|
||
|
ELEM* elem = Scan( lex );
|
||
|
list->Append( elem );
|
||
|
}
|
||
|
|
||
|
return list;
|
||
|
}
|
||
|
|
||
|
|
||
|
ELEM* ScanAtom( DSNLEXER* lex )
|
||
|
{
|
||
|
return new ELEM( lex->CurText(), lex->CurTok() );
|
||
|
}
|
||
|
|
||
|
|
||
|
int main( int argc, char** argv )
|
||
|
{
|
||
|
if( argc != 2 )
|
||
|
{
|
||
|
usage();
|
||
|
}
|
||
|
|
||
|
FILE* fp = fopen( argv[1], "rt" );
|
||
|
if( !fp )
|
||
|
{
|
||
|
fprintf( stderr, "Unable to open '%s'\n", argv[1] );
|
||
|
usage();
|
||
|
}
|
||
|
|
||
|
DSNLEXER lexer( empty_keywords, 0, fp, wxString( FROM_UTF8( argv[1] ) ) );
|
||
|
|
||
|
try
|
||
|
{
|
||
|
ELEM* elem = Scan( &lexer );
|
||
|
|
||
|
if( elem )
|
||
|
{
|
||
|
STRING_FORMATTER sf;
|
||
|
|
||
|
elem->Format( &sf, 0 );
|
||
|
|
||
|
printf( "%s", sf.GetString().c_str() );
|
||
|
}
|
||
|
}
|
||
|
catch( IO_ERROR ioe )
|
||
|
{
|
||
|
fprintf( stderr, "%s\n", TO_UTF8( ioe.errorText ) );
|
||
|
}
|
||
|
}
|
||
|
|