Line data Source code
1 : /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 : /*
3 : * This file is part of the LibreOffice project.
4 : *
5 : * This Source Code Form is subject to the terms of the Mozilla Public
6 : * License, v. 2.0. If a copy of the MPL was not distributed with this
7 : * file, You can obtain one at http://mozilla.org/MPL/2.0/.
8 : *
9 : * This file incorporates work covered by the following license notice:
10 : *
11 : * Licensed to the Apache Software Foundation (ASF) under one or more
12 : * contributor license agreements. See the NOTICE file distributed
13 : * with this work for additional information regarding copyright
14 : * ownership. The ASF licenses this file to you under the Apache
15 : * License, Version 2.0 (the "License"); you may not use this file
16 : * except in compliance with the License. You may obtain a copy of
17 : * the License at http://www.apache.org/licenses/LICENSE-2.0 .
18 : */
19 :
20 : #include <assert.h>
21 : #include <stdio.h>
22 : #include <string.h>
23 : #include <ctype.h>
24 :
25 : // DVO: always use standard headers:
26 : #include <istream>
27 : #include <sstream>
28 : using namespace std;
29 :
30 : #include "mzstring.h"
31 : #include "hwpeq.h"
32 : #include <sal/types.h>
33 : #include <sal/macros.h>
34 :
35 : /* @Man: hwp수식을 LaTeX으로 바꾸기 */
36 : #ifdef WIN32
37 : # define ENDL "\r\n"
38 : #else /* !WIN32 */
39 : # define ENDL "\n"
40 : #endif
41 :
42 : #define WS " \t\r\n\v\f"
43 :
44 : #define EQ_CASE 0x01 // case sensitive cmd
45 : #define EQ_ENV 0x02 // equiv to latex environment
46 : #define EQ_ATOP 0x04 // must revert order
47 :
48 : #define IS_WS(ch) (strchr(WS, ch))
49 : #define IS_BINARY(ch) (strchr("+-<=>", ch))
50 :
51 : #ifdef WIN32
52 : #define STRICMP stricmp
53 : #else
54 : #define STRICMP strcasecmp
55 : #endif
56 :
57 : // sub and sup scipt script status
58 : enum { SCRIPT_NONE, SCRIPT_SUB, SCRIPT_SUP, SCRIPT_ALL};
59 :
60 : static int eq_word(MzString& outs, istream *strm, int script = SCRIPT_NONE);
61 : static bool eq_sentence(MzString& outs, istream *strm, const char *end = 0);
62 :
63 : struct hwpeq {
64 : const char *key; // hwp math keyword
65 : const char *latex; // corresponding latex keywork
66 : int nargs; // # of argument
67 : unsigned char flag; // case sensitive?
68 : };
69 :
70 : static const hwpeq eq_tbl[] = {
71 : { "!=", "\\equiv ", 0, 0 },
72 : { "#", "\\\\", 0, 0 },
73 : { "+-", "\\pm ", 0, 0 },
74 : { "-+", "\\mp ", 0, 0 },
75 : { "<=", "\\leq ", 0, 0 },
76 : { "==", "\\equiv ", 0, 0 },
77 : { ">=", "\\geq ", 0, 0 },
78 : { "Pr", NULL, 0, 0 },
79 : { "^", "^", 1, 0 },
80 : { "_", "_", 1, 0 },
81 : { "`", "\\;", 0, 0 },
82 : { "acute", NULL, 1, 0 },
83 : { "aleph", NULL, 0, 0 },
84 : { "alpha", NULL, 0, EQ_CASE },
85 : { "amalg", NULL, 0, 0 },
86 : { "and", NULL, 0, 0 },
87 : { "angle", NULL, 0, 0 },
88 : { "angstrom", NULL, 0, 0 },
89 : { "approx", NULL, 0, 0 },
90 : { "arc", NULL, 0, 0 },
91 : { "arccos", NULL, 0, 0 },
92 : { "arch", NULL, 0, 0 },
93 : { "arcsin", NULL, 0, 0 },
94 : { "arctan", NULL, 0, 0 },
95 : { "arg", NULL, 0, 0 },
96 : { "assert", "\\vdash", 0, 0 },
97 : { "ast", NULL, 0, 0 },
98 : { "asymp", NULL, 0, 0 },
99 : { "atop", NULL, 1, EQ_ATOP },
100 : { "backslash", NULL, 0, 0 },
101 : { "bar", NULL, 1, 0 },
102 : { "because", NULL, 0, 0 },
103 : { "beta", NULL, 0, EQ_CASE },
104 : { "big", NULL, 0, EQ_CASE },
105 : { "bigcap", NULL, 0, 0 },
106 : { "bigcirc", NULL, 0, 0 },
107 : { "bigcup", NULL, 0, 0 },
108 : { "bigg", NULL, 0, EQ_CASE },
109 : { "bigodiv", NULL, 0, 0 },
110 : { "bigodot", NULL, 0, 0 },
111 : { "bigominus", NULL, 0, 0 },
112 : { "bigoplus", NULL, 0, 0 },
113 : { "bigotimes", NULL, 0, 0 },
114 : { "bigsqcap", NULL, 0, 0 },
115 : { "bigsqcup", NULL, 0, 0 },
116 : { "biguplus", NULL, 0, 0 },
117 : { "bigvee", NULL, 0, 0 },
118 : { "bigwedge", NULL, 0, 0 },
119 : { "binom", NULL, 2, 0 },
120 : { "bmatrix", NULL, 0, EQ_ENV },
121 : { "bold", NULL, 0, 0 },
122 : { "bot", NULL, 0, 0 },
123 : { "breve", NULL, 1, 0 },
124 : { "buildrel", NULL, 0, 0 }, // LATER
125 : { "bullet", NULL, 0, 0 },
126 : { "cap", NULL, 0, 0 },
127 : { "cases", NULL, 0, EQ_ENV },
128 : { "ccol", NULL, 0, 0 }, /* 세로로 가운데 */
129 : { "cdot", NULL, 0, 0 },
130 : { "cdots", NULL, 0, 0 },
131 : { "check", NULL, 1, 0 },
132 : { "chi", NULL, 0, EQ_CASE },
133 : { "choose", NULL, 0, EQ_ATOP },
134 : { "circ", NULL, 0, 0 },
135 : { "col", NULL, 0, 0 }, // LATER
136 : { "cong", NULL, 0, 0 },
137 : { "coprod", NULL, 0, 0 },
138 : { "cos", NULL, 0, 0 },
139 : { "cosec", NULL, 0, 0 },
140 : { "cosh", NULL, 0, 0 },
141 : { "cot", NULL, 0, 0 },
142 : { "coth", NULL, 0, 0 },
143 : { "cpile", NULL, 0, 0 }, // LATER
144 : { "csc", NULL, 0, 0 },
145 : { "cup", NULL, 0, 0 },
146 : { "dagger", NULL, 0, 0 },
147 : { "dashv", NULL, 0, 0 },
148 : { "ddagger", NULL, 0, 0 },
149 : { "ddot", NULL, 1, 0 },
150 : { "ddots", NULL, 0, 0 },
151 : { "def", NULL, 0, 0 },
152 : { "deg", NULL, 0, 0 },
153 : { "del", NULL, 0, 0 },
154 : { "delta", NULL, 0, EQ_CASE },
155 : { "diamond", NULL, 0, 0 },
156 : { "dim", NULL, 0, 0 },
157 : { "div", NULL, 0, 0 },
158 : { "divide", NULL, 0, 0 },
159 : { "dline", NULL, 0, 0 },
160 : { "dmatrix", NULL, 0, EQ_ENV },
161 : { "dot", NULL, 1, 0 },
162 : { "doteq", NULL, 0, 0 },
163 : { "dotsaxis", NULL, 0, 0 },
164 : { "dotsdiag", NULL, 0, 0 },
165 : { "dotslow", "\\ldots", 0, 0 },
166 : { "dotsvert", "\\vdots", 0, 0 },
167 : { "downarrow", NULL, 0, EQ_CASE },
168 : { "dsum", "+", 0, 0 },
169 : { "dyad", NULL, 0, 0 }, // LATER
170 : { "ell", NULL, 0, 0 },
171 : { "emptyset", NULL, 0, 0 },
172 : { "epsilon", NULL, 0, EQ_CASE },
173 : { "eqalign", NULL, 0, EQ_ENV },
174 : { "equiv", NULL, 0, 0 },
175 : { "eta", NULL, 0, EQ_CASE },
176 : { "exarrow", NULL, 0, 0 },
177 : { "exist", "\\exists", 0, 0 },
178 : { "exists", NULL, 0, 0 },
179 : { "exp", NULL, 0, EQ_CASE },
180 : { "for", NULL, 0, 0 },
181 : { "forall", NULL, 0, 0 },
182 : { "from", "_", 1, 0 },
183 : { "gamma", NULL, 0, EQ_CASE },
184 : { "gcd", NULL, 0, 0 },
185 : { "ge", "\\geq", 0, 0 },
186 : { "geq", NULL, 0, 0 },
187 : { "ggg", NULL, 0, 0 },
188 : { "grad", NULL, 0, 0 },
189 : { "grave", NULL, 1, 0 },
190 : { "hat", "\\widehat", 1, 0 },
191 : { "hbar", NULL, 0, 0 },
192 : { "hom", NULL, 0, 0 },
193 : { "hookleft", NULL, 0, 0 },
194 : { "hookright", NULL, 0, 0 },
195 : { "identical", NULL, 0, 0 }, // LATER
196 : { "if", NULL, 0, 0 },
197 : { "imag", NULL, 0, 0 },
198 : { "image", NULL, 0, 0 },
199 : { "imath", NULL, 0, 0 },
200 : { "in", NULL, 0, 0 },
201 : { "inf", "\\infty", 0, 0 },
202 : { "infinity", "\\infty", 0, 0 },
203 : { "infty", NULL, 0, 0 },
204 : { "int", NULL, 0, 0 },
205 : { "integral", "\\int", 0, 0 },
206 : { "inter", "\\bigcap", 0, 0 },
207 : { "iota", NULL, 0, EQ_CASE },
208 : { "iso", NULL, 0, 0 }, // ams
209 : { "it", NULL, 0, 0 },
210 : { "jmath", NULL, 0, 0 },
211 : { "kappa", NULL, 0, EQ_CASE },
212 : { "ker", NULL, 0, 0 },
213 : { "lambda", NULL, 0, EQ_CASE },
214 : { "land", NULL, 0, 0 }, // LATER
215 : { "langle", NULL, 0, 0 },
216 : { "larrow", "\\leftarrow", 0, EQ_CASE },
217 : { "lbrace", NULL, 0, 0 },
218 : { "lbrack", "[", 0, 0 },
219 : { "lceil", NULL, 0, 0 },
220 : { "lcol", NULL, 0, 0 }, // LATER
221 : { "ldots", NULL, 0, 0 },
222 : { "le", NULL, 0, 0 },
223 : { "left", NULL, 0, 0 },
224 : { "leftarrow", NULL, 0, EQ_CASE },
225 : { "leq", NULL, 0, 0 },
226 : { "lfloor", NULL, 0, 0 },
227 : { "lg", NULL, 0, 0 },
228 : { "lim", NULL, 0, EQ_CASE },
229 : { "line", "\\vert", 0, 0 },
230 : { "liter", "\\ell", 0, 0 },
231 : { "lll", NULL, 0, 0 }, // ams
232 : { "ln", NULL, 0, 0 },
233 : { "log", NULL, 0, 0 },
234 : { "lor", "\\vee", 0, 0 },
235 : { "lparen", "(", 0, 0 },
236 : { "lpile", NULL, 0, 0 }, // LATER
237 : { "lrarrow", "\\leftrightarrow", 0, EQ_CASE },
238 : { "lrharpoons", "\\leftrightharpoons",0, 0 },
239 : { "mapsto", NULL, 0, 0 },
240 : { "massert", "\\dashv", 0, 0 },
241 : { "matrix", NULL, 0, EQ_ENV },
242 : { "max", NULL, 0, 0 },
243 : { "mho", NULL, 0, 0 }, // ams
244 : { "min", NULL, 0, 0 },
245 : { "minusplus", NULL, 0, 0 },
246 : { "mit", "", 0, 0 }, // font
247 : { "mod", "\\bmod", 0, 0 },
248 : { "models", NULL, 0, 0 },
249 : { "msangle", NULL, 0, 0 }, // LATER
250 : { "mu", NULL, 0, EQ_CASE },
251 : { "nabla", NULL, 0, 0 },
252 : { "ne", NULL, 0, 0 },
253 : { "nearrow", NULL, 0, 0 },
254 : { "neg", NULL, 0, 0 },
255 : { "neq", NULL, 0, 0 },
256 : { "nequiv", NULL, 0, 0 },
257 : { "ni", NULL, 0, 0 },
258 : { "not", NULL, 0, 0 },
259 : { "notin", NULL, 0, 0 },
260 : { "nu", NULL, 0, EQ_CASE },
261 : { "nwarrow", NULL, 0, 0 },
262 : { "odiv", NULL, 0, 0 },
263 : { "odot", NULL, 0, 0 },
264 : { "oint", NULL, 0, 0 },
265 : { "omega", NULL, 0, EQ_CASE },
266 : { "omicron", NULL, 0, EQ_CASE },
267 : { "ominus", NULL, 0, 0 },
268 : { "oplus", NULL, 0, 0 },
269 : { "or ", NULL, 0, 0 },
270 : { "oslash", NULL, 0, 0 },
271 : { "otimes", NULL, 0, 0 },
272 : { "over", NULL, 1, EQ_ATOP },
273 : { "overline", NULL, 1, 0 },
274 : { "owns", "\\ni", 0, 0 },
275 : { "parallel", NULL, 0, 0 },
276 : { "partial", NULL, 0, 0 },
277 : { "phantom", NULL, 0, 0 },
278 : { "phi", NULL, 0, EQ_CASE },
279 : { "pi", NULL, 0, EQ_CASE },
280 : { "pile", NULL, 0, 0 }, // LATER
281 : { "plusminus", "\\pm", 0, 0 },
282 : { "pmatrix", NULL, 0, EQ_ENV },
283 : { "prec", NULL, 0, 0 },
284 : { "prep", NULL, 0, 0 },
285 : { "prime", NULL, 0, 0 },
286 : { "prod", NULL, 0, 0 },
287 : { "propto", NULL, 0, 0 },
288 : { "psi", NULL, 0, EQ_CASE },
289 : { "rangle", NULL, 0, 0 },
290 : { "rarrow", "\\rightarrow", 0, EQ_CASE },
291 : { "rbrace", "]", 0, 0 },
292 : { "rbrace", NULL, 0, 0 },
293 : { "rceil", NULL, 0, 0 },
294 : { "rcol", NULL, 0, 0 }, // LATER
295 : { "real", "\\Re", 0, 0 },
296 : { "reimage", NULL, 0, 0 },
297 : { "rel", NULL, 0, 0 },
298 : { "rfloor", NULL, 0, 0 },
299 : { "rho", NULL, 0, EQ_CASE },
300 : { "right", NULL, 0, 0 },
301 : { "rightarrow", NULL, 0, EQ_CASE },
302 : { "rlharpoons", NULL, 0, 0 },
303 : { "rm", NULL, 0, 0 },
304 : { "root", "\\sqrt", 1, 0 },
305 : { "rparen", ")", 0, 0 },
306 : { "rpile", NULL, 0, 0 }, // LATER
307 : { "rtangle", NULL, 0, 0 },
308 : { "sangle", NULL, 0, 0 },
309 : { "scale", NULL, 0, 0 },
310 : { "searrow", NULL, 0, 0 },
311 : { "sec", NULL, 0, 0 },
312 : { "sigma", NULL, 0, EQ_CASE },
313 : { "sim", NULL, 0, 0 },
314 : { "simeq", NULL, 0, 0 },
315 : { "sin", NULL, 0, 0 },
316 : { "sinh", NULL, 0, 0 },
317 : { "slash", NULL, 0, 0 },
318 : { "smallint", NULL, 0, 0 },
319 : { "smallinter", NULL, 0, 0 },
320 : { "smalloint", NULL, 0, 0 },
321 : { "smallprod", NULL, 0, 0 },
322 : { "smallsum", NULL, 0, 0 },
323 : { "smallunion", NULL, 0, 0 },
324 : { "smcoprod", NULL, 0, 0 },
325 : { "sqcap", NULL, 0, 0 },
326 : { "sqcup", NULL, 0, 0 },
327 : { "sqrt", NULL, 1, 0 },
328 : { "sqsubset", NULL, 0, 0 },
329 : { "sqsubseteq", NULL, 0, 0 },
330 : { "sqsupset", NULL, 0, 0 },
331 : { "sqsupseteq", NULL, 0, 0 },
332 : { "star", NULL, 0, 0 },
333 : { "sub", "_", 0, 0 },
334 : { "subset", NULL, 0, 0 },
335 : { "subseteq", NULL, 0, 0 },
336 : { "succ", NULL, 0, 0 },
337 : { "sum", NULL, 0, 0 },
338 : { "sup", "^", 0, 0 },
339 : { "superset", NULL, 0, 0 },
340 : { "supset", NULL, 0, 0 },
341 : { "supseteq", NULL, 0, 0 },
342 : { "swarrow", NULL, 0, 0 },
343 : { "tan", NULL, 0, 0 },
344 : { "tanh", NULL, 0, 0 },
345 : { "tau", NULL, 0, EQ_CASE },
346 : { "therefore", NULL, 0, 0 },
347 : { "theta", NULL, 0, EQ_CASE },
348 : { "tilde", "\\widetilde", 1, 0 },
349 : { "times", NULL, 0, 0 },
350 : { "to", "^", 1, 0 },
351 : { "top", NULL, 0, 0 },
352 : { "triangle", NULL, 0, 0 },
353 : { "triangled", NULL, 0, 0 },
354 : { "trianglel", NULL, 0, 0 },
355 : { "triangler", NULL, 0, 0 },
356 : { "triangleu", NULL, 0, 0 },
357 : { "udarrow", "\\updownarrow",0, EQ_CASE },
358 : { "under", "\\underline", 1, 0 },
359 : { "underline", "\\underline", 1, 0 },
360 : { "union", "\\bigcup", 0, 0 },
361 : { "uparrow", NULL, 0, EQ_CASE },
362 : { "uplus", NULL, 0, 0 },
363 : { "upsilon", NULL, 0, EQ_CASE },
364 : { "varepsilon", NULL, 0, 0 },
365 : { "varphi", NULL, 0, 0 },
366 : { "varpi", NULL, 0, 0 },
367 : { "varrho", NULL, 0, 0 },
368 : { "varsigma", NULL, 0, 0 },
369 : { "vartheta", NULL, 0, 0 },
370 : { "varupsilon", NULL, 0, 0 },
371 : { "vdash", NULL, 0, 0 },
372 : { "vdots", NULL, 0, 0 },
373 : { "vec", NULL, 1, 0 },
374 : { "vee", NULL, 0, 0 },
375 : { "vert", NULL, 0, 0 },
376 : { "wedge", NULL, 0, 0 },
377 : { "wp", NULL, 0, 0 },
378 : { "xi", NULL, 0, EQ_CASE },
379 : { "xor", NULL, 0, 0 },
380 : { "zeta", NULL, 0, EQ_CASE }
381 : };
382 :
383 0 : static const hwpeq *lookup_eqn(char *str)
384 : {
385 : static const int eqCount = SAL_N_ELEMENTS(eq_tbl);
386 0 : int m, k, l = 0, r = eqCount;
387 0 : const hwpeq *result = 0;
388 :
389 0 : while( l < r ) {
390 0 : m = (l + r) / 2;
391 0 : k = strcmp(eq_tbl[m].key, str);
392 0 : if( k == 0 ) {
393 0 : result = eq_tbl + m;
394 0 : break;
395 : }
396 0 : else if( k < 0 )
397 0 : l = m + 1;
398 : else
399 0 : r = m;
400 : }
401 0 : return result;
402 : }
403 :
404 : /* 첫자만 대문자이거나 전부 대문자면 소문자로 바꾼다. */
405 0 : void make_keyword( char *keyword, const char *token)
406 : {
407 : char* ptr;
408 0 : bool result = true;
409 0 : int len = strlen(token);
410 : assert(keyword);
411 :
412 0 : if( 255 < len )
413 : {
414 0 : len = 255;
415 : }
416 0 : memcpy(keyword, token, len);
417 0 : keyword[len] = 0;
418 :
419 0 : if( (token[0] & 0x80) || islower(token[0]) || strlen(token) < 2 )
420 0 : return;
421 :
422 0 : int capital = isupper(keyword[1]);
423 0 : for( ptr = keyword + 2; *ptr && result; ptr++ )
424 : {
425 0 : if( (*ptr & 0x80) ||
426 0 : (!capital && isupper(*ptr)) ||
427 0 : (capital && islower(*ptr)) )
428 : {
429 0 : result = false;
430 : }
431 : }
432 :
433 0 : if( result )
434 : {
435 0 : ptr = keyword;
436 0 : while( *ptr )
437 : {
438 0 : if( isupper(*ptr) )
439 0 : *ptr = sal::static_int_cast<char>(tolower(*ptr));
440 0 : ptr++;
441 : }
442 : }
443 0 : return;
444 : }
445 :
446 : // token reading function
447 0 : struct eq_stack {
448 : MzString white;
449 : MzString token;
450 : istream *strm;
451 :
452 0 : eq_stack() { strm = 0; };
453 0 : bool state(istream *s) {
454 0 : if( strm != s) { white = 0; token = 0; }
455 0 : return token.length() != 0;
456 : }
457 : };
458 :
459 : static eq_stack *stk = 0;
460 :
461 0 : void push_token(MzString &white, MzString &token, istream *strm)
462 : {
463 : // one time stack
464 : assert(stk->state(strm) == false);
465 :
466 0 : stk->white = white;
467 0 : stk->token = token;
468 0 : stk->strm = strm;
469 0 : }
470 :
471 : /*
472 : 읽은 토큰의 길이를 반환한다.
473 : */
474 : /* control char, control sequence, binary sequence,
475 : alphabet string, sigle character */
476 0 : static int next_token(MzString &white, MzString &token, istream *strm)
477 : {
478 0 : int ch = 0;
479 :
480 0 : if( stk->state(strm) ) {
481 0 : white = stk->white;
482 0 : token = stk->token;
483 0 : stk->token = 0;
484 0 : stk->white = 0;
485 0 : return token.length();
486 : }
487 :
488 0 : token = 0;
489 0 : white = 0;
490 0 : if( !strm->good() || (ch = strm->get()) == EOF )
491 0 : return 0;
492 :
493 : // read preceding ws
494 0 : if( IS_WS(ch) ) {
495 0 : do white << (char) ch;
496 0 : while( IS_WS(ch = strm->get()) );
497 : }
498 :
499 0 : if( ch == '\\' || ch & 0x80 || isalpha(ch) ) {
500 0 : if( ch == '\\' ) {
501 0 : token << (char) ch;
502 0 : ch = strm->get();
503 : }
504 0 : do {
505 0 : token << (char) ch;
506 0 : ch = strm->get();
507 0 : } while( ch != EOF && (ch & 0x80 || isalpha(ch)) ) ;
508 0 : strm->putback(sal::static_int_cast<char>(ch));
509 : /* sub, sub, over, atop 특수 처리
510 : 그 이유는 next_state()에 영향을 미치기 때문이다.
511 : */
512 0 : if( !STRICMP("sub", token) || !STRICMP("from", token) ||
513 0 : !STRICMP("sup", token) || !STRICMP("to", token) ||
514 0 : !STRICMP("over", token) || !STRICMP("atop", token) ||
515 0 : !STRICMP("left", token) || !STRICMP("right", token) )
516 : {
517 : char buf[256];
518 0 : make_keyword(buf, token);
519 0 : token = buf;
520 : }
521 0 : if( !token.compare("sub") || !token.compare("from") )
522 0 : token = "_";
523 0 : if( !token.compare("sup") || !token.compare("to") )
524 0 : token = "^";
525 : }
526 0 : else if( IS_BINARY(ch) ) {
527 0 : do token << (char) ch;
528 0 : while( IS_BINARY(ch = strm->get()) );
529 0 : strm->putback(sal::static_int_cast<char>(ch));
530 : }
531 0 : else if( isdigit(ch) ) {
532 0 : do token << (char) ch;
533 0 : while( isdigit(ch = strm->get()) );
534 0 : strm->putback(sal::static_int_cast<char>(ch));
535 : }
536 : else
537 0 : token << (char) ch;
538 :
539 0 : return token.length();
540 : }
541 :
542 0 : static int read_white_space(MzString& outs, istream *strm)
543 : {
544 : int result;
545 :
546 0 : if( stk->state(strm) ) {
547 0 : outs << stk->white;
548 0 : stk->white = 0;
549 0 : result = stk->token[0];
550 : }
551 : else {
552 : int ch;
553 0 : while( IS_WS(ch = strm->get()) )
554 0 : outs << (char )ch;
555 0 : strm->putback(sal::static_int_cast<char>(ch));
556 0 : result = ch;
557 : }
558 0 : return result;
559 : }
560 :
561 : /* 인수가 필요하지 않은 경우 각 항목간의 구분은 space와 brace
562 : sqrt {ab}c = sqrt{ab} c
563 : (, }는 grouping
564 : ^, _ 는 앞뒤로 결합한다.
565 :
566 : sqrt 등과 같이 인수가 있는 형식 정리
567 : sqrt a -> sqrt{a}
568 : sqrt {a} -> sqrt{a}
569 : 1 이상의 인수가 있는 경우 인수들간의 역백은 없앤다.
570 : \frac a b -> frac{a}{b}
571 : over의 형식 정리
572 : a over b -> {a}over{b}
573 : */
574 :
575 0 : static int eq_word(MzString& outs, istream *strm, int status)
576 : {
577 0 : MzString token, white, state;
578 : int result;
579 : char keyword[256];
580 : const hwpeq *eq;
581 :
582 0 : next_token(white, token, strm);
583 0 : if (token.length() <= 0)
584 0 : return 0;
585 0 : result = token[0];
586 :
587 0 : if( token.compare("{") == 0 ) {
588 0 : state << white << token;
589 0 : eq_sentence(state, strm, "}");
590 : }
591 0 : else if( token.compare("left") == 0 ) {
592 0 : state << white << token;
593 0 : next_token(white, token, strm);
594 0 : state << white << token;
595 :
596 0 : eq_sentence(state, strm, "right");
597 :
598 0 : next_token(white, token, strm);
599 0 : state << white << token;
600 : }
601 : else {
602 : /* 정상적인 token */
603 0 : int script_status = SCRIPT_NONE;
604 : while( true ) {
605 0 : state << white << token;
606 0 : make_keyword(keyword, token);
607 0 : if( token[0] == '^' )
608 0 : script_status |= SCRIPT_SUP;
609 0 : else if( token[0] == '_' )
610 0 : script_status |= SCRIPT_SUB;
611 : else
612 0 : script_status = SCRIPT_NONE;
613 :
614 0 : if( 0 != (eq = lookup_eqn(keyword)) ) {
615 0 : int nargs = eq->nargs;
616 : int ch;
617 0 : while( nargs-- ) {
618 0 : ch = read_white_space(state, strm);
619 0 : if( ch != '{' ) state << '{';
620 0 : eq_word(state, strm, script_status);
621 0 : if( ch != '{' ) state << '}';
622 : }
623 : }
624 :
625 0 : if( !next_token(white, token, strm) )
626 0 : break;
627 : // end loop and restart with this
628 0 : if( (token[0] == '^' && status && !(status & SCRIPT_SUP)) ||
629 0 : (token[0] == '_' && status && !(status & SCRIPT_SUB)) ||
630 0 : strcmp("over", token) == 0 || strcmp("atop", token) == 0 ||
631 0 : strchr("{}#&`", token[0]) ||
632 0 : (!strchr("^_", token[0]) && white.length()) )
633 : {
634 0 : push_token(white, token, strm);
635 0 : break;
636 : }
637 0 : }
638 : }
639 0 : outs << state;
640 :
641 0 : return result;
642 : }
643 :
644 0 : static bool eq_sentence(MzString& outs, istream *strm, const char *end)
645 : {
646 0 : MzString state;
647 0 : MzString white, token;
648 0 : bool multiline = false;
649 :
650 0 : read_white_space(outs, strm);
651 0 : while( eq_word(state, strm) ) {
652 0 : if( !next_token(white, token, strm) ||
653 0 : (end && strcmp(token.c_str(), end) == 0) )
654 : {
655 0 : state << white << token;
656 0 : break;
657 : }
658 0 : push_token(white, token, strm);
659 0 : if( !token.compare("atop") || !token.compare("over") )
660 0 : outs << '{' << state << '}';
661 : else {
662 0 : if( !token.compare("#") )
663 0 : multiline = true;
664 0 : outs << state;
665 : }
666 0 : state = 0;
667 0 : read_white_space(outs, strm);
668 : }
669 0 : outs << state;
670 0 : return multiline;
671 : }
672 :
673 0 : static char eq2ltxconv(MzString& sstr, istream *strm, const char *sentinel)
674 : {
675 0 : MzString white, token;
676 : char key[256];
677 : int ch, result;
678 0 : const hwpeq *eq = 0;
679 :
680 0 : while( 0 != (result = next_token(white, token, strm)) ) {
681 0 : if( sentinel && (result == 1) && strchr(sentinel, token[0]) )
682 0 : break;
683 0 : make_keyword(key, token);
684 0 : if( (eq = lookup_eqn(key)) != 0 ) {
685 0 : if( eq->latex )
686 0 : strcpy(key, eq->latex);
687 : else {
688 0 : key[0] = '\\';
689 0 : strcpy(key + 1, eq->key);
690 : }
691 0 : if( (eq->flag & EQ_CASE) && isupper(token[0]) )
692 0 : key[1] = sal::static_int_cast<char>(toupper(key[1]));
693 0 : token = key;
694 : }
695 :
696 0 : if( token[0] == '{' ) { // grouping
697 0 : sstr << white << token;
698 0 : eq2ltxconv(sstr, strm, "}");
699 0 : sstr << '}';
700 : }
701 0 : else if( eq && (eq->flag & EQ_ENV) ) {
702 0 : next_token(white, token, strm);
703 0 : if( token[0] != '{' )
704 0 : return 0;
705 0 : sstr << "\\begin" << "{" << eq->key << "}" << ENDL ;
706 0 : eq2ltxconv(sstr, strm, "}");
707 0 : if( sstr[sstr.length() - 1] != '\n' )
708 0 : sstr << ENDL ;
709 0 : sstr << "\\end" << "{" << eq->key << "}" << ENDL ;
710 : }
711 0 : else if( eq && (eq->flag & EQ_ATOP) ) {
712 0 : if( sstr.length() == 0 )
713 0 : sstr << '{';
714 : else {
715 0 : int pos = sstr.rfind('}');
716 0 : if( 0 < pos)
717 0 : sstr.replace(pos, ' ');
718 : }
719 0 : sstr << token;
720 0 : while( (ch = strm->get()) != EOF && IS_WS(ch) )
721 0 : sstr << (char)ch;
722 0 : if( ch != '{' )
723 0 : sstr << "{}";
724 : else {
725 0 : eq2ltxconv(sstr, strm, "}");
726 0 : sstr << '}';
727 0 : }
728 : }
729 : else
730 0 : sstr << white << token;
731 : }
732 0 : return token[0];
733 : }
734 :
735 0 : void eq2latex(MzString& outs, char *s)
736 : {
737 : assert(s);
738 0 : if( stk == 0 )
739 0 : stk = new eq_stack;
740 :
741 0 : MzString tstr;
742 :
743 0 : istringstream tstrm(s);
744 0 : bool eqnarray = eq_sentence(tstr, &tstrm);
745 0 : istringstream strm(tstr.c_str());
746 :
747 0 : if( eqnarray )
748 0 : outs << "\\begin{array}{rllll}" << ENDL;
749 0 : eq2ltxconv(outs, &strm, 0);
750 0 : outs << ENDL;
751 0 : if( eqnarray )
752 0 : outs << "\\end{array}" << ENDL;
753 0 : delete stk;
754 0 : stk = 0;
755 0 : }
756 :
757 : /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
|