tri.red 59 KB

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