* Added the basic structure to the Sweet parser in sch_part.cpp.
  * Got inheritence working off of the 'extends' keyword and PART::inherit()
  * Tossed the units support out of sweet.keywords, since we agreed to go dimensionless.
++richio:
  * Added the problemInputLine support to PARSE_ERROR, so UI can show the
    offending line of bytes.  Yes bytes, not even guaranteed to be characters.
This commit is contained in:
Dick Hollenbeck 2011-01-01 16:56:07 -06:00
parent e7d5770f42
commit 84ed5f501d
13 changed files with 381 additions and 76 deletions

View File

@ -4,6 +4,17 @@ KiCad ChangeLog 2010
Please add newer entries at the top, list the date and your name with
email address.
2011-Jan-1 UPDATE Dick Hollenbeck <dick@softplc.com>
================================================================================
++new:
* Added the basic structure to the Sweet parser in sch_part.cpp.
* Got inheritence working off of the 'extends' keyword and PART::inherit()
* Tossed the units support out of sweet.keywords, since we agreed to go dimensionless.
++richio:
* Added the problemInputLine support to PARSE_ERROR, so UI can show the
offending line of bytes. Yes bytes, not even guaranteed to be characters.
2010-dec-31 UPDATE Wayne Stambaugh <stambaughw@verizon.net>
================================================================================
++all

View File

@ -231,7 +231,7 @@ wxString DSNLEXER::GetTokenString( int aTok )
{
wxString ret;
ret << wxT("'") << wxConvertMB2WX( GetTokenText(aTok) ) << wxT("'");
ret << wxT("'") << wxString::FromUTF8( GetTokenText(aTok) ) << wxT("'");
return ret;
}
@ -249,24 +249,11 @@ bool DSNLEXER::IsSymbol( int aTok )
}
void DSNLEXER::ThrowIOError( wxString aText, int charOffset ) throw( IO_ERROR )
{
// @todo convert this to THROW_PARSE_ERROR()
// append to aText, do not overwrite
aText << wxT(" ") << _("in") << wxT(" \"") << CurSource()
<< wxT("\" ") << _("on line") << wxT(" ") << reader->LineNumber()
<< wxT(" ") << _("at offset") << wxT(" ") << charOffset;
THROW_IO_ERROR( aText );
}
void DSNLEXER::Expecting( int aTok ) throw( IO_ERROR )
{
wxString errText( _("Expecting") );
errText << wxT(" ") << GetTokenString( aTok );
ThrowIOError( errText, CurOffset() );
THROW_PARSE_ERROR( errText, CurSource(), CurLine(), CurLineNumber(), CurOffset() );
}
@ -274,7 +261,7 @@ void DSNLEXER::Expecting( const wxString& text ) throw( IO_ERROR )
{
wxString errText( _("Expecting") );
errText << wxT(" '") << text << wxT("'");
ThrowIOError( errText, CurOffset() );
THROW_PARSE_ERROR( errText, CurSource(), CurLine(), CurLineNumber(), CurOffset() );
}
@ -282,15 +269,22 @@ void DSNLEXER::Unexpected( int aTok ) throw( IO_ERROR )
{
wxString errText( _("Unexpected") );
errText << wxT(" ") << GetTokenString( aTok );
ThrowIOError( errText, CurOffset() );
THROW_PARSE_ERROR( errText, CurSource(), CurLine(), CurLineNumber(), CurOffset() );
}
void DSNLEXER::Duplicate( int aTok ) throw( IO_ERROR )
{
wxString errText;
errText.Printf( _("%s is a duplicate"), GetTokenString( aTok ).GetData() );
THROW_PARSE_ERROR( errText, CurSource(), CurLine(), CurLineNumber(), CurOffset() );
}
void DSNLEXER::Unexpected( const wxString& text ) throw( IO_ERROR )
{
wxString errText( _("Unexpected") );
errText << wxT(" '") << text << wxT("'");
ThrowIOError( errText, CurOffset() );
THROW_PARSE_ERROR( errText, CurSource(), CurLine(), CurLineNumber(), CurOffset() );
}
@ -408,7 +402,7 @@ L_read:
case '"':
break;
default:
ThrowIOError( errtxt, CurOffset() );
THROW_PARSE_ERROR( errtxt, CurSource(), CurLine(), CurLineNumber(), CurOffset() );
}
curText = cc;
@ -417,7 +411,7 @@ L_read:
if( head<limit && *head!=')' && *head!='(' && !isSpace(*head) )
{
ThrowIOError( errtxt, CurOffset() );
THROW_PARSE_ERROR( errtxt, CurSource(), CurLine(), CurLineNumber(), CurOffset() );
}
curTok = DSN_QUOTE_DEF;
@ -514,7 +508,7 @@ L_read:
}
wxString errtxt(_("Un-terminated delimited string") );
ThrowIOError( errtxt, CurOffset() );
THROW_PARSE_ERROR( errtxt, CurSource(), CurLine(), CurLineNumber(), CurOffset() );
#else // old code, did not understand nested quotes
++cur; // skip over the leading delimiter: ",', or $
@ -527,7 +521,7 @@ L_read:
if( head >= limit )
{
wxString errtxt(_("Un-terminated delimited string") );
ThrowIOError( errtxt, CurOffset() );
THROW_PARSE_ERROR( errtxt, CurSource(), CurLine(), CurLineNumber(), CurOffset() );
}
curText.clear();

View File

@ -330,13 +330,6 @@ public:
*/
static bool IsSymbol( int aTok );
/**
* Function ThrowIOError
* encapsulates the formatting of an error message which contains the exact
* location within the input file of something the caller is rejecting.
*/
void ThrowIOError( wxString aText, int charOffset ) throw( IO_ERROR );
/**
* Function Expecting
* throws an IO_ERROR exception with an input file specific error message.
@ -363,6 +356,16 @@ public:
*/
void Unexpected( int aTok ) throw( IO_ERROR );
/**
* Function Duplicate
* throws an IO_ERROR exception with a message saying specifically that aTok
* is a duplicate of one already seen in current context.
* @param aTok is the token/keyword type which was not expected at the
* current input location.
* @throw IO_ERROR with the location within the input file of the problem.
*/
void Duplicate( int aTok ) throw( IO_ERROR );
/**
* Function Unexpected
* throws an IO_ERROR exception with an input file specific error message.
@ -411,6 +414,16 @@ public:
return curText.c_str();
}
/**
* Function FromUTF8
* returns the current token text as a wxString, assuming that the input
* byte stream is UTF8 encoded.
*/
wxString FromUTF8()
{
return wxString::FromUTF8( curText.c_str() );
}
/**
* Function CurLineNumber
* returns the current line number within my LINE_READER
@ -420,6 +433,16 @@ public:
return reader->LineNumber();
}
/**
* Function CurLine
* returns the current line of text, from which the CurText() would return
* its token.
*/
const char* CurLine()
{
return (const char*)(*reader);
}
/**
* Function CurFilename
* returns the current LINE_READER source.
@ -433,7 +456,7 @@ public:
/**
* Function CurOffset
* returns the char offset within the current line, using a 1 based index.
* returns the byte offset within the current line, using a 1 based index.
* @return int - a one based index into the current line.
*/
int CurOffset()

View File

@ -46,8 +46,8 @@
*/
#define IO_FORMAT _( "IO_ERROR: '%s'\n from %s : %s" )
#define PARSE_FORMAT _( "PARSE_ERROR: '%s' in input/source '%s', line %d, offset %d\n from %s : %s" )
#define IO_FORMAT _( "IO_ERROR: %s\n from %s : %s" )
#define PARSE_FORMAT _( "PARSE_ERROR: %s in input/source \"%s\", line %d, offset %d\n from %s : %s" )
// references:
// http://stackoverflow.com/questions/2670816/how-can-i-use-the-compile-time-constant-line-in-a-string
@ -123,39 +123,50 @@ struct IO_ERROR // : std::exception
};
#define THROW_PARSE_ERROR( msg, input, line, offset ) throw PARSE_ERROR( __FILE__, __LOC__, msg, input, line, offset )
/**
* Class PARSE_ERROR
* contains a filename or source description, a line number, a character offset,
* and an error message.
* contains a filename or source description, a problem input line, a line number,
* a byte offset, and an error message which contains the the caller's report and his
* call site information: CPP source file, function, and line number.
* @author Dick Hollenbeck
*/
struct PARSE_ERROR : public IO_ERROR
{
// wxString errorText is still public from IO_ERROR
int lineNumber; ///< at which line number, 1 based index.
int byteIndex; ///< at which character position within the line, 1 based index
int lineNumber; ///< at which line number, 1 based index.
int byteIndex; ///< at which character position within the line, 1 based index
/// problem line of input [say, from a LINE_READER].
/// this is brought up in original byte format rather than wxString form, incase
/// there was a problem with the encoding, in which case converting to wxString is
/// not reliable in this context.
std::string inputLine;
/**
* Constructor
* which is normally called via the macro THROW_PARSE_ERROR so that
* __FILE__ and __LOC__ can be captured from the call site.
*/
PARSE_ERROR( const char* aThrowersFile, const char* aThrowersLoc,
const wxString& aMsg, const wxString& aSource,
const char* aInputLine,
int aLineNumber, int aByteIndex ) :
IO_ERROR()
{
init( aThrowersFile, aThrowersLoc, aMsg, aSource, aLineNumber, aByteIndex );
init( aThrowersFile, aThrowersLoc, aMsg, aSource, aInputLine, aLineNumber, aByteIndex );
}
void init( const char* aThrowersFile, const char* aThrowersLoc,
const wxString& aMsg, const wxString& aSource,
const char* aInputLine,
int aLineNumber, int aByteIndex )
{
// save line and offset in binary for Sweet text editor, which will catch exceptions
// save inpuLine, lineNumber, and offset for UI (.e.g. Sweet text editor)
inputLine = aInputLine;
lineNumber = aLineNumber;
byteIndex = aByteIndex;
// #define PARSE_FORMAT _( "PARSE_ERROR: %s in source %s, line %d, offset %d\nfrom cpp:%s func:%s" )
errorText.Printf( PARSE_FORMAT, aMsg.GetData(), aSource.GetData(),
aLineNumber, aByteIndex,
wxString::FromUTF8( aThrowersFile ).GetData(),
@ -165,6 +176,11 @@ struct PARSE_ERROR : public IO_ERROR
~PARSE_ERROR() throw ( /*none*/ ){}
};
#define THROW_PARSE_ERROR( aMsg, aSource, aInputLine, aLineNumber, aByteIndex ) \
throw PARSE_ERROR( __FILE__, __LOC__, aMsg, aSource, aInputLine, aLineNumber, aByteIndex )
/** @} exception_types */

View File

@ -16,8 +16,7 @@ for C in ${CATEGORIES}; do
for P in ${PARTS}; do
for R in ${REVS}; do
echo "#$R: (part $C/$P)" > $BASEDIR/$C/$P.part.$R
(part $C/$P/$R extends $P/$R (value 22)(footprint SM0805)) > $BASEDIR/$C/$P.part.$R
done
done
done

View File

@ -413,6 +413,12 @@ int main( int argc, char** argv )
lib_table.Test();
}
catch( PARSE_ERROR& ioe ) // most derived class first
{
printf( "%s\n", (const char*) ioe.errorText.ToUTF8() );
printf( "%s", ioe.inputLine.c_str() ); // rare not to have \n at end
printf( "%*s^\n", ioe.byteIndex-1, " " );
}
catch( IO_ERROR& ioe )
{
printf( "%s\n", (const char*) ioe.errorText.ToUTF8() );

View File

@ -187,6 +187,7 @@ LPID::LPID( const STRING& aLPID ) throw( PARSE_ERROR )
THROW_PARSE_ERROR(
_( "Illegal character found in LPID string" ),
wxConvertMB2WX( aLPID.c_str() ),
aLPID.c_str(),
0,
offset
);

View File

@ -59,7 +59,7 @@ class LPID // aka GUID
{
public:
LPID();
LPID() {}
/**
* Constructor LPID

View File

@ -21,22 +21,259 @@
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
*/
#include <wx/wx.h> // _()
#include <sch_part.h>
#include <sweet_lexer.h>
#include <wx/wx.h> // _()
#include <sch_lpid.h>
#include <sch_lib_table.h>
using namespace SCH;
void PART::Parse( SWEET_LEXER* aLexer, LIB_TABLE* aTable ) throw( PARSE_ERROR )
{
// Wayne's world, if he still wants it.
//-----<temporary home for PART sub objects, move after stable>------------------
//-----</temporary home for PART sub objects, move after stable>-----------------
/**
* Class PART_PARSER
* is a localized/hidden PART Parser. You get here through the public interface
* PART::Parse(). Advantages of private class declaration in this situation:
* 1) keeps all the recursive parsing helper functions out of the main public PART
* header file and so should speed up compilation.
* 2) Allows use of cost-less Java like inline functions, since nobody knows about
* them but this source file. Most are only used once and called from one place.
* <p>
* All the functions in this class throw PARSE_ERROR. If SWEET_LEXER throws, it
* may be an IO_ERROR, propogated from here also. The throws() statements are left off
* to keep the noise level down.
*/
class PART_PARSER
{
SWEET_LEXER* in;
LIB_TABLE* libs;
int contains;
public:
PART_PARSER( PART* aPart, SWEET_LEXER* aLexer, LIB_TABLE* aTable ) :
in( aLexer ),
libs( aTable ),
contains( 0 )
{
parsePart( aPart );
}
/// @param me = ja mir, the object getting stuffed, from its perspective
void parsePart( PART* me )
{
PART_T tok;
if( (tok = in->NextTok()) == T_LEFT )
tok = in->NextTok();
// a token "( part .." i.e. class PART
// Be flexible regarding the starting point of the stream.
// Caller may not have read the first two tokens out of the
// stream: T_LEFT and T_part, so ignore them if seen here.
// The 1st two tokens T_LEFT and T_part are then optional in the grammar.
if( tok == T_part )
{
in->NeedSYMBOLorNUMBER(); // read in part NAME_HINT, and toss
tok = in->NextTok();
}
// extends must be _first_ thing, if it is present at all, after NAME_HINT
if( tok == T_extends )
{
PART* base;
int offset;
if( contains & PB(EXTENDS) )
in->Duplicate( tok );
in->NeedSYMBOLorNUMBER();
me->setExtends( new LPID() );
offset = me->extends->Parse( in->CurText() );
if( offset > -1 ) // -1 is success
THROW_PARSE_ERROR( _("invalid extends LPID"),
in->CurSource(),
in->CurLine(),
in->CurLineNumber(),
in->CurOffset() + offset );
// we could be going in circles here, recursively, @todo add a max counter or stack chain
base = libs->LookupPart( *me->extends, me->Owner() );
me->inherit( *base );
contains |= PB(EXTENDS);
tok = in->NextTok();
}
for( ; tok!=T_RIGHT && tok!=T_EOF; tok = in->NextTok() )
{
if( tok == T_LEFT )
tok = in->NextTok();
switch( tok )
{
default:
// describe what we expect at this level
in->Expecting( wxT(
"anchor|value|footprint|model|keywords|alternates\n"
"|property\n"
" |property_del\n"
"|pin\n"
" |pin_merge|pin_swap|pin_renum|pin_rename|route_pin_swap\n"
"|polyline|line|rectangle|circle|arc|bezier|text"
)
);
break;
case T_anchor:
if( contains & PB(ANCHOR) )
in->Duplicate( tok );
contains |= PB(ANCHOR);
break;
/*
case T_value:
if( contains & PB(VALUE) )
in->Duplicate( tok );
contains |= PB(VALUE);
in->NeedSYMBOLorNUMBER();
// me->value = in->CurText();
in->NeedRIGHT();
break;
case T_footprint:
break;
case T_model:
break;
case T_keywords:
break;
case T_alternates:
break;
case T_property:
break;
case T_property_del:
break;
case T_pin:
break;
case T_pin_merge:
break;
case T_pin_swap:
break;
case T_pin_renum:
break;
case T_pin_rename:
break;
case T_route_pin_swap:
break;
case T_polyline:
break;
case T_line:
break;
case T_rectangle:
break;
case T_circle:
break;
case T_arc:
break;
case T_bezier:
break;
case T_text:
break;
*/
// Not sure about reference in a PART, comes in at COMPONENT object.
// It is maybe just a hint here or a prefix.
case T_reference:
if( contains & PB(REFERENCE) )
in->Duplicate( tok );
contains |= PB(REFERENCE);
break;
}
}
}
void parseAt( PART* me )
{
}
};
PART::PART( LIB* aOwner, const STRING& aPartName, const STRING& aRevision ) :
owner( aOwner ),
contains( 0 ),
partName( aPartName ),
revision( aRevision ),
extends( 0 )
{}
PART::~PART()
{
delete extends;
}
void PART::setExtends( LPID* aLPID )
{
delete extends;
extends = aLPID;
}
void PART::inherit( const PART& other )
{
contains = other.contains;
setExtends( other.extends ? new LPID( *other.extends ) : 0 );
body = other.body;
}
PART& PART::operator=( const PART& other )
{
owner = other.owner;
partName = other.partName;
revision = other.revision;
// maintain inherit() as a partial assignment operator.
inherit( other );
return *this;
}
void PART::Parse( SWEET_LEXER* aLexer, LIB_TABLE* aTable ) throw( IO_ERROR )
{
PART_PARSER( this, aLexer, aTable );
}
#if 0 && defined(DEBUG)
int main( int argc, char** argv )

View File

@ -28,9 +28,13 @@
#include <sch_lib.h>
class SWEET_LEXER;
class PART_PARSER;
namespace SCH {
class LPID;
/**
* Enum PartBit
* is a set of bit positions that can be used to create flag bits within
@ -39,9 +43,16 @@ namespace SCH {
*/
enum PartBit
{
BODY, ///< body has been read in.
PARSED, ///< have parsed this part already, otherwise 'body' text must be parsed
EXTENDS, ///< saw and "extends" keyword, inheriting from another PART
VALUE,
ANCHOR,
REFERENCE,
FOOTPRINT,
DATASHEET,
MODEL,
KEYWORDS,
};
/// Function PB
@ -63,34 +74,40 @@ static inline const int PB( PartBit oneBitOnly )
*/
class PART
{
/// LIB class has great license to modify what's in here, nobody else does.
/// Modification is done through the LIB so it can track the state of the
/// PART and take action as needed. Actually most of the modification will
/// be done by PARTS_LIST, a class derived from LIB.
friend class LIB;
/// a private constructor, only a LIB can instantiate a PART.
PART( LIB* aOwner, const STRING& aPartName, const STRING& aRevision ) :
owner( aOwner ),
contains( 0 ),
partName( aPartName ),
revision( aRevision )
{}
friend class LIB; // is the owner of all PARTS, afterall
friend class ::PART_PARSER;
protected: // not likely to have C++ descendants, but protected none-the-less.
/// a protected constructor, only a LIB can instantiate a PART.
PART( LIB* aOwner, const STRING& aPartName, const STRING& aRevision );
/**
* Function inherit
* is a specialized assignment function that copies a specific subset, enough
* to fulfill the requirements of the Sweet s-expression language.
*/
void inherit( const PART& aBasePart );
//PART( LIB* aOwner );
LIB* owner; ///< which LIB am I a part of (pun if you want)
int contains; ///< has bits from Enum PartParts
STRING extends; ///< LPID of base part
STRING partName; ///< example "passives/R", immutable.
STRING revision; // @todo need a single search key, this won't do.
LPID* extends; ///< of base part, NULL if none, otherwise I own it.
/// encapsulate the old version deletion, take ownership of @a aLPID
void setExtends( LPID* aLPID );
/// s-expression text for the part, initially empty, and read in as this part
/// actually becomes cached in RAM.
STRING body;
// 3 separate lists for speed:
/// A property list.
@ -105,16 +122,13 @@ protected: // not likely to have C++ descendants, but protected none-the-le
/// Alternate body forms.
//ALTERNATES alternates;
// lots of other stuff, like the mandatory properties.
// lots of other stuff, like the mandatory properties, but no units, since we went with dimensionless
public:
/**
* Function Inherit
* is a specialized assignment function that copies a specific subset, enough
* to fulfill the requirements of the Sweet s-expression language.
*/
void Inherit( const PART& aBasePart );
~PART();
PART& operator=( const PART& other );
/**
* Function Owner
@ -122,6 +136,7 @@ public:
*/
LIB* Owner() { return owner; }
/**
* Function Parse
* translates a Sweet string into a binary form that is represented
@ -133,7 +148,7 @@ public:
* @param aLibTable is the LIB_TABLE view that is in effect for inheritance,
* and comes from the big containing SCHEMATIC object.
*/
void Parse( SWEET_LEXER* aLexer, LIB_TABLE* aTable ) throw( PARSE_ERROR );
void Parse( SWEET_LEXER* aLexer, LIB_TABLE* aTable ) throw( IO_ERROR );
/*
void SetBody( const STR_UTF& aSExpression )

View File

@ -25,6 +25,7 @@ keywords
length
line
line_width
model
name
none
number
@ -38,6 +39,7 @@ pin_del
pin_merge
pin_rename
pin_renum
pin_swap
polyline
pos
power_in
@ -58,5 +60,6 @@ tristate
unconnected
units
unspecified
value
visible
xy

View File

@ -111,7 +111,7 @@ void WinEDA_PcbFrame::ExportToSpecctra( wxCommandEvent& event )
// if an exception is thrown by FromBOARD or ExportPCB(), then
// ~SPECCTRA_DB() will close the file.
}
catch( IO_ERROR ioe )
catch( IO_ERROR& ioe )
{
ok = false;

View File

@ -96,7 +96,7 @@ void WinEDA_PcbFrame::ImportSpecctraSession( wxCommandEvent& event )
db.LoadSESSION( fullFileName );
db.FromSESSION( GetBoard() );
}
catch( IO_ERROR ioe )
catch( IO_ERROR& ioe )
{
SetLocaleTo_Default( ); // revert to the current locale