Flexible Bible Reference Parser
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.

225 lines
4.7 KiB

  1. #include "fbrp.h"
  2. // Struct to store read tokens
  3. struct Read {
  4. char text[MAX_WORD];
  5. int length;
  6. int type;
  7. };
  8. enum Types {
  9. DEFAULT,
  10. DIGIT,
  11. ALPHA,
  12. SEPERATOR,
  13. RANGE,
  14. MULTIPLE
  15. };
  16. // Test for a char in a string. Ex: 'c' in "cake"
  17. int testCharString(char test, char *seperators) {
  18. for (int c = 0; seperators[c] != '\0'; c++) {
  19. if (seperators[c] == test) {
  20. return 1;
  21. }
  22. }
  23. return 0;
  24. }
  25. // Test type of a char
  26. int determineType(char input) {
  27. if ((input >= 'A' && input <= 'Z') || (input >= 'a' && input <= 'z')) {
  28. return ALPHA;
  29. } else if (input >= '0' && input <= '9') {
  30. return DIGIT;
  31. } else if (testCharString(input, "_ :./\n\0")) {
  32. return SEPERATOR;
  33. } else if (input == '-') {
  34. return RANGE;
  35. } else if (input == ',') {
  36. return MULTIPLE;
  37. } else {
  38. return SEPERATOR;
  39. }
  40. }
  41. // Custom STRTOL like function
  42. int strspt(char *string, char *result) {
  43. int integer = 0;
  44. int resultC = 0;
  45. for (int c = 0; string[c] != '\0'; c++) {
  46. int charType = determineType(string[c]);
  47. if (charType == DIGIT) {
  48. integer = integer * 10;
  49. integer += string[c] - '0';
  50. } else if (charType == ALPHA) {
  51. result[resultC] = string[c];
  52. resultC++;
  53. }
  54. }
  55. result[resultC] = '\0';
  56. return integer;
  57. }
  58. // Simply make a custom function for strcat
  59. void mstrcat(char *s, char *t) {
  60. while(*s++);
  61. --s;
  62. while((*s++ = *t++));
  63. }
  64. // A simple function to increment a the next verse or chapter.
  65. // A complicated use of pointers could have been used,
  66. // but this is overall simpler.
  67. void setInt(struct Reference *ref, int on, int currentlyOn, int value, int append) {
  68. if (currentlyOn == 1) {
  69. ref->chapter[ref->chapterLength].r[on] = value;
  70. ref->chapterLength += append;
  71. } else if (currentlyOn == 2) {
  72. ref->verse[ref->verseLength].r[on] = value;
  73. ref->verseLength += append;
  74. }
  75. }
  76. // Main parsing function.
  77. void parseReference(struct Reference *ref, char *string) {
  78. // 2D Array for interpreting
  79. struct Read read[MAX_READ];
  80. int readX = 0;
  81. int readY = 0;
  82. int lastType = 0;
  83. int partType = 0;
  84. read[0].length = 0;
  85. for (int c = 0; string[c] != '\0'; c++) {
  86. int type = determineType(string[c]);
  87. // Skip seperator, but set lastType
  88. if (type == SEPERATOR) {
  89. lastType = type;
  90. // Set type when on last char
  91. if (string[c + 1] == '\0') {
  92. read[readY].type = partType;
  93. }
  94. continue;
  95. }
  96. // Jump to next part in read
  97. if (type != lastType && c != 0) {
  98. read[readY].text[readX] = '\0';
  99. read[readY].type = partType;
  100. readY++;
  101. read[readY].length = 0;
  102. readX = 0;
  103. partType = 0;
  104. }
  105. partType = type;
  106. // Append the character, make sure it
  107. // doesn't go out of bounds
  108. if (readX <= MAX_WORD - 2) {
  109. read[readY].text[readX] = string[c];
  110. readX++;
  111. }
  112. read[readY].length++;
  113. lastType = type;
  114. }
  115. // Null terminate last part
  116. read[readY].text[readX] = '\0';
  117. read[readY].type = partType;
  118. // readY++ for the last part
  119. readY++;
  120. // Now, start interpreting
  121. ref->chapterLength = 0;
  122. ref->verseLength = 0;
  123. ref->book[0] = '\0';
  124. int currentlyOn = 0;
  125. int jumping = 0;
  126. for (int p = 0; p < readY; p++) {
  127. // Skip range/multiple chars
  128. if (read[p].type == RANGE || read[p].type == MULTIPLE) {
  129. continue;
  130. }
  131. // Try to parse what could be string, int, or both.
  132. char tryString[MAX_WORD];
  133. int tryInt = -1;
  134. tryInt = strspt(read[p].text, tryString);
  135. // If chapter added and not jumping, then set verse
  136. if (ref->chapterLength >= 1 && jumping == 0) {
  137. currentlyOn = 2;
  138. }
  139. // if book and str undefined and p != 0 then SET chapter
  140. if (currentlyOn == 0 && *tryString == '\0' && p != 0) {
  141. currentlyOn = 1;
  142. }
  143. // if book and str undefined and p == 0 then assume part of book (Ex: [3] John)
  144. if (currentlyOn == 0 && *tryString == '\0' && p == 0) {
  145. mstrcat(ref->book, read[p].text);
  146. continue;
  147. }
  148. // if book and str valid then assume book
  149. if (currentlyOn == 0 && *tryString != '\0') {
  150. mstrcat(ref->book, read[p].text);
  151. continue;
  152. }
  153. // check next type
  154. int nextType = 0;
  155. if (p != readY - 1) {
  156. nextType = read[p + 1].type;
  157. }
  158. // Handle previous set jumps for range/multiple
  159. if (jumping == 1) {
  160. setInt(ref, 1, currentlyOn, tryInt, 1);
  161. jumping = 0;
  162. // Multiples after range
  163. if (nextType == MULTIPLE) {
  164. jumping = 2;
  165. continue;
  166. } else {
  167. currentlyOn = 2;
  168. continue;
  169. }
  170. } else if (jumping == 2) {
  171. jumping = 0;
  172. }
  173. // Check for the next type (range, multiple)
  174. if (nextType == RANGE) {
  175. setInt(ref, 0, currentlyOn, tryInt, 0);
  176. jumping = 1;
  177. continue;
  178. } else if (nextType == MULTIPLE) {
  179. setInt(ref, 0, currentlyOn, tryInt, 0);
  180. setInt(ref, 1, currentlyOn, tryInt, 1);
  181. jumping = 2;
  182. continue;
  183. }
  184. // Regular non range-multiple digit appending
  185. if (tryInt != -1 && jumping == 0) {
  186. setInt(ref, 0, currentlyOn, tryInt, 0);
  187. setInt(ref, 1, currentlyOn, tryInt, 1);
  188. }
  189. }
  190. }