kicad/eeschema/sch_sexpr_parser.cpp

1358 lines
34 KiB
C++

/*
* This program source code file is part of KiCad, a free EDA CAD application.
*
* Copyright (C) 2020 CERN
*
* @author Wayne Stambaugh <stambaughw@gmail.com>
*
* 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
*/
/**
* @file sch_sexpr_parser.cpp
* @brief Schematic and symbol library s-expression file format parser implementations.
*/
#include <wx/tokenzr.h>
#include <class_libentry.h>
#include <lib_arc.h>
#include <lib_bezier.h>
#include <lib_circle.h>
#include <lib_pin.h>
#include <lib_polyline.h>
#include <lib_rectangle.h>
#include <lib_text.h>
#include <sch_file_versions.h>
#include <sch_sexpr_parser.h>
#include <template_fieldnames.h>
using namespace TSYMBOL_LIB_T;
SCH_SEXPR_PARSER::SCH_SEXPR_PARSER( LINE_READER* aLineReader ) :
SCHEMATIC_LEXER( aLineReader ),
m_requiredVersion( 0 ),
m_unit( 1 ),
m_convert( 1 )
{
}
bool SCH_SEXPR_PARSER::IsTooRecent() const
{
return m_requiredVersion && m_requiredVersion > SEXPR_SYMBOL_LIB_FILE_VERSION;
}
void SCH_SEXPR_PARSER::ParseLib( LIB_PART_MAP& aSymbolLibMap )
{
T token;
NeedLEFT();
NextTok();
parseHeader();
for( token = NextTok(); token != T_RIGHT; token = NextTok() )
{
if( token != T_LEFT )
Expecting( T_LEFT );
token = NextTok();
if( token == T_symbol )
{
m_unit = 1;
m_convert = 1;
LIB_PART* symbol = ParseSymbol( aSymbolLibMap );
aSymbolLibMap[symbol->GetName()] = symbol;
}
else
{
Expecting( "symbol" );
}
}
}
LIB_PART* SCH_SEXPR_PARSER::ParseSymbol( LIB_PART_MAP& aSymbolLibMap )
{
wxCHECK_MSG( CurTok() == T_symbol, nullptr,
wxT( "Cannot parse " ) + GetTokenString( CurTok() ) + wxT( " as a symbol." ) );
T token;
long tmp;
wxString error;
wxString name;
LIB_ITEM* item;
std::unique_ptr<LIB_PART> symbol( new LIB_PART( wxEmptyString ) );
symbol->SetUnitCount( 1 );
m_fieldId = MANDATORY_FIELDS;
token = NextTok();
if( !IsSymbol( token ) )
{
error.Printf( _( "Invalid symbol name in\nfile: \"%s\"\nline: %d\noffset: %d" ),
CurSource().c_str(), CurLineNumber(), CurOffset() );
THROW_IO_ERROR( error );
}
name = FromUTF8();
if( name.IsEmpty() )
{
error.Printf( _( "Empty symbol name in\nfile: \"%s\"\nline: %d\noffset: %d" ),
CurSource().c_str(), CurLineNumber(), CurOffset() );
THROW_IO_ERROR( error );
}
m_symbolName = name;
symbol->SetName( name );
for( token = NextTok(); token != T_RIGHT; token = NextTok() )
{
if( token != T_LEFT )
Expecting( T_LEFT );
token = NextTok();
switch( token )
{
case T_pin_names:
parsePinNames( symbol );
break;
case T_pin_numbers:
token = NextTok();
if( token != T_hide )
Expecting( "hide" );
symbol->SetShowPinNumbers( false );
NeedRIGHT();
break;
case T_property:
parseProperty( symbol );
break;
case T_extends:
{
token = NextTok();
if( !IsSymbol( token ) )
{
error.Printf(
_( "Invalid symbol extends name in\nfile: \"%s\"\nline: %d\noffset: %d" ),
CurSource().c_str(), CurLineNumber(), CurOffset() );
THROW_IO_ERROR( error );
}
name = FromUTF8();
auto it = aSymbolLibMap.find( name );
if( it == aSymbolLibMap.end() )
{
error.Printf(
_( "No parent for extended symbol %s in\nfile: \"%s\"\nline: %d\noffset: %d" ),
name.c_str(), CurSource().c_str(), CurLineNumber(), CurOffset() );
THROW_IO_ERROR( error );
}
symbol->SetParent( it->second );
NeedRIGHT();
break;
}
case T_symbol:
{
token = NextTok();
if( !IsSymbol( token ) )
{
error.Printf(
_( "Invalid symbol unit name in\nfile: \"%s\"\nline: %d\noffset: %d" ),
CurSource().c_str(), CurLineNumber(), CurOffset() );
THROW_IO_ERROR( error );
}
name = FromUTF8();
if( !name.StartsWith( m_symbolName ) )
{
error.Printf(
_( "Invalid symbol unit name prefix %s in\nfile: \"%s\"\nline: %d\noffset: %d" ),
name.c_str(), CurSource().c_str(), CurLineNumber(), CurOffset() );
THROW_IO_ERROR( error );
}
name = name.Right( name.Length() - m_symbolName.Length() - 1 );
wxStringTokenizer tokenizer( name, "_" );
if( tokenizer.CountTokens() != 2 )
{
error.Printf(
_( "Invalid symbol unit name suffix %s in\nfile: \"%s\"\nline: %d\noffset: %d" ),
name.c_str(), CurSource().c_str(), CurLineNumber(), CurOffset() );
THROW_IO_ERROR( error );
}
if( !tokenizer.GetNextToken().ToLong( &tmp ) )
{
error.Printf(
_( "Invalid symbol unit number %s in\nfile: \"%s\"\nline: %d\noffset: %d" ),
name.c_str(), CurSource().c_str(), CurLineNumber(), CurOffset() );
THROW_IO_ERROR( error );
}
m_unit = static_cast<int>( tmp );
if( !tokenizer.GetNextToken().ToLong( &tmp ) )
{
error.Printf(
_( "Invalid symbol convert number %s in\nfile: \"%s\"\nline: %d\noffset: %d" ),
name.c_str(), CurSource().c_str(), CurLineNumber(), CurOffset() );
THROW_IO_ERROR( error );
}
m_convert = static_cast<int>( tmp );
if( m_convert > 1 )
symbol->SetConversion( true, false );
if( m_unit > symbol->GetUnitCount() )
symbol->SetUnitCount( m_unit, false );
for( token = NextTok(); token != T_RIGHT; token = NextTok() )
{
if( token != T_LEFT )
Expecting( T_LEFT );
token = NextTok();
switch( token )
{
case T_arc:
case T_bezier:
case T_circle:
case T_pin:
case T_polyline:
case T_rectangle:
case T_text:
item = ParseDrawItem();
wxCHECK_MSG( item, nullptr, "Invalid draw item pointer." );
item->SetParent( symbol.get() );
symbol->AddDrawItem( item );
break;
default:
Expecting( "arc, bezier, circle, pin, polyline, rectangle, or text" );
};
}
m_unit = 1;
m_convert = 1;
break;
}
case T_arc:
case T_bezier:
case T_circle:
case T_pin:
case T_polyline:
case T_rectangle:
case T_text:
item = ParseDrawItem();
wxCHECK_MSG( item, nullptr, "Invalid draw item pointer." );
item->SetParent( symbol.get() );
symbol->AddDrawItem( item );
break;
default:
Expecting( "pin_names, pin_numbers, arc, bezier, circle, pin, polyline, "
"rectangle, or text" );
}
}
m_symbolName.clear();
return symbol.release();
}
LIB_ITEM* SCH_SEXPR_PARSER::ParseDrawItem()
{
switch( CurTok() )
{
case T_arc:
return static_cast<LIB_ITEM*>( parseArc() );
break;
case T_bezier:
return static_cast<LIB_ITEM*>( parseBezier() );
break;
case T_circle:
return static_cast<LIB_ITEM*>( parseCircle() );
break;
case T_pin:
return static_cast<LIB_ITEM*>( parsePin() );
break;
case T_polyline:
return static_cast<LIB_ITEM*>( parsePolyLine() );
break;
case T_rectangle:
return static_cast<LIB_ITEM*>( parseRectangle() );
break;
case T_text:
return static_cast<LIB_TEXT*>( parseText() );
break;
default:
Expecting( "arc, bezier, circle, pin, polyline, rectangle, or text" );
}
return nullptr;
}
double SCH_SEXPR_PARSER::parseDouble()
{
char* tmp;
errno = 0;
double fval = strtod( CurText(), &tmp );
if( errno )
{
wxString error;
error.Printf( _( "Invalid floating point number in\nfile: \"%s\"\nline: %d\noffset: %d" ),
CurSource().c_str(), CurLineNumber(), CurOffset() );
THROW_IO_ERROR( error );
}
if( CurText() == tmp )
{
wxString error;
error.Printf( _( "Missing floating point number in\nfile: \"%s\"\nline: %d\noffset: %d" ),
CurSource().c_str(), CurLineNumber(), CurOffset() );
THROW_IO_ERROR( error );
}
return fval;
}
FILL_T SCH_SEXPR_PARSER::parseFillMode()
{
wxCHECK_MSG( CurTok() == T_fill, NO_FILL,
wxT( "Cannot parse " ) + GetTokenString( CurTok() ) + wxT( " as fill." ) );
NeedLEFT();
T token = NextTok();
if( token != T_type )
Expecting( "type" );
token = NextTok();
FILL_T fillType = NO_FILL;
switch( token )
{
case T_none:
fillType = NO_FILL;
break;
case T_outline:
fillType = FILLED_SHAPE;
break;
case T_background:
fillType = FILLED_WITH_BG_BODYCOLOR;
break;
default:
Expecting( "none, outline, or background" );
}
NeedRIGHT(); // Closes type token.
NeedRIGHT(); // Closes fill token.
return fillType;
}
void SCH_SEXPR_PARSER::parseEDA_TEXT( EDA_TEXT* aText )
{
wxCHECK_RET( aText && CurTok() == T_effects,
wxT( "Cannot parse " ) + GetTokenString( CurTok() ) + wxT( " as EDA_TEXT." ) );
T token;
for( token = NextTok(); token != T_RIGHT; token = NextTok() )
{
if( token == T_LEFT )
token = NextTok();
switch( token )
{
case T_font:
for( token = NextTok(); token != T_RIGHT; token = NextTok() )
{
if( token == T_LEFT )
token = NextTok();
switch( token )
{
case T_size:
{
wxSize sz;
sz.SetHeight( parseInternalUnits( "text height" ) );
sz.SetWidth( parseInternalUnits( "text width" ) );
aText->SetTextSize( sz );
NeedRIGHT();
break;
}
case T_thickness:
aText->SetThickness( parseInternalUnits( "text thickness" ) );
NeedRIGHT();
break;
case T_bold:
aText->SetBold( true );
break;
case T_italic:
aText->SetItalic( true );
break;
default:
Expecting( "size, bold, or italic" );
}
}
break;
case T_justify:
for( token = NextTok(); token != T_RIGHT; token = NextTok() )
{
switch( token )
{
case T_left:
aText->SetHorizJustify( GR_TEXT_HJUSTIFY_LEFT );
break;
case T_right:
aText->SetHorizJustify( GR_TEXT_HJUSTIFY_RIGHT );
break;
case T_top:
aText->SetVertJustify( GR_TEXT_VJUSTIFY_TOP );
break;
case T_bottom:
aText->SetVertJustify( GR_TEXT_VJUSTIFY_BOTTOM );
break;
case T_mirror:
aText->SetMirrored( true );
break;
default:
Expecting( "left, right, top, bottom, or mirror" );
}
}
break;
case T_hide:
aText->SetVisible( false );
break;
default:
Expecting( "font, justify, or hide" );
}
}
}
void SCH_SEXPR_PARSER::parseHeader()
{
wxCHECK_RET( CurTok() == T_kicad_symbol_lib,
wxT( "Cannot parse " ) + GetTokenString( CurTok() ) + wxT( " as a header." ) );
NeedLEFT();
T tok = NextTok();
if( tok == T_version )
{
m_requiredVersion = parseInt( FromUTF8().mb_str( wxConvUTF8 ) );
NeedRIGHT();
// Skip the host name and host build version information.
NeedLEFT();
NeedSYMBOL();
NeedSYMBOL();
NeedSYMBOL();
NeedRIGHT();
}
else
{
m_requiredVersion = SEXPR_SYMBOL_LIB_FILE_VERSION;
// Skip the host name and host build version information.
NeedSYMBOL();
NeedSYMBOL();
NeedRIGHT();
}
}
void SCH_SEXPR_PARSER::parsePinNames( std::unique_ptr<LIB_PART>& aSymbol )
{
wxCHECK_RET( CurTok() == T_pin_names,
wxT( "Cannot parse " ) + GetTokenString( CurTok() ) + wxT( " as a pin_name token." ) );
wxString error;
T token = NextTok();
if( token == T_LEFT )
{
token = NextTok();
if( token != T_offset )
Expecting( "offset" );
aSymbol->SetPinNameOffset( parseInternalUnits( "pin name offset" ) );
NeedRIGHT();
token = NextTok(); // Either ) or hide
}
if( token == T_hide )
{
aSymbol->SetShowPinNames( false );
NeedRIGHT();
}
else if( token != T_RIGHT )
{
error.Printf(
_( "Invalid symbol names definition in\nfile: \"%s\"\nline: %d\noffset: %d" ),
CurSource().c_str(), CurLineNumber(), CurOffset() );
THROW_IO_ERROR( error );
}
}
void SCH_SEXPR_PARSER::parseProperty( std::unique_ptr<LIB_PART>& aSymbol )
{
wxCHECK_RET( CurTok() == T_property,
wxT( "Cannot parse " ) + GetTokenString( CurTok() ) + wxT( " as a property token." ) );
wxString error;
wxString name;
wxString value;
std::unique_ptr<LIB_FIELD> tmp( new LIB_FIELD( MANDATORY_FIELDS ) );
T token = NextTok();
if( !IsSymbol( token ) )
{
error.Printf( _( "Invalid property name in\nfile: \"%s\"\nline: %d\noffset: %d" ),
CurSource().c_str(), CurLineNumber(), CurOffset() );
THROW_IO_ERROR( error );
}
name = FromUTF8();
if( name.IsEmpty() )
{
error.Printf( _( "Empty property name in\nfile: \"%s\"\nline: %d\noffset: %d" ),
CurSource().c_str(), CurLineNumber(), CurOffset() );
THROW_IO_ERROR( error );
}
token = NextTok();
if( !IsSymbol( token ) )
{
error.Printf( _( "Invalid property value in\nfile: \"%s\"\nline: %d\noffset: %d" ),
CurSource().c_str(), CurLineNumber(), CurOffset() );
THROW_IO_ERROR( error );
}
// Empty property values are valid.
value = FromUTF8();
LIB_FIELD* field;
if( name == "ki_reference" )
{
field = &aSymbol->GetReferenceField();
field->SetText( value );
}
else if( name == "ki_value" )
{
field = &aSymbol->GetValueField();
field->SetText( value );
}
else if( name == "ki_footprint" )
{
field = &aSymbol->GetFootprintField();
field->SetText( value );
}
else if( name == "ki_datasheet" )
{
field = aSymbol->GetField( DATASHEET );
aSymbol->SetDocFileName( value );
}
else if( name == "ki_keywords" )
{
// Not a LIB_FIELD object yet.
aSymbol->SetKeyWords( value );
field = tmp.get();
}
else if( name == "ki_description" )
{
// Not a LIB_FIELD object yet.
aSymbol->SetDescription( value );
field = tmp.get();
}
else if( name == "ki_fp_filters" )
{
// Not a LIB_FIELD object yet.
wxArrayString filters;
wxStringTokenizer tokenizer( value );
while( tokenizer.HasMoreTokens() )
filters.Add( tokenizer.GetNextToken() );
aSymbol->SetFootprintFilters( filters );
field = tmp.get();
}
else if( name == "ki_locked" )
{
aSymbol->LockUnits( true );
field = tmp.get();
}
else
{
field = aSymbol->FindField( name );
if( !field )
{
field = new LIB_FIELD( m_fieldId, name );
aSymbol->AddDrawItem( field );
m_fieldId += 1;
}
field->SetText( value );
}
for( token = NextTok(); token != T_RIGHT; token = NextTok() )
{
if( token != T_LEFT )
Expecting( T_LEFT );
token = NextTok();
switch( token )
{
case T_at:
field->SetPosition( parseXY() );
field->SetTextAngle( static_cast<int>( parseDouble( "text angle" ) * 10.0 ) );
NeedRIGHT();
break;
case T_effects:
parseEDA_TEXT( static_cast<EDA_TEXT*>( field ) );
break;
default:
Expecting( "at or effects" );
}
}
}
LIB_ARC* SCH_SEXPR_PARSER::parseArc()
{
wxCHECK_MSG( CurTok() == T_arc, nullptr,
wxT( "Cannot parse " ) + GetTokenString( CurTok() ) + wxT( " as an arc token." ) );
T token;
wxPoint startPoint;
wxPoint midPoint;
wxPoint endPoint;
wxPoint pos;
bool hasMidPoint = false;
std::unique_ptr<LIB_ARC> arc( new LIB_ARC( nullptr ) );
arc->SetUnit( m_unit );
arc->SetConvert( m_convert );
for( token = NextTok(); token != T_RIGHT; token = NextTok() )
{
if( token != T_LEFT )
Expecting( T_LEFT );
token = NextTok();
switch( token )
{
case T_start:
startPoint = parseXY();
NeedRIGHT();
break;
case T_mid:
midPoint = parseXY();
NeedRIGHT();
hasMidPoint = true;
break;
case T_end:
endPoint = parseXY();
NeedRIGHT();
break;
case T_radius:
for( token = NextTok(); token != T_RIGHT; token = NextTok() )
{
if( token != T_LEFT )
Expecting( T_LEFT );
token = NextTok();
switch( token )
{
case T_at:
pos = parseXY();
NeedRIGHT();
break;
case T_length:
arc->SetRadius( parseInternalUnits( "radius length" ) );
NeedRIGHT();
break;
case T_angles:
{
int angle1 = KiROUND( parseDouble( "start radius angle" ) * 10.0 );
int angle2 = KiROUND( parseDouble( "end radius angle" ) * 10.0 );
NORMALIZE_ANGLE_POS( angle1 );
NORMALIZE_ANGLE_POS( angle2 );
arc->SetFirstRadiusAngle( angle1 );
arc->SetSecondRadiusAngle( angle2 );
NeedRIGHT();
break;
}
default:
Expecting( "at, length, or angle" );
}
}
break;
case T_stroke:
NeedLEFT();
token = NextTok();
if( token != T_width )
Expecting( "width" );
arc->SetWidth( parseInternalUnits( "stroke width" ) );
NeedRIGHT(); // Closes width token;
NeedRIGHT(); // Closes stroke token;
break;
case T_fill:
arc->SetFillMode( parseFillMode() );
break;
default:
Expecting( "start, end, radius, stroke, or fill" );
}
}
arc->SetPosition( pos );
arc->SetStart( startPoint );
arc->SetEnd( endPoint );
if( hasMidPoint )
{
VECTOR2I center = GetArcCenter( arc->GetStart(), midPoint, arc->GetEnd() );
arc->SetPosition( wxPoint( center.x, center.y ) );
// @todo Calculate the radius.
arc->CalcRadiusAngles();
}
return arc.release();
}
LIB_BEZIER* SCH_SEXPR_PARSER::parseBezier()
{
wxCHECK_MSG( CurTok() == T_bezier, nullptr,
wxT( "Cannot parse " ) + GetTokenString( CurTok() ) + wxT( " as a bezier." ) );
T token;
std::unique_ptr<LIB_BEZIER> bezier( new LIB_BEZIER( nullptr ) );
bezier->SetUnit( m_unit );
bezier->SetConvert( m_convert );
for( token = NextTok(); token != T_RIGHT; token = NextTok() )
{
if( token != T_LEFT )
Expecting( T_LEFT );
token = NextTok();
switch( token )
{
case T_pts:
for( token = NextTok(); token != T_RIGHT; token = NextTok() )
{
if( token != T_LEFT )
Expecting( T_LEFT );
token = NextTok();
if( token != T_xy )
Expecting( "xy" );
bezier->AddPoint( parseXY() );
NeedRIGHT();
}
break;
case T_stroke:
NeedLEFT();
token = NextTok();
if( token != T_width )
Expecting( "width" );
bezier->SetWidth( parseInternalUnits( "stroke width" ) );
NeedRIGHT(); // Closes width token;
NeedRIGHT(); // Closes stroke token;
break;
case T_fill:
bezier->SetFillMode( parseFillMode() );
break;
default:
Expecting( "pts, stroke, or fill" );
}
}
return bezier.release();
}
LIB_CIRCLE* SCH_SEXPR_PARSER::parseCircle()
{
wxCHECK_MSG( CurTok() == T_circle, nullptr,
wxT( "Cannot parse " ) + GetTokenString( CurTok() ) + wxT( " as a circle token." ) );
T token;
std::unique_ptr<LIB_CIRCLE> circle( new LIB_CIRCLE( nullptr ) );
circle->SetUnit( m_unit );
circle->SetConvert( m_convert );
for( token = NextTok(); token != T_RIGHT; token = NextTok() )
{
if( token != T_LEFT )
Expecting( T_LEFT );
token = NextTok();
switch( token )
{
case T_center:
circle->SetPosition( parseXY() );
NeedRIGHT();
break;
case T_radius:
circle->SetRadius( parseInternalUnits( "radius length" ) );
NeedRIGHT();
break;
case T_stroke:
NeedLEFT();
token = NextTok();
if( token != T_width )
Expecting( "width" );
circle->SetWidth( parseInternalUnits( "stroke width" ) );
NeedRIGHT(); // Closes width token;
NeedRIGHT(); // Closes stroke token;
break;
case T_fill:
circle->SetFillMode( parseFillMode() );
break;
default:
Expecting( "start, end, radius, stroke, or fill" );
}
}
return circle.release();
}
LIB_PIN* SCH_SEXPR_PARSER::parsePin()
{
wxCHECK_MSG( CurTok() == T_pin, nullptr,
wxT( "Cannot parse " ) + GetTokenString( CurTok() ) + wxT( " as a pin token." ) );
T token;
wxString tmp;
wxString error;
std::unique_ptr<LIB_PIN> pin( new LIB_PIN( nullptr ) );
pin->SetUnit( m_unit );
pin->SetConvert( m_convert );
// Pin electrical type.
token = NextTok();
switch( token )
{
case T_input:
pin->SetType( ELECTRICAL_PINTYPE::PT_INPUT );
break;
case T_output:
pin->SetType( ELECTRICAL_PINTYPE::PT_OUTPUT );
break;
case T_bidirectional:
pin->SetType( ELECTRICAL_PINTYPE::PT_BIDI );
break;
case T_tri_state:
pin->SetType( ELECTRICAL_PINTYPE::PT_TRISTATE );
break;
case T_passive:
pin->SetType( ELECTRICAL_PINTYPE::PT_PASSIVE );
break;
case T_unspecified:
pin->SetType( ELECTRICAL_PINTYPE::PT_UNSPECIFIED );
break;
case T_power_in:
pin->SetType( ELECTRICAL_PINTYPE::PT_POWER_IN );
break;
case T_power_out:
pin->SetType( ELECTRICAL_PINTYPE::PT_POWER_OUT );
break;
case T_open_collector:
pin->SetType( ELECTRICAL_PINTYPE::PT_OPENCOLLECTOR );
break;
case T_open_emitter:
pin->SetType( ELECTRICAL_PINTYPE::PT_OPENEMITTER );
break;
case T_unconnected:
pin->SetType( ELECTRICAL_PINTYPE::PT_NC );
break;
default:
Expecting( "input, output, bidirectional, tri_state, passive, unspecified, "
"power_in, power_out, open_collector, open_emitter, or unconnected" );
}
// Pin shape.
token = NextTok();
switch( token )
{
case T_line:
pin->SetShape( GRAPHIC_PINSHAPE::LINE );
break;
case T_inverted:
pin->SetShape( GRAPHIC_PINSHAPE::INVERTED );
break;
case T_clock:
pin->SetShape( GRAPHIC_PINSHAPE::CLOCK );
break;
case T_inverted_clock:
pin->SetShape( GRAPHIC_PINSHAPE::INVERTED_CLOCK );
break;
case T_input_low:
pin->SetShape( GRAPHIC_PINSHAPE::INPUT_LOW );
break;
case T_clock_low:
pin->SetShape( GRAPHIC_PINSHAPE::CLOCK_LOW );
break;
case T_output_low:
pin->SetShape( GRAPHIC_PINSHAPE::OUTPUT_LOW );
break;
case T_edge_clock_high:
pin->SetShape( GRAPHIC_PINSHAPE::FALLING_EDGE_CLOCK );
break;
case T_non_logic:
pin->SetShape( GRAPHIC_PINSHAPE::NONLOGIC );
break;
default:
Expecting( "line, inverted, clock, inverted_clock, input_low, clock_low, "
"output_low, edge_clock_high, non_logic" );
}
for( token = NextTok(); token != T_RIGHT; token = NextTok() )
{
if( token == T_hide )
{
pin->SetVisible( false );
continue;
}
if( token != T_LEFT )
Expecting( T_LEFT );
token = NextTok();
switch( token )
{
case T_at:
pin->SetPosition( parseXY() );
switch( parseInt( "pin orientation" ) )
{
case 0:
pin->SetOrientation( PIN_RIGHT );
break;
case 90:
pin->SetOrientation( PIN_UP );
break;
case 180:
pin->SetOrientation( PIN_LEFT );
break;
case 270:
pin->SetOrientation( PIN_DOWN );
break;
default:
Expecting( "0, 90, 180, or 270" );
}
NeedRIGHT();
break;
case T_length:
pin->SetLength( parseInternalUnits( "pin length" ) );
NeedRIGHT();
break;
case T_name:
token = NextTok();
if( !IsSymbol( token ) )
{
error.Printf( _( "Invalid pin name in\nfile: \"%s\"\nline: %d\noffset: %d" ),
CurSource().c_str(), CurLineNumber(), CurOffset() );
THROW_IO_ERROR( error );
}
pin->SetName( FromUTF8() );
token = NextTok();
if( token != T_RIGHT )
{
token = NextTok();
if( token == T_effects )
{
// The EDA_TEXT font effects formatting is used so use and EDA_TEXT object
// so duplicate parsing is not required.
EDA_TEXT text;
parseEDA_TEXT( &text );
pin->SetNameTextSize( text.GetTextHeight() );
NeedRIGHT();
}
else
{
Expecting( "effects" );
}
}
break;
case T_number:
token = NextTok();
if( !IsSymbol( token ) )
{
error.Printf( _( "Invalid pin number in\nfile: \"%s\"\nline: %d\noffset: %d" ),
CurSource().c_str(), CurLineNumber(), CurOffset() );
THROW_IO_ERROR( error );
}
pin->SetNumber( FromUTF8() );
token = NextTok();
if( token != T_RIGHT )
{
token = NextTok();
if( token == T_effects )
{
// The EDA_TEXT font effects formatting is used so use and EDA_TEXT object
// so duplicate parsing is not required.
EDA_TEXT text;
parseEDA_TEXT( &text );
pin->SetNumberTextSize( text.GetTextHeight(), false );
NeedRIGHT();
}
else
{
Expecting( "effects" );
}
}
break;
default:
Expecting( "at, name, number, or length" );
}
}
return pin.release();
}
LIB_POLYLINE* SCH_SEXPR_PARSER::parsePolyLine()
{
wxCHECK_MSG( CurTok() == T_polyline, nullptr,
wxT( "Cannot parse " ) + GetTokenString( CurTok() ) + wxT( " as a polyline." ) );
T token;
std::unique_ptr<LIB_POLYLINE> polyLine( new LIB_POLYLINE( nullptr ) );
polyLine->SetUnit( m_unit );
polyLine->SetConvert( m_convert );
for( token = NextTok(); token != T_RIGHT; token = NextTok() )
{
if( token != T_LEFT )
Expecting( T_LEFT );
token = NextTok();
switch( token )
{
case T_pts:
for( token = NextTok(); token != T_RIGHT; token = NextTok() )
{
if( token != T_LEFT )
Expecting( T_LEFT );
token = NextTok();
if( token != T_xy )
Expecting( "xy" );
polyLine->AddPoint( parseXY() );
NeedRIGHT();
}
break;
case T_stroke:
NeedLEFT();
token = NextTok();
if( token != T_width )
Expecting( "width" );
polyLine->SetWidth( parseInternalUnits( "stroke width" ) );
NeedRIGHT(); // Closes width token;
NeedRIGHT(); // Closes stroke token;
break;
case T_fill:
polyLine->SetFillMode( parseFillMode() );
break;
default:
Expecting( "pts, stroke, or fill" );
}
}
return polyLine.release();
}
LIB_RECTANGLE* SCH_SEXPR_PARSER::parseRectangle()
{
wxCHECK_MSG( CurTok() == T_rectangle, nullptr,
wxT( "Cannot parse " ) + GetTokenString( CurTok() ) + wxT( " as a rectangle token." ) );
T token;
std::unique_ptr<LIB_RECTANGLE> rectangle( new LIB_RECTANGLE( nullptr ) );
rectangle->SetUnit( m_unit );
rectangle->SetConvert( m_convert );
for( token = NextTok(); token != T_RIGHT; token = NextTok() )
{
if( token != T_LEFT )
Expecting( T_LEFT );
token = NextTok();
switch( token )
{
case T_start:
rectangle->SetPosition( parseXY() );
NeedRIGHT();
break;
case T_end:
rectangle->SetEnd( parseXY() );
NeedRIGHT();
break;
case T_stroke:
NeedLEFT();
token = NextTok();
if( token != T_width )
Expecting( "width" );
rectangle->SetWidth( parseInternalUnits( "stroke width" ) );
NeedRIGHT(); // Closes width token;
NeedRIGHT(); // Closes stroke token;
break;
case T_fill:
rectangle->SetFillMode( parseFillMode() );
break;
default:
Expecting( "start, end, stroke, or fill" );
}
}
return rectangle.release();
}
LIB_TEXT* SCH_SEXPR_PARSER::parseText()
{
wxCHECK_MSG( CurTok() == T_text, nullptr,
wxT( "Cannot parse " ) + GetTokenString( CurTok() ) + wxT( " as a text token." ) );
T token;
wxString tmp;
wxString error;
std::unique_ptr<LIB_TEXT> text( new LIB_TEXT( nullptr ) );
text->SetUnit( m_unit );
text->SetConvert( m_convert );
token = NextTok();
if( !IsSymbol( token ) )
{
error.Printf( _( "Invalid text string in\nfile: \"%s\"\nline: %d\noffset: %d" ),
CurSource().c_str(), CurLineNumber(), CurOffset() );
THROW_IO_ERROR( error );
}
text->SetText( FromUTF8() );
for( token = NextTok(); token != T_RIGHT; token = NextTok() )
{
if( token != T_LEFT )
Expecting( T_LEFT );
token = NextTok();
switch( token )
{
case T_at:
text->SetPosition( parseXY() );
text->SetTextAngle( parseDouble( "text angle" ) );
NeedRIGHT();
break;
case T_effects:
parseEDA_TEXT( static_cast<EDA_TEXT*>( text.get() ) );
break;
default:
Expecting( "at or effects" );
}
}
return text.release();
}