tri.red 65 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541
  1. % ======================================================================
  2. % T h e T e X - R e d u c e - I n t e r f a c e
  3. % ======================================================================
  4. % (C) 1987/1988 by Rechenzentrum der Universitaet zu Koeln
  5. % (University of Cologne Computer Center)
  6. % Abt. Anwendungssoftware
  7. % (Application Software Department)
  8. % ATTN: Werner Antweiler
  9. % Robert-Koch-Str. 10
  10. % 5000 Koeln 41
  11. % Federal Republic of Germany
  12. % E-Mail: reduce@rrz.Uni-Koeln.DE
  13. % All rights reserved. Permission to copy without fee all or part of
  14. % this software product is hereby granted provided that the copies are
  15. % not made or distributed for direct commercial advantage, this copy-
  16. % right notice and its date appear, and notice is given that copying is
  17. % by permission of the authors. To copy otherwise requires a fee and/or
  18. % specific permission. This software product has been developed by
  19. % WERNER ANTWEILER at the University of Cologne Computer Center, West
  20. % Germany. The TeX-Reduce-Interface has been totally written in REDUCE-
  21. % LISP.
  22. % ======================================================================
  23. % Last Update: 19-Mar-93 Version 0.60
  24. % ======================================================================
  25. %
  26. % Section Survey
  27. % ----------------------------------------------------------------------
  28. % Section Contents Page
  29. % ----------------------------------------------------------------------
  30. % 0 Main Procedure (and Interfacing) 2
  31. % 1 Creating a TeX item list 5
  32. % 1.1 Operator Administration Routines 5
  33. % 1.2 Prefix to Infix Conversion 6
  34. % 1.3 Making a TeX item 9
  35. % 2 Inserting Glue Items 16
  36. % 3 Line Breaking 18
  37. % 3.1 Resolving Fraction Expressions 20
  38. % 3.2 Creating a Break List 21
  39. % 3.3 Major Line Breaking Routine 23
  40. % 4 TeX-Output Routines 28
  41. % 5 User Interface 30
  42. % ----------------------------------------------------------------------
  43. % Note: page breaks (form feeds) are indicated by "%ff" lines
  44. %ff
  45. % ----------------------------------------------------------------------
  46. % Section 0: Global Variables, Main Procedure and Interfacing
  47. % ----------------------------------------------------------------------
  48. % IMPORTANT NOTICE FOR REDUCE 3.2 USERS:
  49. % This code was written to run under REDUCE 3.3. Users of REDUCE 3.2
  50. % therefore have to change two lines of this code before compiling it:
  51. % 1) the line `switch ...;` must be deleted
  52. % 2) the construct `FOR EACH ... IN ... JOIN ...` must be changed
  53. % to `FOR EACH ... IN ... CONC ...` because only the latter is
  54. % accepted by REDUCE 3.2.
  55. % Furthermore, the TRI supports features of REDUCE that are new in ver-
  56. % sion 3.3. You cannot take advantage of them under version 3.2. In parti-
  57. % cular, some of the examples in the accompanying test file may fail.
  58. module tri;
  59. global '(
  60. % -----------------+---------------------------------------------------+
  61. % GLOBAL VARIABLES | EXPLANATION |
  62. % -----------------+---------------------------------------------------+
  63. !*TeX % flag to be switched ON and OFF (TeX Output Mode)
  64. !*TeXBreak % flag to be switched ON and OFF (Break Facility)
  65. !*TeXIndent % flag to be switched ON and OFF (Indentation Mode)
  66. TeXstack!* % stack to save expressions for an unfilled line
  67. hsize!* % total page width in scaled points (sp)
  68. % note: 65536sp = 1pt = 1/72.27 inch
  69. hss!* % total line stretchability/shrinkability (in sp)
  70. hww!* % optimum page fit width (= 3/4 hsize) (in sp)
  71. tolerance!* % value within break points are considered to be
  72. % feasible (range: 0..10000)
  73. metricu!* % ??
  74. indxl!* % ??
  75. % -----------------+---------------------------------------------------+
  76. );
  77. % declare switches:
  78. switch TeX,TeXBreak,TeXIndent;
  79. % declare switch dependencies:
  80. put('TeXIndent,'simpfg,'((T (progn (setq !*TeX T)
  81. (setq !*TeXBreak T))) ));
  82. put('TeXBreak,'simpfg,'((T (setq !*TeX T)) ));
  83. put('TeX,'simpfg,'((NIL (progn (setq !*TeXBreak NIL)
  84. (setq !*TeXIndent NIL))) ));
  85. symbolic procedure tri!-error(strlst,errclass);
  86. << for each x in strlst do prin2 x; terpri();
  87. if errclass='fatal then
  88. rederr "Aborting."
  89. >>;
  90. %ff
  91. %%symbolic procedure varpri(u,v,w); % REDUCE 3.3 version
  92. %%% parameters: u ..... expression to be printed
  93. %%% v ..... a list of expressions assigned to <u>
  94. %%% w ..... a flag (true if <u> is last in current set)
  95. %%begin
  96. %% scalar x;
  97. %% if null u then u:=0; % allow for unset array elements
  98. %% if !*NERO and (u=0) then return;
  99. %% v:=setvars v;
  100. %% if !*TeX then return if (x := getrtype u) and get(x,'TeXprifn)
  101. %% then apply3(get(x,'TeXprifn),u,v,w)
  102. %% else TeXvarpri(u,v,w);
  103. %% if (x:=getrtype u) and flagp(x,'SPRIFN)
  104. %% then return if null v then apply1(get(get(x,'TAG),'PRIFN),u)
  105. %% else maprin list('SETQ,car v,u);
  106. %% if w memq '(FIRST ONLY) then terpri!*(T);
  107. %% if !*FORT then return fvarpri(u,v,w);
  108. %% if v then u:='SETQ . ACONC(V,U);
  109. %% maprin u;
  110. %% if null w or (w eq 'FIRST) then return nil
  111. %% else if not !*NAT then prin2!*("$");
  112. %% terpri!*(not !*NAT); return(nil)
  113. %%end;
  114. symbolic procedure varpri(u,v,w); % REDUCE 3.4 version
  115. begin scalar x;
  116. %U is expression being printed
  117. %V is the original form that was evaluated.
  118. %W is an id that indicates if U is the first, only or last element
  119. % in the current set (or NIL otherwise).
  120. testing!-width!* := overflowed!* := nil;
  121. if null u then u := 0;
  122. if !*nero and u=0 then return nil;
  123. v := setvars v;
  124. if !*TeX then return if (x := getrtype u) and get(x,'TeXprifn)
  125. then apply3(get(x,'TeXprifn),u,v,w)
  126. else TeXvarpri(u,v,w);
  127. if (x := getrtype u) and flagp(x,'sprifn)
  128. then return if null v then apply1(get(get(x,'tag),'prifn),u)
  129. else maprin list('setq,car v,u);
  130. if w memq '(first only) then terpri!* t;
  131. if !*fort then return fvarpri(u,v,w);
  132. if v then u := 'setq . aconc(v,u);
  133. maprin u;
  134. if null w or w eq 'first then return nil
  135. else if not !*nat then prin2!* "$";
  136. terpri!*(not !*nat);
  137. return nil
  138. end;
  139. symbolic procedure TeXvarpri(u,v,w); % same parameters as above
  140. << if w memq '(FIRST ONLY) then TeXStack!*:=nil; u:=makeprefix u;
  141. if v then for each x in reverse v do u:=list('SETQ,x,u);
  142. TeXStack!* := nconc(TeXStack!*,mktag(u,0,nil));
  143. if (w=T) or (w='ONLY) or (w='LAST) then
  144. << if !*TeXBreak then
  145. << TeXStack!* := insertglue(TeXStack!*);
  146. TeXStack!* := trybreak(TeXStack!*,breaklist(TeXStack!*))
  147. >>;
  148. TeXout(TeXStack!*,!*TeXBreak); TeXStack!*:=nil
  149. >>;
  150. if (null w) or (w eq 'FIRST) then
  151. TeXStack!* := nconc(TeXStack!*,list '!\!q!u!a!d! );
  152. nil
  153. >>;
  154. %ff
  155. % The following procedure interfaces to E. Schruefer's EXCALC package.
  156. % Courtesy: E. Schruefer.
  157. put('form!-with!-free!-indices,'TeXprifn,'TeXindxpri);
  158. symbolic procedure TeXindxpri(u,v,w);
  159. begin scalar metricu,il,dnlist,uplist,r,x,y,z;
  160. if v then go to a;
  161. metricu := metricu!*;
  162. metricu!* := nil;
  163. il := allind !*t2f lt numr simp0 u;
  164. for each j in il do
  165. if atom revalind j
  166. then uplist := j . uplist
  167. else dnlist := cadr j . dnlist;
  168. for each j in intersection(uplist,dnlist) do
  169. il := delete(j,delete(revalind
  170. lowerind j,il));
  171. metricu!* := metricu;
  172. y := flatindxl il;
  173. r := simp!* u;
  174. for each j in mkaindxc y do
  175. <<x := pair(y,j);
  176. z := exc!-mk!*sq2 multsq(subfindices(numr r,x),1 ./ denr r);
  177. if null(!*nero and (z = 0))
  178. then TeXvarpri(z,list subla(x,'ns . il),'only)>>;
  179. return u;
  180. a: v := car v;
  181. y := flatindxl allindk v;
  182. for each j in if flagp(car v,'antisymmetric) and
  183. coposp cdr v then comb(indxl!*,length y)
  184. else mkaindxc y do
  185. <<x := pair(y,j);
  186. z := aeval subla(x,v);
  187. if null(!*nero and (z = 0))
  188. then TeXvarpri(z,list subla(x,v),'only)>>;
  189. return u
  190. end;
  191. %ff
  192. % ----------------------------------------------------------------------
  193. % Section 1: Creating a TeX item list
  194. % ----------------------------------------------------------------------
  195. %
  196. % Linearization is performed by expanding REDUCE prefix expressions into
  197. % a so called "TeX item list". Any TeX item is a readable TeX primitive
  198. % or macro (i.e. a LISP atom), with properties 'CLASS, 'TEXTAG, 'TEXNAME
  199. % and eventually 'TEXPREC, 'TEXPATT and 'TEXUBY bound to them, depending
  200. % on what kind of TeX item it actually is. (See Section 1.3 for further
  201. % information.)
  202. % A REDUCE expression is expanded using the two functions "mktag"
  203. % and "makefunc". Function "mktag" identifies the operator and is able
  204. % to put some brackets around the expression if necessary. "makefunc" is
  205. % a pattern oriented 'unification' function, which matches the arguments
  206. % of a REDUCE expression in order of appearance with so called 'unifica-
  207. % tion tags', as explained below. "mktag" and "makefunc" are highly
  208. % recursive functions.
  209. % The patterns mentioned above are lists (consisting of 'tags') asso-
  210. % ciated with each REDUCE operator. A tag is defined as either an atom
  211. % declared as a TeX item or one of the following 'unification tags':
  212. % (F) ............ insert operator
  213. % (X) ............ insert non-associative argument
  214. % (Y) ............ insert (left/right-) associative argument
  215. % (Z) ............ insert superscript/subscript argument
  216. % (R) ............ use tail recursion to unify remaining arguments
  217. % (associativity depends on previous (X) or (Y) )
  218. % (L) ............ insert a list of arguments (eat up all arguments)
  219. % (M) ............ insert a matrix (and eat up all arguments)
  220. % (APPLY <fun>) ... apply function <fun> to remaining argument list
  221. % ----------------------------------------------------------------------
  222. symbolic procedure makeprefix(ex); % change to standard list notation
  223. (lambda aexpr; % not tricky but time consuming
  224. (if atom aexpr then aexpr
  225. else if car(aexpr)='MAT
  226. then 'MAT . (for each column in cdr aexpr collect
  227. for each row in column collect prepsq!* simp row)
  228. else if car(aexpr)='LIST
  229. then 'LIST . (for each item in cdr aexpr collect makeprefix item)
  230. else if car(aexpr)='EQUAL
  231. then 'EQUAL .(for each item in cdr aexpr collect makeprefix item)
  232. else prepsq!* simp aexpr))
  233. aeval ex;
  234. % ----------------------------------------------------------------------
  235. % Section 1.1: Operator Administration Routines
  236. % ----------------------------------------------------------------------
  237. symbolic procedure makeop(op,prec,patt,uby);
  238. << put(op,'TEXPREC,prec); put(op,'TEXPATT,patt);
  239. put(op,'TEXUBY,if uby then (car uby).(cadr uby) else nil.nil)
  240. >>;
  241. symbolic procedure makeops(l);
  242. for each w in l do makeop(car w,cadr w,caddr w,cadddr w);
  243. %ff
  244. makeops('(
  245. %-----------+----------+---------------------+-------------------------+
  246. % Name |Precedence|Expansion List |Unary/Binary Interchange |
  247. %-----------+----------+---------------------+-------------------------+
  248. (SETQ 1 ((X) (F) !\![ (X) !\!]) NIL)
  249. (EQUAL 50 ((X) (F) !\![ (X) !\!]) NIL)
  250. (WEDGE 150 ((Y) (F) (R)) NIL) % EXCALC
  251. (PLUS 100 ((Y) (F) (R)) (MINUS DIFFERENCE))
  252. (MINUS 600 ((F) (Y)) NIL)
  253. (DIFFERENCE 100 ((Y) (F) (X)) NIL)
  254. (TIMES 200 ((Y) (F) (R)) (RECIP QUOTIENT))
  255. (RECIP 700 ((F) !1 !}!{ (Z) !}) NIL)
  256. (QUOTIENT 200 ((F) (Z) !}!{ (Z) !}) NIL)
  257. (EXPT 850 ((X) !^!{ (Z) !}) NIL)
  258. (SQRT 800 ((F) ! ! ! (Z) !}) NIL)
  259. (MAT 999 ((F) (M !\!c!r! !&) !}) NIL)
  260. (LIST 999 (!\!{ (L !\CO! ) !\!}) NIL)
  261. (DF 999 ((APPLY makeDF)) NIL)
  262. (INT 999 ((F) (Z) !\!, !d (Z)) NIL)
  263. (SUM 999 ((F) !_!{ (Z) != (Z)
  264. !}!^!{ (Z) !} (X)) NIL)
  265. (PROD 999 ((F) !_!{ (Z) != (Z)
  266. !}!^!{ (Z) !} (X)) NIL)
  267. (PARTDF 999 ((F) (APPLY makePARTDF)) NIL) % EXCALC
  268. (D 999 (!\!d! (X)) NIL) % EXCALC
  269. (INNERPROD 999 (!\!< (L !,) !\!>) NIL) % EXCALC
  270. (!:ps!: 999 ((APPLY make!:ps!:)) nil) % TPS
  271. (rest!_order 999 (!{ !\!r!m! !O !} (X)) nil) % TPS
  272. %-----------+----------+----------------------+------------------------+
  273. ));
  274. % ----------------------------------------------------------------------
  275. % Section 1.2 : Prefix to Infix Conversion
  276. % ----------------------------------------------------------------------
  277. symbolic procedure mktag(tag,prec,assf);
  278. % analyze an operator and decide what to do
  279. % parameters: tag ....... the term itself
  280. % prec ...... outer precedence
  281. % assf ...... outer associativity flag
  282. if null tag then nil else
  283. if atom tag then texexplode(tag) else
  284. begin
  285. scalar tagprec,term;
  286. tagprec:=get(car tag,'TEXPREC) or 999; % get the operator's precedence
  287. term:=makefunc(car tag,cdr tag,tagprec); % expand expression and if it
  288. % is necessary, put a left and a right bracket around the expression.
  289. if (assf and (prec = tagprec)) or (tagprec < prec)
  290. then term:=nconc('!\!( . term , '!\!) . nil);
  291. return(term)
  292. end;
  293. symbolic procedure makearg(l,s);
  294. % collect arguments from a list <l> and put seperators <s> between them
  295. if null l then nil
  296. else if null cdr l then mktag(car l,0,nil)
  297. else nconc(mktag(car l,0,nil), s . makearg(cdr l,s));
  298. symbolic procedure makemat(m,v,h);
  299. % make a matrix <m> and use <h> as a horizontal seperator and <v> as
  300. % a vertical terminator.
  301. if null m then nil else nconc(makearg(car m,h), v . makemat(cdr m,v,h));
  302. %ff
  303. smacro procedure istag(v,w); car v=w;
  304. smacro procedure unary(uby); car uby;
  305. smacro procedure binary(uby); cdr uby;
  306. smacro procedure lcopy(a); for each x in a collect x;
  307. symbolic procedure makefunc(op,arg,prec);
  308. begin
  309. scalar term,tag,a,pattern,uby;
  310. term:=nil;
  311. pattern:=get(op,'TEXPATT)
  312. or ( if flagp(op,'INDEXVAR) then '((APPLY makeExcInx))
  313. else '( (F) !\!( (L !,) !\!) ));
  314. uby:=get(op,'TEXUBY);
  315. while pattern do
  316. << tag:=car pattern; pattern:=cdr pattern;
  317. if (atom tag) then a:=tag.nil
  318. else if (not atom car tag) then a:=nil
  319. else if istag(tag,'F) then
  320. % test for unary to binary operator interchange
  321. if (not atom car arg)and uby and (caar arg=unary(uby))
  322. then << a:=texexplode(binary(uby)); arg:=cadar arg.cdr arg >>
  323. else a:=texexplode(op)
  324. else if istag(tag,'APPLY)
  325. then << a:=apply3(cadr tag,op,arg,prec); arg:=nil >>
  326. else if null arg then a:=nil
  327. else if istag(tag,'X)
  328. then << a:=mktag(car arg,prec,nil); arg:=cdr arg >>
  329. else if istag(tag,'Y)
  330. then << a:=mktag(car arg,prec,T); arg:=cdr arg >>
  331. else if istag(tag,'Z)
  332. then << a:=mktag(car arg,0,nil); arg:=cdr arg >>
  333. else if istag(tag,'R) then
  334. if cdr arg % more than one argument ?
  335. then << pattern:=get(op,'TEXPATT); a:=nil >>
  336. else << a:=mktag(car arg,prec,nil); arg:=cdr arg >>
  337. else if istag(tag,'L)
  338. then << a:=makearg(arg,cadr tag); arg:=nil >>
  339. else if istag(tag,'M)
  340. then << a:=makemat(arg,cadr tag,caddr tag); arg:=nil >>
  341. else a:=nil;
  342. if a then term:=nconc(term,a)
  343. >>;
  344. return(term)
  345. end;
  346. %ff
  347. symbolic procedure makeDF(op,arg,prec); % DF operators are tricky
  348. begin
  349. scalar dfx,f,vvv; integer degree;
  350. dfx:=lcopy(f:=texexplode op); degree:=0;
  351. nconc(dfx,mktag(car arg,prec,nil)); dfx:=nconc(dfx,list '!}!{);
  352. for each item in cdr arg do
  353. if numberp(item) then
  354. << dfx:= nconc(dfx,'!^!{ .texexplode(item));
  355. dfx:= nconc(dfx,list '!});
  356. degree:=degree+item-1;
  357. >>
  358. else
  359. << dfx:= nconc(dfx,append(f,mktag(item,prec,nil))); degree:=degree+1
  360. >>;
  361. if degree>1 then
  362. << vvv:=nconc(texexplode(degree), '!} . cdr dfx);
  363. rplacd(dfx,'!^!{ . vvv)
  364. >>;
  365. return ('!\!f!r!a!c!{ . nconc(dfx, list '!}))
  366. end;
  367. symbolic procedure makePARTDF(op,arg,prec); % EXCALC extension
  368. if cdr arg then
  369. ('!_!{ . nconc(makearg(cdr arg,'!,), '!} . mktag(car arg,prec,nil)))
  370. else ('!_!{ . nconc(mktag(car arg,prec,nil), list '!}));
  371. smacro procedure InxExtend(item,ld,rd);
  372. nconc(result,ld.nconc(texexplode(item),list rd));
  373. symbolic procedure makeExcInx(op,arg,prec); % EXCALC extension
  374. begin scalar result;
  375. result:=nconc('!{.nil,texexplode(op));
  376. for each item in arg do
  377. if numberp item then
  378. if minusp item then InxExtend(-item,'!{!}!_!{,'!})
  379. else InxExtend(item ,'!{!}!^!{,'!}) else
  380. if atom item then InxExtend(item ,'!{!}!^!{,'!}) else
  381. if car item='MINUS then InxExtend(cadr item ,'!{!}!_!{,'!})
  382. else InxExtend('! ,'!{!}!_!{,'!});
  383. return nconc(result,'!}.nil)
  384. end;
  385. % The following is part of the interface to TPS.
  386. % Andreas Strotmann, 19 Mar 93
  387. % ps:numberp smacro required for compilation; copied over from tps.red
  388. symbolic smacro procedure ps!:numberp u;
  389. numberp u or (car u neq '!:ps!: and get(car u,'dname));
  390. % fluid declaration to avoid compiler warnings
  391. fluid '(ps!:exp!-lim);
  392. % symbolic procedure ps!:prin!: p;
  393. symbolic procedure make!:ps!:(op, arg, prec); % TPS interface,
  394. % (lambda (first,u,delta,symbolic!-exp!-pt,about,atinf);
  395. (lambda (first,u,delta,symbolic!-exp!-pt,about,atinf,texps,p);
  396. << % if !*nat and posn!*<20 then orig!*:=posn!*;
  397. atinf:=(about='ps!:inf);
  398. ps!:find!-order p;
  399. delta:=prepf((ps!:depvar p) .** 1 .*1 .+
  400. (negf if atinf then nil
  401. % expansion about infinity
  402. else if idp about then !*k2f about
  403. else if ps!:numberp about then !*n2f about
  404. else if (u:=!*pre2dp about) then !*n2f u
  405. else !*k2f(symbolic!-exp!-pt:= compress
  406. append(explode ps!:depvar p, explode '0))));
  407. % if symbolic!-exp!-pt then prin2!* "[";
  408. % prin2!* "{";
  409. texps := nconc(texps, list '!\!{ );
  410. %
  411. for i:=(ps!:order p): ps!:exp!-lim do
  412. << u:=ps!:term(p,i);
  413. if not null numr u then
  414. <<if minusf numr u then <<u:=negsq u; % prin2!* " - ">>
  415. texps := nconc(texps, list '!-)
  416. >>
  417. else if not first then % prin2!* " + ";
  418. texps := nconc(texps, list '!+);
  419. first := nil;
  420. % if posn!*>55 then <<terpri!* nil;prin2!* " ">>;
  421. if denr u neq 1 then % prin2!* "(";
  422. texps := nconc(texps, list '!\!( );
  423. if u neq '(1 . 1) then
  424. % maprint(prepsq u,get('times,'infix))
  425. texps := nconc(texps,
  426. mktag(prepsq u,
  427. get('times, 'texprec),
  428. nil))
  429. else if i=0 then % prin2!* 1;
  430. texps := nconc(texps, list '!1);
  431. if denr u neq 1 then % prin2!* ")";
  432. texps := nconc(texps, list '!\!) );
  433. if i neq 0 and u neq '(1 . 1) then % prin2!* "*";
  434. texps := nconc(texps, list get('times,'texname) );
  435. if i neq 0 then
  436. % xprinf(!*p2f mksp(delta,
  437. % if atinf then -i else i),nil,nil)
  438. texps := (lambda i;
  439. nconc(texps,
  440. mktag (if (i = 1) then delta
  441. else list('expt, delta, i),
  442. get('times, 'texprec),
  443. nil)))
  444. (if atinf then -i else i);
  445. >>
  446. >>;
  447. if first then % prin2!* "0";
  448. texps := nconc(texps, list '!0 );
  449. % if posn!*>55 then terpri!* nil;
  450. u:=ps!:exp!-lim +1;
  451. texps := (lambda u;
  452. nconc(texps,
  453. '!+ . mktag(list('rest!_order,
  454. if (u = 1) then delta
  455. else list('expt, delta, u)),
  456. get('plus, 'texprec),
  457. nil)))
  458. (if atinf then -u else u);
  459. %if (u=1) and not atinf and (about neq 0) then
  460. % prin2!* " + O"
  461. %else prin2!* " + O(";
  462. %xprinf(!*p2f mksp(delta,if atinf then -u else u),nil,nil);
  463. %if (u=1) and not atinf and (about neq 0) then
  464. % prin2!* "}"
  465. % else prin2!* ")}";
  466. texps := nconc(texps, list '!\!} );
  467. if symbolic!-exp!-pt then
  468. << %if posn!*>45 then terpri!* nil;
  469. %prin2!* " where ";
  470. texps := nconc(texps, list '!_!{ );
  471. %prin2!* symbolic!-exp!-pt;
  472. texps := nconc(texps, texexplode symbolic!-exp!-pt);
  473. %prin2!* " = ";
  474. texps := nconc(texps, list '!= );
  475. %maprin about;
  476. texps := nconc(texps, mktag(makeprefix about,
  477. get('equal, 'texprec), nil));
  478. texps := nconc(texps, list '!} );
  479. %prin2!* "]"
  480. >>;
  481. texps
  482. >>)
  483. % (t,nil,nil,nil,ps!:expansion!-point p,nil);
  484. (t,nil,nil,nil,ps!:expansion!-point(op . arg),nil,nil,op . arg);
  485. %ff
  486. % ----------------------------------------------------------------------
  487. % Section 1.3 : Making a TeX Item
  488. % ----------------------------------------------------------------------
  489. % Properties of TeX items:
  490. % 'CLASS ..... one of the following class specifiers
  491. % 'ORD .... ordinary symbols
  492. % 'LOP .... large operators
  493. % 'BIN .... binary operators
  494. % 'REL .... relational operators
  495. % 'OPN .... opening symbols (left parenthesis)
  496. % 'CLO .... closing symbols (right parenthesis)
  497. % 'PCT .... punctuation symbols
  498. % 'INN .... inner TeX group delimiters
  499. % 'TEXTAG ..... one of the following lists or atoms
  500. % <kind> .. an atom describing an 'INN class group delimiter
  501. % (<w1> <w2> <w3>) ... where is
  502. % <w1> ..... width for text style (cmmi10)
  503. % <w2> ..... width for scriptstyle (cmmi8)
  504. % <w3> ..... width for scriptscriptstyle (cmmi5)
  505. % The parital lists of the list which is passed to makeitems have the
  506. % following general structure:
  507. % (<TeX-item> <class> <TeX-tag> <v1> <v2> ... )
  508. % where is
  509. % <TeX-item> .... the atom which actually is the TeX code
  510. % <class> ....... the 'CLASS property as explained above
  511. % <TeX-tag> ..... the 'TEXTAG property as explained above
  512. % <v1> etc. ..... atoms which will be bound to specific TeX items
  513. % by its property 'TEXNAME
  514. % ----------------------------------------------------------------------
  515. smacro procedure triassert(name,item); put(name,'TEXNAME,item);
  516. smacro procedure assertl(l); for each v in l do triassert(car v,cadr v);
  517. smacro procedure retract(name); put(name,'TEXNAME,nil);
  518. smacro procedure retractl(l); for each v in l do retract(car v);
  519. smacro procedure gettexitem(a); get(a,'TEXNAME) or (get(a,'CLASS)and a);
  520. put ('TeXitem,'STAT,'RLIS); % handle argument passing for func. TeXitem
  521. symbolic procedure TeXitem(arglist);
  522. begin scalar x,OK,item,class,tag;
  523. if not length(arglist)=3
  524. then rederr "Usage: TeXitem(item,class,width-list);";
  525. item:=car arglist; class:= cadr arglist; tag:= caddr arglist;
  526. OK:=memq(class,'(ORD BIN REL PCT OPN CLO LOP));
  527. if not OK then << prin2 "% illegal item class "; print class >>;
  528. if atom tag then OK:=nil else
  529. << if car(tag)='LIST then tag:=cdr tag; % accept algebraic lists
  530. for each x in tag do if not numberp x then OK:=nil
  531. >>;
  532. if not OK then << prin2 "% illegal width tag "; print tag >>;
  533. if OK then
  534. << item:=intern(item); put(item,'CLASS,class); put(item,'TEXTAG,tag)
  535. >>;
  536. prin2 "% Item "; prin2 item;
  537. if not OK then prin2 "not "; prin2 " added"; terpri();
  538. return nil
  539. end;
  540. %ff
  541. symbolic procedure makeitems(l);
  542. for each w in l do
  543. begin scalar iw;
  544. iw:=intern(car w);
  545. put(iw,'CLASS,cadr w); put(iw,'TEXTAG,caddr w);
  546. for each v in cdddr w do triassert(v,iw);
  547. end;
  548. fluid '(TeXunknowncounter!*);
  549. TeXunknowncounter!*:= 0;
  550. symbolic procedure UnknownItem(a);
  551. << TeXunknowncounter!* := TeXunknowncounter!* +1;
  552. prin2 "% non-fatal error: unknown atom "; prin2 a;
  553. prin2 " replaced by ?_{"; prin2 TeXunknowncounter!*;
  554. prin2 "}"; terpri();
  555. '!? . '!_!{ . nconc(explode TeXunknowncounter!*, list '!})
  556. >>;
  557. symbolic procedure texexplode(a);
  558. begin scalar b;
  559. b:=if a and (atom a) then
  560. (gettexitem(a)
  561. or if numberp(a) then texcollect(explode(a))
  562. else if stringp(a) then strcollect(explode2(a))
  563. else texexplist(texcollect(explode2(a))));
  564. b:=if null b then list '! else if not atom b then b else list b;
  565. return b
  566. end;
  567. symbolic procedure texcollect(l);
  568. for each el in l join
  569. if null gettexitem(el) then UnknownItem(el)
  570. else gettexitem(el).nil;
  571. smacro procedure strtexitem(e);
  572. if e='! then '!\!
  573. else if liter(e) then e
  574. else gettexitem(e) or UnknownItem(e) or '! ;
  575. symbolic procedure strcollect(l);
  576. for each el in l collect strtexitem el;
  577. symbolic procedure texexplist(r);
  578. begin scalar r,v;
  579. v:=nil;
  580. for each rl on r do
  581. if digit(car rl) and not v then v:=rl
  582. else if v and not digit(car rl) then v:=nil;
  583. if v then
  584. << rplacd(v,car v.cdr v); rplaca(v,'!_!{); nconc(r,'!}.nil) >>;
  585. return r
  586. end;
  587. %ff
  588. makeitems('(
  589. (! INN DMY) % no nonsense dummy item
  590. (!{ INN BEG) % begin of a TeX inner group
  591. (!^!{ INN SUP) % superscript
  592. (!_!{ INN SUB) % subscript nolimits
  593. (!{!}!^!{ INN SUP) % spread superscript
  594. (!{!}!_!{ INN SUB) % spread subscript
  595. (!}!{ INN SEP) % general group seperator
  596. (!}!^!{ INN ESP) % end of group and superscript
  597. (!}!_!{ INN ESB) % end of group and subscript
  598. (!} INN END) % end of TeX inner group
  599. (!\!f!r!a!c!{ INN FRC RECIP QUOTIENT) % fraction group
  600. (!\!s!q!r!t!{ INN FRC SQRT) % square root
  601. (!\!p!m!a!t!r!i!x!{ INN MAT MAT) % matrix group
  602. (!& INN TAB) % horizontal tabulation
  603. (!\!c!r! INN CR ) % vertical tabulation
  604. (!\!n!l! INN CR ) % vertical tabulation (special)
  605. (!\!( OPN (327680 276707 241208)) % test value
  606. (!\!) CLO (327680 276707 241208)) % ...
  607. (!\!{ OPN (327680 276707 241208)) % ...
  608. (!\!} CLO (327680 276707 241208)) % ...
  609. (!\![ OPN (0))
  610. (!\!] CLO (0))
  611. (!\!< OPN (254863 212082 195700))
  612. (!\!> CLO (254863 212082 195700))
  613. (!\!, ORD (80960))
  614. (!\!q!u!a!d! REL (655360))
  615. (! ORD (0)) % dummy item
  616. (!\!r!m! ORD (0)) % dummy def of font change
  617. (!\!i!t! ORD (0)) % dummy def of font change
  618. (!\!b!f! ORD (0)) % dummy def of font change
  619. (!\!h!b!o!x! ORD (0)) % dummy def of box opening
  620. (!! ORD (182045 148367 131984))
  621. (!? ORD (309476 247127 211630))
  622. (!\!l!b!r!a!c!e! ORD (327681 268516 241211) !{)
  623. (!\!r!b!r!a!c!e! ORD (327681 268516 241211) !})
  624. (!\!l!b!r!a!c!k! ORD (182045 148367 131984) ![)
  625. (!\!r!b!r!a!c!k! ORD (182045 148367 131984) !])
  626. (!\!b!a!c!k!s!l!a!s!h! ORD (327681 268516 241211) !\)
  627. (!\!% ORD (546135 430537 359544) !%)
  628. (!\!# ORD (546135 430537 359544) !#)
  629. (!\!& ORD (509726 402320 336788) !&)
  630. (!@ ORD (509726 402320 336788))
  631. (!\!_ ORD (235930) !_)
  632. (!\!$ ORD (327681 261235 223008) !$)
  633. (!; ORD (182045 148367 131984))
  634. (!: ORD (182045 148367 131984))
  635. (!. ORD (182045 148367 131984))
  636. (!, ORD (182045 148367 131984))
  637. (!| ORD (182045 148367 131984))
  638. (!' ORD (183865 177267))
  639. (!` ORD (182045 148367 131984))
  640. (!\! ORD (218453))
  641. %ff
  642. % Fonts ammi10, ammi7, ammi5; ordered by index number
  643. (!\!G!a!m!m!a! ORD (394126 317121 266467))
  644. (!\!D!e!l!t!a! ORD (546133 451470 377742))
  645. (!\!T!h!e!t!a! ORD (481689 395400 331866))
  646. (!\!L!a!m!b!d!a! ORD (418702 346612 293546))
  647. (!\!X!i! ORD (447374 366819 309020))
  648. (!\!P!i! ORD (553870 446190 368185))
  649. (!\!S!i!g!m!a! ORD (511090 417791 348842))
  650. (!\!U!p!s!i!l!o!n! ORD (382293 320398 275342))
  651. (!\!P!h!i! ORD (436906 364088 309475))
  652. (!\!P!s!i! ORD (419430 354622 304150))
  653. (!\!O!m!e!g!a ORD (461596 382217 322806))
  654. (!\!a!l!p!h!a! ORD (419233 350253 299280))
  655. (!\!b!e!t!a! ORD (370688 303376 259231))
  656. (!\!g!a!m!m!a! ORD (353318 296277 256227))
  657. (!\!d!e!l!t!a! ORD (273066 229467 203070))
  658. (!\!e!p!s!i!l!o!n! ORD (266012 222822 197791))
  659. (!\!z!e!t!a! ORD (223686 195060 178221))
  660. (!\!e!t!a! ORD (352407 300373 261688))
  661. (!\!t!h!e!t!a! ORD (298553 247580 216177))
  662. (!\!i!o!t!a! ORD (231955 198883 180224))
  663. (!\!k!a!p!p!a! ORD (377590 315392 271246))
  664. (!\!l!a!m!b!d!a! ORD (382293 320398 275342))
  665. (!\!m!u! ORD (394885 326314 278528))
  666. (!\!n!u! ORD (341940 283534 244849))
  667. (!\!x!i! ORD (327680 276707 241208))
  668. (!\!p!i! ORD (370293 312456 270222))
  669. (!\!r!h!o! ORD (329728 269699 232379))
  670. (!\!s!i!g!m!a! ORD (361737 300646 258776))
  671. (!\!t!a!u! ORD (250083 220910 200430))
  672. (!\!u!p!s!i!l!o!n! ORD (354076 299008 259413))
  673. (!\!p!h!i! ORD (390485 322764 275888))
  674. (!\!c!h!i! ORD (410055 334506 283534))
  675. (!\!p!s!i! ORD (426894 357262 304924))
  676. (!\!o!m!e!g!a! ORD (407931 339968 290360))
  677. (!\!v!a!r!e!p!s!i!l!o!n! ORD (312433 358776 225097))
  678. (!\!v!a!r!t!h!e!t!a! ORD (388513 326997 281713))
  679. (!\!v!a!r!p!i! ORD (504945 424800 359719))
  680. (!\!v!a!r!r!h!o! ORD (329728 369699 232379))
  681. (!\!v!a!r!s!i!g!m!a! ORD (312433 258776 225097))
  682. (!\!v!a!r!p!h!i! ORD (465123 383749 323675))
  683. % omitted: codes 40-47
  684. (!0 ORD (327680 276707 241208))
  685. (!1 ORD (327680 276707 241208))
  686. (!2 ORD (327680 276707 241208))
  687. (!3 ORD (327680 276707 241208))
  688. (!4 ORD (327680 276707 241208))
  689. (!5 ORD (327680 276707 241208))
  690. (!6 ORD (327680 276707 241208))
  691. (!7 ORD (327680 276707 241208))
  692. (!8 ORD (327680 276707 241208))
  693. (!9 ORD (327680 276707 241208))
  694. (!. PCT (182044 160198 150186) CONS)
  695. (!, REL (182044 160198 150186))
  696. (!\CO! REL (182044 160198 150186))
  697. %ff
  698. % omitted: code 60
  699. (!/ BIN (327680 262143 204800))
  700. % omitted : codes 62,63
  701. (!\!p!a!r!t!i!a!l! ORD (384341 314982 268105) PARTDF DF)
  702. (!A ORD (491520 404866 339057))
  703. (!B ORD (497095 406550 339569))
  704. (!C ORD (542583 439273 363451))
  705. (!D ORD (542583 439273 363451))
  706. (!E ORD (468400 387026 326360))
  707. (!F ORD (412330 331684 277845))
  708. (!G ORD (515276 418884 348660))
  709. (!H ORD (544768 439409 363520))
  710. (!I ORD (288085 236475 204913))
  711. (!J ORD (371825 302512 257706))
  712. (!K ORD (556373 450104 371598))
  713. (!L ORD (446008 369914 312888))
  714. (!M ORD (635790 512227 420408))
  715. (!N ORD (526563 424846 352142))
  716. (!O ORD (499893 409964 343244))
  717. (!P ORD (420750 341242 286606))
  718. (!Q ORD (518098 424527 354622))
  719. (!R ORD (482417 399041 335644))
  720. (!S ORD (392760 323128 274887))
  721. (!T ORD (382976 318122 272270))
  722. (!U ORD (447465 366409 309179))
  723. (!V ORD (375011 304014 260266))
  724. (!W ORD (577991 469310 389973))
  725. (!X ORD (533845 433811 359651))
  726. (!Y ORD (388210 317485 270506))
  727. (!Z ORD (429170 352256 397642))
  728. % omitted: codes 91-96
  729. (!a ORD (346415 291999 253770))
  730. (!b ORD (281258 235383 207621))
  731. (!c ORD (283610 240571 212810))
  732. (!d ORD (341105 277890 242392))
  733. (!e ORD (283610 240571 212810))
  734. (!f ORD (320853 260778 224369))
  735. (!g ORD (300980 247580 215995))
  736. (!h ORD (377590 315392 271246))
  737. (!i ORD (231500 191601 174762))
  738. (!j ORD (238933 198883 177493))
  739. (!k ORD (341181 296265 248490))
  740. (!l ORD (195546 169756 157468))
  741. (!m ORD (575411 479687 402318))
  742. (!n ORD (393367 334051 288540))
  743. (!o ORD (317667 264510 230377))
  744. (!p ORD (329728 277435 242392))
  745. (!q ORD (292560 245577 215995))
  746. (!r ORD (277466 235292 208668))
  747. (!s ORD (307200 253041 219818))
  748. (!t ORD (234837 204799 186595))
  749. (!u ORD (375163 319487 277162))
  750. (!v ORD (317667 269881 236657))
  751. (!w ORD (463303 386389 327680))
  752. (!x ORD (361813 296732 253951))
  753. (!y ORD (321308 273066 239388))
  754. (!z ORD (304772 257137 225735))
  755. % omitted: codes 123-127
  756. %ff
  757. % Fonts amsy10, amsy7, amsy5; not ordered.
  758. (!+ BIN (509724 422343 354986) PLUS)
  759. (!- BIN (509724 422343 354986) DIFFERENCE MINUS)
  760. (!* ORD (509724 422343 354986))
  761. (!" ORD (509724 422343 354986))
  762. (!\!c!d!o!t! BIN (182044 160198 150186) TIMES)
  763. (!= REL (509724 422343 354986) EQ EQUAL)
  764. (!:!= REL (691771 550687 468772) SETQ)
  765. (!\!s!u!m! LOP (1000000 700000 500000) SUM)
  766. (!\!p!r!o!d! LOP (1000000 700000 500000) PROD)
  767. (!\!i!n!t! LOP (1000000 700000 500000) INT)
  768. (!\!l!i!m! ORD (910221 771866 678114) LIM)
  769. (!\!s!i!n! ORD (804635 687398 612123) SIN)
  770. (!\!c!o!s! ORD (877454 745653 657634) COS)
  771. (!\!t!a!n! ORD (946630 800994 700869) TAN)
  772. (!\!l!n! ORD (700000 600000 500000) LOG)
  773. (!\!e!x!p! ORD (1001243 844685 735003) EXP)
  774. (!\!a!r!c!t!a!n! ORD (1824539 1543734 1356227) ATAN)
  775. (!\!w!e!d!g!e! ORD (436908 353167 309480) WEDGE)
  776. (!\!d! ORD (364090))
  777. ));
  778. %ff
  779. % ----------------------------------------------------------------------
  780. % You can choose to have some default TEXNAME properties for your
  781. % variables. Function "trimakeset" defines a set of such default names.
  782. % If you want to activate the set, call "TeXassertset(<setname>)" , or
  783. % if you want to deactivate the set, call "TeXretractset(<setname>)" .
  784. % The current <setname>s available are:
  785. % * GREEK : lowercase greek letters
  786. % * LOWERCASE: roman lowercase letters
  787. % ----------------------------------------------------------------------
  788. % handle argument passing
  789. deflist( '((TeXassertset RLIS) (TeXretractset RLIS)), 'STAT);
  790. symbolic procedure TeXassertset(arglist);
  791. if not length(arglist)=1 then rederr "Usage: TeXassertset(setname);"
  792. else begin scalar sym; sym:= car arglist;
  793. if get('TEXSYM,sym) then
  794. << assertl(get('TEXSYM,sym)); prin2 "% set ";
  795. prin2 sym; prin2 " asserted"; terpri()
  796. >> else << prin2 "% no such set"; terpri() >>
  797. end;
  798. symbolic procedure TeXretractset(arglist);
  799. if not length(arglist)=1 then rederr "Usage: TeXretractset(setname);"
  800. else begin scalar sym; sym := car arglist;
  801. if get('TEXSYM,sym) then
  802. << retractl(get('TEXSYM,sym)); prin2 "% set ";
  803. prin2 sym; prin2 " retracted"; terpri()
  804. >> else << prin2 "% no such set"; terpri() >>
  805. end;
  806. symbolic procedure trimakeset(sym,a!_set);
  807. <<put('TEXSYM,sym,a!_set); nil>>;
  808. trimakeset('GREEK,'(
  809. (ALPHA !\!a!l!p!h!a! )
  810. (BETA !\!b!e!t!a! )
  811. (GAMMA !\!g!a!m!m!a! )
  812. (DELTA !\!d!e!l!t!a! )
  813. (EPSILON !\!e!p!s!i!l!o!n! )
  814. (ZETA !\!z!e!t!a! )
  815. (ETA !\!e!t!a! )
  816. (THETA !\!t!h!e!t!a! )
  817. (IOTA !\!i!o!t!a! )
  818. (KAPPA !\!k!a!p!p!a! )
  819. (LAMBDA !\!l!a!m!b!d!a! )
  820. (MU !\!m!u! )
  821. (NU !\!n!u! )
  822. (XI !\!x!i! )
  823. (PI !\!p!i! )
  824. (RHO !\!r!h!o! )
  825. (SIGMA !\!s!i!g!m!a! )
  826. (TAU !\!t!a!u! )
  827. (UPSILON !\!u!p!s!i!l!o!n! )
  828. (PHI !\!p!h!i! )
  829. (CHI !\!c!h!i! )
  830. (PSI !\!p!s!i! )
  831. (OMEGA !\!o!m!e!g!a! ) ));
  832. trimakeset('LOWERCASE,'(
  833. (A !a) (B !b) (C !c) (D !d) (E !e) (F !f) (G !g) (H !h) (I !i) (J !j)
  834. (K !k) (L !l) (M !m) (N !n) (O !o) (P !p) (Q !q) (R !r) (S !s) (T !t)
  835. (U !u) (V !v) (W !w) (X !x) (Y !y) (Z !z) ));
  836. %ff
  837. % ----------------------------------------------------------------------
  838. % Section 2: Inserting Glue into a TeX-Item-List
  839. % ----------------------------------------------------------------------
  840. %
  841. % Glue Items to be inserted between consecutive TeX-Items (similar to
  842. % what TeX does with its items, but this table is slightly modified.)
  843. %
  844. % Class|ORD|LOP|BIN|REL|OPN|CLO|PCT|INN|
  845. % -----+---+---+---+---+---+---+---+---+
  846. % ORD | 0 | 1 |(2)|(3)| 0 | 0 | 0 | 0 |
  847. % LOP | 1 | 1 | * |(3)| 0 | 0 | 0 |(1)|
  848. % BIN |(2)|(2)| * | * |(2)| * | * |(2)|
  849. % REL |(3)|(3)| * | 0 |(3)| 0 | 0 |(3)| columns: right items
  850. % OPN | 0 | 0 | * | 0 | 0 | 0 | 0 | 0 | lines: left items
  851. % CLO | 0 | 1 |(2)|(3)| 0 | 0 | 0 | 0 |
  852. % PCT |(1)|(1)| * |(1)|(1)|(1)|(1)|(1)|
  853. % INN | 0 | 1 |(2)|(3)|(1)| 0 |(1)| 0 |
  854. % -----+---+---+---+---+---+---+---+---+
  855. %
  856. % The glue items and its meanings:
  857. % 0 ......... no space
  858. % 1 (1) ..... thin space (no space if sub-/superscript)
  859. % 2 (2) ..... medium space (no space if sub-/superscript)
  860. % 3 (3) ..... thick space (no space if sub-/superscript)
  861. % * ......... this case never arises (really?)
  862. % ----------------------------------------------------------------------
  863. symbolic procedure makeglue(mx);
  864. if null mx then nil else
  865. begin
  866. scalar id1,id2,row,col;
  867. row:=cdr mx; id1:=car mx;
  868. while(row) do
  869. << id2:=car mx; col:=car row;
  870. while (col) do
  871. << put(car id1,car id2,car col);
  872. col:=cdr col; id2:=cdr id2
  873. >>;
  874. row:=cdr row; id1:=cdr id1
  875. >>
  876. end;
  877. makeglue('(
  878. (ORD LOP BIN REL OPN CLO PCT INN)
  879. ( 0 1 -2 -3 0 0 0 0 )
  880. ( 1 1 0 -3 0 0 0 -1 )
  881. (-2 -2 0 0 -2 0 0 -2 )
  882. (-3 -3 0 0 -3 0 0 -3 )
  883. ( 0 0 0 0 0 0 0 0 )
  884. ( 0 1 -2 -3 0 0 0 0 )
  885. (-1 -1 0 -1 -1 -1 -1 -1 )
  886. ( 0 1 -2 -3 -1 0 -1 0 )
  887. ));
  888. smacro procedure kindof(item); get(item,'TEXTAG);
  889. smacro procedure classof(item); get(item,'CLASS);
  890. %ff
  891. smacro procedure groupbeg(kind); % beginning of a group
  892. memq(kind,'(BEG SUP SUB FRC MAT));
  893. smacro procedure groupend(kind); (kind='END);
  894. smacro procedure grouphs(kind); (kind='TAB);
  895. smacro procedure groupvs(kind); % vertical group seperator
  896. memq(kind,'(ESP ESB SEP CR));
  897. symbolic procedure interglue(left,right,depth,nesting);
  898. % compute the glue to be inserted between two TeX items
  899. % parameters: left,right .......... left/right TeX item
  900. % depth ............... superscript/subscript level
  901. % nesting ............. depth of parenthesis level
  902. % a glue item is a list consisting of two numbers, i.e.
  903. % (<width> <penalty>)
  904. % where <width> is the width of the glue in scaled points and <penalty>
  905. % is a negative numeric value indicating 'merits' for a breakpoint.
  906. if (null left)or(null right)or(not atom left)or(not atom right) then nil
  907. else begin
  908. scalar glue,lc,rc; % glue code and item classes
  909. lc:=classof(left); rc:=classof(right); glue:=get(lc,rc);
  910. if null(glue) then return nil;
  911. if (left='!\CO! ) then return(list(0,-10000));
  912. if glue<0 then if depth>0 then return nil else glue:=(-glue);
  913. if glue=1 then return(list(80960,nesting*10 +20))
  914. else if glue=2 then
  915. << if (left='!+ or left='!-) then return nil;
  916. if (right='!+) then return(list(163840,nesting*30-390));
  917. if (right='!- and (lc='ORD or lc='CLO))
  918. then return(list(163840,nesting*30-210));
  919. if (left='!\!c!d!o!t! ) then return(list(163840,nesting*10+50));
  920. if (right='!\!c!d!o!t! ) then return nil;
  921. return(list(163840,nesting*10))
  922. >>
  923. else if glue=3 then return(list(655360,nesting*10-50)) else return nil
  924. end;
  925. symbolic procedure insertglue(term);
  926. % insert glue into a TeX-Item-List
  927. begin
  928. scalar glueitem,succ,pred,prev,backup; integer depth,nesting;
  929. depth:=nesting:=0; succ:=nil; backup:=term;
  930. while term do
  931. << pred:=succ; succ:=car term;
  932. glueitem:=interglue(pred,succ,depth,nesting);
  933. if glueitem then rplacd(prev,glueitem.term);
  934. prev:=term; term:=cdr term;
  935. if classof(succ)='INN then
  936. << if (groupbeg kindof succ) and
  937. (not ((kindof(succ)='FRC) and (depth=0)))
  938. then depth:=depth+1
  939. else if (groupend kindof succ) and (depth>0) then depth:=depth-1
  940. >>
  941. else if classof(succ)='OPN then nesting:=nesting+1
  942. else if classof(succ)='CLO then nesting:=nesting-1
  943. >>;
  944. return(backup)
  945. end;
  946. %ff
  947. % ----------------------------------------------------------------------
  948. % Section 3 : Line Breaking
  949. % ----------------------------------------------------------------------
  950. %
  951. % How to break up a TeX item list into several independent lines
  952. % ----------------------------------------------------------------------
  953. % Setting break points requires "breaklists". A breaklist is a sequence
  954. % of passive and active nodes, where each active node is followed by an
  955. % pasive node and vice versa. Active nodes represent glue items. Passive
  956. % nodes are integer atoms which represent the width of a sequence of or-
  957. % dinary TeX items. This sequence must not be interspersed with glue
  958. % items. Every breaklist consists of at least one passive node surroun-
  959. % ded by delta nodes representing the beginning and ending of the list.
  960. % <breaklist> ::= ( <delta-node> <passive-node> <active-node> ...
  961. % <passive-node> <active_node> ...
  962. % <passive-node> <delta-node>)
  963. % <active-node> ::= ( <width> <penalty> <offset> )
  964. % <passive-node> ::= <width>
  965. % <delta-node> ::= ( <width> <penalty> <offset>
  966. % <id-num> <ptr> <demerits> <indentation> )
  967. % The breaklist will be created using the function "breaklist". Setting
  968. % the break points (i.e. break items) in the breaklist is done using the
  969. % functions "trybreak". During this phase, some active nodes are con-
  970. % sidered to be "feasible" break points. Thus, they will be extended and
  971. % named "delta nodes" furtheron. By default the first and last node in a
  972. % breaklist are delta nodes. When trybreak has finished, the <ptr>'s of
  973. % the delta nodes recursively pointed to from the last delta node's
  974. % <ptr> represent the best path for breaking the whole breaklist.
  975. % It is:
  976. % <width> : width of item (including glue items)
  977. % <penalty> : a numeric value which prohibits line breaking (if
  978. % negative, line breaking will be merited)
  979. % <offset> : distance to most previous opening bracket
  980. % <id-num> : the identity number of the delta node {1,2,3,...}
  981. % <ptr> : pointer to the best delta node to come from with
  982. % respect to the minimal demerits path. note: a zero
  983. % pointer indicates the very bottom of the stack
  984. % <demerits> : total demerits distance to delta node which is
  985. % pointed to by <ptr>
  986. % <indentation>: amount of indentation when breaking at this point
  987. % ----------------------------------------------------------------------
  988. %ff
  989. symbolic procedure width(item,style);
  990. begin scalar tag;
  991. tag:=get(item,'TEXTAG);
  992. if null tag then tri!-error(list("cannot find item ",item),'fatal);
  993. while (style>0)and(cdr tag) do << tag:=cdr tag; style:=style-1 >>;
  994. return car tag or 0
  995. end;
  996. smacro procedure sp2mm(x); (x/186468); % scaled points to millimeters
  997. symbolic procedure settolerance(tol);
  998. << if tol<0 then tol:=0 else if tol>10000 then tol:=10000;
  999. prin2 "% \tolerance "; print tol; tolerance!*:=tol; nil
  1000. >>;
  1001. symbolic procedure setpagewidth(hsize);
  1002. % hsize can be given either in millimeters or scaled points.
  1003. << if hsize>400 then hsize!*:=hsize else hsize!*:=hsize*186468;
  1004. prin2 "% \hsize="; prin2 sp2mm(hsize!*); prin2 "mm"; terpri();
  1005. hss!*:=float hsize!*/6; % default stretch/shrink width
  1006. hww!*:=float (3*hsize!*)/4; % optimum line width
  1007. >>;
  1008. symbolic procedure setbreak(hsize,tol);
  1009. << settolerance(tol); setpagewidth(hsize) >>;
  1010. smacro procedure badness(hlen,ibadness);
  1011. % The badness is 100*(hlen/hss)**3, corrected for indentation badness
  1012. begin
  1013. real r;
  1014. r:=abs(hlen-hww!*)/hss!*;
  1015. return fix min(10000.0,r*r*r*100.0+ibadness)
  1016. end;
  1017. smacro procedure isglue(l); (not atom l) and (numberp car l);
  1018. smacro procedure isactive(x); not numberp x;
  1019. smacro procedure ispassive(x); numberp x;
  1020. smacro procedure isdelta(x); cdddr x;
  1021. smacro procedure addup(x); if x then eval('PLUS.x) else 0;
  1022. smacro procedure tpush(stack,item); stack:=item.stack;
  1023. smacro procedure tpop(stack);
  1024. if null stack then nil % Error
  1025. else begin scalar z; z:=car stack; stack:=cdr stack; return(z) end;
  1026. smacro procedure poke(stack,ptr,val);
  1027. if null ptr then stack:=nconc(stack,val.nil)
  1028. else << if val>car(ptr) then rplaca(ptr,val); ptr:=cdr ptr >>;
  1029. smacro procedure concatenate(l);
  1030. begin scalar r;
  1031. for each e in l do r:=nconc(r,explode e);
  1032. return compress r
  1033. end;
  1034. %ff
  1035. % ----------------------------------------------------------------------
  1036. % Section 3.1: Resolving Fraction Expressions
  1037. % ----------------------------------------------------------------------
  1038. symbolic procedure resolve(term);
  1039. % resolve a \frac{...}{...} sequence and transform it into a .../...
  1040. % sequence, where any ... argument may become parenthesized depending on
  1041. % the question if there is any non-ORD-class item within this argument.
  1042. % Furthermore, resolve a \sqrt{...} expression to \(...\)^{\frac{1}{2}}.
  1043. begin
  1044. scalar item,l,m,r,lflag,rflag;
  1045. integer depth;
  1046. l:=term; % save pointer to functor
  1047. depth:=0; m:=r:=lflag:=rflag:=nil; item:=T;
  1048. while term and item do
  1049. << item:=car term; % take first item from list
  1050. if classof(item)='INN then % check inner class item
  1051. << item:=kindof(item);
  1052. if groupbeg(item) then depth:=depth+1
  1053. else if groupend(item) then
  1054. if depth=1 then % outermost level ?
  1055. << r:=term; item:=nil % save pointer to right bracket
  1056. >> % and quit using item as a flag
  1057. else depth:=depth-1
  1058. else if groupvs(item) then % if outermost level then save
  1059. if (depth=1) then m:=term % pointer to intermediate brackets
  1060. >>
  1061. else if not(classof(item)='ORD) then % non-ORD-class item ?
  1062. << if m then rflag:=T else lflag:=T
  1063. >>;
  1064. term:=cdr term % step ahead
  1065. >>;
  1066. if car l='!\!f!r!a!c!{ then
  1067. << if lflag and rflag
  1068. then item:=list('!/,list(655360,-10000))
  1069. else item:=list('!/);
  1070. if lflag then << rplaca(l,'!\!(); item:='!\!).item >>
  1071. else rplaca(l,'! );
  1072. if rflag then << rplaca(r,'!\!)); nconc(item,'!\!(.nil) >>
  1073. else rplaca(r,'! );
  1074. rplaca(m,car item); item:=cdr item;
  1075. if item then rplacd(m,nconc(item,cdr m))
  1076. >> else if car l='!\!s!q!r!t!{ then
  1077. << rplaca(l,'!\!(); rplaca(r,'!\!));
  1078. rplacd(r,'!^!{ . '!1 . '!/ . '!2 . '!} . cdr r)
  1079. >>;
  1080. return(l) % return changed list pointer
  1081. end;
  1082. %ff
  1083. % ----------------------------------------------------------------------
  1084. % Section 3.2 : Create a Break List
  1085. % ----------------------------------------------------------------------
  1086. symbolic procedure breaklist(term);
  1087. begin
  1088. scalar item,result,kind,vstack,hstack,fstack,pstack,p,flag,backup;
  1089. integer depth,acc,aux,LOPw,total,indent;
  1090. p:=result:=vstack:=hstack:=fstack:=nil; backup:=term;
  1091. depth:=total:=acc:=LOPw:=indent:=0;
  1092. while term do
  1093. << item:=car term; flag:=T; % get first item from term
  1094. if null item
  1095. then TRI!-error(list("found NIL in term : ",backup),'fatal);
  1096. if (isglue(item)) then % do we have glue ahead ?
  1097. if (depth<1) then % are we on the top level ?
  1098. << % insert a passive node followed by an active node, clear acc.
  1099. total:=total+acc+car item; nconc(item,indent.nil);
  1100. result:=nconc(result,acc.item.nil); acc:=0
  1101. >>
  1102. else acc:=acc+car item % add up glue width
  1103. else if (classof(item)='LOP) then LOPw:=width(item,depth)
  1104. else if classof(item)='INN then
  1105. << kind:=kindof(item);
  1106. if kind='FRC then
  1107. << tpush(fstack,term); tpush(fstack,depth)
  1108. >>;
  1109. if groupend(kind) then % end of TeX group ?
  1110. << depth:=depth-1; % decrement term depth
  1111. if acc>0 % if <acc> hasn't been poked
  1112. then poke(vstack,p,acc); % yet, then poke it
  1113. acc:=tpop(hstack); % get old acc value
  1114. aux:=addup(vstack); % compute vstack width
  1115. if fstack and (depth=car fstack) then
  1116. << tpop(fstack); % first waste depth info
  1117. if aux>hww!* then % check if it doesn't fit
  1118. << term:=resolve tpop fstack;% resolve fraction
  1119. flag:=nil % evaluate new list
  1120. >>
  1121. else % waste fraction term pointer
  1122. << tpop(fstack); acc:=acc+aux
  1123. >>
  1124. >> else acc:=acc+aux;
  1125. p:=tpop(hstack); vstack:=tpop(hstack) % reset old status
  1126. >>
  1127. else if groupbeg(kind) then % begin of TeX group ?
  1128. << depth:=depth+1; % increment term depth
  1129. tpush(hstack,vstack); % save current <vstack> and
  1130. tpush(hstack,p); % current <p> as well as
  1131. tpush(hstack,acc); % current <acc> to <hstack>
  1132. acc:=0; p:=vstack:=nil; % clear vertical stack
  1133. if LOPw>0 then poke(vstack,p,LOPw); LOPw:=0
  1134. >>
  1135. else if grouphs(kind) then % horizontal separator ?
  1136. << poke(vstack,p,acc); acc:=0 % poke <acc> to <vstack>
  1137. >>
  1138. else if groupvs(kind) then % vertical separator ?
  1139. << poke(vstack,p,acc); acc:=0; p:=vstack % reset
  1140. >>
  1141. >>
  1142. %ff
  1143. else if depth<1 then
  1144. << aux:=width(item,depth); % add up item width
  1145. if classof(item)='OPN then
  1146. << tpush(pstack,indent); indent:=total+acc+aux
  1147. >>;
  1148. if classof(item)='CLO then indent:=tpop(pstack) or 0;
  1149. acc:=acc+aux
  1150. >>
  1151. else acc:=acc+width(item,depth); % add up item width
  1152. if LOPw>0 then << acc:=acc+LOPw; LOPw:=0 >>;
  1153. if flag then term:=cdr term
  1154. >>;
  1155. if acc then total:=total+acc;
  1156. if (total<hsize!*) then return nil % need no breaking
  1157. else return(list(0,0,0,0,0,0,0).nconc(result,acc.
  1158. list(0,0,total,-1,0,2147483647,0).nil)) % return break list
  1159. end;
  1160. %ff
  1161. % ----------------------------------------------------------------------
  1162. % Section 3.3 : Major Line Breaking Routine
  1163. % ----------------------------------------------------------------------
  1164. smacro procedure widthof(deltanode); car deltanode;
  1165. smacro procedure penaltyof(deltanode); cadr deltanode;
  1166. smacro procedure totalof(deltanode); cadr deltanode;
  1167. smacro procedure offsetof(deltanode); caddr deltanode;
  1168. smacro procedure idof(deltanode); cadddr deltanode;
  1169. smacro procedure ptrof(deltanode); car cddddr deltanode;
  1170. smacro procedure indentof(deltanode); caddr cddddr deltanode;
  1171. smacro procedure tailof(deltanode); cddddr deltanode;
  1172. symbolic procedure offsetitem(item);
  1173. concatenate list('!\!o!f!f!{,item,'!} );
  1174. smacro procedure stepahead(ptr,val);
  1175. << if ispassive car ptr then val:=val+car ptr else val:=val+caar ptr;
  1176. ptr:=cdr ptr
  1177. >>;
  1178. smacro procedure findindent(offt,ptr);
  1179. if offt=lastoff and ptr=lastptr then lastindent else
  1180. begin % search the deltastack for previous indentation
  1181. scalar node,p,stack; integer tot;
  1182. stack:=deltastack; p:=lastptr:=ptr; lastoff:=offt;
  1183. while stack do
  1184. << if p=idof (node:=car stack) then
  1185. << p:=ptrof node; tot:=totalof node;
  1186. if tot<offt then stack:=nil
  1187. >>;
  1188. if stack then stack:=cdr stack;
  1189. >>;
  1190. return(lastindent:=offt-tot+indentof node)
  1191. end;
  1192. %ff
  1193. symbolic procedure trybreak(term,brkl);
  1194. % parameters: term .... TeX item list, as created by "interglue"
  1195. % brkl .... the breaklist to be processed by this routine
  1196. begin
  1197. scalar bottom,top,base,item,deltastack,pred;
  1198. integer depth; % depth of expression when rebuilding
  1199. integer feasible,id; % number of feasible delta node
  1200. integer len,total; % current and total length so far
  1201. integer dm,basedm; % current and base demerits
  1202. integer bd; % current badness
  1203. integer penalty;
  1204. integer offset,baseoffset; % current and base parenthesis offset
  1205. integer baseptr; % pointer to best way to come from
  1206. integer indent,baseindent; % current and base indentation
  1207. integer lastoff,lastindent,lastptr; % temp. var. for speedup
  1208. real indentbadness; % correction for indentation badness
  1209. if null brkl then goto retain;
  1210. bottom:=brkl;
  1211. lastoff:=lastptr:=lastindent:=feasible:=indent:=total:=0;
  1212. while bottom do
  1213. << top:=cdr bottom; base:=car bottom; pred:=tailof base;
  1214. id:=idof base; % id of current delta node
  1215. if penaltyof base=-10000 % break item ?
  1216. then rplaca(cdr pred,0); % new line
  1217. basedm:=cadr pred; % demerits so far
  1218. % save the delta node to the delta-stack. thus deltastack holds
  1219. % all the feasible breakpoints in reverse order.
  1220. deltastack:=base.deltastack;
  1221. len:=baseindent:=indentof(base); % indentation for this line
  1222. indentbadness:=2500.0*(float(baseindent)/float(hww!*));
  1223. baseoffset:=offsetof base;% current offset amount
  1224. baseptr:=car pred; % pointer to best node to come from
  1225. total:=total+widthof base;% correct total length
  1226. %--- debug ---
  1227. % prin2 "Base ["; prin2 id; prin2 "] basedm="; prin2 basedm;
  1228. % prin2 " ibd="; prin2 indentbadness;
  1229. % prin2 " indent="; prin2 baseindent; terpri();
  1230. %--- debug ---
  1231. %ff
  1232. while top and len<hsize!* do % loop once thru a potential line
  1233. % note that we use the local hsize instead of the full hsize
  1234. << item:=car top;
  1235. if ispassive(item) then len:=len+item else
  1236. << bd:=badness(len,indentbadness);
  1237. penalty:=penaltyof item;
  1238. offset:=offsetof item;
  1239. if (bd<tolerance!*) % is the breakpoint feasible?
  1240. or (bd+penalty<1) % got a break bonus ?
  1241. or (null cdr top) then % or did we reach last delta node?
  1242. << dm:=bd*bd+basedm+penalty*abs(penalty);
  1243. if isdelta(item) then
  1244. << pred:=tailof item;
  1245. if dm<cadr pred then % found a better path?
  1246. << % save the pointer to best breakpoint to come from
  1247. % and the minimum demerits to reach it
  1248. rplaca(pred,id); rplaca(cdr pred,dm);
  1249. if !*TeXIndent then % save the current indentation
  1250. << if offset>total
  1251. then indent:=offset-total+baseindent
  1252. else if offset<baseoffset
  1253. then indent:=findindent(offset,baseptr)
  1254. else indent:=baseindent;
  1255. rplaca(cddr pred,indent)
  1256. >>
  1257. >>
  1258. >>
  1259. else % create a new delta node
  1260. << feasible:=feasible+1;
  1261. if !*TeXIndent then
  1262. if offset>total
  1263. then indent:=offset-total+baseindent
  1264. else if offset<baseoffset
  1265. then indent:=findindent(offset,baseptr)
  1266. else indent:=baseindent
  1267. else indent:=0;
  1268. rplacd(cddr item,feasible.id.dm.indent.nil)
  1269. >>;
  1270. %--- debug ---
  1271. % prin2 "-->["; prin2 idof item; prin2 "] dm="; prin2 dm;
  1272. % prin2 " bd="; prin2 bd; prin2 " p="; prin2 penalty;
  1273. % if !*TeXindent then << prin2 " ind="; prin2 indent >>; terpri();
  1274. %--- debug ---
  1275. if penalty=-10000 then top:=nil
  1276. >>;
  1277. len:=len+car item % count the length anyway
  1278. >>;
  1279. if top then top:=cdr top
  1280. >>;
  1281. %ff
  1282. rplaca(cdr base,total); % replace penalty by total width so far
  1283. bottom:=cdr bottom; % depart from this delta node
  1284. while bottom and (ispassive(car bottom) or not isdelta(car bottom))
  1285. do stepahead(bottom,total); % move to next delta node in list
  1286. >>;
  1287. bottom:=deltastack; feasible:=-1; top:=nil;
  1288. while bottom do % loop thru the delta-node stack
  1289. << id:=idof car bottom; % id is the current id number
  1290. if id=feasible then % is this node the one pointed to?
  1291. << feasible:=ptrof car bottom; % feasible is the new back-pointer
  1292. top:=id.top; % save the path element
  1293. >>;
  1294. bottom:=cdr bottom % step ahead
  1295. >>; % now deltastack contains the best path
  1296. deltastack:=cdr top; % in forward order
  1297. %--- debug ---
  1298. % print term; print deltastack;
  1299. %--- debug ---
  1300. if car deltastack= -1 then
  1301. << prin2 "% Warning: no suitable way of breaking found"; terpri();
  1302. prin2 "% ======== retry with a greater tolerance..."; terpri();
  1303. prin2 "% (output will produce overfull box if printed)"; terpri()
  1304. >>;
  1305. brkl:=cdr brkl; % strip the dummy node at the list's head
  1306. %ff
  1307. % --------------------------------------------------------------------
  1308. % now remove all glue items but retain all break items
  1309. retain: % ------------------------------------------------------------
  1310. offset:=depth:=0; bottom:=term;
  1311. if brkl then brkl:=cdr brkl; % ensure first item is an active node
  1312. while term and (cdr term) do
  1313. << item:=car term;
  1314. if isglue(item) then % if this is a glue item
  1315. if (depth=0) and brkl then % and we are on the top level
  1316. << top:=car brkl;
  1317. if isdelta(top) then % consider delta nodes only
  1318. << if (idof top=car deltastack) then % break point?
  1319. << deltastack:=cdr deltastack;
  1320. %--- debug ---
  1321. % prin2 "% ["; prin2 idof top; prin2 "] ";
  1322. % prin2 sp2mm(totalof(top)+indentof(top)-offset); terpri();
  1323. % offset:=totalof(top);
  1324. %--- debug ---
  1325. if (len:=indentof top)>0
  1326. then rplacd(pred,'!\!n!l! . offsetitem(len) . cdr term)
  1327. else rplacd(pred,'!\!n!l! . cdr term)
  1328. >>
  1329. else rplacd(pred,cdr term)
  1330. >>
  1331. else rplacd(pred,cdr term);
  1332. if brkl and (cdr brkl) % check for next active node
  1333. then brkl:=cddr brkl % skip to next active node
  1334. >>
  1335. else rplacd(pred,cdr term) % remove glue item
  1336. else if classof(item)='INN then
  1337. << if groupbeg(kindof(item)) then depth:=depth+1 else
  1338. if groupend(kindof(item)) then depth:=depth-1
  1339. >>;
  1340. pred:=term; term:=cdr term
  1341. >>;
  1342. %--- debug ---
  1343. % top:=car term; prin2 "% [-1] ";
  1344. % prin2 sp2mm(totalof(top)+indentof(top)-offset); terpri();
  1345. %--- debug ---
  1346. return(bottom)
  1347. end;
  1348. %ff
  1349. % ----------------------------------------------------------------------
  1350. % Section 4 : Output of TeX-Code
  1351. % ----------------------------------------------------------------------
  1352. symbolic procedure TeXstrlen(s);
  1353. begin
  1354. integer length;
  1355. scalar flag;
  1356. length:=0; flag:=nil;
  1357. for each c in s do
  1358. if not flag and c='!! then flag:=T
  1359. else << length:=length+1; flag:=nil >>;
  1360. return length
  1361. end;
  1362. smacro procedure newline();
  1363. if nlflag then cc:=indent
  1364. else if (cc>indent) then << terpri(); cc:=indent; nlflag:=T >>;
  1365. %ff
  1366. symbolic procedure TeXout(itemlist,flag);
  1367. if null itemlist then nil else
  1368. begin
  1369. integer cc,len,indent,ccmax,lines;
  1370. scalar item,class,tag,oldtag,lasttag,indentstack,ispd,nlflag;
  1371. ccmax:=64; cc:=indent:=lines:=0; % initializations
  1372. tag:=ispd:=nlflag:=indentstack:=nil; % initializations
  1373. prin2('!$!$); % begin TeX math group
  1374. if flag then prin2('!\!d!i!s!p!l!a!y!l!i!n!e!s!{!\!q!d!d);
  1375. terpri(); % start new line
  1376. while itemlist do
  1377. << item:=car itemlist; itemlist:=cdr itemlist;
  1378. len:=TeXstrlen(explode(item)); oldtag:=nil; lasttag:=tag or class;
  1379. class:=classof(item); tag:=(class='INN)and(kindof(item));
  1380. %ispd:=(class='ORD)and itemlist and(classof(car itemlist)='OPN);
  1381. if (tag='MAT)or(tag='FRC)or(class='OPN) %or ispd
  1382. then newline(); % start new line
  1383. if (groupbeg(tag))or(class='OPN) then
  1384. << tpush(indentstack,indent); % push it to the stack
  1385. tpush(indentstack,lasttag); % the reason for pushing
  1386. if (cc+cc < ccmax) % within left half of page ?
  1387. then if ((class='OPN)and(lasttag='ORD))or % predicate?
  1388. (groupbeg(tag)and not((tag='FRC)or(tag='MAT)))
  1389. then indent:=cc+len % take current position
  1390. else indent:=indent+len % compute new indentation
  1391. >>
  1392. else if (groupend(tag))or(class='CLO) then
  1393. << oldtag:=tpop(indentstack); indent:=tpop(indentstack)
  1394. >>;
  1395. if (cc+len > ccmax) or % beyond right margin ?
  1396. (item='!+)or(item='!-)or(class='CLO) % important item?
  1397. then newline();
  1398. if nlflag then << nlflag:=nil; spaces(cc) >>;
  1399. if tag='CR then lines:=lines+1;
  1400. if not(item='! ) then prin2(item); % print the item and
  1401. cc:=cc+len; % count the characters
  1402. if groupvs(tag) or % vertical seperator ?
  1403. (groupend(tag) and % end of a large group,
  1404. ((oldtag='FRC) or (oldtag='MAT)))% i.e. fraction, matrix ?
  1405. or (class='CLO) or % closing parenthesis ?
  1406. (((class='REL)or(class='BIN))and % binary/relational operator?
  1407. (cc+cc+cc > ccmax+ccmax)) % within last third of page?
  1408. or item='!, or null class
  1409. then newline()
  1410. >>;
  1411. newline(); % start final line
  1412. if flag then
  1413. if lines=0 then prin2('!\!c!r!})
  1414. else prin2('!\!N!l!}); % end multi-line output
  1415. prin2('!$!$); terpri(); return(nil) % end math group
  1416. end;
  1417. %ff
  1418. % ----------------------------------------------------------------------
  1419. % Section 5: User Interface
  1420. % ----------------------------------------------------------------------
  1421. % handle argument passing for following the functions, compelling that
  1422. % properties are used during compile time
  1423. deflist( '((TeXdisplay RLIS) (TeXlet RLIS)), 'STAT);
  1424. algebraic procedure TeXsetbreak(hsize,tol); lisp setbreak(hsize,tol);
  1425. algebraic procedure TeXtolerance(tol); lisp settolerance(tol);
  1426. algebraic procedure TeXpagewidth(hsize); lisp setpagewidth(hsize);
  1427. symbolic procedure TeXlet(arglist);
  1428. begin scalar class,sym,item;
  1429. if not length(arglist)=2 then rederr "Usage: TeXlet(symbol,item);";
  1430. sym:= car arglist; item:=intern cadr arglist; class:=classof(item);
  1431. if null class then
  1432. << prin2 "% No such TeX symbol available"; terpri()
  1433. >>
  1434. else if (class='INN) then % prevent from TeXequiv'ing inner symbols
  1435. << prin2 "% cannot assign inner TeX symbols yet"; terpri()
  1436. >>
  1437. else triassert(sym,item);
  1438. return nil
  1439. end;
  1440. symbolic procedure Texdisplay(arglist);
  1441. begin scalar item,tag,class;
  1442. if not length(arglist)=1 then rederr "Usage: TeXdisplay(item);";
  1443. item:=get(car arglist,'TEXNAME);
  1444. if not item then
  1445. << prin2 "% "; prin2 car arglist; prin2 " is not defined"; terpri()
  1446. >>;
  1447. if not item then return nil;
  1448. tag:=get(item,'TEXTAG); class:=get(item,'CLASS);
  1449. prin2 "% TeX item "; prin2 item; prin2 " is of class "; prin2 class;
  1450. prin2 " and has following widths: "; terpri(); prin2 "% ";
  1451. for each w in tag do
  1452. begin real v; v:=w/65536.0; prin2 v; prin2 "pt " end;
  1453. terpri(); return nil
  1454. end;
  1455. % ----------------------- share name between both modes ----------------
  1456. symbolic operator TeXlet;
  1457. symbolic operator TeXitem;
  1458. symbolic operator TeXdisplay;
  1459. symbolic operator TeXassertset;
  1460. symbolic operator TeXretractset;
  1461. % ------------------------ Default Initializations ---------------------
  1462. << prin2 "% TeX-REDUCE-Interface 0.50"; terpri() >>;
  1463. TeXassertset(GREEK); TeXassertset(LOWERCASE);
  1464. TeXtolerance(10); TeXpagewidth(150);
  1465. % *************** E N D O F S O U R C E C O D E **************
  1466. endmodule;
  1467. end;