mathematical 0.4.2 → 0.5.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (40) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +20 -9
  3. data/Rakefile +5 -24
  4. data/ext/mathematical/extconf.rb +8 -3
  5. data/ext/mathematical/lasem/src/lsmdomparser.c +7 -7
  6. data/ext/mathematical/lasem/src/lsmdomparser.h +2 -2
  7. data/ext/mathematical/lasem/src/lsmitex.c +8 -5
  8. data/ext/mathematical/lasem/src/lsmitex.h +1 -1
  9. data/ext/mathematical/lasem/src/lsmmathmldocument.c +2 -2
  10. data/ext/mathematical/lasem/src/lsmmathmldocument.h +1 -1
  11. data/ext/mathematical/lasem/tests/lsmtest.c +2 -2
  12. data/ext/mathematical/mathematical.c +119 -10
  13. data/ext/mathematical/mtex2MML/ext/extconf.rb +4 -0
  14. data/ext/mathematical/mtex2MML/src/lex.yy.c +6791 -0
  15. data/ext/mathematical/mtex2MML/src/mtex2MML.h +59 -0
  16. data/ext/mathematical/mtex2MML/src/parse_extras.c +306 -0
  17. data/ext/mathematical/mtex2MML/src/parse_extras.h +78 -0
  18. data/ext/mathematical/mtex2MML/src/stack.c +80 -0
  19. data/ext/mathematical/mtex2MML/src/stack.h +101 -0
  20. data/ext/mathematical/mtex2MML/src/string_extras.c +211 -0
  21. data/ext/mathematical/mtex2MML/src/string_extras.h +46 -0
  22. data/ext/mathematical/mtex2MML/src/y.tab.c +7090 -0
  23. data/ext/mathematical/mtex2MML/src/y.tab.h +393 -0
  24. data/lib/mathematical.rb +1 -0
  25. data/lib/mathematical/corrections.rb +34 -0
  26. data/lib/mathematical/render.rb +21 -12
  27. data/lib/mathematical/version.rb +1 -1
  28. data/mathematical.gemspec +2 -2
  29. data/test/mathematical/corrections_test.rb +56 -0
  30. data/test/mathematical/fixtures/png/pmatrix.png +0 -0
  31. data/test/mathematical/fixtures_test.rb +11 -1
  32. data/test/mathematical/maliciousness_test.rb +10 -2
  33. data/test/mathematical/mathml_test.rb +22 -0
  34. data/test/mathematical/png_test.rb +26 -0
  35. data/test/test_helper.rb +13 -0
  36. metadata +150 -134
  37. data/ext/mathematical/itexToMML/itex2MML.h +0 -63
  38. data/ext/mathematical/itexToMML/lex.yy.c +0 -6548
  39. data/ext/mathematical/itexToMML/y.tab.c +0 -6179
  40. data/ext/mathematical/itexToMML/y.tab.h +0 -389
@@ -0,0 +1,59 @@
1
+ #ifndef MTEX2MML_H
2
+ #define MTEX2MML_H
3
+
4
+ #define MTEX2MML_VERSION "1.0.0"
5
+
6
+ #ifdef __cplusplus
7
+ extern "C" {
8
+ #endif
9
+
10
+ /* Step 1. Parse a buffer with mtex source; return value is mathml, or 0 on failure (e.g., parse error).
11
+ */
12
+ extern char * mtex2MML_parse (const char * buffer, unsigned long length);
13
+
14
+ /* Step 2. Free the string from Step 1.
15
+ */
16
+ extern void mtex2MML_free_string (char * str);
17
+
18
+
19
+ /* Alternatively, to filter generic source and converting embedded equations, use:
20
+ */
21
+ extern int mtex2MML_filter (const char * buffer, unsigned long length);
22
+
23
+ extern int mtex2MML_html_filter (const char * buffer, unsigned long length);
24
+ extern int mtex2MML_strict_html_filter (const char * buffer, unsigned long length);
25
+
26
+
27
+ /* To change output methods:
28
+ *
29
+ * Note: If length is 0, then buffer is treated like a string; otherwise only length bytes are written.
30
+ */
31
+ extern void (*mtex2MML_write) (const char * buffer, unsigned long length); /* default writes to stdout */
32
+ extern void (*mtex2MML_write_mathml) (const char * mathml); /* default calls mtex2MML_write(mathml,0) */
33
+ extern void (*mtex2MML_error) (const char * msg); /* default writes to stderr */
34
+
35
+
36
+ /* Other stuff:
37
+ */
38
+ extern void mtex2MML_setup (const char * buffer, unsigned long length);
39
+
40
+ extern void mtex2MML_restart ();
41
+
42
+ extern char * mtex2MML_copy_string (const char * str);
43
+ extern char * mtex2MML_copy_string_extra (const char * str, unsigned extra);
44
+ extern char * mtex2MML_copy2 (const char * first, const char * second);
45
+ extern char * mtex2MML_copy3 (const char * first, const char * second, const char * third);
46
+ extern char * mtex2MML_copy_escaped (const char * str);
47
+
48
+ extern char * mtex2MML_empty_string;
49
+
50
+ extern int mtex2MML_lineno;
51
+
52
+ extern int mtex2MML_rowposn;
53
+ extern int mtex2MML_displaymode;
54
+
55
+ #ifdef __cplusplus
56
+ }
57
+ #endif
58
+
59
+ #endif /* ! MTEX2MML_H */
@@ -0,0 +1,306 @@
1
+ #include <stdio.h>
2
+ #include <stdlib.h>
3
+ #include <string.h>
4
+ #include "parse_extras.h"
5
+ #include "string_extras.h"
6
+ #include "stack.h"
7
+
8
+ #ifdef FLIP_OFFSET_VAL
9
+ #define OFFSET_VAL 0
10
+ #else
11
+ #define OFFSET_VAL 1
12
+ #endif
13
+
14
+ void initSymbolDataArray(symbolDataArray *a, size_t initialSize)
15
+ {
16
+ // Allocate initial space
17
+ a->array = (symbolData *)malloc(initialSize * sizeof(symbolData));
18
+
19
+ a->used = 0; // no elements used
20
+ a->size = initialSize; // available number of elements
21
+
22
+ // Initialize all elements of the array at once: they are contiguous
23
+ memset(&a->array[0], 0, sizeof(symbolData) * initialSize);
24
+ }
25
+
26
+ void insertSymbolDataArray(symbolDataArray *a, symbolData element)
27
+ {
28
+ if (a->used == a->size) {
29
+ a->size *= 2;
30
+ a->array = (symbolData *)realloc(a->array, a->size * sizeof(symbolData));
31
+ // Initialize the last/new elements of the reallocated array
32
+ memset(&a->array[a->used],0, sizeof(symbolData) * (a->size - a->used));
33
+ }
34
+
35
+ a->array[a->used].attribute = (char*)malloc(strlen(element.attribute) + 1);
36
+ strcpy(a->array[a->used].attribute, element.attribute);
37
+
38
+ a->array[a->used].offset_pos = element.offset_pos;
39
+
40
+ a->used++;
41
+ }
42
+
43
+ void sortSymbolDataArray(symbolDataArray *a) {
44
+ int i, j, n = a->used;
45
+
46
+ for(i = 1; i < n; i++) {
47
+ for(j = 0; j < n - i; j++) {
48
+ if(a->array[j].offset_pos < a->array[j+1].offset_pos) {
49
+ symbolData temp = a->array[j];
50
+ a->array[j] = a->array[j+1];
51
+ a->array[j+1] = temp;
52
+ }
53
+ }
54
+ }
55
+ }
56
+
57
+ void deleteSymbolDataArray(symbolDataArray *a)
58
+ {
59
+ int i;
60
+
61
+ // Free all name variables of each array element first
62
+ for(i = 0; i <a->used; i++)
63
+ {
64
+ free(a->array[i].attribute);
65
+ a->array[i].attribute = NULL;
66
+ }
67
+
68
+ // Now free the array
69
+ free(a->array);
70
+ a->array = NULL;
71
+
72
+ a->used = 0;
73
+ a->size = 0;
74
+ }
75
+
76
+ char * env_replacements(const char *string) {
77
+ stackT array_stack;
78
+ stackElementT stack_item, last_stack_item;
79
+
80
+ char *tok = NULL, *at_top = NULL;
81
+ char *new_environment = dupe_string(string), *dupe_str = dupe_string(string);
82
+ char *line = strtok(dupe_str, "\n");
83
+ char *attr_rowlines = "", *attr_rowspacing = "", *em_str, *temp = "";
84
+
85
+ const char *from = "\\begin", *until = "\\end", *hline = "\\hline", *hdashline = "\\hdashline",
86
+ *line_separator = "\\\\",
87
+ *em_pattern_begin = "\\[", *em_pattern_end = "]";
88
+
89
+ int start = 0, offset = 0, attr_rowlines_len = 0, str_len = 0, i = 0;
90
+ symbolDataArray hline_data_array;
91
+ symbolData hline_data;
92
+
93
+ symbolDataArray row_spacing_data_array;
94
+ symbolData row_spacing_data;
95
+
96
+ // set up the array stack
97
+ StackInit(&array_stack, strlen(new_environment));
98
+ initSymbolDataArray(&hline_data_array, 5);
99
+ initSymbolDataArray(&row_spacing_data_array, 1);
100
+
101
+ // if not an environment, don't bother going on
102
+ if ((strstr(string, from) == NULL && strstr(string, until) == NULL)) {
103
+ StackDestroy(&array_stack);
104
+ deleteSymbolDataArray(&hline_data_array);
105
+ deleteSymbolDataArray(&row_spacing_data_array);
106
+ free(dupe_str);
107
+
108
+ return string;
109
+ }
110
+
111
+ while (line != NULL) {
112
+ str_len = strlen(line) + 1;
113
+
114
+ stack_item.line = line;
115
+ stack_item.line_pos = start + 1;
116
+
117
+ start += str_len;
118
+
119
+ StackPush(&array_stack, stack_item);
120
+
121
+ if (strstr(line, until) != NULL) {
122
+ while (!StackIsEmpty(&array_stack)) {
123
+ last_stack_item = StackPop(&array_stack);
124
+ attr_rowlines_len = strlen(attr_rowlines);
125
+ at_top = strstr(last_stack_item.line, from);
126
+
127
+ // we've reached the top, but there looks like there might be some data
128
+ if (at_top != NULL && strstr(last_stack_item.line, line_separator) == NULL) {
129
+ break;
130
+ }
131
+
132
+ // looking for a line match
133
+ if (strstr(last_stack_item.line, hline) != NULL) {
134
+ if (attr_rowlines_len > 0)
135
+ remove_last_char(attr_rowlines);
136
+ attr_rowlines = join(attr_rowlines, "s");
137
+ }
138
+ else if (strstr(last_stack_item.line, hdashline) != NULL) {
139
+ if (attr_rowlines_len > 0)
140
+ remove_last_char(attr_rowlines);
141
+ attr_rowlines = join(attr_rowlines, "d");
142
+ }
143
+ else {
144
+ attr_rowlines = join(attr_rowlines, "0");
145
+ }
146
+
147
+ if (strstr(last_stack_item.line, line_separator) != NULL) {
148
+ if ( (tok = strstr(last_stack_item.line, em_pattern_begin)) != NULL) {
149
+ temp = tok + 2;
150
+ if ( (tok = strstr(temp, em_pattern_end)) != NULL) {
151
+ offset = (int)(tok - temp);
152
+ em_str = malloc(offset);
153
+ if (strlen(em_str) != 0) {
154
+ em_str = dupe_string_n(temp, offset);
155
+ // MathML always expectes "em" points
156
+ convertToEm(em_str);
157
+ row_spacing_data.attribute = em_str;
158
+ row_spacing_data.offset_pos = -1; // this value is not really important
159
+ insertSymbolDataArray(&row_spacing_data_array, row_spacing_data);
160
+ free(em_str);
161
+ }
162
+ }
163
+ }
164
+ else {
165
+ row_spacing_data.attribute = "0em";
166
+ row_spacing_data.offset_pos = -1; // this value is not really important
167
+ insertSymbolDataArray(&row_spacing_data_array, row_spacing_data);
168
+ }
169
+ }
170
+
171
+ // we've reached the top, so stop.
172
+ if (at_top != NULL)
173
+ break;
174
+ }
175
+
176
+ if (attr_rowlines_len != 0) {
177
+ // array is form of \begin{array}[t]{cc..c}
178
+ tok = strstr(last_stack_item.line, "]{");
179
+ if (tok == NULL) {
180
+ // array is form of \begin{array}{cc..c}
181
+ tok = strstr(last_stack_item.line, "}{");
182
+ }
183
+ if (tok == NULL) {
184
+ // not an array, but rather, some env, like \begin{cases}
185
+ tok = strstr(last_stack_item.line, "}");
186
+ }
187
+
188
+ offset = last_stack_item.line_pos + (tok - last_stack_item.line);
189
+ // we cut the last char because we can skip the first row
190
+ remove_last_char(attr_rowlines);
191
+
192
+ // we reverse the string, because we're going backwards
193
+ strrev(attr_rowlines);
194
+
195
+ // empty rowlines should be reset
196
+ if (strlen(attr_rowlines) == 0)
197
+ attr_rowlines = join(attr_rowlines, "0");
198
+
199
+ attr_rowlines = join(join("(", attr_rowlines), ")");
200
+
201
+ for (i = row_spacing_data_array.used - 1; i >= 0; i--) {
202
+ attr_rowspacing = join(join(attr_rowspacing, row_spacing_data_array.array[i].attribute), "|");
203
+ }
204
+
205
+ if (strlen(attr_rowspacing) > 0) {
206
+ if (empty_row_spacings(attr_rowspacing) == 1) {
207
+ attr_rowspacing = "0.5ex";
208
+ }
209
+ else {
210
+ // last char is a pipe (|)
211
+ remove_last_char(attr_rowspacing);
212
+ }
213
+
214
+ attr_rowspacing = join(join("<", attr_rowspacing), ">");
215
+ }
216
+
217
+ hline_data.attribute = join(attr_rowspacing, attr_rowlines);
218
+ hline_data.offset_pos = offset + OFFSET_VAL;
219
+ insertSymbolDataArray(&hline_data_array, hline_data);
220
+ }
221
+
222
+ attr_rowlines = "";
223
+ attr_rowspacing = "";
224
+ attr_rowlines_len = 0;
225
+ }
226
+
227
+ line = strtok(NULL, "\n");
228
+ }
229
+
230
+ // sort array by highest values first, so that we can insert to new_environment from
231
+ // the bottom to the top (ensuring line numbers don't shift)
232
+ sortSymbolDataArray(&hline_data_array);
233
+
234
+ for (i = 0; i < hline_data_array.used; i++) {
235
+ insert_substring(&new_environment, hline_data_array.array[i].attribute, hline_data_array.array[i].offset_pos);
236
+ }
237
+
238
+ StackDestroy(&array_stack);
239
+ deleteSymbolDataArray(&hline_data_array);
240
+ deleteSymbolDataArray(&row_spacing_data_array);
241
+ free(dupe_str);
242
+
243
+ return new_environment;
244
+ }
245
+
246
+ const char *vertical_pipe_extract(const char *string) {
247
+ char *orig = dupe_string(string);
248
+ char *columnlines = "", *previous_column = "";
249
+ int i = 0;
250
+
251
+ if (strncmp(orig, "s", 1) == 0) {
252
+ columnlines = "frame=\"solid\" columnlines=\"";
253
+ remove_first_char(orig);
254
+ }
255
+ else if (strncmp(orig, "d", 1) == 0) {
256
+ columnlines = "frame=\"dashed\" columnlines=\"";
257
+ remove_first_char(orig);
258
+ }
259
+ else {
260
+ columnlines = "columnlines=\"";
261
+ }
262
+
263
+ char *token = strtok(orig, " ");
264
+
265
+ while (token != NULL) {
266
+ if (strncmp(token, "s", 1) == 0) {
267
+ previous_column = "s";
268
+ columnlines = join(columnlines, "solid ");
269
+ }
270
+ else if (strncmp(token, "d", 1) == 0) {
271
+ previous_column = "d";
272
+ columnlines = join(columnlines, "dashed ");
273
+ }
274
+ else {
275
+ if (i >= 1) { // we must skip the first blank col
276
+ // only if there is no previous border should a border be considered, eg. "cc", not "c|c"
277
+ if (strncmp(previous_column, "s", 1) != 0 && strncmp(previous_column, "d", 1) != 0) {
278
+ columnlines = join(columnlines, "none ");
279
+ }
280
+ previous_column = "0";
281
+ }
282
+ }
283
+
284
+ i++;
285
+ token = strtok(NULL, " ");
286
+ }
287
+
288
+ // an empty string here angers Lasem
289
+ if (strncmp(columnlines, "columnlines=\"\0", 14) == 0)
290
+ columnlines = "columnlines=\"none";
291
+ // an empty space also angers Lasem
292
+ else
293
+ remove_last_char(columnlines);
294
+
295
+ free(orig);
296
+ return columnlines;
297
+ }
298
+
299
+ const char *remove_excess_pipe_chars(const char *string) {
300
+ char *dup = dupe_string(string);
301
+
302
+ dup = replace_str(dup, "s", "");
303
+ dup = replace_str(dup, "d", "");
304
+
305
+ return dup;
306
+ }
@@ -0,0 +1,78 @@
1
+ #ifndef PARSE_EXTRAS_H
2
+ #define PARSE_EXTRAS_H
3
+
4
+ #ifdef __cplusplus
5
+ extern "C" {
6
+ #endif
7
+
8
+ typedef struct {
9
+ char *attribute;
10
+ int offset_pos;
11
+ } symbolData;
12
+
13
+ // array of structs
14
+ typedef struct
15
+ {
16
+ symbolData *array;
17
+ size_t used;
18
+ size_t size;
19
+ } symbolDataArray;
20
+
21
+ // set up array of symbols (like hlines)
22
+ extern void initSymbolDataArray(symbolDataArray *a, size_t initialSize);
23
+
24
+ // insert into symbol array
25
+ extern void insertSymbolDataArray(symbolDataArray *a, symbolData element);
26
+
27
+ // sort the array based on offset_pos
28
+ extern void sortSymbolDataArray(symbolDataArray *a);
29
+
30
+ // destroyes the array
31
+ extern void deleteSymbolDataArray(symbolDataArray *a);
32
+
33
+ // Move various symbols not easily supported inline with the `\begin` line
34
+ // This is so that the Bison parser can properly act on these. For example,
35
+ // `\hline`, `\hlinedash`, or `\\[2ex]` declerations.
36
+ //
37
+ // In order to properly address these situations, we need to parse them, find their symbols,
38
+ // and then modify the starting environment declaration to point out where those symbols are.
39
+ //
40
+ // Suppose there is an array like:
41
+ //
42
+ // \begin{array}{c:c}
43
+ // \begin{array}{c|cclc}
44
+ // \text{min} & 0 & 1 & 2 & 3\\\\
45
+ // \hline
46
+ // 0 & 0 & 0 & 0 & 0\\\\
47
+ // 1 & 0 & 1 & 1 & 1\\\\
48
+ // 2 & 0 & 1 & 2 & 2\\\\
49
+ // 3 & 0 & 1 & 2 & 3
50
+ // \end{array}
51
+ // &
52
+ // \begin{array}{c|cccl}
53
+ // \text{max}&0&1&2&3\\\\
54
+ // \hline
55
+ // 0 & 0 & 1 & 2 & 3\\\\
56
+ // 1 & 1 & 1 & 2 & 3\\\\
57
+ // 2 & 2 & 2 & 2 & 3\\\\
58
+ // 3 & 3 & 3 & 3 & 3
59
+ // \end{array}
60
+ // \end{array}
61
+ //
62
+ // The env_replacements function will push every line onto a stack. When an \end
63
+ // is detected, it starts popping off the stack until it reaches the corresponding
64
+ // \begin. It then modifies that line with attribute strings, an arrangement of the
65
+ // the symbols encountered while popping lines off.
66
+ extern char * env_replacements(const char *string);
67
+
68
+ // determines the column border arrangement from the array environment definition
69
+ extern const char *vertical_pipe_extract(const char *string);
70
+
71
+ // removes placeholder pipe characters from columnalign
72
+ extern const char *remove_excess_pipe_chars(const char *string);
73
+
74
+ #ifdef __cplusplus
75
+ }
76
+ #endif
77
+
78
+ #endif /* ! PARSE_EXTRAS_H */
@@ -0,0 +1,80 @@
1
+ /*
2
+ * File: stack.c
3
+ * Author: Robert I. Pitts <rip@cs.bu.edu>
4
+ * Last Modified: March 7, 2000
5
+ * Topic: Stack - Array Implementation
6
+ * ----------------------------------------------------------------
7
+ *
8
+ * This is an array implementation of a character stack.
9
+ */
10
+
11
+ #include <stdio.h>
12
+ #include <stdlib.h>
13
+ #include "stack.h"
14
+
15
+ /************************ Function Definitions **********************/
16
+
17
+ void StackInit(stackT *stackP, int maxSize)
18
+ {
19
+ stackElementT *newContents;
20
+
21
+ /* Allocate a new array to hold the contents. */
22
+
23
+ newContents = (stackElementT *)malloc(sizeof(stackElementT) * maxSize);
24
+
25
+ if (newContents == NULL) {
26
+ fprintf(stderr, "Insufficient memory to initialize stack.\n");
27
+ exit(1); /* Exit, returning error code. */
28
+ }
29
+
30
+ stackP->contents = newContents;
31
+ stackP->maxSize = maxSize;
32
+ stackP->top = -1; /* I.e., empty */
33
+ }
34
+
35
+ void StackDestroy(stackT *stackP)
36
+ {
37
+ /* Get rid of array. */
38
+ free(stackP->contents);
39
+
40
+ stackP->contents = NULL;
41
+ stackP->maxSize = 0;
42
+ stackP->top = -1; /* I.e., empty */
43
+ }
44
+
45
+ void StackPush(stackT *stackP, stackElementT element)
46
+ {
47
+ if (StackIsFull(stackP)) {
48
+ fprintf(stderr, "Can't push element on stack: stack is full.\n");
49
+ exit(1); /* Exit, returning error code. */
50
+ }
51
+
52
+ /* Put information in array; update top. */
53
+
54
+ stackP->contents[++stackP->top] = element;
55
+ }
56
+
57
+ stackElementT StackPop(stackT *stackP)
58
+ {
59
+ if (StackIsEmpty(stackP)) {
60
+ fprintf(stderr, "Can't pop element from stack: stack is empty.\n");
61
+ exit(1); /* Exit, returning error code. */
62
+ }
63
+
64
+ return stackP->contents[stackP->top--];
65
+ }
66
+
67
+ int StackIsEmpty(stackT *stackP)
68
+ {
69
+ return stackP->top < 0;
70
+ }
71
+
72
+ int StackIsFull(stackT *stackP)
73
+ {
74
+ return stackP->top >= stackP->maxSize - 1;
75
+ }
76
+
77
+ stackElementT StackTop(stackT *stackP)
78
+ {
79
+ return stackP->contents[stackP->top];
80
+ }