From 5679e675d2449ab5a499fb27598e01769b8fb523 Mon Sep 17 00:00:00 2001 From: Dick Hollenbeck Date: Mon, 18 Apr 2011 01:26:00 -0500 Subject: [PATCH] 1) make PART::Format() omit any defaults for conciseness. 2) switch spec format to *.fodt, and edit it, mentioning lots of defaults. 3) Code pin_merge, but did not test it. --- new/eeschema_part_sexpr_format_EN.odt | Bin 22402 -> 0 bytes ...part_sexpr_format_EN.odt.obsolete_use_fodt | Bin 0 -> 22531 bytes new/make-dir-lib-source-test-data.sh | 36 +- new/sch_lib.h | 1 - new/sch_part.cpp | 150 +- new/sch_part.h | 119 +- new/sch_sweet_parser.cpp | 318 ++-- new/sch_sweet_parser.h | 2 + new/sweet.fodt | 1659 +++++++++++++++++ new/sweet.keywords | 12 +- 10 files changed, 2056 insertions(+), 241 deletions(-) delete mode 100644 new/eeschema_part_sexpr_format_EN.odt create mode 100644 new/eeschema_part_sexpr_format_EN.odt.obsolete_use_fodt create mode 100644 new/sweet.fodt diff --git a/new/eeschema_part_sexpr_format_EN.odt b/new/eeschema_part_sexpr_format_EN.odt deleted file mode 100644 index 1ebabdc61dcd7a67b1a1e1530f323a21e57c0944..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 22402 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 J&kTu*QUCz~{dN5@0ohvGngTo=Obs0zY%Glp z0hac546b%2^!A3%md^C{4yJY{_Qo!@rgi{&fT=q`;cqfXNXWm*{`mjr2lMC7*xnBC z7t-C<=2F+zd7U%y)3*oYu&hI>yi&bqaLlgrFd}ELC3Zh5Cu6Eei9S4-w2~@OM4_22 z?v!Kw5qu>mR*_EyN1*&ieLEXH#_K zIui{=>t0=tuV=QG2Iux+U$^Qt>nW-3rd~@9gd4SH9(Isl%odmB>Tjp#Rfap=cddzM zDH9OFUC>qPxeObnzbX#aYbRl+W20CMIH&wK^#srwVvTbZ3-s$a) zy)n`=ZMGio*t^x`s?LFzA@~tYLnFP!+c|1l-V9HAtXQwEuFl+eTo_K+v>5ceZDtqJ z4QPC42*BaHOts;5A#r8cv>w$+JlzO|bJO^}IV2N#Z~>z6S!}!qC`zg=T^rbr>sfu+ z6*EB_?LrJCLmkG)7}v2mHat6qexWk(*aSO7ACJ2>l`wsT*BUQ&k*aEh9~%pc_7v-0 zxfn^2aE$@TZI@=wEd_q^oj%Vy=WYS8-{ajkJ#Sp1c+@5x?HOY%Z}cjF?H32>$}KBj zMj_b>g*pldMi^+k%$C1i99%6Qhy3I8Q}6YwCDlVV@SMp7i6*m8^+*uAF@l+l+h2iE ze*TKJJP!jkZ*JRPIQgY#u&%5R=H!ZP@MhrNq)m0@E48~j@OD!##4r>|6M~{C`Cgq7 z+xq;S(2;yY=zA#~DkEI-z`Z`1Rtc?IS&jqtn0g-eOJyh|Jf`Weq&vL<;?%P$rh|IH z1u()*!YNF8`PtwFHnLRCnI6!Spms6}J8#E2{DmE!?<#k6b}l>SuW*5r!21f9HZrT` zT3nYGbvV7-5t*-8UiU7Sq2F8nEzOwuDi7m=+=VA&YWurS0uu@$0Xv{iQIMP+!EJR2 zx#zN{H$AP_Wj&Kt`FVDAg9aN%`=X&G$ ze*T>Xe**FJ-v1fo>bVf&1~|0#L0Yy&SdG2qhaQrV6~BKnq%c(b%A=lU?!FN3y>im0 zI4K3CyMOl6-Pl|nd`hX1prw}^+g@e?BW&moAP=6X1P;O3(Se$vAMa~b>G@{&K%e^l z`CWG)(b^^!XFyWhi`*dIDZRd4V5Dwr1;?DHozxqb=$MwmA@{K=Dlh6NYw3P+zjHq1 z0usmdcuT?|oYpRqIinU#6oX}Ssq?L~HDeR>bV+9|ZDw|5IOIbaQAXsqBX;R)A56cX{R z>9e_~5&Mat!5oo`xH}@>RonCwU`=_rmQKGVPS{jGx+IaZc46Qq@ho2+xlBByW0f|! zxSxdvm?O{~WS;iew?9yH3_=nq^O;4_M?=JY zb0(TzsN?<=A^6bM+Axb;(#Thb-C+qP=Z1v{Tx8#m>{}y7l!D5FLz|?yy0}|TN}|z# zy6N+Wa4~D$0IL5PDMs8BTAcs1GU;rylw!btR7XBzHeWRIa55wCc{nQ98#KrE>071W z#m`ma<(iHy#%Ku>t2~k&e7x5vt9&L!grXD2mAekQ-+ma|$btu}i)92600~O91Gf}{ z$&pJ;3F*Cs6)I@F*bv(g-ZScFu*wdL%#7cKgivq@QJ{&Fd<=bp$m_5QB0D~d1CA)&ZzXEO5^pCST>D~70z9~@J+k!yN&H=Gj(Eh;zXT-6 z&Knmp+hy}4Pha#)NeVFn&3$3FlrgvXb{<0wa<(^?VI~0PnRu22XgwHTw7F~J`$wjwY$#rM;L~fWe=?K4Ym_VFthY0mRr(E(uMQ~4=$y5T1m9fKk#*I0u z-HwTO9MW``V9yA@SY;!`#OEOMhe(!=TLM4mLE^qoB6Q*GQp-w<&pL~5#n%Nc3SFzh z1{4>P(w+>jIExQLhM26fb6MABxCf%U|3dWWEpZMG1=|PrCjch~<=+t?l|}SlI}<_q zsWb4h5`Q7M6}Ss>%^3hBivZC9b`vyX(I2o3yj^jd1c33+dJL=8fH2u-=UyPavJ-z{ z4CO=mhW{Ds!IIv`ZkdX^!^!AHi0w|ylh3$I$pV7_96UOhF_6d{9NYMuA#}I`)%Vku z;*P3S25b|Y*cYdtl}h`^M`k+64STynbeRfz3%bjYJt!mTQviv1WQURD7I+TOEm5c? z+(xg+jyS~Q6hsZDQx$(SxnJ0f@jd7?oDeb;r{tPsT1+R=&0OjxQkUJqMUXmh6#g5# zIOBJ2)ZVIKgV}rp3n~B>G@CGXbnEbjv?Piy$~*8-pAe}ACO4xA-KYY+jm`%?HIL@}ik-z|smc~fw9qu6Z00U%f%LCK} zFTNZ^L-4*L9s4e3PRklai2L1t!M*z1TM(kq8d^j_8~h|H4Eo;wr`>e z;t&4eE17V)Y2B z2-gFqSo7v=hI25xmQHk_yl2KWYdt|?fiHyIz!t422JoSI!u!9zje7&1sQB1JP9R)u z+$IoTiq(WyFWIVoxN3g4`gKE{ko6y1MS&GtM-U3 z574cX(1q%N!yX>Q?izw5e}rv`&NoIRlvJfCP&FDeb0i>%qI@%;pzxfa#WO14OG-_M z3OGS}9t{xgy}g>1>}93H+W`jiJ)%Ma*(IAc2#nYn7d}A?cc!ESgQ=^pXw+DpJG@`v z8*(-A-0wz!Ar0BRtC9dW=d9IBab@lv01`+2N#qxxifOV1iFtj_QO_{5Y^Y5g7+kvK z2K(_GeaBBJvsdZg`^_9e3FRi0LkdV~P4TpKI9OvrV&L|7{j%GRe5uf8=Yg|ZB%1## zAiE%33GQZo_o;Oq)duy{)%+ZB@t~yu;*)5TL z>Faj#aYGA37g=-Q{-9ED{`n_#q3sD8P?JamrUQMK;F`bX>bsmiC;y8n#&PsCjCuim zI)4ClBG4pm;0y#ZVYl)M&5e83hx3URc@wE{MK>JKkZ1_(=gr(d!^OYtc5Nx^u`>x>`4eIp`TVAZM>;jLSdRF%ODUuP8#d_+Li2l*o~N# z{zk}3nDP_F97_aWwyc5saZ$v#H}VBB9_^Tcxay$U%caEkNA9m+0yHo3vo3CngpfiZ zlBtDbB&mgdjCC>Wc-Aad62Ir(^Nc2(VS!$_Z0x}#da&vnrk#9vBijcT(8vr*&?N$@ z%+|m|SaxADR`Xg+ixL4HEd$5Uf`yuS_}}HFzmykvtfb7A3su>$A%n<5zIWGTB^YVJ zJ@iK?_+%}4D2A=nhCCr~FIhEaPC+m^6U4XX1y4eZe2~u8WrnD04OyrZ#&i9m1-4g4 zgTGHs+ZsH0%4D=vq0A_e$biRzYYN zUIHP8X!tvN?7NO0&6d?iL_vsI{lgKV;ZGoV1W%CExSNFFL+%#ar26ZVS@xWYyu1@x zSl?v4HR3#wVZjN`$h5YKDv_|r!a*@$P9!P(RcO$C%5}AHf0`sS^B2(?Fom;ei%xTY zc|j^%Kv-9IMwRRC!PEmZOyQ(3Nt729FIYV0juLH0DNwsuFqxyurVx?^Hfwe%XCsK8 z$#R{7j9^QkC%?3n%uGsZJLXP#koO z?3jGegMnjn@4i#;X|?27+ddddmKG}hXycL`zz2W@%rni&nsU;Sx z=sSZCsR?V_;l^$|y9P7lQkMo%NJYV{ViYv8P8oORgwd=j@`bnG-0E%*lgeu>J+kSE z=K?Ef$1^>LMb3^M1-cMFU~2cZ%mhSA(&kve(-S!$pc7YL;z&C$aNG}o6v`VLdxsE! zO&ob*`-T0#@yc#tw2p*RpyT1mi=TFu7hyG&AmVy+duUHypr;+s*IoWxFVfh&7~H2P zj0-7I#|J(z?JtJ+^9Dz6M9a3!My$Q z@v)B1AP3;=v+4O-4_08`(Y0WO*D2_gZ5iK58^00}g%M z>pf`aWFlJCeuFeaVQwrO8F}$Q)cPtP<0r4h43cLOC~P|hBr!@0QoHv_Flq-|#)VbT zB6?5j=<_|K?R-aFjrj5Pv|Kk|4KmjAzM4mq<8%9_)phrZ*Xq`K1m3}VoI&nEszD0P z1?5YOM%oJKJPyaS1 zCMkVN*7&F|Ew4xnV{EHeA7iH-&*~S;3;Pw%Gpp*7$gjiQKX`#C=3QYciZ|4B*aqJR zefvoKF4V-t)mgPH9L-(bnH(tbZ+P+-nw?OqiUs;+|=-2*-hJ(6A!u!RC|%Yj58kEYEZKg!Ohq z1C}^jIQjD=a;~1UkuJMt>(JC89E~5EP&g1LbCEkC{bQ-9cnd?l!oNR(m>sZFpgWAo z&ishacK+@ZA7fe^&rUE6_XV9H%N?XCrFn$RDWronl{*tF^JZWd^m|U!>8Y%#41iaC zFS}ZG>mD*N5+Tl=q{{sDAQKYcMzW#~a&BMg-zD_Y?$!knU}J6HvdW&bTYh`af}=dk zP7c&Fr{A`}7r|cvXXAzflSp6PZb`6`{RLpEqj&U^Kb|eC4eY+uiaotgOO}utN6O31 z(be~9B`QS<(NDMfvoGsWh#6Uugq_2lo;v@ClYsRHMf^5NflJ(rc zwYV7mgs^uV8>_&VK2pz@l)T%yIC#gS^lI9lZmc5!`rRs7um{hqr`wLKR-F^9LE9Ai2x2&)vQ90E#94D5 zsYkjlJH&IV$k_$>IN9`6*!sR?rL;D5X1(}>2{e>U+gcA{TTd7nL15DFTUD3K%Q3-M zyOa)jpW8#TH^O!0uAyVg=O3HiXKe2kZ5_ti-e?>Pt48>lXSuxwNL3ey1bE=2PG7rUzSTJ;{!Mpb!?%C@L{Zz zm61c}k;Hd1OrK4OlSMvE`S1*{$>J;#DN|+t`$cJI^W-fY&#&8s(=T7%9&YZA&+C~q z^%DCYZ)1Hwu2+D|8~N>A;=CJKxc&%UE!qKVj1_5|rUWm3;bcSs#A2C|Z6y1N4#QZj z>K*Cy!&x8;l>!pH*c5lt*CtZS1c4~?Y2~Lf1-HK=pnT?oY?D{QE@h|rfZYKm zhkAO$iTB*>y|3wT*1mVpdZdG8=5%Df5zrO{7F8Xx$&zB{|I@lXUGwT@=AA4T0KG9eTYM=Z3dyE_ej@r+O)}M=!DCt+0Uud12rN&2G!n;1(v=USVIxMjGu5$hVSe zXGhLm##anU^C~w9PFBCDR1P``DPGOe5aPVogbFpC{2`^&jvj@sQV&EmMn_aEZ58yW zbW-~9lJ8*&93cG8j*7|Ox9)g|k;{ir4iz2?=7M{*h1y&o+_w4NA_GI=W=RcW-sEgT zc9T!JHD7H5ecmX@d|s%dP`5!6c<)56FGsIDk)n##I%A24teraOX`$F8RBP=Jl=_{& z10$tZIC)9+PztA!N)LUTDuxDNM*9}HzLFPdt@Yhi)_3t6a!A>j7xd}(v%%Si3_$R% zVX)LKE%cdIw}_0gBz3UnjNo0$-uN+sK%V#8T>K#qP^$PCT!F6mV>+eePhONKqX_x3 z`yY;I-Se$RXKYAlW7>g@$@EwX>Uja<9oCJNy2(*p$Ps8dzu-h?QRY-_sF85oMFaj^gQcQGCDGxolgs04k>Ti?jNL zSe8*#Lav|6;EI%HKZmOn7)f`gUk`fzV`X=g!xW7~@2M=ESBssRDh~>A28YLCCt}6SqPuZP{sOsL9UMJ5y#vS-PH1FG%vB5{FY6CTBm}u0DO@A-(T;q7E@@;ZW0m3X4AmVDt zoO?R0tSpa)D3U;=XtLgJl4OVj&1z{_WXTms6<4*3740qifns~02=I_2EFqDgPY-L% zF32g@?Lb|C`vzWN_)Slxn$wWL@lLp?{8NGhzEWD5i9JXuV+MB_pM<|;oa3!1Z26qD84>eN1Mwx^L`Pt9`C1K^7CZvL|Q!=onf zJR&iGdzif1kC#Wx7A()f_l_oHR};$n%Nya_pgYwDN5hh`9@D+7jqQtoS$C{dZZnua z>F_EL9W)69-*44$;{MapCy11V@O$O+u!YcRs9Ldhb)}A>0zp<*3U+1}AaD_iWX$lB z_6rr3mG(a5=k)gmfNXB4x|u-VU=>?G7@u)d*Y%6<0rI2vT$$ZM{9L$v{F&5 zd{L~t`NV9j2GxYEkG={})fTr#HC3CnGzf zp$)LD=CPQhc3h?&<8yhbJ#@Qeu~qb3Z&qHb`L1^4B+A)xsK+2<9%RN&731{Ss5z+f z%I??=xzU4Vjtx#47~Ny5Y5IfP=b zLhMg{31~Qz)xe>5sbRvbP%5}1!w=M}WGLbn|)h8W$duAD%)U+zr zY)ZQ0S{Rl}`NbYbe$WWCYQSP37fs#Ewy4TWBl-H}4j`g)8FC_*<3eK0Cza1sHM1w` z=Nv=@{N7J|NoMR4%kKt$h|v`A5wx!w;RE^J7SIC%&@2zm zn0>FesR2HkjHD#&0^dZ~{icjk7ILcq;*TC4fvt2`yNY%Yp|4i20sQkA}Kma5r|rudD%XPfg9ZirT{m?ZMQX*pB&YCryGoTL zxYyITSWl#S@+wxGi2Donw^(|-x`*!W8}$6&)N7f6_4cm5@nY^iKHZMBx84QNfq1}l ziMv51y0|lhbiV`Z@TOA3nwWSF?|hlQcP+ow{>H-J<3T@BUa2#OyEQHE z3`jRD{_SSz*eSseOY38gSXNgi+a-W*%TIkn{0+}DjsBf;R`t)@S7hk`9+9EgkzH2b zrLdi)M@XHLc*DMYCH*TE7iLWi0)<$0==eA#2 zr8^_FU+C6N_+wu!YjLPRjjs53z?@N6z{Vfff~6L(_p@}FY(e3~tAyIH7R}$+Z0L_= ziku53xSG-)bAD~itXku#_==P?=S?T2wH7plaYUBOPNH>fC1vPVufr`coGw0G95rrk zENBWi+b*r!E>#m;sN2^woK5{GL~U6y(_&bk;^iZ1xTOe zk)4%uofub5UuyLC3bux+oK>o;(u*9;P6i&obgJ#^#6H%1#0-m^B*d z=a~$CZ}IxD>*m|%wsj!pofVxnOuuW{@TxvNY_APPAxbFdO_GsMdB@Y7GDygMoGS{~ zggDZ;VRX-ES?FiHu?%S9RB>yb0()VV);%QnG+~t7?&9BEbt8FD9a-&{w&zUjKkaa* zhUa*9Ea36S<~qGACxXCIU*jNIN6rygpTI5H1(OIY*q@WtA=$*sY92w?i>`x`iT2@r zH;v_iz`scG$3& zy~h|yDFGx?Bu%?PvQ@=mC05I&^bEgos#YVg4e?D2dsUbq@TR)<>?1%^1wlhqX5LY6^<}aJn-vA3#5% zg?E&L?cIT$TU=jn_beGFkBHQLh^S391YkeZW5kvlLd)^Txq*}lDRht=N({YHtvH#3 z5rkhL_+i2VO2fv#aJq=;D$9N3;vcoz7+im=t=K5-+E!-yu-y$=Z(s4lS#E^SoZoe21aE<%67kcV3Ahqf-1SN85}GDh1z)lf6<%a=Hz#W2@y>hDIi^ zRVCl^jFC^p-w|b=E7i_($p~fb}?(sL0XY z&c)eIZr`Wfj6&rXtg8dM_2o$6tAa70x zD83N^0cD{B1O4yf3D`g42^&KXdlvwmv7xbrsj-QH2_>YlfdS->iGjgxAQQkK+8xsv zIhqMy`-J>6T7+ua+d+nClh_)rUltgX8pi*CG_YA(gjyNEbJJONW zl}HQ3CbOD39ug|(7JFE_WB=XUq! z(M+fQ_SZh1ZjEFx22+eH)pYR85K~_#L=a2&>|jxg@oFHk+zYV?)e#I5okc7Qi-n;x z!=lPt1oDzgt5LH!j5ne#g!I;$M&lWO!=LsD>}3ahYNvl0pSNrWJpaw0zOuj%%h+$0 z7LF1UPd=x%A)^>UeZD={*Wy0o>_HCvc?hUYDxB1>cX-mM>bP(YfrlpEh))9nyRvnH zx{pbn%~GrMx`sG;`gy~Zl zE-GDQ zeW$+$Zc)D6KrX^$nlTa32@U|qOjK`zXgACLxREC^?jsYQJe4OX%)gYL6vuTGi{D}*t$_VGIvsfUS*9j*3yaB2BtADe8W-a8gkNn=&QGV$PfPELUf zsueVX=~pG?SWD4s3O%lRESytR5VLoYv=G6i? z-u?_v#5rAZO!uJ{hQqr$fBN1pcq*l`YA~&eWM-dIc1o7SsDKa$k>7P}$wufRFE_6$ zxi+9E-WCZON-aOB8c07rS1WcZzpJG=(A+*xb0Z5j^BrktmqA}pP=+e)(dee?iS?aP zQT@6;v45qH8kB?C0L4;+ItRaC;Q8rDDnO2yk&#V$5_dcxiAL{|Hw8RJt(Lo^k-Di> zzU$ncn?3KI(6PgtmsQ>-ZSSlS{rk1<(rkq^uK1cMn>g{+>E0OE!M#8E`lY$gNhJQn zcWK-Rs(Dk~zCk^CEIs}cd=)2~LWnDE%lmwgzwr0)!Oc&koNcCQA0ZA6n?rszvF)OW zOQ7!N_KqqC^v<>2P!;Dsh}vF}Pob@gI% z@$61RtYU}@NLEwI=uX4R zC{*=@_pFzguFLPBSdsRCSao3V7M&Dkau@PanU&(tWYprkyRzMJEG!CyY_#-fu;ZE3=6k7+#L-~ZSe zHGtW~D%&{;kryggoaRm;RrWZQqvSKIYWU1M%HQP}6fIpvW0E(YdXWs|(qq(J7kMf)lvVhJ6}464|%R&=Zm`aoMh zaA!^1e7(&V0ybr1n6)ffd4Zv${L3^5X`^jYh$MFc<>`0wr6HlbSRa5*S9gwhd@~Xdi2#)Ci?VltLZ)3acF!EKd*;yP zbQtqvSL+3C{im9L&3N=%FF6s81UZsK0>i*V_}67C;dvngM?K>y{!{Br%83@uYKW1G zHe0<`=^)b`|{4(&s995fuAfHKQ|pQ>v|jOQ0gOHJ(UQ-JTbD2sp$GWFNKe58!eqB zB;qmQcQZwg&|$`HV#eu%?9bs0xX%aP4#wObkTK%-Fh!TpVajh}%Kzya!x?xf0NaP) z7)D?SxP6E*&Fbd_5aYJr!0Cf*zODX?MmFifUrNm0|_VoE_%Y# z@zj=kK3hIbC;zS$wm6e&LlYK)`yn(!BISB_<$gPBF-!;eHRb3P?OQ}ulPc|TB+UzD z;Rfk|zOHUy0~VnzM4~6GXStJE{1{UCi8|cHt zF7JEr8v1U)a0e$OPXQbP$r<>y7>me%XnDewj!nT9sf1fFIUWCb*ZSMa@bN?OaAB($}C& zz|Fwm=H^E4#zJrJWX{0E#l^+&cQ5h3LYWx<2j}48Wb-%5#F)X<#`G^$xHAJ2Jrl#f zv43Uw|2L_>u>YNsy}kW^0{vCPKPCPhj+v2>mEoWEzayDDnV8u82mfCM{K2v?m@}9d z0u1R~Elu4>hzb8Sj{h~_%*2F$6L15Z4DFoF?44}?R=)p-@6V9`5x@T>fv5W`mA{kt zhviSX%na_%HUI{D6Eo&N+bH@!4PyR(KdZlr;^+OlG-D^zze(x-q+-g?%*ez<$H+>@ z#Hqr_#LdXe&BVpa@c)7P$Jf-v67Xj-(3!Y6{msFzz$p4J`2W@Q|MoEUF#fZN;b;0k z(El?3gEcp`GyTJB@5C=-Y2;)o|MxUwV4-IrRkgEpCzStF@jte|>ehE~vbQod1~9O& zFxqmF3cFa^n9%*3{(qSMVgAolbq4&&+ZkYKO!#+=07D}iQ#xaN7drqS@t@iLx6#tp z(ER^0+8g~9?Ei#1{1=kx-~JAUPKM@Ah7K10Mzb(;{EOPn-pS3quD%b#t?i0*71@bkNG@Y)sx$(}_f-x%via zNB3Rp%I&!)!>hF;Eo(h(^L2XqYT|0r7rg*NnS#uau&=L==+~k@=KDT`gM))(_|0bo zE*i9jqr(X}8QAR%Wd}0I(fwcJB(Ts3w81DFJu!gDdNu!1vlFA{=vgC zyH(%J$g#vaNSN(ozTT5BFqrK)^`&A8zj?gDyY+n}TR1`(y~$@RxpWNjEKq2q>*sDW z_t3F-B}Xa;o6*($PwLdEdXMMQg#{vEi zcOHg{MeU@SnOwh*_yc<60u9c7-l-sfNF?+Ej8k;|x|OWt*Y6 zQVGqxJ5VAixWxOQ$2<>~Pr;@lvsyaJV*ERJek9!@6QpJxz~?IM(P7^VL83(o^%Zl z>3F9l_&)$AgyWcq`^pf+P5>(!l^}yNAkFEHU1E`W$^q)dekmE|Eo`se>zL&QVD*)$ zNPD?z>;eN+wEj0s{?-;XpJKkjYmw8jLq%fRg`j}900!0N< zz7>T*6kX538O z8AeRj)RS=oX0R|`^Tyi_l^e&g6q3IXUz!XBHtk&=)Ov}i#XnStO}<<@C91KGxp;%` zb^$_Y9r;?MqycT<5NerirR}hUs~yD#^9ktoTsm^$K2W2IlpmQQC(($Ki}0hrc>I~m zobnEuPQB>Zo)aocN3v{b@MmH(KXnUH9ss=#-&TFVi98RTq&-xBWQtpNcSWe4$)&%P zADa;hd~_0koc>a8R2Jl4IR6kd++$eayU(c_Pk#hy3O0i|@;Z}PgobR(kN zSdS;zjcv#Pi^W}XxiOu`54%T8@YRR4iu@^Fyz?uDf~S+TQALNf${cnJYQb*r{^-2f z0I$lFaZCBe#=)1g&Hy+N_23gznR41j7x%1f8vN_9UwOj93u!o8s_O_jJw#OsSf~E2 zWvzWk>Ht*^%x_(!wt_C6aA#L%ghR((@0&oAd0y}eR?d*>u_ehcdSa zDqg5a6pH7pQ$bDua#q+QK)Epq%BJ#g^lSuNu$sVJrN1$$&pCg@lRZjK&$hZLTELBM z(~Ja9Wmb}zB~QW=7QkNojt6de4>uE=QC zGsQSHm8MyZ?-v#kU@ks4p4`ebh}xuB&->Z7o%+jpcOuVRTMbe)G|t@&prAcnSMgJQ z1T3Tk9}E_py1+_3GR~Tr=H28brPP$&K29JoHYmjWfAgKRMI9HM5ysKle z?7M{$?iY+Z=wajA+8v(p8iDER`XD|qTzC;l*`Kq06_1YVEM|pM8)Wai>qPsudHSjX zZxdnjC4IgLP;5I_QF1pY9PqpN$wdCIL^0Cu&ZmM@ZN6vszD2gv%fv733_;_Q_sTX4 z;Nln?9+54gl(<;mP-nGuEhW9~!_`HzQVwwhE=XGxz)T+|XMYG_-A*9Fs`*~%T2!JV zqIJ1VKQ&vAj4+zPKVZz$bB91`qH&6J_q!Llg6p85$C@op1xG1Ku%MAfd%96KiX^cO z8^X4ih0uv>v4vgF93BWb+%?B(qzm5WXCHY?8Kn0fu#D;AWCY%rdAt7%xV=Y9zOYtp zTbQc8RTdOcZ_7vieF)+r>upHpH-Uo5E_{k_pl3K2|!;bocV z6!fA%V_)72^-)57CJf%pgX+jH#rb0oIe8gacrDw49T(8y3lHc}9!qS+wl#I~$vYfD zgkmGyG486=Ly-$eQjewlcg58zV2WB;ZAj4w*>$^~xP>n$-)-9R{3rpB7TY^v7@dL{ zBrl{Jr%0dem0++rxAd)G{(m+=FPRiJy%b0e% zg(uldYClRY$<4kG5tE|NUuPW;Ed2H<3Pn#%Yc5wqq8IWwQjFGiG|g^G|EeAB-rMQ2 zj_YE6N^=}r6G3r7&?q|f`ebR2Gn6Trp=#5V3Bu?w83N?}{C#VknW(<<>XJ69>t|WU z`$frZ<@0I_HR+3qTBOtd=Ri<$#=(Ad&w)I&Z&8g5D)!w3mj#d&b>uv3|2JK9Qbch@+v9xScPhxegTH<70|4U?5hL4h|MZ z$S1y-$?cc9@ye5oPU>Sx)k>wZJW;g*a$4p7*`BnsI|d?(>1^QFlY*2mj&r`v2dj$4 zG?Tp8rngVKx#{2!HHf00i}pFu4SAqy+SG4IoSuAIt)-xf|_6hAg6Oa;42_3R~T!ziDOn{*4qQYCC#=vSX1+trL@ z>?Mzt>D$STMj$G^n6c5B!*V35pW5b7>M&f7fQ>u2DOF_)<7?!SVb=k+M0ZrsMRR5# z^ce%l?3}*)zA9T`IdOD>dPSw^&#AOuz3$j@B_2i6i!jN96flt>^ zpeTJwzBS-WWu1^6{qaV2VKkGQSkfbEd8;3*$(mVWQvwZt!=`HLN%g*gbYdX03S@0h z|1=EfY+k`cRET;w_kCBfGMVsrO{BopJrGHiPVGp584DqMW%G4@WY?9nfAweEN!(35 zt^n%(bI3OW9SRF!8uH97C@4RSyFYnACUq0O(`*zXKu6o8Ep)}5Wc&*tB?=Yz|S&#Q-2p^p_On9h~ZjXlW}@~4kpm^FWn{l-NXQLHvmp@-zu`r>7h=2NZKd`D)Ms}yf9SGQ}dq%uV~tn}3~_=IJo zB$Z`;OM^gRP`(QA2R-y*u}>7#)7Sg;NjYkaeXHK38bw?IVry zbDW=^?2D4=`GSPa&xMNIp@^ri)D&PC1-}(|K^S(s6wDL#5?6kc79WxT2tI*i4TH%*4@o@rYe%}4jvt6}+IZ)=`yfpaFdr>YqGhLz}Z z0{3Pv(2-hWT|u4SAXACz9`-eovVec++e#am4@!xpFX^IO_M1y+vTN&tt9!>Jzwg~^ z2PH!UpbWg5XRH-AdeMI#&$;v`uL38WPM<>oZWjFHa&Lbe!*a|<#oIElZ_x``9aRBu zM5bq_d}syUjMH~cjPGm2Opw-6WaPWT2G*IZ0g)O67UXKmsNl?uPJZGWzu|&{W09!=!U@?+*<{CE{Z_Z2*RD#+Jss5EgZ==j|SONn>?q9`tj=Mgr$fu zn<7IK|D(#oLwvq(+|%J)t4L&?m7}c&K_?>gvNVhLLzdQog56sH<9x?Vrarcs=p@3V8ELB?6x@gN@ev(%BGfj1#!JM)WmG1@+8qTM z)2#il?>a4h?do!K{QUtU>zj?8jVK(bAy5n(Upktm)~j$m?}BUmU4dbeec=vl8?OxpMnHDh4>&gG%76QOr~zc0#K zBokx!0WetuGsXDAUb?uO8p%$dwkf2L7K)t~?ydwtd^z zLXus`zK*e^8jWm?tYcRw`@ZiHB1TfSAxkL65((KRJK6Uk`yL@X33+Y5nZDOY?@Pz` zyXKFXIgUBc`@Wy&p65P~<2tVkK+#Gd1PZ35gG34gCRXUd!kK}6`yf_iQ*C44`V6UM zq|k0UGzQsR4N;1u?+MzhK2Uh<_iD-f5!+4FmfaUUW)xlb%uf^Rlyg?@`HMN*((`gp z*X~t{iW#Nm_-A=?=y^VFcb$KgP@wuW{D2~MB9h8dh|;8FED#9yQD$iY=Thbph-^xLcaCPFuA3rrtM5|V3<@H}Ux#v(Cp!MbmTyTtS}?EFqxXhXbOKm_pM;Ns_; zjqFvC>&aYMiw~MQquLUpX%;-}^5l!pt_HMibgF^EC9I{Ld;Hf%{JV>kXhP``UjP`^jR<0eUmyzgTUxwTow5W1VY+?rH`MG?K@X6*orBug=x;nq)?T5(MF z3Z&9X=?^lB!8Z;PF5zG1{k{eH%hLTmu^+K5k?(;v#0u8;>6~*{n|D{dP4En23tfRVEg{8{L()UKB zKZ7nh?E4OibxcI-e@!>4_bd&aX_MymzDn}3$&6(^H@TP@>GhUP%6*5$-hrJs!#YHO z0U9Nz-P5_aH+vD+?%Didg*zRKc~s|8$ePJF1asr0XsvU$#nl$hTbH$UkuD@o*8sC{ zU~EV(Bdhq(_r|2f!YCb7=2Tvl&31DnTa%*5!sqb}0WBtW?1b1sd@5;0<8FfF)Gv`0(0d;VW7sPuk(SG&Qhq4QqxZLo4C9BsVImt zFPEDoX_M}~?Q~FfZ3w@dbTsayvJ$-r&d!KMp+t((A)t#Hf$dG|33IktuOKhjw7+c+ z(Qcf&UJvwiL*zku%YjZACeh#TZ!*7eA-hZ9Ps_|jX9cMo*7NN%alc`5F0Mm-R9sN< z&H2^m+&2g-fcA-QWIZ#Qldv}r_0o2ev|klY76Y&;sA$-~d;dT}(s=Uk7F&OLox^9{ z$TqLiOSgIZ`c3%36+E?;2QVy3dbdx4`r+8MtgAoyyKc3hh6=A7@^*c8kBB4>uaIM? zf~uefZNLfSbTW^w)Y*aBh?pSz*^-r7cB#UgM8SKkl2e9}Z?x(jJ+}g^39HT4vo+OO zZ7quoHTU^7#t$cAd3}_H7&M4qR1kn$&P>Z(tU+c+8wnvEuNA<$Z_#{j!xktICnC7^ zz!px=Uuk4n)RNNaFul>Z#Y5`7w?S>>(7!MJRA&^2K=bP)pWP0g$Ajg?RXKjH7hmk1 zKFtz7QSPkal4AhElDoyr24x=+D{<(m*=nXdQ{mU}84M2s36KbgJ?CF0!VYU5$h4Xn zR0u2H77+pU;Njgg41HR4=SppFm|;8VY21*ruPAjufm zHog%yvDtm*j~kr*ruNj8fX<*-w$52LH|ojdXRX`h>+)PvHh_tPSwbvu{P31gUiKkN zaJ61-tp9Z|C3H=0o7>2r{6nOVb7pD^-Lcp1d346NC94^iMI{y`8cUGVB-y&9w7F z!rD}Az*GZfs++@3pI@gnchN}3;ohTb;tI#U`%wv}NwfOx_+B*m?TYjtL_Ygma;#Ga z?sg_d=@|`NQ<_NW#E!3Ql$GFfChOVIpYPV!9Hh-gB_O-PTuifb%d0E7`;D*9t#_4~ ziy;$tbLkL?>)oOCB-M*{G|UZ6AnB%#MUs$IHzr9 zTDSgqmWP$<;EDF1b6zXeLT%bbX~H#23%fM7zD$+=`q!hH!Rp#`>vM{2@6K;)cu*kI zv_lV_)JaioPj)O?n{KJ8I^BuN6%T8=dL5a_=&iCuq=#(HfnE^AWzrykK3v#X(x=aH zMAYSyeLx;Qn14slkAk)q=~e0J#*Y+lvuNzQmcE}H{6v}VQ$WVDsq0iGB--&kPGN%Q z!VtS*y-LiS0XXL6A-N%Gf^a*8`u3P>Y4Dx#vJ%A&URt3;r*hoT6oUgA{XI#d?Q2Ut z@nzanu@jC5KxHl3H0_<4SAM_plC{rqmfSFz3A*FF&wIy_ivF&wS!A@6g^ZQbk5s5?LNE^ z0@r~iASyOGRrtnCeq<7dk_@8Xr22hcaN4CM8VOByJ~nCValv<)SRi*e@0v~BF{x%&D(vsMR}mzMHm zdJKdUa5p_F$je@u-_E93SB~^y72(vZdSz z%hboEpv+ded_&%BIi_x}!)82p`(U?|+-qE?21e_>2r{Sp%;fJp)t3hV037VwMvS*; z#{54F@sz78t&oy;xMof+?DS<88h@(Bey!y3$_G7K9vK`QH&sE-IzuO#sibnt!IGAh z#IWZbLrjg)jp2e6$J{R$WSZe%bLmxqDyk13>&=KriI7Rfpq3tww-UY1lCj9T^BTo! zqc=uoT{hRCv$Nw5XHsv$zG}5~?8wbbT6aHUpZwVn^~_CuJnO5kebOK=zquRki&{%$ zrli6B2aKG1T*jj8YOmNTUQS$cGTMAzOiEU}(6c&(oOLo;ox`xo@ zlFECf5)_d?u<-B*KD|sOC5)?@p7IWCDX+wNKSoY*XGQhhP>?K8hJBxFZ-LZ*8C|$KHNX_!f?e9KbxX#JEcEfsSs48$F891m zrzs%P|4xr%*$sJSTMfcj?e3yGGPfz_YZPw=!Lo~ktS@?n8+iqLEzMmsHP?BfevU&U zPc%{pJLqC{geMf#7Haje%+j%?w})?)XF-wbRXRdR_0l;a1Rll8$G3gvsZ_PAH$~c$ zt3uT3O;u=+!4PiSgy1)@d?CQI27@7{{m(KqFL#~zUv|+)7w`Yf<%(MZHuNko*7N6+ zT`QwZxE#HH@yT7D>yKMQWbp-Zi&CBiT?J-|+U-QkvO$>{=RN7n4#|YnYN_Ryk~Eo2 zjhJ!Q0w55n**9uMK}j6-!d;ODw)J3%@IZo1vF# zA&%bG!X02q((0);3jWD1kTZ zbDBoVlPIOVjA8PDZsq3&KRhNc)lZc8d=@O9Lw-Yz%1#!j*owCjEM2N1Vf-L2SY%RJ z7=xk*?$(8o3R6jJZ88+>2($bMSd;5UWOKZn?`b0@MNshfM+o91umrw0vG67xK_}>9 zuj5_w<(P+tAasxk{s#)rm+}mB-76qdtWm{zt{?LyeYI# z9Hf>)ng!d?JhG5lfo$Ea;(LWt911s^t}qxOXMNTnh45J!c=5fzi>URXWd7Sj9E?S1 zlO&Gs4n7uED!LHhY!|^L^dneVPE$(Yj>>Jof1UbhzfSSXT9rCxZAMfE1JA49TlTwbz4ZrHK`i@7Xi%KPO%th%664c~ZWYkOg zd$p(RHWMU6W~S`NvF8bJV}qrxE+HHfwXb;b3g9s*1dz$JWEYWTmGHXD>-&ZQAU#up zAY$=hLonW7U{Gy-e}v0wKILR$H;LyO4^orHTN@}x^&CWB?k8`D25>A%JzYV{Q|-Le4oQsvAASpu6aI+#An4lF&1 zlm!VnR?N2h?So+Q(_?^C!QSX~aR%h_ZnH!MXIPS-VD(K->Q&4=BPM*`?u3nn6^wRn zK^3uasIbm%qKUq7C*r83<`L@XxII}4g6Wod9LngACwgc86L_+|19MIv$20VXI$1Yz zggmSM;~2SuM*eq?p>0e@$fL=gbhTlMBaVZV?PRIM5$>qJ=s!Jg$2eO7^oVGC4aNa? zggP4Sq*ld9t>d@@Jlk4x#B#J+4CQ|3OoD}lQMt#Vcnaz7 zn)mlzbBx5o`hAf>r!XCB;ImmUV(&Q0PhmN!iT_ugjZ=6|YUHzdeqR-z;=eEYv1a~X zS&U9$Io8l;vtU&8am1d&@-Iz&HrekNy7v^alN$R!JV$%`fA2Y^-!=Eq);mI; hd<0<*hvNuWJL9N;D&iAhenyFYW#gf*zXs;p{{i5x&b$Bs literal 0 HcmV?d00001 diff --git a/new/make-dir-lib-source-test-data.sh b/new/make-dir-lib-source-test-data.sh index 831d012b66..296c92de17 100755 --- a/new/make-dir-lib-source-test-data.sh +++ b/new/make-dir-lib-source-test-data.sh @@ -11,56 +11,56 @@ REVS="rev1 rev5 rev10" REFERENCE=" (reference U? - (effects (at 12 13 180)(font (size .7 1))(visible yes)) + (effects (at 12 13 180)(font (size 7 10))(visible yes)) )" LINE=" (line - (pts (xy 12 13)(xy 12 20))(line_width 1.5) + (pts (xy 12 13)(xy 12 20))(stroke 1.5) )" RECT=" (rectangle - (start 4 5)(end 6 8)(line_width 2.3)(fill transparent) + (start 4 5)(end 6 8)(stroke 2.3)(fill transparent) )" CIRCLE=" (circle - (center 1 0)(radius 5)(line_width 2.1)(fill none) + (center 1 0)(radius 5)(stroke 2.1)(fill none) )" ARC=" (arc - (pos 22 33)(radius 12)(start 2 4)(end 13 33)(line_width 2.3)(fill filled) + (pos 22 33)(radius 12)(start 2 4)(end 13 33)(stroke 2.3)(fill filled) )" BEZIER=" (bezier - (fill none)(line_width 2.0)(pts (xy 0 1)(xy 2 4)) + (fill none)(stroke 2.0)(pts (xy 0 1)(xy 2 4)) )" TEXT=" (text (at 23 23 90.0) \"This is some text\" (justify left bottom)(visible yes)(fill filled) - (font arial (size .8 1.2)) + (font arial (size 8 12)) )" PIN1=" - (pin output line (at 7 8 90)(length 2)(visible yes) - (signal #WE (font (size 0.9 1.1) bold)(visible yes)) - (padname A23 (font (size 0.9 1.1) italic bold) (visible yes)) + (pin out line (at 7 8 90) + (signal #WE (font (size 8 10) bold)(visible no)) + (pad A23 (font (size 9 11) italic bold)) )" PIN2=" - (pin input line (at 8 8)(length 2)(visible yes) - (signal #WAIT (font (size 0.9 1.1) bold)(visible yes)) - (padname A24 (font (size 0.9 1.1) italic bold) (visible yes)) + (pin in line (at 8 8)(visible yes) + (signal #WAIT (visible yes)) + (pad A24 (visible yes)) )" PIN3=" - (pin (padname A25))" + (pin (pad A25))" PINS=" - (pin (padname Z12))(pin (padname Y14))(pin (padname Z13))(pin (padname Y15))" + (pin (pad Z12))(pin (pad Y14))(pin (pad Z13))(pin (pad Y15))" PIN_SWAP=" @@ -70,17 +70,17 @@ PIN_RENUM=" (pin_renum A24 B24)" PIN_RENAME=" - (pin_rename #WE LED)" + (pin_rename B24 LED)" PIN_DELETE=" (pin_del B24)" -PIN_MERGE="(pin_merge A23 (hide Z12 Y14))(pin_merge A25 (hide Z13 Y15))" +PIN_MERGE="(pin_merge A23 (pads Z12 Y14))(pin_merge A25 (pads Z13 Y15))" PROP1=" (property mWatts 12 - (effects (at 1 34 270)(font (size .5 1) italic bold)(visible no)) + (effects (at 1 34 270)(font (size 5 9) italic bold)(visible no)) )" KEYWORDS=" diff --git a/new/sch_lib.h b/new/sch_lib.h index 0ca6514a2e..5161942ecf 100644 --- a/new/sch_lib.h +++ b/new/sch_lib.h @@ -47,7 +47,6 @@ class LIB_TABLE; */ class LIB_SOURCE { - friend class LIBS; ///< the LIB factory is thru LIB_TABLE::LookupPart() friend class LIB; ///< the LIB uses these functions. protected: ///< derived classes must implement diff --git a/new/sch_part.cpp b/new/sch_part.cpp index 9b0fa3e54d..6774d5c7f2 100644 --- a/new/sch_part.cpp +++ b/new/sch_part.cpp @@ -29,7 +29,6 @@ #include #include #include -//#include /** @@ -39,9 +38,19 @@ static void formatAt( OUTPUTFORMATTER* out, const POINT& aPos, ANGLE aAngle, int indent=0 ) throw( IO_ERROR ) { - out->Print( indent, aAngle!=0.0 ? "(at %.6g %.6g %.6g)" : "(at %.6g %.6g)", - InternalToLogical( aPos.x ), InternalToLogical( aPos.y ), - double( aAngle ) ); + // if( aPos.x || aPos.y || aAngle ) + { + out->Print( indent, aAngle!=0.0 ? "(at %.6g %.6g %.6g)" : "(at %.6g %.6g)", + InternalToLogical( aPos.x ), InternalToLogical( aPos.y ), + double( aAngle ) ); + } +} + +static void formatStroke( OUTPUTFORMATTER* out, STROKE aStroke, int indent=0 ) + throw( IO_ERROR ) +{ + if( aStroke == STROKE_DEFAULT ) + out->Print( indent, "(stroke %.6g)", InternalToWidth( aStroke ) ); } @@ -145,13 +154,13 @@ PROPERTY* PART::FieldLookup( PROP_ID aPropertyId ) return p; } -PINS::iterator PART::pinFindByPadName( const wxString& aPadName ) +PINS::iterator PART::pinFindByPad( const wxString& aPad ) { PINS::iterator it; for( it = pins.begin(); it != pins.end(); ++it ) { - if( (*it)->padname.text == aPadName ) + if( (*it)->pad.text == aPad ) break; } @@ -159,23 +168,21 @@ PINS::iterator PART::pinFindByPadName( const wxString& aPadName ) } -PINS::iterator PART::pinFindBySignal( const wxString& aSignal ) +void PART::PinsFindBySignal( PIN_LIST* aResults, const wxString& aSignal ) { - PINS::iterator it; - - for( it = pins.begin(); it != pins.end(); ++it ) + for( PINS::const_iterator it = pins.begin(); it != pins.end(); ++it ) { if( (*it)->signal.text == aSignal ) - break; + { + aResults->push_back( *it ); + } } - - return it; } -bool PART::PinDelete( const wxString& aPadName ) +bool PART::PinDelete( const wxString& aPad ) { - PINS::iterator it = pinFindByPadName( aPadName ); + PINS::iterator it = pinFindByPad( aPad ); if( it != pins.end() ) { delete *it; @@ -313,7 +320,7 @@ void PART::Format( OUTPUTFORMATTER* out, int indent, int ctl ) const for( MERGE_SETS::const_iterator mit = pin_merges.begin(); mit != pin_merges.end(); ++mit ) { - out->Print( indent+1, "(pin_merge %s (hide", out->Quotew( mit->first ).c_str() ); + out->Print( indent+1, "(pin_merge %s (pads", out->Quotew( mit->first ).c_str() ); const MERGE_SET& mset = *mit->second; for( MERGE_SET::const_iterator pit = mset.begin(); pit != mset.end(); ++pit ) @@ -392,42 +399,58 @@ void TEXT_EFFECTS::Format( OUTPUTFORMATTER* out, int indent, int ctl ) const void FONT::Format( OUTPUTFORMATTER* out, int indent, int ctl ) const throw( IO_ERROR ) { - if( name.IsEmpty() ) - out->Print( indent, "(font " ); - else - out->Print( indent, "(font %s ", out->Quotew( name ).c_str() ); + if( italic || bold || !name.IsEmpty() || size.height != FONTZ_DEFAULT || size.width != FONTZ_DEFAULT ) + { + if( name.IsEmpty() ) + out->Print( indent, "(font " ); + else + out->Print( indent, "(font %s ", out->Quotew( name ).c_str() ); - out->Print( 0, "(size %.6g %.6g)", - InternalToLogical( size.GetHeight() ), - InternalToLogical( size.GetWidth() ) ); + out->Print( 0, "(size %.6g %.6g)", + InternalToFontz( size.height ), + InternalToFontz( size.width ) ); - if( italic ) - out->Print( 0, " italic" ); + if( italic ) + out->Print( 0, " italic" ); - if( bold ) - out->Print( 0, " bold" ); + if( bold ) + out->Print( 0, " bold" ); - out->Print( 0, ")%s", (ctl & CTL_OMIT_NL) ? "" : "\n" ); + out->Print( 0, ")%s", (ctl & CTL_OMIT_NL) ? "" : "\n" ); + } } void PIN::Format( OUTPUTFORMATTER* out, int indent, int ctl ) const throw( IO_ERROR ) { - bool hasSignal = !signal.text.IsEmpty(); - bool hasPadName = !padname.text.IsEmpty(); + bool hasSignal = !signal.text.IsEmpty(); + bool hasPad = !pad.text.IsEmpty(); - out->Print( indent, "(pin %s %s ", ShowType(), ShowShape() ); + out->Print( indent, "(pin" ); - formatAt( out, pos, angle ); - out->Print( 0, "(length %.6g)", InternalToLogical( length ) ); - out->Print( 0, "(visible %s)\n", isVisible ? "yes" : "no" ); + if( connectionType != PIN_CONN_DEFAULT ) + out->Print( 0, " %s", ShowType() ); + + if( shape != PIN_SHAPE_DEFAULT ) + out->Print( 0, " %s", ShowShape() ); + + out->Print( 0, " " ); + + if( pos.x || pos.y || angle ) + formatAt( out, pos, angle ); + + if( length != PIN_LEN_DEFAULT ) + out->Print( 0, "(length %.6g)", InternalToLogical( length ) ); + + if( !isVisible ) + out->Print( 0, "(visible %s)", isVisible ? "yes" : "no" ); if( hasSignal ) - signal.Format( out, "signal", indent+1, hasPadName ? 0 : CTL_OMIT_NL ); + signal.Format( out, "signal", 0, CTL_OMIT_NL ); - if( hasPadName ) - padname.Format( out, "padname", indent+1, CTL_OMIT_NL ); + if( hasPad ) + pad.Format( out, "pad", 0, CTL_OMIT_NL ); out->Print( 0, ")\n" ); } @@ -441,11 +464,14 @@ PIN::~PIN() void PINTEXT::Format( OUTPUTFORMATTER* out, const char* aElement, int indent, int ctl ) const throw( IO_ERROR ) { - out->Print( indent, "(%s %s ", aElement, out->Quotew( text ).c_str() ); + out->Print( indent, "(%s %s", aElement, out->Quotew( text ).c_str() ); + font.Format( out, 0, CTL_OMIT_NL ); - out->Print( 0, "(visible %s))%s", - isVisible ? "yes" : "no", - ctl & CTL_OMIT_NL ? "" : "\n" ); + + if( !isVisible ) + out->Print( 0, " (visible %s)", isVisible ? "yes" : "no" ); + + out->Print( 0, ")%s", ctl & CTL_OMIT_NL ? "" : "\n" ); } @@ -460,7 +486,7 @@ void POLY_LINE::Format( OUTPUTFORMATTER* out, int indent, int ctl ) const void POLY_LINE::formatContents( OUTPUTFORMATTER* out, int indent, int ctl ) const throw( IO_ERROR ) { - out->Print( 0, "(line_width %.6g)", InternalToWidth( lineWidth ) ); + formatStroke( out, stroke ); if( fillType != PR::T_none ) out->Print( 0, "(fill %s)", ShowFill( fillType ) ); @@ -506,12 +532,14 @@ void BEZIER::Format( OUTPUTFORMATTER* out, int indent, int ctl ) const void RECTANGLE::Format( OUTPUTFORMATTER* out, int indent, int ctl ) const throw( IO_ERROR ) { - // (rectangle (start X Y) (end X Y) (line_width WIDTH) (fill FILL_TYPE)) + // (rectangle (start X Y) (end X Y) [(stroke WIDTH)] (fill FILL_TYPE)) - out->Print( indent, "(rectangle (start %.6g %.6g)(end %.6g %.6g)(line_width %.6g)", + out->Print( indent, "(rectangle (start %.6g %.6g)(end %.6g %.6g)", InternalToLogical( start.x ), InternalToLogical( start.y ), - InternalToLogical( end.x ), InternalToLogical( end.y ), - InternalToWidth( lineWidth ) ); + InternalToLogical( end.x ), InternalToLogical( end.y ) + ); + + formatStroke( out, stroke ); if( fillType != PR::T_none ) out->Print( 0, "(fill %s)", ShowFill( fillType ) ); @@ -524,13 +552,14 @@ void CIRCLE::Format( OUTPUTFORMATTER* out, int indent, int ctl ) const throw( IO_ERROR ) { /* - (circle (center X Y)(radius LENGTH)(line_width WIDTH)(fill FILL_TYPE)) + (circle (center X Y)(radius LENGTH) [(stroke WIDTH)] (fill FILL_TYPE)) */ - out->Print( indent, "(circle (center %.6g %.6g)(radius %.6g)(line_width %.6g)", + out->Print( indent, "(circle (center %.6g %.6g)(radius %.6g)", InternalToLogical( center.x ), InternalToLogical( center.y ), - InternalToLogical( radius), - InternalToWidth( lineWidth ) ); + InternalToLogical( radius) ); + + formatStroke( out, stroke ); if( fillType != PR::T_none ) out->Print( 0, "(fill %s)", ShowFill( fillType ) ); @@ -543,15 +572,17 @@ void ARC::Format( OUTPUTFORMATTER* out, int indent, int ctl ) const throw( IO_ERROR ) { /* - (arc (pos X Y)(radius RADIUS)(start X Y)(end X Y)(line_width WIDTH)(fill FILL_TYPE)) + (arc (pos X Y)(radius RADIUS)(start X Y)(end X Y) [(stroke WIDTH)] (fill FILL_TYPE)) */ - out->Print( indent, "(arc (pos %.6g %.6g)(radius %.6g)(start %.6g %.6g)(end %.6g %.6g)(line_width %.6g)", + out->Print( indent, "(arc (pos %.6g %.6g)(radius %.6g)(start %.6g %.6g)(end %.6g %.6g)", InternalToLogical( pos.x ), InternalToLogical( pos.y ), InternalToLogical( radius), InternalToLogical( start.x ), InternalToLogical( start.y ), - InternalToLogical( end.x ), InternalToLogical( end.y ), - InternalToWidth( lineWidth ) ); + InternalToLogical( end.x ), InternalToLogical( end.y ) + ); + + formatStroke( out, stroke ); if( fillType != PR::T_none ) out->Print( 0, "(fill %s)", ShowFill( fillType ) ); @@ -574,11 +605,14 @@ void GR_TEXT::Format( OUTPUTFORMATTER* out, int indent, int ctl ) const formatAt( out, pos, angle, indent+1 ); - out->Print( 0, "(justify %s %s)(visible %s)", - ShowJustify( hjustify ), ShowJustify( vjustify ), - isVisible ? "yes" : "no" ); + if( hjustify != PR::T_left || vjustify != PR::T_bottom ) + out->Print( 0, "(justify %s %s)", + ShowJustify( hjustify ), ShowJustify( vjustify ) ); - if( fillType != PR::T_none ) + if( !isVisible ) + out->Print( 0, "(visible %s)", isVisible ? "yes" : "no" ); + + if( fillType != PR::T_filled ) out->Print( 0, "(fill %s)", ShowFill( fillType ) ); font.Format( out, 0, CTL_OMIT_NL ); diff --git a/new/sch_part.h b/new/sch_part.h index fa489cf57c..77e094823f 100644 --- a/new/sch_part.h +++ b/new/sch_part.h @@ -71,6 +71,18 @@ static inline double InternalToWidth( int aWidth ) return InternalToLogical( aWidth ) * 100; } +static inline int FontzToInternal( double aFontSize ) +{ + // sweet font sizes are deci-pins + return LogicalToInternal( aFontSize ) / 10; +} + +static inline double InternalToFontz( int aFontSize ) +{ + // sweet font sizes are deci-pins + return InternalToLogical( aFontSize ) * 10; +} + //----------------------- @@ -113,7 +125,30 @@ public: {} }; + +/** + * Class FONTZ + * is the size of a font, and comes with a constructor which initializes + * height and width to special values which defer font size decision to + * a higher control. + */ +class FONTZ +{ +public: + +#define FONTZ_DEFAULT -1 ///< when size defers to higher control + + FONTZ() : + height( FONTZ_DEFAULT ), + width( FONTZ_DEFAULT ) + {} + + int height; + int width; +}; + typedef float ANGLE; +typedef int STROKE; ///< will be a class someday, currently only line width namespace SCH { @@ -125,7 +160,8 @@ class FONT protected: wxString name; ///< name or other id such as number, TBD - wxSize size; + FONTZ size; + bool italic; bool bold; @@ -161,6 +197,11 @@ struct TEXT_EFFECTS }; +#define STROKE_DEFAULT -1 ///< defer line width decision to higher control + +#define FILL_TYPE_DEFAULT PR::T_none ///< fillType defaut + + class BASE_GRAPHIC { friend class PART; @@ -200,7 +241,7 @@ class POLY_LINE : public BASE_GRAPHIC friend class SWEET_PARSER; protected: - int lineWidth; + STROKE stroke; int fillType; // T_none, T_filled, or T_transparent POINTS pts; @@ -210,7 +251,7 @@ protected: public: POLY_LINE( PART* aOwner ) : BASE_GRAPHIC( aOwner ), - lineWidth( 1 ), + stroke( STROKE_DEFAULT ), fillType( PR::T_none ) { } @@ -228,7 +269,7 @@ public: BEZIER( PART* aOwner ) : POLY_LINE( aOwner ) { - lineWidth = 1; + stroke = STROKE_DEFAULT; fillType = PR::T_none; } @@ -242,7 +283,7 @@ class RECTANGLE : public BASE_GRAPHIC friend class SWEET_PARSER; protected: - int lineWidth; + STROKE stroke; int fillType; // T_none, T_filled, or T_transparent POINT start; POINT end; @@ -250,8 +291,8 @@ protected: public: RECTANGLE( PART* aOwner ) : BASE_GRAPHIC( aOwner ), - lineWidth( 1 ), - fillType( PR::T_none ) + stroke( STROKE_DEFAULT ), + fillType( FILL_TYPE_DEFAULT ) { } @@ -268,15 +309,15 @@ class CIRCLE : public BASE_GRAPHIC protected: POINT center; int radius; - int lineWidth; + STROKE stroke; int fillType; // T_none, T_filled, or T_transparent public: CIRCLE( PART* aOwner ) : BASE_GRAPHIC( aOwner ), radius( LogicalToInternal( 0.5 ) ), - lineWidth( 1 ), - fillType( PR::T_none ) + stroke( STROKE_DEFAULT ), + fillType( FILL_TYPE_DEFAULT ) { } @@ -292,7 +333,7 @@ class ARC : public BASE_GRAPHIC protected: POINT pos; - int lineWidth; + STROKE stroke; int fillType; // T_none, T_filled, or T_transparent int radius; POINT start; @@ -301,8 +342,8 @@ protected: public: ARC( PART* aOwner ) : BASE_GRAPHIC( aOwner ), - lineWidth( 1 ), - fillType( PR::T_none ), + stroke( STROKE_DEFAULT ), + fillType( FILL_TYPE_DEFAULT ), radius( LogicalToInternal( 0.5 ) ) { } @@ -322,6 +363,7 @@ protected: ANGLE angle; int fillType; ///< T_none, T_filled, or T_transparent + int hjustify; ///< T_center, T_right, or T_left int vjustify; ///< T_center, T_top, or T_bottom @@ -413,6 +455,10 @@ struct PINTEXT }; +#define PIN_LEN_DEFAULT -1 ///< use standard pin length for given type +#define PIN_SHAPE_DEFAULT PR::T_line ///< use standard pin shape +#define PIN_CONN_DEFAULT PR::T_in ///< use standard pin connection type + class PIN : public BASE_GRAPHIC { friend class PART; @@ -422,9 +468,9 @@ public: PIN( PART* aOwner ) : BASE_GRAPHIC( aOwner ), angle( 0 ), - connectionType( PR::T_input ), - shape( PR::T_line ), - length( 0 ), + connectionType( PIN_CONN_DEFAULT ), + shape( PIN_SHAPE_DEFAULT ), + length( PIN_LEN_DEFAULT ), isVisible( true ) {} @@ -447,10 +493,10 @@ protected: POINT pos; ANGLE angle; - PINTEXT padname; + PINTEXT pad; PINTEXT signal; - int connectionType; ///< T_input, T_output, T_bidirectional, T_tristate, T_passive, T_unspecified, + int connectionType; ///< T_in, T_out, T_inout, T_tristate, T_passive, T_unspecified, ///< T_power_in, T_power_out, T_open_collector, T_open_emitter, or T_unconnected. int shape; ///< T_none, T_line, T_inverted, T_clock, T_inverted_clk, T_input_low, T_clock_low, @@ -459,7 +505,7 @@ protected: int length; ///< length of pin in internal units bool isVisible; ///< pin is visible - wxString pin_merge; ///< padname of (pin_merge ...) that I am a member of, else empty if none + wxString pin_merge; ///< pad of (pin_merge ...) that I am a member of, else empty if none }; @@ -526,6 +572,7 @@ typedef std::vector< BASE_GRAPHIC* > GRAPHICS; typedef std::vector< PROPERTY* > PROPERTIES; typedef std::vector< PIN* > PINS; +typedef std::vector< PIN* > PIN_LIST; ///< no ownership, used for searches class LPID; @@ -624,29 +671,29 @@ public: PROPERTY* FieldLookup( PROP_ID aPropertyId ); /** - * Function PinFindByPadName - * finds a PIN based on aPadName or returns NULL if not found. - * @param aPadName is the pin to find + * Function PinFindByPad + * finds a PIN based on aPad or returns NULL if not found. + * @param aPad is the pin to find * @return PIN* - the found PIN or NULL if not found. */ - PIN* PinFindByPadName( const wxString& aPadName ) + PIN* PinFindByPad( const wxString& aPad ) { - PINS::iterator it = pinFindByPadName( aPadName ); - return it != pins.end() ? *it : NULL; - } - - PIN* PinFindBySignal( const wxString& aSignal ) - { - PINS::iterator it = pinFindBySignal( aSignal ); + PINS::iterator it = pinFindByPad( aPad ); return it != pins.end() ? *it : NULL; } + /** + * Function PinsFindBySignal + * fetches all the pins matching aSignal into aResults. + */ + void PinsFindBySignal( PIN_LIST* aResults, const wxString& aSignal ); + /** * Function PinDelete - * deletes the pin with aPadName if found and returns true, else false + * deletes the pin with aPad if found and returns true, else false * if not found. */ - bool PinDelete( const wxString& aPadName ); + bool PinDelete( const wxString& aPad ); /* @@ -714,13 +761,11 @@ protected: // not likely to have C++ descendants, but protected none-the-le PROPERTIES::iterator propertyFind( const wxString& aPropertyName ); /** - * Function pinFindByPadName - * searches for a PIN with aPadName and returns a PROPERTIES::iterator which + * Function pinFindByPad + * searches for a PIN with aPad and returns a PROPERTIES::iterator which * is the found item or pins.end() if not found. */ - PINS::iterator pinFindByPadName( const wxString& aPadName ); - PINS::iterator pinFindBySignal( const wxString& aSignal ); - + PINS::iterator pinFindByPad( const wxString& aPad ); POINT anchor; diff --git a/new/sch_sweet_parser.cpp b/new/sch_sweet_parser.cpp index f0cd6dea63..84ed858219 100644 --- a/new/sch_sweet_parser.cpp +++ b/new/sch_sweet_parser.cpp @@ -46,6 +46,11 @@ 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 @@ -265,6 +270,7 @@ void SWEET_PARSER::Parse( PART* me, LIB_TABLE* aTable ) throw( IO_ERROR, PARSE_E break; case T_pin: + // @todo PADNAMEs must be unique PIN* pin; pin = new PIN( me ); me->pins.push_back( pin ); @@ -447,10 +453,10 @@ void SWEET_PARSER::parseFont( FONT* me ) sawSize = true; NeedNUMBER( "size height" ); - me->size.SetHeight( internal( CurText() ) ); + me->size.height = fromFontz( CurText() ); NeedNUMBER( "size width" ); - me->size.SetWidth( internal( CurText() ) ); + me->size.width = fromFontz( CurText() ); NeedRIGHT(); break; @@ -500,18 +506,32 @@ void SWEET_PARSER::parseBool( bool* aBool ) } +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 - (padname PADNAME (font [FONT] (size HEIGHT WIDTH) [italic] [bold])(visible YES)) + (pad PADNAME (font [FONT] (size HEIGHT WIDTH) [italic] [bold])(visible YES)) */ T tok; bool sawFont = false; bool sawVis = false; - // padname or signal text + // pad or signal text NeedSYMBOLorNUMBER(); me->text = FromUTF8(); @@ -562,7 +582,7 @@ void SWEET_PARSER::parsePin( PIN* me ) (at X Y [ANGLE]) (length LENGTH) (signal NAME (font [FONT] (size HEIGHT WIDTH) [italic] [bold])(visible YES)) - (padname NUMBER (font [FONT] (size HEIGHT WIDTH) [italic] [bold] (visible YES)) + (pad NUMBER (font [FONT] (size HEIGHT WIDTH) [italic] [bold] (visible YES)) (visible YES) ) */ @@ -573,7 +593,7 @@ void SWEET_PARSER::parsePin( PIN* me ) bool sawAt = false; bool sawLen = false; bool sawSignal = false; - bool sawPadName = false; + bool sawPad = false; bool sawVis = false; while( ( tok = NextTok() ) != T_RIGHT ) @@ -607,11 +627,11 @@ void SWEET_PARSER::parsePin( PIN* me ) parsePinText( &me->signal ); break; - case T_padname: - if( sawPadName ) + case T_pad: + if( sawPad ) Duplicate( tok ); - sawPadName = true; - parsePinText( &me->padname ); + sawPad = true; + parsePinText( &me->pad ); break; case T_visible: @@ -631,9 +651,9 @@ void SWEET_PARSER::parsePin( PIN* me ) { switch( tok ) { - case T_input: - case T_output: - case T_bidirectional: + case T_in: + case T_out: + case T_inout: case T_tristate: case T_passive: case T_unspecified: @@ -673,17 +693,17 @@ void SWEET_PARSER::parsePin( PIN* me ) void SWEET_PARSER::parsePinDel( PART* me ) { - wxString padName; + 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(); - padName = FromUTF8(); + pad = FromUTF8(); // lookup now while CurOffset() is still meaningful. - PINS::iterator it = me->pinFindByPadName( padName ); + PINS::iterator it = me->pinFindByPad( pad ); if( it == me->pins.end() ) { THROW_PARSE_ERROR( _("undefined pin"), @@ -716,13 +736,13 @@ void SWEET_PARSER::parsePinSwap( PART* me ) PIN* pin1; PIN* pin2; - wxString padName; + wxString pad; NeedSYMBOLorNUMBER(); - padName = FromUTF8(); + pad = FromUTF8(); // lookup now while CurOffset() is still meaningful. - pin1 = me->PinFindByPadName( padName ); + pin1 = me->PinFindByPad( pad ); if( !pin1 ) { THROW_PARSE_ERROR( _("undefined pin"), @@ -733,9 +753,9 @@ void SWEET_PARSER::parsePinSwap( PART* me ) } NeedSYMBOLorNUMBER(); - padName = FromUTF8(); + pad = FromUTF8(); - pin2 = me->PinFindByPadName( padName ); + pin2 = me->PinFindByPad( pad ); if( !pin2 ) { THROW_PARSE_ERROR( _("undefined pin"), @@ -748,8 +768,8 @@ void SWEET_PARSER::parsePinSwap( PART* me ) NeedRIGHT(); // swap only the text, but might want to swap entire PIN_TEXTs - pin2->padname.text = pin1->padname.text; - pin1->padname.text = padName; + pin2->pad.text = pin1->pad.text; + pin1->pad.text = pad; } @@ -757,14 +777,14 @@ void SWEET_PARSER::parsePinRenum( PART* me ) { PIN* pin; - wxString oldPadName; - wxString newPadName; + wxString oldPad; + wxString newPad; NeedSYMBOLorNUMBER(); - oldPadName = FromUTF8(); + oldPad = FromUTF8(); // lookup now while CurOffset() is still meaningful. - pin = me->PinFindByPadName( oldPadName ); + pin = me->PinFindByPad( oldPad ); if( !pin ) { THROW_PARSE_ERROR( _("undefined pin"), @@ -775,12 +795,12 @@ void SWEET_PARSER::parsePinRenum( PART* me ) } NeedSYMBOLorNUMBER(); - newPadName = FromUTF8(); + newPad = FromUTF8(); NeedRIGHT(); - // @todo: check for padname legalities - pin->padname.text = newPadName; + // @todo: check for pad legalities + pin->pad.text = newPad; } @@ -788,14 +808,14 @@ void SWEET_PARSER::parsePinRename( PART* me ) { PIN* pin; - wxString oldSignal; + wxString pad; wxString newSignal; NeedSYMBOLorNUMBER(); - oldSignal = FromUTF8(); + pad = FromUTF8(); // lookup now while CurOffset() is still meaningful. - pin = me->PinFindBySignal( oldSignal ); + pin = me->PinFindByPad( pad ); if( !pin ) { THROW_PARSE_ERROR( _("undefined pin"), @@ -817,18 +837,19 @@ void SWEET_PARSER::parsePinRename( PART* me ) void SWEET_PARSER::parsePinMerge( PART* me ) { T tok; - wxString padName; + wxString pad; + wxString signal; wxString msg; NeedSYMBOLorNUMBER(); - wxString anchorPadName = FromUTF8(); + wxString anchorPad = FromUTF8(); // lookup now while CurOffset() is still good. - PINS::iterator pit = me->pinFindByPadName( anchorPadName ); + PINS::iterator pit = me->pinFindByPad( anchorPad ); if( pit == me->pins.end() ) { - msg.Printf( _( "undefined pin %s" ), anchorPadName.GetData() ); + msg.Printf( _( "undefined pin %s" ), anchorPad.GetData() ); THROW_PARSE_ERROR( msg, CurSource(), CurLine(), @@ -836,10 +857,10 @@ void SWEET_PARSER::parsePinMerge( PART* me ) CurOffset() ); } - if( !(*pit)->pin_merge.IsEmpty() && anchorPadName != (*pit)->pin_merge ) + if( !(*pit)->pin_merge.IsEmpty() && anchorPad != (*pit)->pin_merge ) { msg.Printf( _( "pin %s already in pin_merge group %s" ), - anchorPadName.GetData(), (*pit)->pin_merge.GetData() ); + anchorPad.GetData(), (*pit)->pin_merge.GetData() ); THROW_PARSE_ERROR( msg, CurSource(), @@ -848,60 +869,120 @@ void SWEET_PARSER::parsePinMerge( PART* me ) CurOffset() ); } - NeedLEFT(); - - tok = NextTok(); - if( tok != T_hide ) - Expecting( T_hide ); - (*pit)->isVisible = true; - (*pit)->pin_merge = anchorPadName; + (*pit)->pin_merge = anchorPad; // allocate or find a MERGE_SET; - MERGE_SET& ms = me->pin_merges[anchorPadName]; + MERGE_SET& ms = me->pin_merges[anchorPad]; while( ( tok = NextTok() ) != T_RIGHT ) { - if( !IsSymbol( tok ) && tok != T_NUMBER ) - Expecting( "padname" ); - - padName = FromUTF8(); - - D(printf("padName=%s\n", TO_UTF8( padName ) );) - - // find the PIN and mark it as being in this MERGE_SET or throw - // error if already in another MERGET_SET. - - pit = me->pinFindByPadName( padName ); - if( pit == me->pins.end() ) + if( tok == T_LEFT ) { - msg.Printf( _( "undefined pin %s" ), padName.GetData() ); - THROW_PARSE_ERROR( msg, - CurSource(), - CurLine(), - CurLineNumber(), - CurOffset() ); - } + tok = NextTok(); - if( !(*pit)->pin_merge.IsEmpty() && anchorPadName != (*pit)->pin_merge ) + 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 { - msg.Printf( _( "pin %s already in pin_merge group %s" ), - padName.GetData(), (*pit)->pin_merge.GetData() ); - - THROW_PARSE_ERROR( msg, - CurSource(), - CurLine(), - CurLineNumber(), - CurOffset() ); + Expecting( T_LEFT ); } - - (*pit)->isVisible = false; - (*pit)->pin_merge = anchorPadName; - - ms.insert( padName ); } - - NeedRIGHT(); } @@ -998,8 +1079,9 @@ 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 units as defined above. - (line_width WIDTH) + # Line widths are in percent of a pin delta + [(stroke [WIDTH] [(style [(dashed...)]...)])] + # Valid fill types are none, filled, and transparent. (fill FILL_TYPE) @@ -1008,8 +1090,8 @@ void SWEET_PARSER::parsePolyLine( POLY_LINE* me ) T tok; int count = 0; - bool sawWidth = false; - bool sawFill = false; + bool sawStroke = false; + bool sawFill = false; while( ( tok = NextTok() ) != T_RIGHT ) { @@ -1020,13 +1102,11 @@ void SWEET_PARSER::parsePolyLine( POLY_LINE* me ) switch( tok ) { - case T_line_width: - if( sawWidth ) + case T_stroke: + if( sawStroke ) Duplicate( tok ); - NeedNUMBER( "line_width" ); - me->lineWidth = fromWidth( CurText() ); - NeedRIGHT(); - sawWidth = true; + sawStroke = true; + parseStroke( &me->stroke ); break; case T_pts: @@ -1074,7 +1154,7 @@ void SWEET_PARSER::parsePolyLine( POLY_LINE* me ) break; default: - Expecting( "pts|line_width|fill" ); + Expecting( "pts|stroke|fill" ); } } } @@ -1089,14 +1169,14 @@ void SWEET_PARSER::parseBezier( BEZIER* me ) void SWEET_PARSER::parseRectangle( RECTANGLE* me ) { /* - (rectangle (start X Y) (end X Y) (line_width WIDTH) (fill FILL_TYPE)) + (rectangle (start X Y) (end X Y) (stroke WIDTH) (fill FILL_TYPE)) */ T tok; bool sawStart = false; - bool sawEnd = false; - bool sawWidth = false; - bool sawFill = false; + bool sawEnd = false; + bool sawStroke = false; + bool sawFill = false; while( ( tok = NextTok() ) != T_RIGHT ) { @@ -1107,13 +1187,11 @@ void SWEET_PARSER::parseRectangle( RECTANGLE* me ) switch( tok ) { - case T_line_width: - if( sawWidth ) + case T_stroke: + if( sawStroke ) Duplicate( tok ); - sawWidth = true; - NeedNUMBER( "line_width" ); - me->lineWidth = fromWidth( CurText() ); - NeedRIGHT(); + sawStroke = true; + parseStroke( &me->stroke ); break; case T_fill: @@ -1157,7 +1235,7 @@ void SWEET_PARSER::parseRectangle( RECTANGLE* me ) break; default: - Expecting( "start|end|line_width|fill" ); + Expecting( "start|end|stroke|fill" ); } } } @@ -1169,7 +1247,7 @@ void SWEET_PARSER::parseCircle( CIRCLE* me ) (circle (center X Y) # Radius length is in units if defined or mils. (radius LENGTH) - (line_width WIDTH) + (stroke WIDTH) (fill FILL_TYPE) ) */ @@ -1177,7 +1255,7 @@ void SWEET_PARSER::parseCircle( CIRCLE* me ) T tok; bool sawCenter = false; bool sawRadius = false; - bool sawWidth = false; + bool sawStroke = false; bool sawFill = false; while( ( tok = NextTok() ) != T_RIGHT ) @@ -1189,13 +1267,11 @@ void SWEET_PARSER::parseCircle( CIRCLE* me ) switch( tok ) { - case T_line_width: - if( sawWidth ) + case T_stroke: + if( sawStroke ) Duplicate( tok ); - sawWidth = true; - NeedNUMBER( "line_width" ); - me->lineWidth = fromWidth( CurText() ); - NeedRIGHT(); + sawStroke = true; + parseStroke( &me->stroke ); break; case T_fill: @@ -1237,7 +1313,7 @@ void SWEET_PARSER::parseCircle( CIRCLE* me ) break; default: - Expecting( "center|radius|line_width|fill" ); + Expecting( "center|radius|stroke|fill" ); } } } @@ -1247,7 +1323,7 @@ void SWEET_PARSER::parseArc( ARC* me ) { /* (arc (pos X Y) (radius RADIUS) (start X Y) (end X Y) - (line_width WIDTH) + (stroke WIDTH) (fill FILL_TYPE) ) */ @@ -1257,7 +1333,7 @@ void SWEET_PARSER::parseArc( ARC* me ) bool sawStart = false; bool sawEnd = false; bool sawRadius = false; - bool sawWidth = false; + bool sawStroke = false; bool sawFill = false; while( ( tok = NextTok() ) != T_RIGHT ) @@ -1269,13 +1345,11 @@ void SWEET_PARSER::parseArc( ARC* me ) switch( tok ) { - case T_line_width: - if( sawWidth ) + case T_stroke: + if( sawStroke ) Duplicate( tok ); - sawWidth = true; - NeedNUMBER( "line_width" ); - me->lineWidth = fromWidth( CurText() ); - NeedRIGHT(); + sawStroke = true; + parseStroke( &me->stroke ); break; case T_fill: @@ -1339,7 +1413,7 @@ void SWEET_PARSER::parseArc( ARC* me ) break; default: - Expecting( "center|radius|line_width|fill" ); + Expecting( "center|radius|stroke|fill" ); } } } diff --git a/new/sch_sweet_parser.h b/new/sch_sweet_parser.h index 7f80c05313..9b55c2a89e 100644 --- a/new/sch_sweet_parser.h +++ b/new/sch_sweet_parser.h @@ -30,6 +30,7 @@ class POINT; +typedef int STROKE; namespace SCH { @@ -90,6 +91,7 @@ class SWEET_PARSER : public SWEET_LEXER void parsePinRenum( PART* me ); void parsePinRename( PART* me ); void parsePinMerge( PART* me ); + void parseStroke( STROKE* me ); public: diff --git a/new/sweet.fodt b/new/sweet.fodt new file mode 100644 index 0000000000..2b3cfa79b3 --- /dev/null +++ b/new/sweet.fodt @@ -0,0 +1,1659 @@ + + + + 2011-04-18T01:23:31 + P0D + 1 + LibreOffice/3.3$Unix OpenOffice.org_project/330m9$Build-1 + + + + + + + + 27919 + 0 + 23670 + 12026 + true + false + + + view2 + 5590 + 38299 + 0 + 27919 + 23668 + 39943 + 3 + 1 + false + 205 + false + + + + + true + false + false + true + false + false + true + false + 1 + true + + true + false + + false + true + true + true + false + + false + true + true + true + false + 0 + false + + false + false + false + false + false + 0 + + false + false + false + true + 0 + true + true + false + false + false + true + true + false + false + false + false + false + true + high-resolution + + false + false + true + true + false + false + false + true + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + SWEET S-Expression Grammar Describing a + Schematic Part String Format Specification + + Authors: + Wayne Stambaugh + Dick Hollenbeck + + + Original Edits: January 2011 + Latest Edits: + April 2011 + + Introduction + The purpose of this document is to define the new format for the Kicad schematic part descriptions, as expressed using the new SWEET grammar. This format supersedes the old file format defined in the file “library_file_format_EN.odt. The idea of component library files is no longer valid. The unit of retrieval from the new distributed library system is the part, not a library. To learn more about the new distributed library system see the design.h document in the Kicad source tree, or better yet, the Doxygen generated material therefrom. + The SWEET grammar format is based on the Specttra DSN lexer + 1 + + See source files dsnlexer.cpp and dsnlexer.h in the Kicad source code for more information about how the DSN lexer works. + + work graciously coded and contributed by Dick Hollenbeck. For those not familiar with the DSN format, it is very similar the the LISP programming language in that keywords are delimited by braces. It is also known as an S-expression format. A keyword may have zero or more tokens that behave like parameters for the keyword. Tokens may be defined as additional keywords creating a programming language like structure. The decision to change to this format is driven by a desire to provide a more robust file lexer, a human readable file format, better retrieval error reporting, and improved extensibility. The other driving factor is to provide a more robust format for handling the various requirements of the component library developers. + Front Matter + This is a living document designed to be kept synchronized with the actual source code. If you modify the lexer, please update this specification as courtesy to other developers. There are many third party tools that are used to generate, manipulate, and parse component library files. Keeping this document current is vital to developers of these tools. + Formatting + The DSN lexer enforces strict formatting requirements. Files must be encoded in UTF8 format. All lexer keywords are comprised of lower case letters, numbers, and underscores. Tokens may be any alphanumeric character delimited by a white space character. Each new delimited level of keywords is indented to make files easier to read and edit with a plain text editor. Comments are supported as separate lines indented at the same level as the current keyword delimiter and begin with the # character. In line comments are not allowed. There is more information on s-expression support in the Kicad source tree in file Documentation/s-expressions.txt. + Using this document + This document is nothing more than several well commented part files. You should always be able to copy the text from the opening delimiter “(“ to the closing delimeter “)” into a file and open it with the library part file editor or library part viewer in Kicad's schematic capture program EESchema. It may not actually draw a useful component or be a very useful part but it should always be syntactically correct. The comments for a specific keyword always appears above the keyword it describes. Keywords defined within brackets [] are optional. A simple syntax coloring scheme has been employed to make this document more readable. Keywords are highlighted in blue, comments are highlighted in turquoise, and strings are highlighted in red. + + Logical coordinates. + The SWEET grammar expresses schematic parts using a coordinate system without units of measure. That is, logical coordinates used are dimensionless. The advantage of having coordinates without units is any item that has an integer (non-fractional) coordinate in x and y, will be “on grid”. This helps ensure all connectable items such as pins, wires, junctions, etc., can be easily connected using normal grid snap. The normal spacing between neighboring part pins is 1 logical unit, and it is dimensionless. In fact, this is the definition of what a logical unit is, the normal spacing between two neighboring pins. Only non-connectable objects (e.g. arcs, text, etc.) should have non-integer coordinates. The zoom factor at time of printing or plotting determines the physical size of any drawing output. + Line Widths + Line widths are given as a percent of a logical unit. In other words, a line width of 1 means 1/100th of a logical unit. Recall that a logical unit of 1 is the normal spacing between two neighboring schematic pins. So a line width of 50 would mean 50/100 of a pin interval, or half the normal space between two pins. A line width of 50 represents a very wide line indeed, a more normal line width would be about 1-5 (percent). + An example file showing syntax for all drawable item types. + # The schematic part element is the largest entity addressed by the SWEET grammar. + # A schematic part can inherited from another part by using the “extends” keyword. + # The part name is normally the file name, although SWEET parts will not always + # exist as mere files. They are actually best thought of as strings held in a + # a container called a lib_source. The NAME_HINT is to be used in a manner yet to + # be determined, depending on the context from which the SWEET part came from. + (part + NAME_HINT[ + extends + LPID] + + + + # Change the anchor position to something other than 0, 0. Anchor coordinates + + # must be integers. + + [(anchor (at X Y))] + + + # The reference token is a reserved property. Other reserved properties are + + # value, footprint, datasheet, model, and keywords. + + [(reference + REF + + + + # Effects define how a property is displayed. If no effect is defined then + + # the property is not displayed. Effects can also be used to override the + + # effects of a property in an inherited part. When (effects...) is used + + # in a COMPONENT to override the effects of a PART, the PROPERTY + + # specifier must be present. + + [(effects [PROPERTY] + + + # Position requires an 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 & + + # style are required. Italic and bold styles are optional. The font size + + # height and width are in units yet to be determined. Only a variance + + # from defaults needs to be stated. + + [(font + [FONT] (size HEIGHT WIDTH) [italic] [bold])] + + + # Default visiblity for fields is no, only a variance from the + + # the default needs to be stated. + + [(visible yes)] + + )] + + )] + + + # Add a new user definable property to this part. Also can be use to override + + # a property defined in extended part. + + [(property + NAME VALUE + + + + [( + effects + + + + + + ( + at + X Y [ANGLE]) + + + + + + ( + font + [FONT] (size HEIGHT WIDTH) [italic] [bold]) + + + # Default visiblity for fields is no, only a variance from the + + # the default needs to be stated. + + [(visible yes)] + + )] + + )] + + + # Either polyline or line are can be used to define a line or series of lines. + + # Coordinates can be non-integer. + + + (polyline + or + + + (line + + + (pts (xy X Y)(xy X Y)(xy X Y)(xy X Y)(xy X Y)) + + + # Line widths are in units as defined above. + + [(stroke [WIDTH] [(style [(dashed...)]...)])] + + + # Valid fill types are none, filled, and transparent. Default fill for + + # a poly_line is none. Only a variance from the default needs to be stated. + + [(fill FILL_TYPE)] + + ) + + + + + (rectangle + ( + start X Y) (end X Y) (stroke WIDTH) (fill FILL_TYPE)) + + + + + + (circle + + + (center X Y) + + + # Radius length is in units if defined or mils. + + (radius LENGTH) + + (stroke WIDTH) + + (fill FILL_TYPE) + + ) + + + + + (arc + ( + pos + X Y) ( + radius + RADIUS) ( + start X Y) (end X Y) + + + + + + + ( + stroke WIDTH) + + (fill FILL_TYPE) + + ) + + + + (bezier + + + (pts + ( + xy X Y) (xy X Y) (xy X Y) (xy X Y) (xy X Y) (xy X Y)) + + (stroke WIDTH) + + (fill FILL_TYPE) + + ) + + + + (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. Defaults are + + # left and bottom. Only need to state a non-default. + + [(justify [HORIZONTAL_JUSTIFY] [VERTICAL_JUSTIFY])] + + + [(font + [FONT] (size HEIGHT WIDTH) [italic] [bold])] + + + # default visibility is yes, only need to state a non-default + + [(visible no)] + + + # default fill type for text is filled, only need to state a non-default + + [(fill FILL_TYPE)] + + ) + + + + # A pin's type is it's electrical connection. Valid connection types are + + # input, output, bidirectional, tristate, passive, unspecified, power_in, + + # power_out, open_collector, open_emitter, unconnected. Valid pin shape + + # values are none, line, inverted, clock, inverted_clk, input_low, clock_low, + + # falling_edge, and non_logic. + + (pin + TYPE SHAPE + + + + # Pin coordinates must be integers. The angle values 0, 180, 90, and 270 + + # replace the current right, left, up, and down specifiers respectively, + + # and are the only angles currently supported. + + (at X Y [ANGLE]) + + + # Length of the pin in logical units. This is optional, depending on + + # SHAPE, which in some cases may imply a fixed length + + [(length LENGTH)] + + + # Use signal to describe the purpose of the pin. Signals do not have to be + + # uniquely named within a part. Signal can be blank. + + + [ + ( + signal + “NAME” + ( + font + [FONT] (size HEIGHT WIDTH) [italic][bold]) + + (visible + YES))] + + + # Use pad to uniquely identify the pin, according to the pin number + + # in the datasheet for the part. Padnames may not be blank and must be + + + # be unique within the part. + + + + + + (pad + “NUMBER” + ( + font + [FONT] (size HEIGHT WIDTH) [italic][bold]) + + (visible + YES)) + + + + + # default visibility for pins is yes, so only a deviation from the + + # the default needs to be stated. + + [(visible no)] + + ) + + + + # Pin merge is used to group a number of pins that are connected to the same + + # net externally and show only one pin symbolizing the group. The group is + + # called a merge set or merge group. It is typically used to hide all but + + # one power pin on parts that have a large number of power connections + + # such as FPGAs and micro-contollers. VISIBLE_PAD is the pad of + + # the one pin which remains visible within the merge set. Pins with pads + + # listed within the (pads...) element are all made invisible, and so + + # are are all pins which may have any of the signal names listed in the + + # (signals...) element. A pin may only exist within one merge group. + + # Any number of (pin_merge...) statements maybe made, including more than + + # one re-stating the same VISIBLE_PAD, which is the anchor pin for the + + # merge set. + + [(pin_merge VISIBLE_PAD + + [(pads HIDDEN_PAD1 …)][(signals HIDDEN_SIGNAL1 …)])] + + + # The pin swap hinting flag is used to indicate to an external tool ( i.e. an + + # auto router ) that the pins defined are functionally equivalent and + + # interchangeable. + + [(hint_pin_swap NUMBER1 NUMBER2 ...)] + ) + + Inheritance syntax example file. + (part + NAME_HINT + extends + LPID + + + + # Remove pin PAD defined in the base part. Only valid for extended parts. + + [(pin_del PAD)] + + + # Swap pin PAD1 and pin PAD2 in the base part. + + + # Only valid for extended parts. + + [(pin_swap PAD1 PAD2)] + + + # Change the pad of pin OLDPAD to NEWPAD in the base part. + + # Only valid for extended parts. + + [(pin_renum OLDPAD NEWPAD)] + + + # Change the signal of pin PAD to NEWSIGNAL in the base part. + + + # Only valid for extended parts. + + [(pin_rename PAD NEWSIGNAL)] + + + # Remove property NAME from the base part. Only valid for extended parts. + + [(property_del NAME)] + + + # Alternates are used to define multiple part per package devices and/or + + # alternate body styles (DeMorgan). Parts must be defined in order. There + + # can more than one alternates defined. + + [(alternates LPID [LPID...])] + + + # Add a new property to this part. Also can be use to override a property + + # defined in the base part. + + + [(property + NAME VALUE + + + + [( + effects + ( + at + X Y [ANGLE]) + + + + ( + font + [FONT] (size HEIGHT WIDTH) [italic] [bold]) + + (visible YES)] + + )] + + + # The alternates swap hinting flag is used to indicate to an external tool + + # that the parts defined in the alternates list are functionally equivalent + + # and interchangeable. + + [(hint_alt_swap LPID1 LPID2 ...)] + ) + + # This is an example of a dual input NAND gate A of a 7400. + (part + “dual_input_nand_a” + + + (reference + “U”) + + (arc (pos + 2 0) ( + radius + 0.4) ( + start + 2 -4) (start_angle -90) (end + 2 4) + + (end_angle 90) + ( + stroke + 0) (fill + none)) + + (polyline (xy + 2 4) (xy -6 4) (xy -6 -4) (xy 2 -4) + + (stroke 0) (fill none)) + + (pin + input line (at + -12 2 3.6 180) + + + + + (signal + “” + ( + font (size + 1.2 1.2)) (visible + yes)) + + + (pad + “1” + ( + font (size + 1.2 1.2)) (visible + yes)) + + (visible + yes)) + + (pin + input line (at + -12 -2 3.6) + + + + + (signal + “” + ( + font (size + 1.2 1.2)) (visible + yes)) + + + (pad + “2” + ( + font (size 1.2 1.2)) (visible + yes)) + + (visible + yes)) + + (pin + output line (at + -12 0) + + + + + (signal + “” + ( + font (size + 1.2 1.2)) (visible + yes)) + + + (pad + “3” + ( + font (size + 1.2 1.2)) (visible + yes)) + + (visible + yes)) + + (pin + power_in none (at + -4 -4 90) + + + + + (signal + “GND” + ( + font (size + 1.2 1.2)) (visible + no)) + + + (pad + “7” + ( + font (size 1.2 1.2)) (visible + no)) + + (visible + no)) + + (pin + power_in none (at + -4 4 90) + + + + + (signal + “VCC” + ( + font (size 1.2 1.2)) (visible + no)) + + + (pad + “7” + ( + font (size 1.2 1.2)) (visible + no)) + + (visible + no)) + + (hint_pin_swap + “1” + “2”) + ) + + # All LPIDs below this point assume that the base part is located in the same + # library as the parts that are derived from them. See the “Distributed Library + # & EESchema Parts List Design Documentation” in the Kicad source file “design.h” + # for more information on LPIDs. + + # This is the B gate of a 7400. + (part + “dual_input_nand_b” + inherits + “dual_in_nand_a/rev1” + + + (pin_del 7) + + + (pin_del 14) + + (pin_renum 1 4) + + (pin_renum 2 5) + + (pin_renum 3 6) + ) + + # This is the C gate of a 7400. + (part + “dual_input_nand_c” + inherits + “dual_in_nand_a/rev1” + + + (pin_del 7) + + (pin_del 14) + + (pin_renum 8 4) + + (pin_renum 9 5) + + (pin_renum 10 6) + ) + + # This is the D gate of a 7400. + (part + “dual_input_nand_b” + inherits + “dual_in_nand_a/rev1” + + + (pin_del 7) + + (pin_del 14) + + (pin_renum 1 11) + + (pin_renum 2 12) + + (pin_renum 3 13) + ) + + # This is a DeMorgan representation of a dual input NAND gate A. + (part + “dual_input_nand_ + demorgan + _a” + + + (reference + “U”) + + (arc (pos + -9.3 0) ( + radius + 0.518) ( + start + -6 4) (start_angle 50.4) + + (end + -6 -4) (end_angle -50.4) + ( + stroke + 0) (fill + none)) + + (arc (pos + -0.22 2.86) ( + radius + 7.06) ( + start + 0 -200) (start_angle -88.1) + + (end + 6 0) (end_angle -24.6) + ( + stroke + 0) (fill + none)) + + (arc (pos + -0.2 2.82) ( + radius + 6.8) ( + start + 6 0) (start_angle -24.4) (end + 0 4) + + (end_angle 88.3) + ( + stroke + 0) (fill + none)) + + (polyline (xy + -6 4) (xy 0 4) (xy 0 -4) (xy + -6 -4) + + (stroke 0) (fill none)) + + (pin + input line (at + -12 2 180) (length 6) + + + + + (signal + “” + ( + font (size 1.2 1.2)) (visible + yes)) + + + (pad + “1” + ( + font (size + 1.2 1.2)) (visible + yes)) + + (visible + yes)) + + (pin + input line (at + -12 -2 180) (length 6) + + + + + (signal + “” + ( + font (size 1.2 1.2)) (visible + yes)) + + + (pad + “2” + ( + font (size 1.2 1.2)) (visible + yes)) + + (visible + yes)) + + (pin + output line (at + -12 0) (length 7.4) + + + + + (signal + “” + ( + font (size 1.2 1.2)) (visible + yes)) + + + (pad + “3” + ( + font (size 1.2 1.2)) (visible + yes)) + + (visible + yes)) + + (pin + power_in none (at + -4 -4 90) (length 0) + + + + + (signal + “GND” + ( + font (size 1.2 1.2)) (visible + no)) + + + (pad + “7” + ( + font (size 1.2 1.2)) (visible + no)) + + (visible + no)) + + + (pin + power_in none (at + -4 4 90) (length 0) + + + + + (signal + “VCC” + ( + font (size 1.2 1.2)) (visible + no)) + + + (pad + “7” + ( + font (size + 1.2 1.2)) (visible + no)) + + (visible + no)) + + (hint_pin_swap 1 2) + ) + + # This is the DeMorgan representation of gate B of a 7400. + (part + “dual_input_nand_demorgan_b” + + + + inherits + “dual_in_nand_demorgan_a/rev1” + + + (pin_del 7) + + (pin_del 14) + + (pin_renum 1 4) + + (pin_renum 2 5) + + (pin_renum 3 6) + + (pin_rename 1 “D”) + + (pin_rename 2 “E”) + + (pin_rename 3 “F”) + ) + + # This is the DeMorgan representation of gate C of a 7400. + (part + “dual_input_nand_demorgan_c” + + + + inherits + “dual_in_nand_demorgan_a/rev1” + + + (pin_del 7) + + (pin_del 14) + + (pin_renum 1 8) + + (pin_renum 2 9) + + (pin_renum 3 10) + ) + + # This is the DeMorgan representation of gate D of a 7400. + (part + “dual_input_nand_demorgan_d” + + + + inherits + “dual_in_nand_demorgan_a/rev1” + + + (pin_del 7) + + (pin_del 14) + + (pin_renum 1 11) + + (pin_renum 2 12) + + (pin_renum 3 13) + ) + + # An example of putting it all together to create a 7400. + (part + “7400” + + + (value + “7400”) + + (alternates + + + + “dual_in_nand_a/rev1” + + + + “dual_in_nand_b/rev1” + + + + “dual_in_nand_c/rev1” + + + + “dual_in_nand_d/rev1” + + + ) + + (alternates + + + + “dual_in_nand_demorgan_a/rev1” + + + + + “dual_in_nand_demorgan_b/rev1” + + + + “dual_in_nand_demorgan_c/rev1” + + + + “dual_in_nand_demorgan_d/rev1” + + + ) + + (hint_alt_swap + + + + “dual_in_nand_a/rev1” + + + + “dual_in_nand_b/rev1” + + + + “dual_in_nand_c/rev1” + + + + “dual_in_nand_d/rev1” + + + ) + ) + + # A 74LS00 is as simple as this. + (part + “74LS00” + inherits + “7400/rev1” + ( + value + “74LS00” + )) + + # This is a so called heavy weight part definition of a Texas Instruments + # part number SN74HCT00NSR. + (part + “SN74HCT00NSR” + inherits + “7400/rev1” + + + + + + ( + value + “SN74HCT00NSR” + ) + + + + ( + footprint + “14-SOP” + ) + + + + ( + datasheet + “http://focus.ti.com/general/docs/lit/getliterature.tsp?genericPartNumber=sn74hct00&fileType=pdf” + ) + + + + ( + keywords + “LOGIC” “NAND” + ) + + + + ( + property + “Description” + “IC GATE POS-NAND QD 2IN 14-SOP” + ) + + + + ( + property + “Manufacturer” + “Texas Instruments” + ) + + + + ( + property + “Vendor” + “Digikey” + ) + + + + ( + property + “Vendor Part Number” + “SN74HCT00NSR-ND” + ) + + ) + + + + \ No newline at end of file diff --git a/new/sweet.keywords b/new/sweet.keywords index 6d58f73920..475402d711 100644 --- a/new/sweet.keywords +++ b/new/sweet.keywords @@ -3,7 +3,7 @@ anchor arc at bezier -bidirectional +inout bold bottom center @@ -21,7 +21,7 @@ filled font footprint hide -input +in input_low inverted inverted_clk @@ -31,15 +31,15 @@ keywords left length line -line_width model no non_logic none open_collector open_emitter -output -padname +out +pad +pads part passive pin @@ -62,9 +62,11 @@ right route_alt_swap route_pin_swap signal +signals size start start_angle +stroke text top transparent