/* * This program source code file is part of KICAD, a free EDA CAD application. * * Copyright (C) 2011 SoftPLC Corporation, Dick Hollenbeck * Copyright (C) 2010 Kicad Developers, see change_log.txt for contributors. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, you may find one here: * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html * or you may search the http://www.gnu.org website for the version 2 license, * or you may write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA */ #include #include #include #include #include using namespace SCH; using namespace PR; #define MAX_INHERITANCE_NESTING 6 ///< max depth of inheritance, no problem going larger static inline int internal( const STRING& aCoord ) { return LogicalToInternal( strtod( aCoord.c_str(), NULL ) ); } static inline int fromWidth( const STRING& aWidth ) { return WidthToInternal( strtod( aWidth.c_str(), NULL ) ); } static inline int fromFontz( const STRING& aFontSize ) { return FontzToInternal( strtod( aFontSize.c_str(), NULL ) ); } /** * Enum PartBit * is a set of bit positions that can be used to create flag bits within * PART::contains to indicate what state the PART is in and what it contains, i.e. * whether the PART has been parsed, and what the PART contains, categorically. */ enum PartBit { parsed, ///< have parsed this part already, otherwise 'body' text must be parsed extends, ///< saw "extends" keyword, inheriting from another PART value, anchor, reference, footprint, datasheet, model, keywords, }; /// Function PB /// is a PartBit shifter for PART::contains field. static inline const int PB( PartBit oneBitOnly ) { return ( 1 << oneBitOnly ); } void SWEET_PARSER::Parse( PART* me, LIB_TABLE* aTable ) throw( IO_ERROR, PARSE_ERROR ) { T tok; libs = aTable; // empty everything out, could be re-parsing this object and it may not be empty. me->clear(); #if 0 // Be flexible regarding the starting point of the stream. // Caller may not have read the first two tokens out of the // stream: T_LEFT and T_part, so ignore them if seen here. // The 1st two tokens T_LEFT and T_part are then optional in the grammar. if( ( tok = NextTok() ) == T_LEFT ) { if( ( tok = NextTok() ) != T_part ) Expecting( T_part ); } #else // "( part" are not optional NeedLEFT(); if( ( tok = NextTok() ) != T_part ) Expecting( T_part ); #endif NeedSYMBOLorNUMBER(); // toss NAME_HINT tok = NextTok(); // extends must be _first_ thing, if it is present at all, after NAME_HINT if( tok == T_extends ) { parseExtends( me ); tok = NextTok(); } for( ; tok!=T_RIGHT; tok = NextTok() ) { if( tok == T_LEFT ) { PROPERTY* prop; tok = NextTok(); // because exceptions are thrown, any 'new' allocation has to be stored // somewhere other than on the stack, ASAP. switch( tok ) { default: // describe what we expect at this level Expecting( "anchor|value|footprint|model|keywords|alternates\n" "|property\n" " |property_del\n" "|pin\n" " |pin_merge|pin_swap|pin_renum|pin_rename|route_pin_swap\n" "|polyline|line|rectangle|circle|arc|bezier|text" ); break; case T_anchor: if( contains & PB(anchor) ) Duplicate( tok ); NeedNUMBER( "anchor x" ); me->anchor.x = internal( CurText() ); NeedNUMBER( "anchor y" ); me->anchor.y = internal( CurText() ); contains |= PB(anchor); break; case T_line: case T_polyline: POLY_LINE* pl; pl = new POLY_LINE( me ); me->graphics.push_back( pl ); parsePolyLine( pl ); break; case T_rectangle: RECTANGLE* rect; rect = new RECTANGLE( me ); me->graphics.push_back( rect ); parseRectangle( rect ); break; case T_circle: CIRCLE* circ; circ = new CIRCLE( me ); me->graphics.push_back( circ ); parseCircle( circ ); break; case T_arc: ARC* arc; arc = new ARC( me ); me->graphics.push_back( arc ); parseArc( arc ); break; case T_bezier: BEZIER* bezier; bezier = new BEZIER( me ); me->graphics.push_back( bezier ); parseBezier( bezier ); break; case T_text: GR_TEXT* text; text = new GR_TEXT( me ); me->graphics.push_back( text ); parseText( text ); break; case T_property: prop = new PROPERTY( me ); // @todo check for uniqueness me->properties.push_back( prop ); NeedSYMBOLorNUMBER(); prop->name = FromUTF8(); L_prop: NeedSYMBOLorNUMBER(); prop->text = FromUTF8(); tok = NextTok(); if( tok == T_LEFT ) { tok = NextTok(); if( tok != T_effects ) Expecting( T_effects ); parseTextEffects( prop->EffectsLookup() ); NeedRIGHT(); } else if( tok != T_RIGHT ) Expecting( ") | effects" ); break; case T_property_del: parsePropertyDel( me ); break; // reference in a PART is incomplete, it is just the prefix of an // unannotated reference. Only components have full reference designators. case T_reference: if( contains & PB(reference) ) Duplicate( tok ); contains |= PB(reference); prop = me->FieldLookup( PART::REFERENCE ); goto L_prop; case T_value: if( contains & PB(value) ) Duplicate( tok ); contains |= PB(value); prop = me->FieldLookup( PART::VALUE ); goto L_prop; case T_footprint: if( contains & PB(footprint) ) Duplicate( tok ); contains |= PB(footprint); prop = me->FieldLookup( PART::FOOTPRINT ); goto L_prop; case T_datasheet: if( contains & PB(datasheet) ) Duplicate( tok ); contains |= PB(datasheet); prop = me->FieldLookup( PART::DATASHEET ); goto L_prop; case T_model: if( contains & PB(model) ) Duplicate( tok ); contains |= PB(model); prop = me->FieldLookup( PART::MODEL ); goto L_prop; case T_keywords: parseKeywords( me ); break; case T_alternates: // @todo: do we want to inherit alternates? parseAlternates( me ); break; case T_pin: // @todo PADNAMEs must be unique PIN* pin; pin = new PIN( me ); me->pins.push_back( pin ); parsePin( pin ); break; case T_pin_del: parsePinDel( me ); break; case T_pin_swap: parsePinSwap( me ); break; case T_pin_renum: parsePinRenum( me ); break; case T_pin_rename: parsePinRename( me ); break; case T_pin_merge: parsePinMerge( me ); break; /* @todo case T_route_pin_swap: break; */ } } else { switch( tok ) { default: Unexpected( tok ); } } } contains |= PB(parsed); me->contains |= contains; } void SWEET_PARSER::parseExtends( PART* me ) { PART* base; int offset; if( contains & PB(extends) ) Duplicate( T_extends ); NeedSYMBOLorNUMBER(); me->setExtends( new LPID() ); offset = me->extends->Parse( CurText() ); if( offset > -1 ) // -1 is success THROW_PARSE_ERROR( _("invalid extends LPID"), CurSource(), CurLine(), CurLineNumber(), CurOffset() + offset ); base = libs->LookupPart( *me->extends, me->Owner() ); // we could be going in circles here, recursively, or too deep, set limits // and disallow extending from self (even indirectly) int extendsDepth = 0; for( PART* ancestor = base; ancestor && extendsDepthbase ) { if( ancestor == me ) { THROW_PARSE_ERROR( _("'extends' may not have self as any ancestor"), CurSource(), CurLine(), CurLineNumber(), CurOffset() ); } } if( extendsDepth == MAX_INHERITANCE_NESTING ) { THROW_PARSE_ERROR( _("max allowed extends depth exceeded"), CurSource(), CurLine(), CurLineNumber(), CurOffset() ); } me->inherit( *base ); me->base = base; contains |= PB(extends); } void SWEET_PARSER::parseAlternates( PART* me ) { T tok; PART_REF lpid; int offset; while( ( tok = NextTok() ) != T_RIGHT ) { if( !IsSymbol( tok ) && tok != T_NUMBER ) Expecting( "lpid" ); // lpid.clear(); Parse does this offset = lpid.Parse( CurText() ); if( offset > -1 ) THROW_PARSE_ERROR( _("invalid alternates LPID"), CurSource(), CurLine(), CurLineNumber(), CurOffset() + offset ); // PART_REF assignment should be OK, it contains no ownership me->alternates.push_back( lpid ); } } void SWEET_PARSER::parseKeywords( PART* me ) { T tok; while( ( tok = NextTok() ) != T_RIGHT ) { if( !IsSymbol( tok ) && tok!=T_NUMBER ) Expecting( "symbol|number" ); // just insert them, duplicates are silently removed and tossed. me->keywords.insert( FromUTF8() ); } } void SWEET_PARSER::parseFont( FONT* me ) { /* # The FONT value needs to be defined. Currently, EESchema does not support # different fonts. In the future this feature may be implemented and at # that time FONT will have to be defined. Initially, only the font size and # style are required. Italic and bold styles are optional. The font size # height and width are in units yet to be determined. (font [FONT] (size HEIGHT WIDTH) [italic] [bold]) */ // handle the [FONT] 'position dependently', i.e. first T tok = NextTok(); bool sawBold = false; bool sawItalic = false; bool sawSize = false; if( IsSymbol( tok ) ) { me->name = FromUTF8(); tok = NextTok(); } for( ; tok != T_RIGHT; tok = NextTok() ) { if( tok == T_LEFT ) { tok = NextTok(); switch( tok ) { case T_size: if( sawSize ) Duplicate( T_size ); sawSize = true; NeedNUMBER( "size height" ); me->size.height = fromFontz( CurText() ); NeedNUMBER( "size width" ); me->size.width = fromFontz( CurText() ); NeedRIGHT(); break; default: Expecting( "size" ); } } else { switch( tok ) { case T_bold: if( sawBold ) Duplicate( T_bold ); sawBold = true; me->bold = true; break; case T_italic: if( sawItalic ) Duplicate( T_italic ); sawItalic = true; me->italic = true; break; default: Unexpected( "bold|italic" ); } } } } void SWEET_PARSER::parseBool( bool* aBool ) { T tok = NeedSYMBOL(); switch( tok ) { case T_yes: case T_no: *aBool = (tok == T_yes); break; default: Expecting( "yes|no" ); } } void SWEET_PARSER::parseStroke( STROKE* me ) { /* (stroke [WIDTH] [(style [(dashed...)]...)]) future place holder for arrow heads, dashed lines, all line glamour */ NeedNUMBER( "stroke" ); *me = fromWidth( CurText() ); NeedRIGHT(); } void SWEET_PARSER::parsePinText( PINTEXT* me ) { /* either: (signal SIGNAL (font [FONT] (size HEIGHT WIDTH) [italic] [bold])(visible YES)) or (pad PADNAME (font [FONT] (size HEIGHT WIDTH) [italic] [bold])(visible YES)) */ T tok; bool sawFont = false; bool sawVis = false; // pad or signal text NeedSYMBOLorNUMBER(); me->text = FromUTF8(); while( ( tok = NextTok() ) != T_RIGHT ) { if( tok == T_LEFT ) { tok = NextTok(); switch( tok ) { case T_font: if( sawFont ) Duplicate( tok ); sawFont = true; parseFont( &me->font ); break; case T_visible: if( sawVis ) Duplicate( tok ); sawVis = true; parseBool( &me->isVisible ); NeedRIGHT(); break; default: Expecting( "font" ); } } else { switch( tok ) { default: Expecting( T_LEFT ); } } } } void SWEET_PARSER::parsePin( PIN* me ) { /* (pin TYPE SHAPE (at X Y [ANGLE]) (length LENGTH) (signal NAME (font [FONT] (size HEIGHT WIDTH) [italic] [bold])(visible YES)) (pad NUMBER (font [FONT] (size HEIGHT WIDTH) [italic] [bold] (visible YES)) (visible YES) ) */ T tok; bool sawShape = false; bool sawType = false; bool sawAt = false; bool sawLen = false; bool sawSignal = false; bool sawPad = false; bool sawVis = false; while( ( tok = NextTok() ) != T_RIGHT ) { if( tok == T_LEFT ) { tok = NextTok(); switch( tok ) { case T_at: if( sawAt ) Duplicate( tok ); sawAt = true; parseAt( &me->pos, &me->angle ); break; case T_length: if( sawLen ) Duplicate( tok ); sawLen = true; NeedNUMBER( "length" ); me->length = internal( CurText() ); NeedRIGHT(); break; case T_signal: if( sawSignal ) Duplicate( tok ); sawSignal = true; parsePinText( &me->signal ); break; case T_pad: if( sawPad ) Duplicate( tok ); sawPad = true; parsePinText( &me->pad ); break; case T_visible: if( sawVis ) Duplicate( tok ); sawVis = true; parseBool( &me->isVisible ); NeedRIGHT(); break; default: Unexpected( tok ); } } else // not wrapped in parentheses { switch( tok ) { case T_in: case T_out: case T_inout: case T_tristate: case T_passive: case T_unspecified: case T_power_in: case T_power_out: case T_open_collector: case T_open_emitter: case T_unconnected: if( sawType ) Duplicate( tok ); sawType = true; me->connectionType = tok; break; case T_none: case T_line: case T_inverted: case T_clock: case T_inverted_clk: case T_input_low: case T_clock_low: case T_falling_edge: case T_non_logic: if( sawShape ) Duplicate( tok ); sawShape = true; me->shape = tok; break; default: Unexpected( tok ); } } } } void SWEET_PARSER::parsePinDel( PART* me ) { wxString pad; // we do this somewhat unorthodoxically because we want to avoid doing two lookups, // which would need to be done to 1) find pin, and 2) delete pin. Only one // lookup is needed with this scheme. NeedSYMBOLorNUMBER(); pad = FromUTF8(); // lookup now while CurOffset() is still meaningful. PINS::iterator it = me->pinFindByPad( pad ); if( it == me->pins.end() ) { THROW_PARSE_ERROR( _("undefined pin"), CurSource(), CurLine(), CurLineNumber(), CurOffset() ); } /* enable in future, but not now while testing if( (*it)->birthplace == me ) { THROW_PARSE_ERROR( _("pin_del allowed for inherited pins only"), CurSource(), CurLine(), CurLineNumber(), CurOffset() ); } */ NeedRIGHT(); delete *it; // good thing I'm a friend. me->pins.erase( it ); } void SWEET_PARSER::parsePinSwap( PART* me ) { PIN* pin1; PIN* pin2; wxString pad; NeedSYMBOLorNUMBER(); pad = FromUTF8(); // lookup now while CurOffset() is still meaningful. pin1 = me->PinFindByPad( pad ); if( !pin1 ) { THROW_PARSE_ERROR( _("undefined pin"), CurSource(), CurLine(), CurLineNumber(), CurOffset() ); } NeedSYMBOLorNUMBER(); pad = FromUTF8(); pin2 = me->PinFindByPad( pad ); if( !pin2 ) { THROW_PARSE_ERROR( _("undefined pin"), CurSource(), CurLine(), CurLineNumber(), CurOffset() ); } NeedRIGHT(); // swap only the text, but might want to swap entire PIN_TEXTs pin2->pad.text = pin1->pad.text; pin1->pad.text = pad; } void SWEET_PARSER::parsePinRenum( PART* me ) { PIN* pin; wxString oldPad; wxString newPad; NeedSYMBOLorNUMBER(); oldPad = FromUTF8(); // lookup now while CurOffset() is still meaningful. pin = me->PinFindByPad( oldPad ); if( !pin ) { THROW_PARSE_ERROR( _("undefined pin"), CurSource(), CurLine(), CurLineNumber(), CurOffset() ); } NeedSYMBOLorNUMBER(); newPad = FromUTF8(); NeedRIGHT(); // @todo: check for pad legalities pin->pad.text = newPad; } void SWEET_PARSER::parsePinRename( PART* me ) { PIN* pin; wxString pad; wxString newSignal; NeedSYMBOLorNUMBER(); pad = FromUTF8(); // lookup now while CurOffset() is still meaningful. pin = me->PinFindByPad( pad ); if( !pin ) { THROW_PARSE_ERROR( _("undefined pin"), CurSource(), CurLine(), CurLineNumber(), CurOffset() ); } NeedSYMBOLorNUMBER(); newSignal = FromUTF8(); NeedRIGHT(); pin->signal.text = newSignal; } void SWEET_PARSER::parsePinMerge( PART* me ) { T tok; wxString pad; wxString signal; wxString msg; NeedSYMBOLorNUMBER(); wxString anchorPad = FromUTF8(); // lookup now while CurOffset() is still good. PINS::iterator pit = me->pinFindByPad( anchorPad ); if( pit == me->pins.end() ) { msg.Printf( _( "undefined pin %s" ), anchorPad.GetData() ); THROW_PARSE_ERROR( msg, CurSource(), CurLine(), CurLineNumber(), CurOffset() ); } if( !(*pit)->pin_merge.IsEmpty() && anchorPad != (*pit)->pin_merge ) { msg.Printf( _( "pin %s already in pin_merge group %s" ), anchorPad.GetData(), (*pit)->pin_merge.GetData() ); THROW_PARSE_ERROR( msg, CurSource(), CurLine(), CurLineNumber(), CurOffset() ); } (*pit)->isVisible = true; (*pit)->pin_merge = anchorPad; // allocate or find a MERGE_SET; MERGE_SET& ms = me->pin_merges[anchorPad]; while( ( tok = NextTok() ) != T_RIGHT ) { if( tok == T_LEFT ) { tok = NextTok(); switch( tok ) { case T_signals: { PINS sigPins; // no ownership while( ( tok = NextTok() ) != T_RIGHT ) { if( !IsSymbol( tok ) && tok != T_NUMBER ) Expecting( "signal" ); signal = FromUTF8(); sigPins.clear(); me->PinsFindBySignal( &sigPins, signal ); if( !sigPins.size() ) { msg.Printf( _( "no pins with signal %s" ), signal.GetData() ); THROW_PARSE_ERROR( msg, CurSource(), CurLine(), CurLineNumber(), CurOffset() ); } for( pit = sigPins.begin(); pit != sigPins.end(); ++pit ) { if( !(*pit)->pin_merge.IsEmpty() && anchorPad != (*pit)->pin_merge ) { msg.Printf( _( "signal pin %s already in pin_merge group %s" ), pad.GetData(), (*pit)->pin_merge.GetData() ); THROW_PARSE_ERROR( msg, CurSource(), CurLine(), CurLineNumber(), CurOffset() ); } (*pit)->isVisible = true; (*pit)->pin_merge = anchorPad; ms.insert( pad ); } } } break; case T_pads: while( ( tok = NextTok() ) != T_RIGHT ) { if( !IsSymbol( tok ) && tok != T_NUMBER ) Expecting( "pad" ); pad = FromUTF8(); D(printf("pad=%s\n", TO_UTF8( pad ) );) // find the PIN and mark it as being in this MERGE_SET or throw // error if already in another MERGET_SET. pit = me->pinFindByPad( pad ); if( pit == me->pins.end() ) { msg.Printf( _( "undefined pin %s" ), pad.GetData() ); THROW_PARSE_ERROR( msg, CurSource(), CurLine(), CurLineNumber(), CurOffset() ); } if( !(*pit)->pin_merge.IsEmpty() /* && anchorPad != (*pit)->pin_merge */ ) { msg.Printf( _( "pin %s already in pin_merge group %s" ), pad.GetData(), (*pit)->pin_merge.GetData() ); THROW_PARSE_ERROR( msg, CurSource(), CurLine(), CurLineNumber(), CurOffset() ); } (*pit)->isVisible = false; (*pit)->pin_merge = anchorPad; ms.insert( pad ); } break; default: Expecting( "pads|signals" ); break; } } else { Expecting( T_LEFT ); } } } void SWEET_PARSER::parsePropertyDel( PART* me ) { NeedSYMBOLorNUMBER(); wxString propertyName = FromUTF8(); if( !me->PropertyDelete( propertyName ) ) { wxString msg; msg.Printf( _( "Unable to find property: %s" ), propertyName.GetData() ); THROW_IO_ERROR( msg ); } NeedRIGHT(); } void SWEET_PARSER::parseTextEffects( TEXT_EFFECTS* me ) { /* (effects [PROPERTY] # Position requires X and Y coordinates. Position coordinates can be # non-intergr. Angle is in degrees and defaults to 0 if not defined. (at X Y [ANGLE]) # The FONT value needs to be defined. Currently, EESchema does not support # different fonts. In the future this feature may be implemented and at # that time FONT will have to be defined. Initially, only the font size and # style are required. Italic and bold styles are optional. The font size # height and width are in units yet to be determined. (font [FONT] (size HEIGHT WIDTH) [italic] [bold]) # Valid visibility values are yes and no. (visible YES) ) */ bool sawFont = false; bool sawAt = false; bool sawVis = false; T tok = NextTok(); if( IsSymbol( tok ) ) { me->propName = FromUTF8(); tok = NextTok(); } for( ; tok != T_RIGHT; tok = NextTok() ) { if( tok != T_LEFT ) Expecting( T_LEFT ); tok = NextTok(); switch( tok ) { case T_at: if( sawAt ) Duplicate( tok ); sawAt = true; parseAt( &me->pos, &me->angle ); break; case T_font: if( sawFont ) Duplicate( tok ); sawFont = true; parseFont( &me->font ); break; case T_visible: if( sawVis ) Duplicate( sawVis ); sawVis = true; parseBool( &me->isVisible ); NeedRIGHT(); break; default: Expecting( "at|font|visible" ); } } } void SWEET_PARSER::parsePolyLine( POLY_LINE* me ) { /* (polyline|line (pts (xy X Y) (xy X Y) (xy X Y) (xy X Y) (xy X Y)) # Line widths are in percent of a pin delta [(stroke [WIDTH] [(style [(dashed...)]...)])] # Valid fill types are none, filled, and transparent. (fill FILL_TYPE) ) */ T tok; int count = 0; bool sawStroke = false; bool sawFill = false; while( ( tok = NextTok() ) != T_RIGHT ) { if( tok != T_LEFT ) Expecting( T_LEFT ); tok = NextTok(); switch( tok ) { case T_stroke: if( sawStroke ) Duplicate( tok ); sawStroke = true; parseStroke( &me->stroke ); break; case T_pts: if( count ) Duplicate( tok ); for( ; ( tok = NextTok() ) != T_RIGHT; ++count ) { if( tok != T_LEFT ) Expecting( T_LEFT ); tok = NeedSYMBOL(); if( tok != T_xy ) Expecting( T_xy ); me->pts.push_back( POINT() ); NeedNUMBER( "x" ); me->pts.back().x = internal( CurText() ); NeedNUMBER( "y" ); me->pts.back().y = internal( CurText() ); NeedRIGHT(); } if( count < 2 ) Expecting( ">= 2 pts" ); break; case T_fill: if( sawFill ) Duplicate( tok ); tok = NeedSYMBOL(); switch( tok ) { case T_none: case T_filled: case T_transparent: me->fillType = tok; break; default: Expecting( "none|filled|transparent" ); } NeedRIGHT(); sawFill = true; break; default: Expecting( "pts|stroke|fill" ); } } } void SWEET_PARSER::parseBezier( BEZIER* me ) { parsePolyLine( me ); } void SWEET_PARSER::parseRectangle( RECTANGLE* me ) { /* (rectangle (start X Y) (end X Y) (stroke WIDTH) (fill FILL_TYPE)) */ T tok; bool sawStart = false; bool sawEnd = false; bool sawStroke = false; bool sawFill = false; while( ( tok = NextTok() ) != T_RIGHT ) { if( tok != T_LEFT ) Expecting( T_LEFT ); tok = NextTok(); switch( tok ) { case T_stroke: if( sawStroke ) Duplicate( tok ); sawStroke = true; parseStroke( &me->stroke ); break; case T_fill: if( sawFill ) Duplicate( tok ); sawFill = true; tok = NeedSYMBOL(); switch( tok ) { case T_none: case T_filled: case T_transparent: me->fillType = tok; break; default: Expecting( "none|filled|transparent" ); } NeedRIGHT(); break; case T_start: if( sawStart ) Duplicate( tok ); sawStart = true; NeedNUMBER( "x" ); me->start.x = internal( CurText() ); NeedNUMBER( "y" ); me->start.y = internal( CurText() ); NeedRIGHT(); break; case T_end: if( sawEnd ) Duplicate( tok ); sawEnd = true; NeedNUMBER( "x" ); me->end.x = internal( CurText() ); NeedNUMBER( "y" ); me->end.y = internal( CurText() ); NeedRIGHT(); break; default: Expecting( "start|end|stroke|fill" ); } } } void SWEET_PARSER::parseCircle( CIRCLE* me ) { /* (circle (center X Y) # Radius length is in units if defined or mils. (radius LENGTH) (stroke WIDTH) (fill FILL_TYPE) ) */ T tok; bool sawCenter = false; bool sawRadius = false; bool sawStroke = false; bool sawFill = false; while( ( tok = NextTok() ) != T_RIGHT ) { if( tok != T_LEFT ) Expecting( T_LEFT ); tok = NextTok(); switch( tok ) { case T_stroke: if( sawStroke ) Duplicate( tok ); sawStroke = true; parseStroke( &me->stroke ); break; case T_fill: if( sawFill ) Duplicate( tok ); sawFill = true; tok = NeedSYMBOL(); switch( tok ) { case T_none: case T_filled: case T_transparent: me->fillType = tok; break; default: Expecting( "none|filled|transparent" ); } NeedRIGHT(); break; case T_center: if( sawCenter ) Duplicate( tok ); sawCenter = true; NeedNUMBER( "center x" ); me->center.x = internal( CurText() ); NeedNUMBER( "center y" ); me->center.y = internal( CurText() ); NeedRIGHT(); break; case T_radius: if( sawRadius ) Duplicate( tok ); sawRadius = true; NeedNUMBER( "radius" ); me->radius = internal( CurText() ); NeedRIGHT(); break; default: Expecting( "center|radius|stroke|fill" ); } } } void SWEET_PARSER::parseArc( ARC* me ) { /* (arc (pos X Y) (radius RADIUS) (start X Y) (end X Y) (stroke WIDTH) (fill FILL_TYPE) ) */ T tok; bool sawPos = false; bool sawStart = false; bool sawEnd = false; bool sawRadius = false; bool sawStroke = false; bool sawFill = false; while( ( tok = NextTok() ) != T_RIGHT ) { if( tok != T_LEFT ) Expecting( T_LEFT ); tok = NextTok(); switch( tok ) { case T_stroke: if( sawStroke ) Duplicate( tok ); sawStroke = true; parseStroke( &me->stroke ); break; case T_fill: if( sawFill ) Duplicate( tok ); sawFill = true; tok = NeedSYMBOL(); switch( tok ) { case T_none: case T_filled: case T_transparent: me->fillType = tok; break; default: Expecting( "none|filled|transparent" ); } NeedRIGHT(); break; case T_pos: if( sawPos ) Duplicate( tok ); sawPos = true; NeedNUMBER( "pos x" ); me->pos.x = internal( CurText() ); NeedNUMBER( "pos y" ); me->pos.y = internal( CurText() ); NeedRIGHT(); break; case T_radius: if( sawRadius ) Duplicate( tok ); sawRadius = true; NeedNUMBER( "radius" ); me->radius = internal( CurText() ); NeedRIGHT(); break; case T_start: if( sawStart ) Duplicate( tok ); sawStart = true; NeedNUMBER( "start x" ); me->start.x = internal( CurText() ); NeedNUMBER( "start y" ); me->start.y = internal( CurText() ); NeedRIGHT(); break; case T_end: if( sawEnd ) Duplicate( tok ); sawEnd = true; NeedNUMBER( "end x" ); me->end.x = internal( CurText() ); NeedNUMBER( "end y" ); me->end.y = internal( CurText() ); NeedRIGHT(); break; default: Expecting( "center|radius|stroke|fill" ); } } } void SWEET_PARSER::parseAt( POINT* pos, float* angle ) { T tok; NeedNUMBER( "at x" ); pos->x = internal( CurText() ); NeedNUMBER( "at y" ); pos->y = internal( CurText() ); tok = NextTok(); if( angle && tok == T_NUMBER ) { *angle = strtod( CurText(), NULL ); tok = NextTok(); } if( tok != T_RIGHT ) Expecting( T_RIGHT ); } void SWEET_PARSER::parseText( GR_TEXT* me ) { /* (text "This is the text that gets drawn." (at X Y [ANGLE]) # Valid horizontal justification values are center, right, and left. Valid # vertical justification values are center, top, bottom. (justify HORIZONTAL_JUSTIFY VERTICAL_JUSTIFY) (font [FONT] (size HEIGHT WIDTH) [italic] [bold]) (visible YES) (fill FILL_TYPE) ) */ T tok; bool sawAt = false; bool sawFill = false; bool sawFont = false; bool sawVis = false; bool sawJust = false; bool sawText = false; while( ( tok = NextTok() ) != T_RIGHT ) { if( tok == T_LEFT ) { tok = NextTok(); switch( tok ) { case T_at: if( sawAt ) Duplicate( tok ); parseAt( &me->pos, &me->angle ); sawAt = true; break; case T_fill: if( sawFill ) Duplicate( tok ); tok = NeedSYMBOL(); switch( tok ) { case T_none: case T_filled: case T_transparent: me->fillType = tok; break; default: Expecting( "none|filled|transparent" ); } NeedRIGHT(); sawFill = true; break; case T_justify: if( sawJust ) Duplicate( tok ); tok = NeedSYMBOL(); switch( tok ) { case T_center: case T_right: case T_left: me->hjustify = tok; break; default: Expecting( "center|right|left" ); } tok = NeedSYMBOL(); switch( tok ) { case T_center: case T_top: case T_bottom: me->vjustify = tok; break; default: Expecting( "center|top|bottom" ); } NeedRIGHT(); sawJust = true; break; case T_visible: if( sawVis ) Duplicate( tok ); parseBool( &me->isVisible ); NeedRIGHT(); sawVis = true; break; case T_font: if( sawFont ) Duplicate( tok ); sawFont = true; parseFont( &me->font ); break; default: Expecting( "at|justify|font|visible|fill" ); } } else { if( !IsSymbol( tok ) && tok != T_NUMBER ) Expecting( T_STRING ); if( sawText ) Duplicate( tok ); sawText = true; me->text = wxString::FromUTF8( CurText() ); } } }