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.
		
		
		
		
		
			
		
			
				
					
					
						
							2735 lines
						
					
					
						
							73 KiB
						
					
					
				
			
		
		
		
			
			
			
		
		
	
	
							2735 lines
						
					
					
						
							73 KiB
						
					
					
				
								/*
							 | 
						|
								 * 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 3 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, see <http://www.gnu.org/licenses/>.
							 | 
						|
								 */
							 | 
						|
								
							 | 
						|
								/**
							 | 
						|
								 * @file sch_sexpr_parser.cpp
							 | 
						|
								 * @brief Schematic and symbol library s-expression file format parser implementations.
							 | 
						|
								 */
							 | 
						|
								
							 | 
						|
								// For some reason wxWidgets is built with wxUSE_BASE64 unset so expose the wxWidgets
							 | 
						|
								// base64 code.
							 | 
						|
								#define wxUSE_BASE64 1
							 | 
						|
								#include <wx/base64.h>
							 | 
						|
								#include <wx/mstream.h>
							 | 
						|
								#include <wx/tokenzr.h>
							 | 
						|
								
							 | 
						|
								#include <common.h>
							 | 
						|
								#include <lib_id.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_bitmap.h>
							 | 
						|
								#include <sch_bus_entry.h>
							 | 
						|
								#include <sch_component.h>
							 | 
						|
								#include <sch_edit_frame.h>          // CMP_ORIENT_XXX
							 | 
						|
								#include <sch_field.h>
							 | 
						|
								#include <sch_line.h>
							 | 
						|
								#include <sch_junction.h>
							 | 
						|
								#include <sch_no_connect.h>
							 | 
						|
								#include <sch_screen.h>
							 | 
						|
								#include <sch_sexpr_parser.h>
							 | 
						|
								#include <template_fieldnames.h>
							 | 
						|
								
							 | 
						|
								
							 | 
						|
								using namespace TSCHEMATIC_T;
							 | 
						|
								
							 | 
						|
								
							 | 
						|
								SCH_SEXPR_PARSER::SCH_SEXPR_PARSER( LINE_READER* aLineReader ) :
							 | 
						|
								    SCHEMATIC_LEXER( aLineReader ),
							 | 
						|
								    m_requiredVersion( 0 ),
							 | 
						|
								    m_fieldId( 0 ),
							 | 
						|
								    m_unit( 1 ),
							 | 
						|
								    m_convert( 1 )
							 | 
						|
								{
							 | 
						|
								}
							 | 
						|
								
							 | 
						|
								
							 | 
						|
								bool SCH_SEXPR_PARSER::parseBool()
							 | 
						|
								{
							 | 
						|
								    T token = NextTok();
							 | 
						|
								
							 | 
						|
								    if( token == T_yes )
							 | 
						|
								        return true;
							 | 
						|
								    else if( token == T_no )
							 | 
						|
								        return false;
							 | 
						|
								    else
							 | 
						|
								        Expecting( "yes or no" );
							 | 
						|
								
							 | 
						|
								    return false;
							 | 
						|
								}
							 | 
						|
								
							 | 
						|
								
							 | 
						|
								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( T_kicad_symbol_lib, SEXPR_SYMBOL_LIB_FILE_VERSION );
							 | 
						|
								
							 | 
						|
								    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, m_requiredVersion );
							 | 
						|
								            aSymbolLibMap[symbol->GetName()] = symbol;
							 | 
						|
								        }
							 | 
						|
								        else
							 | 
						|
								        {
							 | 
						|
								            Expecting( "symbol" );
							 | 
						|
								        }
							 | 
						|
								    }
							 | 
						|
								}
							 | 
						|
								
							 | 
						|
								
							 | 
						|
								LIB_PART* SCH_SEXPR_PARSER::ParseSymbol( LIB_PART_MAP& aSymbolLibMap, int aFileVersion )
							 | 
						|
								{
							 | 
						|
								    wxCHECK_MSG( CurTok() == T_symbol, nullptr,
							 | 
						|
								                 wxT( "Cannot parse " ) + GetTokenString( CurTok() ) + wxT( " as a symbol." ) );
							 | 
						|
								
							 | 
						|
								    T token;
							 | 
						|
								    long tmp;
							 | 
						|
								    wxString name;
							 | 
						|
								    wxString error;
							 | 
						|
								    LIB_ITEM* item;
							 | 
						|
								    std::unique_ptr<LIB_PART> symbol( new LIB_PART( wxEmptyString ) );
							 | 
						|
								
							 | 
						|
								    m_requiredVersion = aFileVersion;
							 | 
						|
								    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();
							 | 
						|
								
							 | 
						|
								    LIB_ID id;
							 | 
						|
								
							 | 
						|
								    if( id.Parse( name, LIB_ID::ID_SCH ) >= 0 )
							 | 
						|
								    {
							 | 
						|
								        error.Printf( _( "Invalid library identifier in\nfile: \"%s\"\nline: %d\noffset: %d" ),
							 | 
						|
								                      CurSource().c_str(), CurLineNumber(), CurOffset() );
							 | 
						|
								        THROW_IO_ERROR( error );
							 | 
						|
								    }
							 | 
						|
								
							 | 
						|
								    m_symbolName = id.GetLibItemName().wx_str();
							 | 
						|
								    symbol->SetName( m_symbolName );
							 | 
						|
								    symbol->SetLibId( id );
							 | 
						|
								
							 | 
						|
								    for( token = NextTok();  token != T_RIGHT;  token = NextTok() )
							 | 
						|
								    {
							 | 
						|
								        if( token != T_LEFT )
							 | 
						|
								            Expecting( T_LEFT );
							 | 
						|
								
							 | 
						|
								        token = NextTok();
							 | 
						|
								
							 | 
						|
								        switch( token )
							 | 
						|
								        {
							 | 
						|
								        case T_power:
							 | 
						|
								            symbol->SetPower();
							 | 
						|
								            NeedRIGHT();
							 | 
						|
								            break;
							 | 
						|
								
							 | 
						|
								        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_in_bom:
							 | 
						|
								            symbol->SetIncludeInBom( parseBool() );
							 | 
						|
								            NeedRIGHT();
							 | 
						|
								            break;
							 | 
						|
								
							 | 
						|
								        case T_on_board:
							 | 
						|
								            symbol->SetIncludeOnBoard( parseBool() );
							 | 
						|
								            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\"\n"
							 | 
						|
								                       "line: %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\"\n"
							 | 
						|
								                       "line: %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;
							 | 
						|
								}
							 | 
						|
								
							 | 
						|
								
							 | 
						|
								void SCH_SEXPR_PARSER::parseStroke( STROKE_PARAMS& aStroke )
							 | 
						|
								{
							 | 
						|
								    wxCHECK_RET( CurTok() == T_stroke,
							 | 
						|
								                 wxT( "Cannot parse " ) + GetTokenString( CurTok() ) + wxT( " as a stroke." ) );
							 | 
						|
								
							 | 
						|
								    aStroke.SetWidth( Mils2iu( DEFAULT_LINE_THICKNESS ) );
							 | 
						|
								    aStroke.SetType( PLOT_DASH_TYPE::DEFAULT );
							 | 
						|
								    aStroke.SetColor( COLOR4D::UNSPECIFIED );
							 | 
						|
								
							 | 
						|
								    T token;
							 | 
						|
								
							 | 
						|
								    for( token = NextTok();  token != T_RIGHT;  token = NextTok() )
							 | 
						|
								    {
							 | 
						|
								        if( token != T_LEFT )
							 | 
						|
								            Expecting( T_LEFT );
							 | 
						|
								
							 | 
						|
								        token = NextTok();
							 | 
						|
								
							 | 
						|
								        switch( token )
							 | 
						|
								        {
							 | 
						|
								        case T_width:
							 | 
						|
								            aStroke.SetWidth( parseInternalUnits( "stroke width" ) );
							 | 
						|
								            NeedRIGHT();
							 | 
						|
								            break;
							 | 
						|
								
							 | 
						|
								        case T_type:
							 | 
						|
								        {
							 | 
						|
								            token = NextTok();
							 | 
						|
								
							 | 
						|
								            switch( token )
							 | 
						|
								            {
							 | 
						|
								            case T_dash:      aStroke.SetType( PLOT_DASH_TYPE::DASH );      break;
							 | 
						|
								            case T_dot:       aStroke.SetType( PLOT_DASH_TYPE::DOT );       break;
							 | 
						|
								            case T_dash_dot:  aStroke.SetType( PLOT_DASH_TYPE::DASHDOT );   break;
							 | 
						|
								            case T_solid:     aStroke.SetType( PLOT_DASH_TYPE::SOLID );     break;
							 | 
						|
								            default:
							 | 
						|
								                Expecting( "solid, dash, dash_dot, or dot" );
							 | 
						|
								            }
							 | 
						|
								
							 | 
						|
								            NeedRIGHT();
							 | 
						|
								            break;
							 | 
						|
								        }
							 | 
						|
								
							 | 
						|
								        case T_color:
							 | 
						|
								        {
							 | 
						|
								            COLOR4D color;
							 | 
						|
								
							 | 
						|
								            color.r = parseInt( "red" ) / 255.0;
							 | 
						|
								            color.g = parseInt( "green" ) / 255.0;
							 | 
						|
								            color.b = parseInt( "blue" ) / 255.0;
							 | 
						|
								            color.a = Clamp( parseDouble( "alpha" ), 0.0, 1.0 );
							 | 
						|
								
							 | 
						|
								            aStroke.SetColor( color );
							 | 
						|
								            NeedRIGHT();
							 | 
						|
								            break;
							 | 
						|
								        }
							 | 
						|
								
							 | 
						|
								        default:
							 | 
						|
								            Expecting( "width, type, or color" );
							 | 
						|
								        }
							 | 
						|
								
							 | 
						|
								    }
							 | 
						|
								}
							 | 
						|
								
							 | 
						|
								
							 | 
						|
								void SCH_SEXPR_PARSER::parseFill( FILL_PARAMS& aFill )
							 | 
						|
								{
							 | 
						|
								    wxCHECK_RET( CurTok() == T_fill,
							 | 
						|
								                 wxT( "Cannot parse " ) + GetTokenString( CurTok() ) + wxT( " as fill." ) );
							 | 
						|
								
							 | 
						|
								    aFill.m_FillType = NO_FILL;
							 | 
						|
								    aFill.m_Color = COLOR4D::UNSPECIFIED;
							 | 
						|
								
							 | 
						|
								    T token;
							 | 
						|
								
							 | 
						|
								    for( token = NextTok();  token != T_RIGHT;  token = NextTok() )
							 | 
						|
								    {
							 | 
						|
								        if( token != T_LEFT )
							 | 
						|
								            Expecting( T_LEFT );
							 | 
						|
								
							 | 
						|
								        token = NextTok();
							 | 
						|
								
							 | 
						|
								        switch( token )
							 | 
						|
								        {
							 | 
						|
								        case T_type:
							 | 
						|
								        {
							 | 
						|
								            token = NextTok();
							 | 
						|
								
							 | 
						|
								            switch( token )
							 | 
						|
								            {
							 | 
						|
								            case T_none:       aFill.m_FillType = NO_FILL;                  break;
							 | 
						|
								            case T_outline:    aFill.m_FillType = FILLED_SHAPE;             break;
							 | 
						|
								            case T_background: aFill.m_FillType = FILLED_WITH_BG_BODYCOLOR; break;
							 | 
						|
								            default:
							 | 
						|
								                Expecting( "none, outline, or background" );
							 | 
						|
								            }
							 | 
						|
								
							 | 
						|
								            NeedRIGHT();
							 | 
						|
								            break;
							 | 
						|
								        }
							 | 
						|
								
							 | 
						|
								        case T_color:
							 | 
						|
								        {
							 | 
						|
								            COLOR4D color;
							 | 
						|
								
							 | 
						|
								            color.r = parseInt( "red" ) / 255.0;
							 | 
						|
								            color.g = parseInt( "green" ) / 255.0;
							 | 
						|
								            color.b = parseInt( "blue" ) / 255.0;
							 | 
						|
								            color.a = Clamp( parseDouble( "alpha" ), 0.0, 1.0 );
							 | 
						|
								            aFill.m_Color = color;
							 | 
						|
								            NeedRIGHT();
							 | 
						|
								            break;
							 | 
						|
								        }
							 | 
						|
								
							 | 
						|
								        default:
							 | 
						|
								            Expecting( "type or color" );
							 | 
						|
								        }
							 | 
						|
								    }
							 | 
						|
								}
							 | 
						|
								
							 | 
						|
								
							 | 
						|
								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->SetTextThickness( 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( TSCHEMATIC_T::T aHeaderType, int aFileVersion )
							 | 
						|
								{
							 | 
						|
								    wxCHECK_RET( CurTok() == aHeaderType,
							 | 
						|
								                 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();
							 | 
						|
								
							 | 
						|
								        if( m_requiredVersion < 20200827 )
							 | 
						|
								            NeedSYMBOL();
							 | 
						|
								
							 | 
						|
								        NeedRIGHT();
							 | 
						|
								    }
							 | 
						|
								    else
							 | 
						|
								    {
							 | 
						|
								        m_requiredVersion = aFileVersion;
							 | 
						|
								
							 | 
						|
								        // 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." ) );
							 | 
						|
								    wxCHECK( aSymbol, /* void */ );
							 | 
						|
								
							 | 
						|
								    wxString error;
							 | 
						|
								    wxString name;
							 | 
						|
								    wxString value;
							 | 
						|
								    std::unique_ptr<LIB_FIELD> field( new LIB_FIELD( aSymbol.get(), 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 );
							 | 
						|
								    }
							 | 
						|
								
							 | 
						|
								    field->SetName( name );
							 | 
						|
								    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();
							 | 
						|
								
							 | 
						|
								    field->SetText( value );
							 | 
						|
								
							 | 
						|
								    for( token = NextTok();  token != T_RIGHT;  token = NextTok() )
							 | 
						|
								    {
							 | 
						|
								        if( token != T_LEFT )
							 | 
						|
								            Expecting( T_LEFT );
							 | 
						|
								
							 | 
						|
								        token = NextTok();
							 | 
						|
								
							 | 
						|
								        switch( token )
							 | 
						|
								        {
							 | 
						|
								        case T_id:
							 | 
						|
								            field->SetId( parseInt( "field ID" ) );
							 | 
						|
								            NeedRIGHT();
							 | 
						|
								            break;
							 | 
						|
								
							 | 
						|
								        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.get() ) );
							 | 
						|
								            break;
							 | 
						|
								
							 | 
						|
								        default:
							 | 
						|
								            Expecting( "id, at or effects" );
							 | 
						|
								        }
							 | 
						|
								    }
							 | 
						|
								
							 | 
						|
								    LIB_FIELD* existingField;
							 | 
						|
								
							 | 
						|
								    if( field->GetId() < MANDATORY_FIELDS )
							 | 
						|
								    {
							 | 
						|
								        existingField = aSymbol->GetField( field->GetId() );
							 | 
						|
								
							 | 
						|
								        *existingField = *field;
							 | 
						|
								    }
							 | 
						|
								    else if( name == "ki_keywords" )
							 | 
						|
								    {
							 | 
						|
								        // Not a LIB_FIELD object yet.
							 | 
						|
								        aSymbol->SetKeyWords( value );
							 | 
						|
								    }
							 | 
						|
								    else if( name == "ki_description" )
							 | 
						|
								    {
							 | 
						|
								        // Not a LIB_FIELD object yet.
							 | 
						|
								        aSymbol->SetDescription( value );
							 | 
						|
								    }
							 | 
						|
								    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 );
							 | 
						|
								    }
							 | 
						|
								    else if( name == "ki_locked" )
							 | 
						|
								    {
							 | 
						|
								        // This is a temporary LIB_FIELD object until interchangeable units are determined on
							 | 
						|
								        // the fly.
							 | 
						|
								        aSymbol->LockUnits( true );
							 | 
						|
								    }
							 | 
						|
								    else
							 | 
						|
								    {
							 | 
						|
								        existingField = aSymbol->GetField( field->GetId() );
							 | 
						|
								
							 | 
						|
								        if( !existingField )
							 | 
						|
								        {
							 | 
						|
								            aSymbol->AddDrawItem( field.release() );
							 | 
						|
								        }
							 | 
						|
								        else
							 | 
						|
								        {
							 | 
						|
								            *existingField = *field;
							 | 
						|
								        }
							 | 
						|
								    }
							 | 
						|
								}
							 | 
						|
								
							 | 
						|
								
							 | 
						|
								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;
							 | 
						|
								    FILL_PARAMS fill;
							 | 
						|
								    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:
							 | 
						|
								            parseFill( fill );
							 | 
						|
								            arc->SetFillMode( fill.m_FillType );
							 | 
						|
								            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;
							 | 
						|
								    FILL_PARAMS fill;
							 | 
						|
								    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:
							 | 
						|
								            parseFill( fill );
							 | 
						|
								            bezier->SetFillMode( fill.m_FillType );
							 | 
						|
								            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;
							 | 
						|
								    FILL_PARAMS fill;
							 | 
						|
								    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:
							 | 
						|
								            parseFill( fill );
							 | 
						|
								            circle->SetFillMode( fill.m_FillType );
							 | 
						|
								            break;
							 | 
						|
								
							 | 
						|
								        default:
							 | 
						|
								            Expecting( "start, end, radius, stroke, or fill" );
							 | 
						|
								        }
							 | 
						|
								    }
							 | 
						|
								
							 | 
						|
								    return circle.release();
							 | 
						|
								}
							 | 
						|
								
							 | 
						|
								
							 | 
						|
								LIB_PIN* SCH_SEXPR_PARSER::parsePin()
							 | 
						|
								{
							 | 
						|
								    auto parseType = [&]( T token ) -> ELECTRICAL_PINTYPE
							 | 
						|
								                     {
							 | 
						|
								                         switch( token )
							 | 
						|
								                         {
							 | 
						|
								                         case T_input:          return ELECTRICAL_PINTYPE::PT_INPUT;
							 | 
						|
								                         case T_output:         return ELECTRICAL_PINTYPE::PT_OUTPUT;
							 | 
						|
								                         case T_bidirectional:  return ELECTRICAL_PINTYPE::PT_BIDI;
							 | 
						|
								                         case T_tri_state:      return ELECTRICAL_PINTYPE::PT_TRISTATE;
							 | 
						|
								                         case T_passive:        return ELECTRICAL_PINTYPE::PT_PASSIVE;
							 | 
						|
								                         case T_unspecified:    return ELECTRICAL_PINTYPE::PT_UNSPECIFIED;
							 | 
						|
								                         case T_power_in:       return ELECTRICAL_PINTYPE::PT_POWER_IN;
							 | 
						|
								                         case T_power_out:      return ELECTRICAL_PINTYPE::PT_POWER_OUT;
							 | 
						|
								                         case T_open_collector: return ELECTRICAL_PINTYPE::PT_OPENCOLLECTOR;
							 | 
						|
								                         case T_open_emitter:   return ELECTRICAL_PINTYPE::PT_OPENEMITTER;
							 | 
						|
								                         case T_unconnected:    return ELECTRICAL_PINTYPE::PT_NC;
							 | 
						|
								
							 | 
						|
								                         default:
							 | 
						|
								                             Expecting( "input, output, bidirectional, tri_state, passive, "
							 | 
						|
								                                        "unspecified, power_in, power_out, open_collector, "
							 | 
						|
								                                        "open_emitter, or unconnected" );
							 | 
						|
								                             return ELECTRICAL_PINTYPE::PT_UNSPECIFIED;
							 | 
						|
								                         }
							 | 
						|
								                     };
							 | 
						|
								
							 | 
						|
								    auto parseShape = [&]( T token ) -> GRAPHIC_PINSHAPE
							 | 
						|
								                      {
							 | 
						|
								                          switch( token )
							 | 
						|
								                          {
							 | 
						|
								                          case T_line:            return GRAPHIC_PINSHAPE::LINE;
							 | 
						|
								                          case T_inverted:        return GRAPHIC_PINSHAPE::INVERTED;
							 | 
						|
								                          case T_clock:           return GRAPHIC_PINSHAPE::CLOCK;
							 | 
						|
								                          case T_inverted_clock:  return GRAPHIC_PINSHAPE::INVERTED_CLOCK;
							 | 
						|
								                          case T_input_low:       return GRAPHIC_PINSHAPE::INPUT_LOW;
							 | 
						|
								                          case T_clock_low:       return GRAPHIC_PINSHAPE::CLOCK_LOW;
							 | 
						|
								                          case T_output_low:      return GRAPHIC_PINSHAPE::OUTPUT_LOW;
							 | 
						|
								                          case T_edge_clock_high: return GRAPHIC_PINSHAPE::FALLING_EDGE_CLOCK;
							 | 
						|
								                          case T_non_logic:       return GRAPHIC_PINSHAPE::NONLOGIC;
							 | 
						|
								
							 | 
						|
								                          default:
							 | 
						|
								                              Expecting( "line, inverted, clock, inverted_clock, input_low, "
							 | 
						|
								                                         "clock_low, output_low, edge_clock_high, non_logic" );
							 | 
						|
								                              return GRAPHIC_PINSHAPE::LINE;
							 | 
						|
								                          }
							 | 
						|
								                      };
							 | 
						|
								
							 | 
						|
								    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();
							 | 
						|
								    pin->SetType( parseType( token ) );
							 | 
						|
								
							 | 
						|
								    // Pin shape.
							 | 
						|
								    token = NextTok();
							 | 
						|
								    pin->SetShape( parseShape( token ) );
							 | 
						|
								
							 | 
						|
								    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() );
							 | 
						|
								                    NeedRIGHT();
							 | 
						|
								                }
							 | 
						|
								                else
							 | 
						|
								                {
							 | 
						|
								                    Expecting( "effects" );
							 | 
						|
								                }
							 | 
						|
								            }
							 | 
						|
								
							 | 
						|
								            break;
							 | 
						|
								
							 | 
						|
								        case T_alternate:
							 | 
						|
								        {
							 | 
						|
								            LIB_PIN::ALT alt;
							 | 
						|
								
							 | 
						|
								            token = NextTok();
							 | 
						|
								
							 | 
						|
								            if( !IsSymbol( token ) )
							 | 
						|
								            {
							 | 
						|
								                error.Printf( _( "Invalid alternate pin name in\nfile: \"%s\"\nline: %d\noffset: %d" ),
							 | 
						|
								                              CurSource().c_str(), CurLineNumber(), CurOffset() );
							 | 
						|
								                THROW_IO_ERROR( error );
							 | 
						|
								            }
							 | 
						|
								
							 | 
						|
								            alt.m_Name = FromUTF8();
							 | 
						|
								
							 | 
						|
								            token = NextTok();
							 | 
						|
								            alt.m_Type = parseType( token );
							 | 
						|
								
							 | 
						|
								            token = NextTok();
							 | 
						|
								            alt.m_Shape = parseShape( token );
							 | 
						|
								
							 | 
						|
								            pin->GetAlternates()[ alt.m_Name ] = alt;
							 | 
						|
								
							 | 
						|
								            NeedRIGHT();
							 | 
						|
								        }
							 | 
						|
								            break;
							 | 
						|
								
							 | 
						|
								        default:
							 | 
						|
								            Expecting( "at, name, number, length, or alternate" );
							 | 
						|
								        }
							 | 
						|
								    }
							 | 
						|
								
							 | 
						|
								    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;
							 | 
						|
								    FILL_PARAMS fill;
							 | 
						|
								    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:
							 | 
						|
								            parseFill( fill );
							 | 
						|
								            polyLine->SetFillMode( fill.m_FillType );
							 | 
						|
								            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;
							 | 
						|
								    FILL_PARAMS fill;
							 | 
						|
								    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:
							 | 
						|
								            parseFill( fill );
							 | 
						|
								            rectangle->SetFillMode( fill.m_FillType );
							 | 
						|
								            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();
							 | 
						|
								}
							 | 
						|
								
							 | 
						|
								
							 | 
						|
								void SCH_SEXPR_PARSER::parsePAGE_INFO( PAGE_INFO& aPageInfo )
							 | 
						|
								{
							 | 
						|
								    wxCHECK_RET( ( CurTok() == T_page && m_requiredVersion <= 20200506 ) || CurTok() == T_paper,
							 | 
						|
								                 wxT( "Cannot parse " ) + GetTokenString( CurTok() ) + wxT( " as a PAGE_INFO." ) );
							 | 
						|
								
							 | 
						|
								    T token;
							 | 
						|
								
							 | 
						|
								    NeedSYMBOL();
							 | 
						|
								
							 | 
						|
								    wxString pageType = FromUTF8();
							 | 
						|
								
							 | 
						|
								    if( !aPageInfo.SetType( pageType ) )
							 | 
						|
								    {
							 | 
						|
								        wxString err;
							 | 
						|
								        err.Printf( _( "Page type \"%s\" is not valid " ), GetChars( FromUTF8() ) );
							 | 
						|
								        THROW_PARSE_ERROR( err, CurSource(), CurLine(), CurLineNumber(), CurOffset() );
							 | 
						|
								    }
							 | 
						|
								
							 | 
						|
								    if( pageType == PAGE_INFO::Custom )
							 | 
						|
								    {
							 | 
						|
								        double width = parseDouble( "width" );      // width in mm
							 | 
						|
								
							 | 
						|
								        // Perform some controls to avoid crashes if the size is edited by hands
							 | 
						|
								        if( width < 100.0 )
							 | 
						|
								            width = 100.0;
							 | 
						|
								        else if( width > 1200.0 )
							 | 
						|
								            width = 1200.0;
							 | 
						|
								
							 | 
						|
								        double height = parseDouble( "height" );    // height in mm
							 | 
						|
								
							 | 
						|
								        if( height < 100.0 )
							 | 
						|
								            height = 100.0;
							 | 
						|
								        else if( height > 1200.0 )
							 | 
						|
								            height = 1200.0;
							 | 
						|
								
							 | 
						|
								        aPageInfo.SetWidthMils( Mm2mils( width ) );
							 | 
						|
								        aPageInfo.SetHeightMils( Mm2mils( height ) );
							 | 
						|
								    }
							 | 
						|
								
							 | 
						|
								    token = NextTok();
							 | 
						|
								
							 | 
						|
								    if( token == T_portrait )
							 | 
						|
								    {
							 | 
						|
								        aPageInfo.SetPortrait( true );
							 | 
						|
								        NeedRIGHT();
							 | 
						|
								    }
							 | 
						|
								    else if( token != T_RIGHT )
							 | 
						|
								    {
							 | 
						|
								        Expecting( "portrait" );
							 | 
						|
								    }
							 | 
						|
								}
							 | 
						|
								
							 | 
						|
								
							 | 
						|
								void SCH_SEXPR_PARSER::parseTITLE_BLOCK( TITLE_BLOCK& aTitleBlock )
							 | 
						|
								{
							 | 
						|
								    wxCHECK_RET( CurTok() == T_title_block,
							 | 
						|
								                 wxT( "Cannot parse " ) + GetTokenString( CurTok() ) +
							 | 
						|
								                 wxT( " as TITLE_BLOCK." ) );
							 | 
						|
								
							 | 
						|
								    T token;
							 | 
						|
								
							 | 
						|
								    for( token = NextTok();  token != T_RIGHT;  token = NextTok() )
							 | 
						|
								    {
							 | 
						|
								        if( token != T_LEFT )
							 | 
						|
								            Expecting( T_LEFT );
							 | 
						|
								
							 | 
						|
								        token = NextTok();
							 | 
						|
								
							 | 
						|
								        switch( token )
							 | 
						|
								        {
							 | 
						|
								        case T_title:
							 | 
						|
								            NextTok();
							 | 
						|
								            aTitleBlock.SetTitle( FromUTF8() );
							 | 
						|
								            break;
							 | 
						|
								
							 | 
						|
								        case T_date:
							 | 
						|
								            NextTok();
							 | 
						|
								            aTitleBlock.SetDate( FromUTF8() );
							 | 
						|
								            break;
							 | 
						|
								
							 | 
						|
								        case T_rev:
							 | 
						|
								            NextTok();
							 | 
						|
								            aTitleBlock.SetRevision( FromUTF8() );
							 | 
						|
								            break;
							 | 
						|
								
							 | 
						|
								        case T_company:
							 | 
						|
								            NextTok();
							 | 
						|
								            aTitleBlock.SetCompany( FromUTF8() );
							 | 
						|
								            break;
							 | 
						|
								
							 | 
						|
								        case T_comment:
							 | 
						|
								        {
							 | 
						|
								            int commentNumber = parseInt( "comment" );
							 | 
						|
								
							 | 
						|
								            switch( commentNumber )
							 | 
						|
								            {
							 | 
						|
								            case 1:
							 | 
						|
								                NextTok();
							 | 
						|
								                aTitleBlock.SetComment( 0, FromUTF8() );
							 | 
						|
								                break;
							 | 
						|
								
							 | 
						|
								            case 2:
							 | 
						|
								                NextTok();
							 | 
						|
								                aTitleBlock.SetComment( 1, FromUTF8() );
							 | 
						|
								                break;
							 | 
						|
								
							 | 
						|
								            case 3:
							 | 
						|
								                NextTok();
							 | 
						|
								                aTitleBlock.SetComment( 2, FromUTF8() );
							 | 
						|
								                break;
							 | 
						|
								
							 | 
						|
								            case 4:
							 | 
						|
								                NextTok();
							 | 
						|
								                aTitleBlock.SetComment( 3, FromUTF8() );
							 | 
						|
								                break;
							 | 
						|
								
							 | 
						|
								            case 5:
							 | 
						|
								                NextTok();
							 | 
						|
								                aTitleBlock.SetComment( 4, FromUTF8() );
							 | 
						|
								                break;
							 | 
						|
								
							 | 
						|
								            case 6:
							 | 
						|
								                NextTok();
							 | 
						|
								                aTitleBlock.SetComment( 5, FromUTF8() );
							 | 
						|
								                break;
							 | 
						|
								
							 | 
						|
								            case 7:
							 | 
						|
								                NextTok();
							 | 
						|
								                aTitleBlock.SetComment( 6, FromUTF8() );
							 | 
						|
								                break;
							 | 
						|
								
							 | 
						|
								            case 8:
							 | 
						|
								                NextTok();
							 | 
						|
								                aTitleBlock.SetComment( 7, FromUTF8() );
							 | 
						|
								                break;
							 | 
						|
								
							 | 
						|
								            case 9:
							 | 
						|
								                NextTok();
							 | 
						|
								                aTitleBlock.SetComment( 8, FromUTF8() );
							 | 
						|
								                break;
							 | 
						|
								
							 | 
						|
								            default:
							 | 
						|
								                wxString err;
							 | 
						|
								                err.Printf( wxT( "%d is not a valid title block comment number" ), commentNumber );
							 | 
						|
								                THROW_PARSE_ERROR( err, CurSource(), CurLine(), CurLineNumber(), CurOffset() );
							 | 
						|
								            }
							 | 
						|
								
							 | 
						|
								            break;
							 | 
						|
								        }
							 | 
						|
								
							 | 
						|
								        default:
							 | 
						|
								            Expecting( "title, date, rev, company, or comment" );
							 | 
						|
								        }
							 | 
						|
								
							 | 
						|
								        NeedRIGHT();
							 | 
						|
								    }
							 | 
						|
								}
							 | 
						|
								
							 | 
						|
								
							 | 
						|
								SCH_FIELD* SCH_SEXPR_PARSER::parseSchField( SCH_ITEM* aParent )
							 | 
						|
								{
							 | 
						|
								    wxCHECK_MSG( CurTok() == T_property, nullptr,
							 | 
						|
								                 wxT( "Cannot parse " ) + GetTokenString( CurTok() ) +
							 | 
						|
								                 wxT( " as a property token." ) );
							 | 
						|
								
							 | 
						|
								    wxString error;
							 | 
						|
								    wxString name;
							 | 
						|
								    wxString value;
							 | 
						|
								
							 | 
						|
								    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();
							 | 
						|
								
							 | 
						|
								    std::unique_ptr<SCH_FIELD> field( new SCH_FIELD( wxDefaultPosition, -1, aParent, name ) );
							 | 
						|
								
							 | 
						|
								    field->SetText( value );
							 | 
						|
								    field->SetVisible( true );
							 | 
						|
								
							 | 
						|
								    for( token = NextTok();  token != T_RIGHT;  token = NextTok() )
							 | 
						|
								    {
							 | 
						|
								        if( token != T_LEFT )
							 | 
						|
								            Expecting( T_LEFT );
							 | 
						|
								
							 | 
						|
								        token = NextTok();
							 | 
						|
								
							 | 
						|
								        switch( token )
							 | 
						|
								        {
							 | 
						|
								        case T_id:
							 | 
						|
								            field->SetId( parseInt( "field ID" ) );
							 | 
						|
								            NeedRIGHT();
							 | 
						|
								            break;
							 | 
						|
								
							 | 
						|
								        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.get() ) );
							 | 
						|
								            break;
							 | 
						|
								
							 | 
						|
								        default:
							 | 
						|
								            Expecting( "at or effects" );
							 | 
						|
								        }
							 | 
						|
								    }
							 | 
						|
								
							 | 
						|
								    return field.release();
							 | 
						|
								}
							 | 
						|
								
							 | 
						|
								
							 | 
						|
								SCH_SHEET_PIN* SCH_SEXPR_PARSER::parseSchSheetPin( SCH_SHEET* aSheet )
							 | 
						|
								{
							 | 
						|
								    wxCHECK_MSG( aSheet != nullptr, nullptr, "" );
							 | 
						|
								    wxCHECK_MSG( CurTok() == T_pin, nullptr,
							 | 
						|
								                 wxT( "Cannot parse " ) + GetTokenString( CurTok() ) +
							 | 
						|
								                 wxT( " as a sheet pin token." ) );
							 | 
						|
								
							 | 
						|
								    wxString error;
							 | 
						|
								    wxString name;
							 | 
						|
								    wxString shape;
							 | 
						|
								
							 | 
						|
								    T token = NextTok();
							 | 
						|
								
							 | 
						|
								    if( !IsSymbol( token ) )
							 | 
						|
								    {
							 | 
						|
								        error.Printf( _( "Invalid sheet pin 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 sheet pin name in\nfile: \"%s\"\nline: %d\noffset: %d" ),
							 | 
						|
								                      CurSource().c_str(), CurLineNumber(), CurOffset() );
							 | 
						|
								        THROW_IO_ERROR( error );
							 | 
						|
								    }
							 | 
						|
								
							 | 
						|
								    std::unique_ptr<SCH_SHEET_PIN> sheetPin( new SCH_SHEET_PIN( aSheet, wxPoint( 0, 0 ), name ) );
							 | 
						|
								
							 | 
						|
								    token = NextTok();
							 | 
						|
								
							 | 
						|
								    switch( token )
							 | 
						|
								    {
							 | 
						|
								    case T_input:          sheetPin->SetShape( PINSHEETLABEL_SHAPE::PS_INPUT );        break;
							 | 
						|
								    case T_output:         sheetPin->SetShape( PINSHEETLABEL_SHAPE::PS_OUTPUT );       break;
							 | 
						|
								    case T_bidirectional:  sheetPin->SetShape( PINSHEETLABEL_SHAPE::PS_BIDI );         break;
							 | 
						|
								    case T_tri_state:      sheetPin->SetShape( PINSHEETLABEL_SHAPE::PS_TRISTATE );     break;
							 | 
						|
								    case T_passive:        sheetPin->SetShape( PINSHEETLABEL_SHAPE::PS_UNSPECIFIED );  break;
							 | 
						|
								    default:
							 | 
						|
								        Expecting( "input, output, bidirectional, tri_state, or passive" );
							 | 
						|
								    }
							 | 
						|
								
							 | 
						|
								    for( token = NextTok();  token != T_RIGHT;  token = NextTok() )
							 | 
						|
								    {
							 | 
						|
								        if( token != T_LEFT )
							 | 
						|
								            Expecting( T_LEFT );
							 | 
						|
								
							 | 
						|
								        token = NextTok();
							 | 
						|
								
							 | 
						|
								        switch( token )
							 | 
						|
								        {
							 | 
						|
								        case T_at:
							 | 
						|
								        {
							 | 
						|
								            sheetPin->SetPosition( parseXY() );
							 | 
						|
								
							 | 
						|
								            double angle = parseDouble( "sheet pin angle (side)" );
							 | 
						|
								
							 | 
						|
								            if( angle == 0.0 )
							 | 
						|
								                sheetPin->SetEdge( SHEET_RIGHT_SIDE );
							 | 
						|
								            else if( angle == 90.0 )
							 | 
						|
								                sheetPin->SetEdge( SHEET_TOP_SIDE );
							 | 
						|
								            else if( angle == 180.0 )
							 | 
						|
								                sheetPin->SetEdge( SHEET_LEFT_SIDE );
							 | 
						|
								            else if( angle == 270.0 )
							 | 
						|
								                sheetPin->SetEdge( SHEET_BOTTOM_SIDE );
							 | 
						|
								            else
							 | 
						|
								                Expecting( "0, 90, 180, or 270" );
							 | 
						|
								
							 | 
						|
								            NeedRIGHT();
							 | 
						|
								            break;
							 | 
						|
								        }
							 | 
						|
								
							 | 
						|
								        case T_effects:
							 | 
						|
								            parseEDA_TEXT( static_cast<EDA_TEXT*>( sheetPin.get() ) );
							 | 
						|
								            break;
							 | 
						|
								
							 | 
						|
								        default:
							 | 
						|
								            Expecting( "at or effects" );
							 | 
						|
								        }
							 | 
						|
								    }
							 | 
						|
								
							 | 
						|
								    return sheetPin.release();
							 | 
						|
								}
							 | 
						|
								
							 | 
						|
								
							 | 
						|
								void SCH_SEXPR_PARSER::parseSchSymbolInstances( SCH_SCREEN* aScreen )
							 | 
						|
								{
							 | 
						|
								    wxCHECK_RET( CurTok() == T_symbol_instances,
							 | 
						|
								                 wxT( "Cannot parse " ) + GetTokenString( CurTok() ) +
							 | 
						|
								                 wxT( " as a instances token." ) );
							 | 
						|
								    wxCHECK( aScreen, /* void */ );
							 | 
						|
								
							 | 
						|
								    T token;
							 | 
						|
								
							 | 
						|
								    for( token = NextTok();  token != T_RIGHT;  token = NextTok() )
							 | 
						|
								    {
							 | 
						|
								        if( token != T_LEFT )
							 | 
						|
								            Expecting( T_LEFT );
							 | 
						|
								
							 | 
						|
								        token = NextTok();
							 | 
						|
								
							 | 
						|
								        switch( token )
							 | 
						|
								        {
							 | 
						|
								        case T_path:
							 | 
						|
								        {
							 | 
						|
								            NeedSYMBOL();
							 | 
						|
								
							 | 
						|
								            COMPONENT_INSTANCE_REFERENCE instance;
							 | 
						|
								
							 | 
						|
								            instance.m_Path = KIID_PATH( FromUTF8() );
							 | 
						|
								
							 | 
						|
								            for( token = NextTok();  token != T_RIGHT;  token = NextTok() )
							 | 
						|
								            {
							 | 
						|
								                if( token != T_LEFT )
							 | 
						|
								                    Expecting( T_LEFT );
							 | 
						|
								
							 | 
						|
								                token = NextTok();
							 | 
						|
								
							 | 
						|
								                switch( token )
							 | 
						|
								                {
							 | 
						|
								                case T_reference:
							 | 
						|
								                    NeedSYMBOL();
							 | 
						|
								                    instance.m_Reference = FromUTF8();
							 | 
						|
								                    NeedRIGHT();
							 | 
						|
								                    break;
							 | 
						|
								
							 | 
						|
								                case T_unit:
							 | 
						|
								                    instance.m_Unit = parseInt( "symbol unit" );
							 | 
						|
								                    NeedRIGHT();
							 | 
						|
								                    break;
							 | 
						|
								
							 | 
						|
								                case T_value:
							 | 
						|
								                    NeedSYMBOL();
							 | 
						|
								                    instance.m_Value = FromUTF8();
							 | 
						|
								                    NeedRIGHT();
							 | 
						|
								                    break;
							 | 
						|
								
							 | 
						|
								                case T_footprint:
							 | 
						|
								                    NeedSYMBOL();
							 | 
						|
								                    instance.m_Footprint = FromUTF8();
							 | 
						|
								                    NeedRIGHT();
							 | 
						|
								                    break;
							 | 
						|
								
							 | 
						|
								                default:
							 | 
						|
								                    Expecting( "path, unit, value or footprint" );
							 | 
						|
								                }
							 | 
						|
								            }
							 | 
						|
								
							 | 
						|
								            aScreen->m_symbolInstances.emplace_back( instance );
							 | 
						|
								            break;
							 | 
						|
								        }
							 | 
						|
								
							 | 
						|
								        default:
							 | 
						|
								            Expecting( "path" );
							 | 
						|
								        }
							 | 
						|
								    }
							 | 
						|
								}
							 | 
						|
								
							 | 
						|
								
							 | 
						|
								void SCH_SEXPR_PARSER::ParseSchematic( SCH_SHEET* aSheet, bool aIsCopyableOnly, int aFileVersion )
							 | 
						|
								{
							 | 
						|
								    wxCHECK( aSheet != nullptr, /* void */ );
							 | 
						|
								
							 | 
						|
								    SCH_SCREEN* screen = aSheet->GetScreen();
							 | 
						|
								
							 | 
						|
								    wxCHECK( screen != nullptr, /* void */ );
							 | 
						|
								
							 | 
						|
								    if( aIsCopyableOnly )
							 | 
						|
								        m_requiredVersion = aFileVersion;
							 | 
						|
								
							 | 
						|
								    T token;
							 | 
						|
								
							 | 
						|
								    if( !aIsCopyableOnly )
							 | 
						|
								    {
							 | 
						|
								        NeedLEFT();
							 | 
						|
								        NextTok();
							 | 
						|
								
							 | 
						|
								        if( CurTok() != T_kicad_sch )
							 | 
						|
								            Expecting( "kicad_sch" );
							 | 
						|
								
							 | 
						|
								        parseHeader( T_kicad_sch, SEXPR_SCHEMATIC_FILE_VERSION );
							 | 
						|
								    }
							 | 
						|
								
							 | 
						|
								    for( token = NextTok();  token != T_RIGHT;  token = NextTok() )
							 | 
						|
								    {
							 | 
						|
								        if( aIsCopyableOnly && token == T_EOF )
							 | 
						|
								            break;
							 | 
						|
								
							 | 
						|
								        if( token != T_LEFT )
							 | 
						|
								            Expecting( T_LEFT );
							 | 
						|
								
							 | 
						|
								        token = NextTok();
							 | 
						|
								
							 | 
						|
								        if( !aIsCopyableOnly && token == T_page && m_requiredVersion <= 20200506 )
							 | 
						|
								            token = T_paper;
							 | 
						|
								
							 | 
						|
								        switch( token )
							 | 
						|
								        {
							 | 
						|
								        case T_paper:
							 | 
						|
								        {
							 | 
						|
								            if( aIsCopyableOnly )
							 | 
						|
								                Unexpected( T_paper );
							 | 
						|
								
							 | 
						|
								            PAGE_INFO pageInfo;
							 | 
						|
								            parsePAGE_INFO( pageInfo );
							 | 
						|
								            screen->SetPageSettings( pageInfo );
							 | 
						|
								            break;
							 | 
						|
								        }
							 | 
						|
								
							 | 
						|
								        case T_page:
							 | 
						|
								        {
							 | 
						|
								            if( aIsCopyableOnly )
							 | 
						|
								                Unexpected( T_page );
							 | 
						|
								
							 | 
						|
								            // Only saved for top-level sniffing in Kicad Manager frame and other external
							 | 
						|
								            // tool usage with flat hierarchies
							 | 
						|
								            NeedSYMBOLorNUMBER();
							 | 
						|
								            NeedSYMBOLorNUMBER();
							 | 
						|
								            NeedRIGHT();
							 | 
						|
								            break;
							 | 
						|
								        }
							 | 
						|
								
							 | 
						|
								        case T_title_block:
							 | 
						|
								        {
							 | 
						|
								            if( aIsCopyableOnly )
							 | 
						|
								                Unexpected( T_title_block );
							 | 
						|
								
							 | 
						|
								            TITLE_BLOCK tb;
							 | 
						|
								            parseTITLE_BLOCK( tb );
							 | 
						|
								            screen->SetTitleBlock( tb );
							 | 
						|
								            break;
							 | 
						|
								        }
							 | 
						|
								
							 | 
						|
								        case T_lib_symbols:
							 | 
						|
								        {
							 | 
						|
								            // Dummy map.  No derived symbols are allowed in the library cache.
							 | 
						|
								            LIB_PART_MAP symbolLibMap;
							 | 
						|
								
							 | 
						|
								            for( token = NextTok();  token != T_RIGHT;  token = NextTok() )
							 | 
						|
								            {
							 | 
						|
								                if( token != T_LEFT )
							 | 
						|
								                    Expecting( T_LEFT );
							 | 
						|
								
							 | 
						|
								                token = NextTok();
							 | 
						|
								
							 | 
						|
								                switch( token )
							 | 
						|
								                {
							 | 
						|
								                case T_symbol:
							 | 
						|
								                    screen->AddLibSymbol( ParseSymbol( symbolLibMap ) );
							 | 
						|
								                    break;
							 | 
						|
								
							 | 
						|
								                default:
							 | 
						|
								                    Expecting( "symbol" );
							 | 
						|
								                }
							 | 
						|
								            }
							 | 
						|
								
							 | 
						|
								            break;
							 | 
						|
								        }
							 | 
						|
								
							 | 
						|
								        case T_symbol:
							 | 
						|
								            screen->Append( static_cast<SCH_ITEM*>( parseSchematicSymbol() ) );
							 | 
						|
								            break;
							 | 
						|
								
							 | 
						|
								        case T_image:
							 | 
						|
								            screen->Append( static_cast<SCH_ITEM*>( parseImage() ) );
							 | 
						|
								            break;
							 | 
						|
								
							 | 
						|
								        case T_sheet:
							 | 
						|
								        {
							 | 
						|
								            SCH_SHEET* sheet = parseSheet();
							 | 
						|
								
							 | 
						|
								            // Set the parent to aSheet.  This effectively creates a method to find
							 | 
						|
								            // the root sheet from any sheet so a pointer to the root sheet does not
							 | 
						|
								            // need to be stored globally.  Note: this is not the same as a hierarchy.
							 | 
						|
								            // Complex hierarchies can have multiple copies of a sheet.  This only
							 | 
						|
								            // provides a simple tree to find the root sheet.
							 | 
						|
								            sheet->SetParent( aSheet );
							 | 
						|
								            screen->Append( static_cast<SCH_ITEM*>( sheet ) );
							 | 
						|
								            break;
							 | 
						|
								        }
							 | 
						|
								
							 | 
						|
								        case T_junction:
							 | 
						|
								            screen->Append( static_cast<SCH_ITEM*>( parseJunction() ) );
							 | 
						|
								            break;
							 | 
						|
								
							 | 
						|
								        case T_no_connect:
							 | 
						|
								            screen->Append( static_cast<SCH_ITEM*>( parseNoConnect() ) );
							 | 
						|
								            break;
							 | 
						|
								
							 | 
						|
								        case T_bus_entry:
							 | 
						|
								            screen->Append( static_cast<SCH_ITEM*>( parseBusEntry() ) );
							 | 
						|
								            break;
							 | 
						|
								
							 | 
						|
								        case T_polyline:
							 | 
						|
								        case T_bus:
							 | 
						|
								        case T_wire:
							 | 
						|
								            screen->Append( static_cast<SCH_ITEM*>( parseLine() ) );
							 | 
						|
								            break;
							 | 
						|
								
							 | 
						|
								        case T_text:
							 | 
						|
								        case T_label:
							 | 
						|
								        case T_global_label:
							 | 
						|
								        case T_hierarchical_label:
							 | 
						|
								            screen->Append( static_cast<SCH_ITEM*>( parseSchText() ) );
							 | 
						|
								            break;
							 | 
						|
								
							 | 
						|
								        case T_symbol_instances:
							 | 
						|
								            if( aIsCopyableOnly )
							 | 
						|
								                Unexpected( T_symbol_instances );
							 | 
						|
								
							 | 
						|
								            parseSchSymbolInstances( screen );
							 | 
						|
								            break;
							 | 
						|
								
							 | 
						|
								        case T_bus_alias:
							 | 
						|
								            if( aIsCopyableOnly )
							 | 
						|
								                Unexpected( T_bus_alias );
							 | 
						|
								
							 | 
						|
								            parseBusAlias( screen );
							 | 
						|
								            break;
							 | 
						|
								
							 | 
						|
								        default:
							 | 
						|
								            Expecting( "symbol, paper, page, title_block, bitmap, sheet, junction, no_connect, "
							 | 
						|
								                       "bus_entry, line, bus, text, label, global_label, hierarchical_label, "
							 | 
						|
								                       "symbol_instances, or bus_alias" );
							 | 
						|
								        }
							 | 
						|
								    }
							 | 
						|
								
							 | 
						|
								    screen->UpdateLocalLibSymbolLinks();
							 | 
						|
								}
							 | 
						|
								
							 | 
						|
								
							 | 
						|
								SCH_COMPONENT* SCH_SEXPR_PARSER::parseSchematicSymbol()
							 | 
						|
								{
							 | 
						|
								    wxCHECK_MSG( CurTok() == T_symbol, nullptr,
							 | 
						|
								                 wxT( "Cannot parse " ) + GetTokenString( CurTok() ) + wxT( " as a symbol." ) );
							 | 
						|
								
							 | 
						|
								    T token;
							 | 
						|
								    wxString tmp;
							 | 
						|
								    wxString error;
							 | 
						|
								    wxString libName;
							 | 
						|
								    SCH_FIELD* field;
							 | 
						|
								    std::unique_ptr<SCH_COMPONENT> symbol( new SCH_COMPONENT() );
							 | 
						|
								    TRANSFORM transform;
							 | 
						|
								    std::set<int> fieldIDsRead;
							 | 
						|
								
							 | 
						|
								    m_fieldId = MANDATORY_FIELDS;
							 | 
						|
								
							 | 
						|
								    for( token = NextTok();  token != T_RIGHT;  token = NextTok() )
							 | 
						|
								    {
							 | 
						|
								        if( token != T_LEFT )
							 | 
						|
								            Expecting( T_LEFT );
							 | 
						|
								
							 | 
						|
								        token = NextTok();
							 | 
						|
								
							 | 
						|
								        switch( token )
							 | 
						|
								        {
							 | 
						|
								        case T_lib_name:
							 | 
						|
								        {
							 | 
						|
								            LIB_ID libId;
							 | 
						|
								
							 | 
						|
								            token = NextTok();
							 | 
						|
								
							 | 
						|
								            if( !IsSymbol( token ) )
							 | 
						|
								            {
							 | 
						|
								                error.Printf( _( "Invalid symbol library name in\nfile: \"%s\"\n"
							 | 
						|
								                                 "line: %d\noffset: %d" ),
							 | 
						|
								                              CurSource().c_str(), CurLineNumber(), CurOffset() );
							 | 
						|
								                THROW_IO_ERROR( error );
							 | 
						|
								            }
							 | 
						|
								
							 | 
						|
								            libName = FromUTF8();
							 | 
						|
								            NeedRIGHT();
							 | 
						|
								            break;
							 | 
						|
								        }
							 | 
						|
								
							 | 
						|
								        case T_lib_id:
							 | 
						|
								        {
							 | 
						|
								            token = NextTok();
							 | 
						|
								
							 | 
						|
								            if( !IsSymbol( token ) && token != T_NUMBER )
							 | 
						|
								                Expecting( "symbol|number" );
							 | 
						|
								
							 | 
						|
								            LIB_ID libId;
							 | 
						|
								
							 | 
						|
								            if( libId.Parse( FromUTF8(), LIB_ID::ID_SCH ) >= 0 )
							 | 
						|
								            {
							 | 
						|
								                error.Printf( _( "Invalid symbol library ID in\nfile: \"%s\"\nline: %d\n"
							 | 
						|
								                                 "offset: %d" ),
							 | 
						|
								                              GetChars( CurSource() ), CurLineNumber(), CurOffset() );
							 | 
						|
								                THROW_IO_ERROR( error );
							 | 
						|
								            }
							 | 
						|
								
							 | 
						|
								            symbol->SetLibId( libId );
							 | 
						|
								            NeedRIGHT();
							 | 
						|
								            break;
							 | 
						|
								        }
							 | 
						|
								
							 | 
						|
								        case T_at:
							 | 
						|
								            symbol->SetPosition( parseXY() );
							 | 
						|
								
							 | 
						|
								            switch( static_cast<int>( parseDouble( "symbol orientation" ) ) )
							 | 
						|
								            {
							 | 
						|
								            case 0:    transform = TRANSFORM();                 break;
							 | 
						|
								            case 90:   transform = TRANSFORM( 0, -1, -1, 0 );   break;
							 | 
						|
								            case 180:  transform = TRANSFORM( -1, 0, 0, 1 );    break;
							 | 
						|
								            case 270:  transform = TRANSFORM( 0, 1, 1, 0 );     break;
							 | 
						|
								            default:   Expecting( "0, 90, 180, or 270" );
							 | 
						|
								            }
							 | 
						|
								
							 | 
						|
								            symbol->SetTransform( transform );
							 | 
						|
								            NeedRIGHT();
							 | 
						|
								            break;
							 | 
						|
								
							 | 
						|
								        case T_mirror:
							 | 
						|
								            token = NextTok();
							 | 
						|
								
							 | 
						|
								            if( token == T_x )
							 | 
						|
								                symbol->SetOrientation( CMP_MIRROR_X );
							 | 
						|
								            else if( token == T_y )
							 | 
						|
								                symbol->SetOrientation( CMP_MIRROR_Y );
							 | 
						|
								            else
							 | 
						|
								                Expecting( "x or y" );
							 | 
						|
								
							 | 
						|
								            NeedRIGHT();
							 | 
						|
								            break;
							 | 
						|
								
							 | 
						|
								        case T_unit:
							 | 
						|
								            symbol->SetUnit( parseInt( "symbol unit" ) );
							 | 
						|
								            NeedRIGHT();
							 | 
						|
								            break;
							 | 
						|
								
							 | 
						|
								        case T_convert:
							 | 
						|
								            symbol->SetConvert( parseInt( "symbol convert" ) );
							 | 
						|
								            NeedRIGHT();
							 | 
						|
								            break;
							 | 
						|
								
							 | 
						|
								        case T_in_bom:
							 | 
						|
								            symbol->SetIncludeInBom( parseBool() );
							 | 
						|
								            NeedRIGHT();
							 | 
						|
								            break;
							 | 
						|
								
							 | 
						|
								        case T_on_board:
							 | 
						|
								            symbol->SetIncludeOnBoard( parseBool() );
							 | 
						|
								            NeedRIGHT();
							 | 
						|
								            break;
							 | 
						|
								
							 | 
						|
								        case T_uuid:
							 | 
						|
								            NeedSYMBOL();
							 | 
						|
								            const_cast<KIID&>( symbol->m_Uuid ) = KIID( FromUTF8() );
							 | 
						|
								            NeedRIGHT();
							 | 
						|
								            break;
							 | 
						|
								
							 | 
						|
								        case T_property:
							 | 
						|
								        {
							 | 
						|
								            // The field parent symbol must be set and it's orientation must be set before
							 | 
						|
								            // the field positions are set.
							 | 
						|
								            field = parseSchField( symbol.get() );
							 | 
						|
								
							 | 
						|
								            // It would appear that at some point we allowed duplicate ids to slip through
							 | 
						|
								            // when writing files.  The easiest (and most complete) solution is to disallow
							 | 
						|
								            // multiple instances of the same id (for all files since the source of the error
							 | 
						|
								            // *might* in fact be hand-edited files).
							 | 
						|
								            //
							 | 
						|
								            // While no longer used, -1 is still a valid id for user fields and will
							 | 
						|
								            // get written out as the next unused number on save.
							 | 
						|
								            if( fieldIDsRead.count( field->GetId() ) )
							 | 
						|
								                field->SetId( -1 );
							 | 
						|
								            else
							 | 
						|
								                fieldIDsRead.insert( field->GetId() );
							 | 
						|
								
							 | 
						|
								            // Set the default symbol reference prefix.
							 | 
						|
								            if( field->GetId() == REFERENCE )
							 | 
						|
								            {
							 | 
						|
								                wxString refDesignator = field->GetText();
							 | 
						|
								
							 | 
						|
								                refDesignator.Replace( "~", " " );
							 | 
						|
								
							 | 
						|
								                wxString prefix = refDesignator;
							 | 
						|
								
							 | 
						|
								                while( prefix.Length() )
							 | 
						|
								                {
							 | 
						|
								                    if( ( prefix.Last() < '0' || prefix.Last() > '9') && prefix.Last() != '?' )
							 | 
						|
								                        break;
							 | 
						|
								
							 | 
						|
								                    prefix.RemoveLast();
							 | 
						|
								                }
							 | 
						|
								
							 | 
						|
								                // Avoid a prefix containing trailing/leading spaces
							 | 
						|
								                prefix.Trim( true );
							 | 
						|
								                prefix.Trim( false );
							 | 
						|
								
							 | 
						|
								                if( prefix.IsEmpty() )
							 | 
						|
								                    symbol->SetPrefix( wxString( "U" ) );
							 | 
						|
								                else
							 | 
						|
								                    symbol->SetPrefix( prefix );
							 | 
						|
								            }
							 | 
						|
								
							 | 
						|
								            if( symbol->GetField( field->GetId() ) )
							 | 
						|
								                *symbol->GetField( field->GetId() ) = *field;
							 | 
						|
								            else
							 | 
						|
								                symbol->AddField( *field );
							 | 
						|
								
							 | 
						|
								            delete field;
							 | 
						|
								            break;
							 | 
						|
								        }
							 | 
						|
								
							 | 
						|
								        case T_pin:
							 | 
						|
								        {
							 | 
						|
								            SCH_PIN* pin = new SCH_PIN( nullptr, symbol.get() );
							 | 
						|
								
							 | 
						|
								            NeedSYMBOL();
							 | 
						|
								            pin->SetNumber( FromUTF8() );
							 | 
						|
								
							 | 
						|
								            token = NextTok();
							 | 
						|
								
							 | 
						|
								            if( token == T_alternate )
							 | 
						|
								            {
							 | 
						|
								                NeedSYMBOL();
							 | 
						|
								                pin->SetAlt( FromUTF8() );
							 | 
						|
								                NeedRIGHT();
							 | 
						|
								            }
							 | 
						|
								            else
							 | 
						|
								            {
							 | 
						|
								                Expecting( "alternate" );
							 | 
						|
								            }
							 | 
						|
								
							 | 
						|
								            symbol->GetPins().push_back( pin );
							 | 
						|
								
							 | 
						|
								            NeedRIGHT();
							 | 
						|
								        }
							 | 
						|
								            break;
							 | 
						|
								
							 | 
						|
								        default:
							 | 
						|
								            Expecting( "lib_id, lib_name, at, mirror, uuid, property, pin, or instances" );
							 | 
						|
								        }
							 | 
						|
								    }
							 | 
						|
								
							 | 
						|
								    if( !libName.IsEmpty() && ( symbol->GetLibId().Format().wx_str() != libName ) )
							 | 
						|
								        symbol->SetSchSymbolLibraryName( libName );
							 | 
						|
								
							 | 
						|
								    // Ensure edit/status flags are cleared after these initializations:
							 | 
						|
								    symbol->ClearFlags();
							 | 
						|
								
							 | 
						|
								    return symbol.release();
							 | 
						|
								}
							 | 
						|
								
							 | 
						|
								
							 | 
						|
								SCH_BITMAP* SCH_SEXPR_PARSER::parseImage()
							 | 
						|
								{
							 | 
						|
								    wxCHECK_MSG( CurTok() == T_image, nullptr,
							 | 
						|
								                 wxT( "Cannot parse " ) + GetTokenString( CurTok() ) + wxT( " as an image." ) );
							 | 
						|
								
							 | 
						|
								    T token;
							 | 
						|
								    std::unique_ptr<SCH_BITMAP> bitmap( new SCH_BITMAP() );
							 | 
						|
								
							 | 
						|
								    for( token = NextTok();  token != T_RIGHT;  token = NextTok() )
							 | 
						|
								    {
							 | 
						|
								        if( token != T_LEFT )
							 | 
						|
								            Expecting( T_LEFT );
							 | 
						|
								
							 | 
						|
								        token = NextTok();
							 | 
						|
								
							 | 
						|
								        switch( token )
							 | 
						|
								        {
							 | 
						|
								        case T_at:
							 | 
						|
								            bitmap->SetPosition( parseXY() );
							 | 
						|
								            NeedRIGHT();
							 | 
						|
								            break;
							 | 
						|
								
							 | 
						|
								        case T_scale:
							 | 
						|
								            bitmap->GetImage()->SetScale( parseDouble( "image scale factor" ) );
							 | 
						|
								
							 | 
						|
								            if( !std::isnormal( bitmap->GetImage()->GetScale() ) )
							 | 
						|
								                bitmap->GetImage()->SetScale( 1.0 );
							 | 
						|
								
							 | 
						|
								            NeedRIGHT();
							 | 
						|
								            break;
							 | 
						|
								
							 | 
						|
								        case T_data:
							 | 
						|
								        {
							 | 
						|
								            token = NextTok();
							 | 
						|
								
							 | 
						|
								            wxString data;
							 | 
						|
								
							 | 
						|
								            // Reserve 128K because most image files are going to be larger than the default
							 | 
						|
								            // 1K that wxString reserves.
							 | 
						|
								            data.reserve( 1 << 17 );
							 | 
						|
								
							 | 
						|
								            while( token != T_RIGHT )
							 | 
						|
								            {
							 | 
						|
								                if( !IsSymbol( token ) )
							 | 
						|
								                    Expecting( "base64 image data" );
							 | 
						|
								
							 | 
						|
								                data += FromUTF8();
							 | 
						|
								                token = NextTok();
							 | 
						|
								            }
							 | 
						|
								
							 | 
						|
								            wxMemoryBuffer buffer = wxBase64Decode( data );
							 | 
						|
								            wxMemoryOutputStream stream( buffer.GetData(), buffer.GetBufSize() );
							 | 
						|
								            wxImage* image = new wxImage();
							 | 
						|
								            wxMemoryInputStream istream( stream );
							 | 
						|
								            image->LoadFile( istream, wxBITMAP_TYPE_PNG );
							 | 
						|
								            bitmap->GetImage()->SetImage( image );
							 | 
						|
								            bitmap->GetImage()->SetBitmap( new wxBitmap( *image ) );
							 | 
						|
								            break;
							 | 
						|
								        }
							 | 
						|
								
							 | 
						|
								        default:
							 | 
						|
								            Expecting( "at, scale, or data" );
							 | 
						|
								        }
							 | 
						|
								    }
							 | 
						|
								
							 | 
						|
								    return bitmap.release();
							 | 
						|
								}
							 | 
						|
								
							 | 
						|
								
							 | 
						|
								SCH_SHEET* SCH_SEXPR_PARSER::parseSheet()
							 | 
						|
								{
							 | 
						|
								    wxCHECK_MSG( CurTok() == T_sheet, nullptr,
							 | 
						|
								                 wxT( "Cannot parse " ) + GetTokenString( CurTok() ) + wxT( " as a sheet." ) );
							 | 
						|
								
							 | 
						|
								    T token;
							 | 
						|
								    STROKE_PARAMS stroke;
							 | 
						|
								    FILL_PARAMS fill;
							 | 
						|
								    SCH_FIELD* field;
							 | 
						|
								    std::vector<SCH_FIELD> fields;
							 | 
						|
								    std::unique_ptr<SCH_SHEET> sheet( new SCH_SHEET() );
							 | 
						|
								    std::set<int> fieldIDsRead;
							 | 
						|
								
							 | 
						|
								    for( token = NextTok();  token != T_RIGHT;  token = NextTok() )
							 | 
						|
								    {
							 | 
						|
								        if( token != T_LEFT )
							 | 
						|
								            Expecting( T_LEFT );
							 | 
						|
								
							 | 
						|
								        token = NextTok();
							 | 
						|
								
							 | 
						|
								        switch( token )
							 | 
						|
								        {
							 | 
						|
								        case T_at:
							 | 
						|
								            sheet->SetPosition( parseXY() );
							 | 
						|
								            NeedRIGHT();
							 | 
						|
								            break;
							 | 
						|
								
							 | 
						|
								        case T_size:
							 | 
						|
								        {
							 | 
						|
								            wxSize size;
							 | 
						|
								            size.SetWidth( parseInternalUnits( "sheet width" ) );
							 | 
						|
								            size.SetHeight( parseInternalUnits( "sheet height" ) );
							 | 
						|
								            sheet->SetSize( size );
							 | 
						|
								            NeedRIGHT();
							 | 
						|
								            break;
							 | 
						|
								        }
							 | 
						|
								
							 | 
						|
								        case T_stroke:
							 | 
						|
								            parseStroke( stroke );
							 | 
						|
								            sheet->SetBorderWidth( stroke.GetWidth() );
							 | 
						|
								            sheet->SetBorderColor( stroke.GetColor() );
							 | 
						|
								            break;
							 | 
						|
								
							 | 
						|
								        case T_fill:
							 | 
						|
								            parseFill( fill );
							 | 
						|
								            sheet->SetBackgroundColor( fill.m_Color );
							 | 
						|
								            break;
							 | 
						|
								
							 | 
						|
								        case T_uuid:
							 | 
						|
								            NeedSYMBOL();
							 | 
						|
								            const_cast<KIID&>( sheet->m_Uuid ) = KIID( FromUTF8() );
							 | 
						|
								            NeedRIGHT();
							 | 
						|
								            break;
							 | 
						|
								
							 | 
						|
								        case T_property:
							 | 
						|
								            field = parseSchField( sheet.get() );
							 | 
						|
								
							 | 
						|
								            if( m_requiredVersion <= 20200310 )
							 | 
						|
								            {
							 | 
						|
								                // Earlier versions had the wrong ids (and names) saved for sheet fields.
							 | 
						|
								                // Fortunately they only saved the sheetname and sheetfilepath (and always
							 | 
						|
								                // in that order), so we can hack in a recovery.
							 | 
						|
								                if( fields.empty() )
							 | 
						|
								                    field->SetId( SHEETNAME );
							 | 
						|
								                else
							 | 
						|
								                    field->SetId( SHEETFILENAME );
							 | 
						|
								            }
							 | 
						|
								
							 | 
						|
								            // It would appear the problem persists past 20200310, but this time with the
							 | 
						|
								            // earlier ids being re-used for later (user) fields.  The easiest (and most
							 | 
						|
								            // complete) solution is to disallow multiple instances of the same id (for all
							 | 
						|
								            // files since the source of the error *might* in fact be hand-edited files).
							 | 
						|
								            //
							 | 
						|
								            // While no longer used, -1 is still a valid id for user fields and will
							 | 
						|
								            // get written out as the next unused number on save.
							 | 
						|
								            if( fieldIDsRead.count( field->GetId() ) )
							 | 
						|
								                field->SetId( -1 );
							 | 
						|
								            else
							 | 
						|
								                fieldIDsRead.insert( field->GetId() );
							 | 
						|
								
							 | 
						|
								            fields.emplace_back( *field );
							 | 
						|
								            delete field;
							 | 
						|
								            break;
							 | 
						|
								
							 | 
						|
								        case T_pin:
							 | 
						|
								            sheet->AddPin( parseSchSheetPin( sheet.get() ) );
							 | 
						|
								            break;
							 | 
						|
								
							 | 
						|
								        default:
							 | 
						|
								            Expecting( "at, size, stroke, background, uuid, property, or pin" );
							 | 
						|
								        }
							 | 
						|
								    }
							 | 
						|
								
							 | 
						|
								    sheet->SetFields( fields );
							 | 
						|
								
							 | 
						|
								    return sheet.release();
							 | 
						|
								}
							 | 
						|
								
							 | 
						|
								
							 | 
						|
								SCH_JUNCTION* SCH_SEXPR_PARSER::parseJunction()
							 | 
						|
								{
							 | 
						|
								    wxCHECK_MSG( CurTok() == T_junction, nullptr,
							 | 
						|
								                 wxT( "Cannot parse " ) + GetTokenString( CurTok() ) + wxT( " as a junction." ) );
							 | 
						|
								
							 | 
						|
								    T token;
							 | 
						|
								    std::unique_ptr<SCH_JUNCTION> junction( new SCH_JUNCTION() );
							 | 
						|
								
							 | 
						|
								    for( token = NextTok();  token != T_RIGHT;  token = NextTok() )
							 | 
						|
								    {
							 | 
						|
								        if( token != T_LEFT )
							 | 
						|
								            Expecting( T_LEFT );
							 | 
						|
								
							 | 
						|
								        token = NextTok();
							 | 
						|
								
							 | 
						|
								        switch( token )
							 | 
						|
								        {
							 | 
						|
								        case T_at:
							 | 
						|
								            junction->SetPosition( parseXY() );
							 | 
						|
								            NeedRIGHT();
							 | 
						|
								            break;
							 | 
						|
								
							 | 
						|
								        case T_diameter:
							 | 
						|
								            junction->SetDiameter( parseInternalUnits( "junction diameter" ) );
							 | 
						|
								            NeedRIGHT();
							 | 
						|
								            break;
							 | 
						|
								
							 | 
						|
								        case T_color:
							 | 
						|
								        {
							 | 
						|
								            COLOR4D color;
							 | 
						|
								
							 | 
						|
								            color.r = parseInt( "red" ) / 255.0;
							 | 
						|
								            color.g = parseInt( "green" ) / 255.0;
							 | 
						|
								            color.b = parseInt( "blue" ) / 255.0;
							 | 
						|
								            color.a = Clamp( parseDouble( "alpha" ), 0.0, 1.0 );
							 | 
						|
								
							 | 
						|
								            junction->SetColor( color );
							 | 
						|
								            NeedRIGHT();
							 | 
						|
								            break;
							 | 
						|
								        }
							 | 
						|
								
							 | 
						|
								        default:
							 | 
						|
								            Expecting( "at" );
							 | 
						|
								        }
							 | 
						|
								    }
							 | 
						|
								
							 | 
						|
								    return junction.release();
							 | 
						|
								}
							 | 
						|
								
							 | 
						|
								
							 | 
						|
								SCH_NO_CONNECT* SCH_SEXPR_PARSER::parseNoConnect()
							 | 
						|
								{
							 | 
						|
								    wxCHECK_MSG( CurTok() == T_no_connect, nullptr,
							 | 
						|
								                 wxT( "Cannot parse " ) + GetTokenString( CurTok() ) + wxT( " as a no connect." ) );
							 | 
						|
								
							 | 
						|
								    T token;
							 | 
						|
								    std::unique_ptr<SCH_NO_CONNECT> no_connect( new SCH_NO_CONNECT() );
							 | 
						|
								
							 | 
						|
								    for( token = NextTok();  token != T_RIGHT;  token = NextTok() )
							 | 
						|
								    {
							 | 
						|
								        if( token != T_LEFT )
							 | 
						|
								            Expecting( T_LEFT );
							 | 
						|
								
							 | 
						|
								        token = NextTok();
							 | 
						|
								
							 | 
						|
								        switch( token )
							 | 
						|
								        {
							 | 
						|
								        case T_at:
							 | 
						|
								            no_connect->SetPosition( parseXY() );
							 | 
						|
								            NeedRIGHT();
							 | 
						|
								            break;
							 | 
						|
								
							 | 
						|
								        default:
							 | 
						|
								            Expecting( "at" );
							 | 
						|
								        }
							 | 
						|
								    }
							 | 
						|
								
							 | 
						|
								    return no_connect.release();
							 | 
						|
								}
							 | 
						|
								
							 | 
						|
								
							 | 
						|
								SCH_BUS_WIRE_ENTRY* SCH_SEXPR_PARSER::parseBusEntry()
							 | 
						|
								{
							 | 
						|
								    wxCHECK_MSG( CurTok() == T_bus_entry, nullptr,
							 | 
						|
								                 wxT( "Cannot parse " ) + GetTokenString( CurTok() ) + wxT( " as a bus entry." ) );
							 | 
						|
								
							 | 
						|
								    T token;
							 | 
						|
								    STROKE_PARAMS stroke;
							 | 
						|
								    std::unique_ptr<SCH_BUS_WIRE_ENTRY> busEntry( new SCH_BUS_WIRE_ENTRY() );
							 | 
						|
								
							 | 
						|
								    for( token = NextTok();  token != T_RIGHT;  token = NextTok() )
							 | 
						|
								    {
							 | 
						|
								        if( token != T_LEFT )
							 | 
						|
								            Expecting( T_LEFT );
							 | 
						|
								
							 | 
						|
								        token = NextTok();
							 | 
						|
								
							 | 
						|
								        switch( token )
							 | 
						|
								        {
							 | 
						|
								        case T_at:
							 | 
						|
								            busEntry->SetPosition( parseXY() );
							 | 
						|
								            NeedRIGHT();
							 | 
						|
								            break;
							 | 
						|
								
							 | 
						|
								        case T_size:
							 | 
						|
								        {
							 | 
						|
								            wxSize size;
							 | 
						|
								
							 | 
						|
								            size.SetWidth( parseInternalUnits( "bus entry height" ) );
							 | 
						|
								            size.SetHeight( parseInternalUnits( "bus entry width" ) );
							 | 
						|
								            busEntry->SetSize( size );
							 | 
						|
								            NeedRIGHT();
							 | 
						|
								            break;
							 | 
						|
								        }
							 | 
						|
								
							 | 
						|
								        case T_stroke:
							 | 
						|
								            parseStroke( stroke );
							 | 
						|
								            busEntry->SetStroke( stroke );
							 | 
						|
								            break;
							 | 
						|
								
							 | 
						|
								        default:
							 | 
						|
								            Expecting( "at, size, or stroke" );
							 | 
						|
								        }
							 | 
						|
								    }
							 | 
						|
								
							 | 
						|
								    return busEntry.release();
							 | 
						|
								}
							 | 
						|
								
							 | 
						|
								
							 | 
						|
								SCH_LINE* SCH_SEXPR_PARSER::parseLine()
							 | 
						|
								{
							 | 
						|
								    T token;
							 | 
						|
								    STROKE_PARAMS stroke;
							 | 
						|
								    std::unique_ptr<SCH_LINE> line( new SCH_LINE() );
							 | 
						|
								
							 | 
						|
								    switch( CurTok() )
							 | 
						|
								    {
							 | 
						|
								    case T_polyline:   line->SetLayer( LAYER_NOTES );   break;
							 | 
						|
								    case T_wire:       line->SetLayer( LAYER_WIRE );    break;
							 | 
						|
								    case T_bus:        line->SetLayer( LAYER_BUS );     break;
							 | 
						|
								    default:
							 | 
						|
								        wxCHECK_MSG( false, nullptr,
							 | 
						|
								                     wxT( "Cannot parse " ) + GetTokenString( CurTok() ) + wxT( " as a line." ) );
							 | 
						|
								    }
							 | 
						|
								
							 | 
						|
								    for( token = NextTok();  token != T_RIGHT;  token = NextTok() )
							 | 
						|
								    {
							 | 
						|
								        if( token != T_LEFT )
							 | 
						|
								            Expecting( T_LEFT );
							 | 
						|
								
							 | 
						|
								        token = NextTok();
							 | 
						|
								
							 | 
						|
								        switch( token )
							 | 
						|
								        {
							 | 
						|
								        case T_pts:
							 | 
						|
								            NeedLEFT();
							 | 
						|
								            token = NextTok();
							 | 
						|
								
							 | 
						|
								            if( token != T_xy )
							 | 
						|
								                Expecting( "xy" );
							 | 
						|
								
							 | 
						|
								            line->SetStartPoint( parseXY() );
							 | 
						|
								            NeedRIGHT();
							 | 
						|
								            NeedLEFT();
							 | 
						|
								            token = NextTok();
							 | 
						|
								
							 | 
						|
								            if( token != T_xy )
							 | 
						|
								                Expecting( "xy" );
							 | 
						|
								
							 | 
						|
								            line->SetEndPoint( parseXY() );
							 | 
						|
								            NeedRIGHT();
							 | 
						|
								            NeedRIGHT();
							 | 
						|
								            break;
							 | 
						|
								
							 | 
						|
								        case T_stroke:
							 | 
						|
								            parseStroke( stroke );
							 | 
						|
								            line->SetStroke( stroke );
							 | 
						|
								            break;
							 | 
						|
								
							 | 
						|
								        default:
							 | 
						|
								            Expecting( "at or stroke" );
							 | 
						|
								        }
							 | 
						|
								    }
							 | 
						|
								
							 | 
						|
								    return line.release();
							 | 
						|
								}
							 | 
						|
								
							 | 
						|
								
							 | 
						|
								SCH_TEXT* SCH_SEXPR_PARSER::parseSchText()
							 | 
						|
								{
							 | 
						|
								    T token;
							 | 
						|
								    std::unique_ptr<SCH_TEXT> text;
							 | 
						|
								
							 | 
						|
								    switch( CurTok() )
							 | 
						|
								    {
							 | 
						|
								    case T_text:                text.reset( new SCH_TEXT );          break;
							 | 
						|
								    case T_label:               text.reset( new SCH_LABEL );         break;
							 | 
						|
								    case T_global_label:        text.reset( new SCH_GLOBALLABEL );   break;
							 | 
						|
								    case T_hierarchical_label:  text.reset( new SCH_HIERLABEL );     break;
							 | 
						|
								    default:
							 | 
						|
								        wxCHECK_MSG( false, nullptr,
							 | 
						|
								                     wxT( "Cannot parse " ) + GetTokenString( CurTok() ) + wxT( " as text." ) );
							 | 
						|
								    }
							 | 
						|
								
							 | 
						|
								    NeedSYMBOL();
							 | 
						|
								
							 | 
						|
								    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() );
							 | 
						|
								
							 | 
						|
								            switch( static_cast<int>( parseDouble( "text angle" ) ) )
							 | 
						|
								            {
							 | 
						|
								            case 0:    text->SetLabelSpinStyle( LABEL_SPIN_STYLE::RIGHT );    break;
							 | 
						|
								            case 90:   text->SetLabelSpinStyle( LABEL_SPIN_STYLE::UP );       break;
							 | 
						|
								            case 180:  text->SetLabelSpinStyle( LABEL_SPIN_STYLE::LEFT );     break;
							 | 
						|
								            case 270:  text->SetLabelSpinStyle( LABEL_SPIN_STYLE::BOTTOM );   break;
							 | 
						|
								            default:
							 | 
						|
								                wxFAIL;
							 | 
						|
								                text->SetLabelSpinStyle( LABEL_SPIN_STYLE::RIGHT );
							 | 
						|
								                break;
							 | 
						|
								            }
							 | 
						|
								
							 | 
						|
								            NeedRIGHT();
							 | 
						|
								            break;
							 | 
						|
								        }
							 | 
						|
								
							 | 
						|
								        case T_shape:
							 | 
						|
								        {
							 | 
						|
								            if( text->Type() == SCH_TEXT_T || text->Type() == SCH_LABEL_T )
							 | 
						|
								                Unexpected( T_shape );
							 | 
						|
								
							 | 
						|
								            token = NextTok();
							 | 
						|
								
							 | 
						|
								            switch( token )
							 | 
						|
								            {
							 | 
						|
								            case T_input:          text->SetShape( PINSHEETLABEL_SHAPE::PS_INPUT );        break;
							 | 
						|
								            case T_output:         text->SetShape( PINSHEETLABEL_SHAPE::PS_OUTPUT );       break;
							 | 
						|
								            case T_bidirectional:  text->SetShape( PINSHEETLABEL_SHAPE::PS_BIDI );         break;
							 | 
						|
								            case T_tri_state:      text->SetShape( PINSHEETLABEL_SHAPE::PS_TRISTATE );     break;
							 | 
						|
								            case T_passive:        text->SetShape( PINSHEETLABEL_SHAPE::PS_UNSPECIFIED );  break;
							 | 
						|
								            default:
							 | 
						|
								                Expecting( "input, output, bidirectional, tri_state, or passive" );
							 | 
						|
								            }
							 | 
						|
								
							 | 
						|
								            NeedRIGHT();
							 | 
						|
								            break;
							 | 
						|
								        }
							 | 
						|
								
							 | 
						|
								        case T_effects:
							 | 
						|
								            parseEDA_TEXT( static_cast<EDA_TEXT*>( text.get() ) );
							 | 
						|
								            break;
							 | 
						|
								
							 | 
						|
								        default:
							 | 
						|
								            Expecting( "at, shape, or effects" );
							 | 
						|
								        }
							 | 
						|
								    }
							 | 
						|
								
							 | 
						|
								    return text.release();
							 | 
						|
								}
							 | 
						|
								
							 | 
						|
								
							 | 
						|
								void SCH_SEXPR_PARSER::parseBusAlias( SCH_SCREEN* aScreen )
							 | 
						|
								{
							 | 
						|
								    wxCHECK_RET( CurTok() == T_bus_alias,
							 | 
						|
								                 wxT( "Cannot parse " ) + GetTokenString( CurTok() ) + wxT( " as a bus alias." ) );
							 | 
						|
								    wxCHECK( aScreen, /* void */ );
							 | 
						|
								
							 | 
						|
								    T token;
							 | 
						|
								    auto busAlias = std::make_shared< BUS_ALIAS >( aScreen );
							 | 
						|
								
							 | 
						|
								    NeedSYMBOL();
							 | 
						|
								    busAlias->SetName( FromUTF8() );
							 | 
						|
								    NeedLEFT();
							 | 
						|
								    token = NextTok();
							 | 
						|
								
							 | 
						|
								    if( token != T_members )
							 | 
						|
								        Expecting( "members" );
							 | 
						|
								
							 | 
						|
								    token = NextTok();
							 | 
						|
								
							 | 
						|
								    while( token != T_RIGHT )
							 | 
						|
								    {
							 | 
						|
								        if( !IsSymbol( token ) )
							 | 
						|
								            Expecting( "quoted string" );
							 | 
						|
								
							 | 
						|
								        busAlias->AddMember( FromUTF8() );
							 | 
						|
								        token = NextTok();
							 | 
						|
								    }
							 | 
						|
								
							 | 
						|
								    NeedRIGHT();
							 | 
						|
								
							 | 
						|
								    aScreen->AddBusAlias( busAlias );
							 | 
						|
								}
							 |