fx9860g and fxcg50 2D math rendering library with support for TeX syntax.
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

TeX.y 13KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505
  1. %{
  2. #include <TeX/config.h>
  3. #include <TeX/node.h>
  4. #include <TeX/flow.h>
  5. #include <TeX/env.h>
  6. #include <stdlib.h>
  7. #include <string.h>
  8. /* yylex(): The lexer, as usual */
  9. int yylex(void);
  10. /* yyerror(): The error function */
  11. void yyerror(char const *message);
  12. //---
  13. // Environment stack management
  14. //---
  15. /* Environment stack and current environment's index */
  16. static struct TeX_Env *env_stack[TEX_ENV_DEPTH] = { NULL };
  17. static int env_index = 0;
  18. /* Current environment and its type */
  19. #define env (env_stack[env_index])
  20. void env_push(char const *type)
  21. {
  22. struct TeX_Env *new_env = NULL;
  23. if(!strcmp(type, "primary"))
  24. new_env = TeX_env_primary();
  25. if(!strcmp(type, "matrix"))
  26. new_env = TeX_env_matrix();
  27. /* If new_env is NULL... still insert it. This is more likely to cause
  28. an error, and avoids asymmetry with pops */
  29. env_index++;
  30. env = new_env;
  31. }
  32. struct TeX_Env *env_pop(void)
  33. {
  34. struct TeX_Env *ret = env;
  35. env_index--;
  36. return ret;
  37. }
  38. //---
  39. // Extra node allocation functions
  40. //---
  41. /* mknode_f(): Make a function node with a flow argument
  42. @name Command name; intended for built-in nodes only
  43. @flow Flow argument
  44. Returns a node for command [name] with as argument [flow]. */
  45. struct TeX_Node *mknode_f(char const *name, struct TeX_Flow *flow)
  46. {
  47. struct TeX_Node *node = TeX_node_command(name);
  48. return TeX_node_add_arg(node, flow);
  49. }
  50. /* mknode_t(): Make a function node with a text argument */
  51. struct TeX_Node *mknode_t(char const *name, char const *text)
  52. {
  53. struct TeX_Flow *flow = TeX_flow_add_node(NULL, TeX_node_text(text));
  54. return mknode_f(name, flow);
  55. }
  56. %}
  57. %define api.value.type union
  58. /* Basic text and commands */
  59. %token <char const *> TEXT
  60. %token <char const *> COMMAND
  61. %token <char const *> COMMAND_ABS
  62. /* Special tokens for environments */
  63. %token ENV_BEGIN
  64. %token ENV_END
  65. %left '^' '_'
  66. %type <struct TeX_Flow *> flow
  67. %type <struct TeX_Node *> node
  68. %type <struct TeX_Node *> node_abs
  69. %type <struct TeX_Node *> env_node
  70. %type <struct TeX_Env *> env_end
  71. %%
  72. env:
  73. %empty { }
  74. | env node { env->add_node(env, $2); }
  75. | env '&' { env->add_separator(env); }
  76. | env '\\' { env->add_break(env); }
  77. flow:
  78. %empty { $$ = NULL; }
  79. | flow node { $$ = TeX_flow_add_node($1, $2); }
  80. node:
  81. TEXT { $$ = TeX_node_text($1); }
  82. | COMMAND { $$ = TeX_node_command($1); }
  83. | node_abs TEXT { $$ = TeX_node_absorb($1, $2); }
  84. | node '{' flow '}' { $$ = TeX_node_add_arg($1, $3); }
  85. /* Special shortcuts for the superscript and subscript classes */
  86. | '^' TEXT { $$ = mknode_t("\\sup", $2); }
  87. | '_' TEXT { $$ = mknode_t("\\sub", $2); }
  88. | '^' '{' flow '}' { $$ = mknode_f("\\sup", $3); }
  89. | '_' '{' flow '}' { $$ = mknode_f("\\sub", $3); }
  90. /* Environments */
  91. | env_node { $$ = $1; }
  92. node_abs:
  93. COMMAND_ABS { $$ = TeX_node_command($1); }
  94. env_node:
  95. /* TODO: Add TeX_mknode_env() */
  96. env_begin env env_end { $$ = TeX_node_env($3); }
  97. env_begin:
  98. ENV_BEGIN '{' TEXT '}' { env_push($3); }
  99. env_end:
  100. ENV_END '{' TEXT '}' { $$ = env_pop(); }
  101. %%
  102. //---
  103. // The lexer
  104. //
  105. // The lexical analysis is actually the subtle part in this program,
  106. // because we want to preserve text sections without cutting them to avoid
  107. // merging string tokens later on.
  108. //
  109. // The program below is a lexer with some hybrid parser features that
  110. // accomplishes this task.
  111. // - It has a lookahead character and never retracts.
  112. // - It has a notion of context (notably single-character mode).
  113. // This machinery, however, can only be edited by hand.
  114. //
  115. // The good side of this is that it's much more efficient because we don't
  116. // break sections without commands, so there are less allocations and less
  117. // tokens to parse.
  118. //---
  119. /* Character source (input formula) */
  120. static char const *lex;
  121. /* Accumulator. This buffer contains text that will be emitted in the next
  122. token, more precisely everything between the start of the token and [lex]
  123. (the [lex] pointer will move forward during lexing), but with escape
  124. characters decoded.
  125. Note that this buffer is static *and* unique, so every token will make its
  126. way through the accumulator. As a consequence, we must apply a parsing rule
  127. that copies the accumulated string every time we flush the buffer. If a
  128. parsing rule has several parameters with data in the accumulator, then all
  129. except the last will have their data overridden before the semantic rule is
  130. executed. */
  131. static char acc_buffer[TEX_LEXER_BUFSIZE];
  132. /* Position of the next free character in [acc_buffer] */
  133. static char *acc;
  134. /* enum state - Lexer automaton states.
  135. The automaton state represents what has been discovered at the previous
  136. step; the state is changed to store instructions for the next character
  137. input round. Often, when a character is read, previously-lexed input is
  138. emitted and the character is stored in the accumulator. Possibly the state
  139. is changed. */
  140. static enum {
  141. /* Reached end-of-file (continuously emits $end tokens) */
  142. eof = 0,
  143. /* When reading text sections to be displayed on the screen, possibly
  144. with escapes - this is the part we don't want to cut. */
  145. text,
  146. /* When reading a command name after a '\' */
  147. command,
  148. /* The following states are transitioned into when their characters are
  149. read from input. The associated token will only be emitted in the
  150. next character input round. */
  151. superscript = '^',
  152. subscript = '_',
  153. lbrace = '{',
  154. rbrace = '}',
  155. break_symbol = '\\',
  156. separator = '&',
  157. } state = text;
  158. /* Single-character mode. When a command name, '^' or '_' is not followed by a
  159. '{', the argument is taken to be the next input character. This mode alters
  160. the behavior of the [text] state (mainly) to emit a token at the next
  161. character instead of storing data in the accumulator. */
  162. int single;
  163. /* Lookahead symbol. The combination of the lookahead character with the
  164. delayed-emission described in [enum state] gives this lexer two characters
  165. to make decisions. For example, when lexing "ab^2", at the third character
  166. input round:
  167. * '^' is read from the input
  168. * "ab" is released as a TEXT token and the accumulator is emptied
  169. * '2' is seen as lookahead and single-character mode is activated */
  170. static int la;
  171. /* forward(): Maybe return
  172. Returns the value of @expr if it's nonnegative; does nothing otherwise. */
  173. #define forward(expr) do { \
  174. int v = expr; \
  175. if(v >= 0) return v; \
  176. } while(0)
  177. /* lexer_init(): TODO: Document lexer_init() */
  178. void lexer_init(char const *formula)
  179. {
  180. acc = acc_buffer;
  181. /* When the formula is empty, don't let the lexer read a lookahead! */
  182. if(!formula || !formula[0])
  183. {
  184. lex = NULL;
  185. state = eof;
  186. la = 0;
  187. single = 0;
  188. return;
  189. }
  190. lex = formula + 1;
  191. state = text;
  192. la = formula[0];
  193. single = 0;
  194. }
  195. /* release(): Release the lexer buffer as a textual token
  196. This function returns produces a text token of the requested types using the
  197. contents of the lexer buffer, but only if the buffer is not empty. Thus the
  198. call must be wrapped into forward() and not return if there is a fallback
  199. action.
  200. @token Requested token type
  201. Returns a nonnegative token number if the buffer is not empty, or -1. */
  202. static int release(int token)
  203. {
  204. if(acc == acc_buffer) return -1;
  205. *acc++ = 0;
  206. acc = acc_buffer;
  207. /* After all we don't need to switch on token */
  208. /* WARN: may be fragile, look for appropriate flags */
  209. yylval.TEXT = yylval.COMMAND = yylval.COMMAND_ABS = acc_buffer;
  210. return token;
  211. }
  212. /* accumulate(): Accumulate characters in the lexer buffer
  213. Adds a new character @c to the lexer buffer. If the buffer is full or if
  214. single-character mode is active, emits a token and empties the buffer. For
  215. this return to work, the call to accumulate() must be wrapped in forward().
  216. @c Character to add (1 byte)
  217. Returns a nonnegative token number if one is emitted, -1 otherwise. */
  218. static int accumulate(int c)
  219. {
  220. *acc++ = c;
  221. /* Send a token if the buffer is full or single-character mode is on */
  222. if(acc >= acc_buffer + TEX_LEXER_BUFSIZE - 1 || single)
  223. {
  224. single = 0;
  225. switch(state)
  226. {
  227. case text: return release(TEXT);
  228. case command: return release(COMMAND);
  229. default: break;
  230. }
  231. }
  232. /* Continue lexing for now */
  233. return -1;
  234. }
  235. /* acccmp(): String comparison with the accumulator
  236. Like strcmp(), but uses the non-NUL-terminated accumulator as one input. */
  237. static int acccmp(char const *str)
  238. {
  239. return strncmp(acc_buffer, str, acc - acc_buffer);
  240. }
  241. /* lexer_text(): Execute a step of lexing from the text state
  242. In text state, we are accumulating characters as long as possible without
  243. releasing tokens. Longer chunks of text render faster on monochrome
  244. calculators and need less memory.
  245. This mode is exited whenever a metacharacter, that is '{', '}', '^' or '_'.
  246. The backslash character '\' is treated with more care because escaped
  247. metacharacters such as '\{' still count as text and don't need us to release
  248. the accumulator.
  249. Returns a token ID if one is emitted, -1 otherwise. */
  250. static int lexer_text(void)
  251. {
  252. /* Feed lexer: this is safe thanks because I heed for c = EOF */
  253. int c = la;
  254. if(!c) { state = eof; return release(TEXT); }
  255. la = *lex++;
  256. /* Escapes and command names */
  257. if(c == '\\')
  258. {
  259. /* Command name: release current buffer and move */
  260. if(la >= 'a' && la <= 'z')
  261. {
  262. state = command;
  263. return release(TEXT);
  264. }
  265. /* Breaks */
  266. if(la == '\\')
  267. {
  268. la = *lex++;
  269. state = '\\';
  270. return release(TEXT);
  271. }
  272. /* Escaped character: accumulate lookahead and feed lexer.
  273. Feeding is safe because current lookahead is not EOF */
  274. if(strchr("{}^_&", la))
  275. {
  276. c = la;
  277. la = *lex++;
  278. /* Intentional fall-through */
  279. }
  280. /* TODO: Emit a warning in an "else" clause? */
  281. return accumulate(c);
  282. }
  283. /* Opening and closing braces are always syntactic elements */
  284. else if(c == '{' || c == '}' || c == '&')
  285. {
  286. state = c;
  287. return release(TEXT);
  288. }
  289. /* Superscript and subscript: heed for various input modes */
  290. else if(c == '^' || c == '_')
  291. {
  292. /* In all cases, prepare to emit c at next lexing round */
  293. state = c;
  294. /* If the next character is not '{', then we don't have a {}
  295. for the argument; enable single-character mode */
  296. if(la != '{') single = 1;
  297. /* Then emit what was already in the buffer, as text */
  298. return release(TEXT);
  299. }
  300. /* Accumulate the current character in the buffer until it's full */
  301. return accumulate(c);
  302. }
  303. /* lexer_command(): Execute a step of lexing from the command state
  304. This state is transitioned into when a '\' followed by a letter is
  305. encountered. The lexer remains there until the end of the command, signaled
  306. by a non-letter character, is reached.
  307. At this point, the lexer can either enter the text mode again and wait for a
  308. '{' to start arguments, or enter the text mode with the single-character
  309. argument flag so any character that comes next is treated as the argument.
  310. The original TeX does this conditionally, using single-character arguments
  311. if and only if the argument does not start with a '{'. However, in this
  312. program, because the number of arguments is not known at parse time, this
  313. would make dumb commands such as '\alpha' gobble an unbounded amount of
  314. arguments. So all commands use brace-only arguments, except for a designated
  315. set. Typically this includes \left and \right which are mainly used without
  316. braces in practice.
  317. Returns a token ID if one is emitted, -1 otherwise. */
  318. static int lexer_command(void)
  319. {
  320. /* Feed lexer; this is safe because I heed for la = EOF */
  321. int c = la;
  322. if(!c) { state = eof; return release(COMMAND); }
  323. la = *lex++;
  324. /* In this state, c is always in the range a .. z */
  325. int ret = accumulate(c);
  326. /* Continue if next character is a command continuation */
  327. if(la >= 'a' && la <= 'z') return ret;
  328. /* Otherwise, release command name */
  329. state = text;
  330. /* Absorbing commands include "left" and "right" */
  331. if(!acccmp("left") || !acccmp("right"))
  332. {
  333. single = 1;
  334. return release(COMMAND_ABS);
  335. }
  336. /* Special environment commands */
  337. if(!acccmp("begin"))
  338. return release(ENV_BEGIN);
  339. if(!acccmp("end"))
  340. return release(ENV_END);
  341. return release(COMMAND);
  342. }
  343. /* yylex(): The lexer
  344. Returns the token type of the next token in the string initialized by the
  345. last call to parse_start(). */
  346. int yylex(void)
  347. {
  348. while(1)
  349. {
  350. if(state == text) forward(lexer_text());
  351. else if(state == command) forward(lexer_command());
  352. else break;
  353. }
  354. /* End-of-File: give up */
  355. if(state == eof) return 0;
  356. /* Character-specific states: feed and return state number */
  357. int c = state;
  358. state = text;
  359. return c;
  360. }
  361. //---
  362. // Parser interface
  363. //---
  364. /* parse(): Parse into a TeX environment */
  365. struct TeX_Env *parse(char const *str)
  366. {
  367. /* Initialize lexer to run on a string */
  368. lexer_init(str);
  369. /* Push a primary environment */
  370. env_push("primary");
  371. int x = yyparse();
  372. /* Pop the primary environment out of stack */
  373. return x ? NULL: env_pop();
  374. }
  375. //---
  376. // Printing and errors
  377. //---
  378. #ifndef TEX_PRINT
  379. void yyerror(TEX_UNUSED char const *error)
  380. {
  381. }
  382. #else
  383. #include <stdio.h>
  384. #include <TeX/print.h>
  385. void yyerror(char const *error)
  386. {
  387. fprintf(stderr, "Parsing failed: %s\n", error);
  388. }
  389. /* TeX_print_lex(): Print the result of lexing */
  390. void TeX_print_lex(char const *formula)
  391. {
  392. lexer_init(formula);
  393. int token;
  394. do
  395. {
  396. token = yylex();
  397. printf("%-3d ", token);
  398. if(token > 0x20 && token < 0x7f)
  399. printf("%c", token);
  400. if(token == TEXT)
  401. printf("TEXT %s", yylval.TEXT);
  402. if(token == COMMAND)
  403. printf("COMMAND %s", yylval.COMMAND);
  404. if(token == COMMAND_ABS)
  405. printf("COMMAND_ABS %s", yylval.COMMAND_ABS);
  406. if(token == ENV_BEGIN)
  407. printf("ENV_BEGIN");
  408. if(token == ENV_END)
  409. printf("ENV_END");
  410. printf("\n");
  411. }
  412. while(token != 0);
  413. }
  414. #endif /* TEX_PRINT */