PageRenderTime 54ms CodeModel.GetById 18ms RepoModel.GetById 1ms app.codeStats 0ms

/compiler.c

https://bitbucket.org/shadwick/alchemy
C | 312 lines | 240 code | 46 blank | 26 comment | 71 complexity | 8fdfecf13fe71f1ee9cf45253eb4c0ad MD5 | raw file
  1. #include <stdlib.h>
  2. #include <stdio.h>
  3. #include <string.h>
  4. #include <stdint.h>
  5. #include <ctype.h>
  6. #include <assert.h>
  7. #include <regex.h>
  8. #include "compiler.h"
  9. #include "program.h"
  10. #include "element.h"
  11. #include "hash.h"
  12. #define MAX_MATCHES 16
  13. /* Macro(s) for the compile function and its helpers */
  14. #define CLEAN_RETURN(RETVAL) \
  15. { ht_end(&state.element_map); \
  16. return (RETVAL); }
  17. #define PREFIX(INST) \
  18. ((program->line_num << 1) | ((INST) & 1))
  19. #define TERMINATE_MATCHES(L, M, J) \
  20. { unsigned int _i; \
  21. for (_i = 1; _i < (J); _i++) \
  22. (L)[(M)[_i].rm_eo] = '\0'; } \
  23. /* ==================== Static Data ==================== */
  24. /* When faced with the problem of matching lines, I thought "I know! I'll use
  25. regular expressions!" Now there are many problems.
  26. */
  27. regex_t re_comment;
  28. regex_t re_step;
  29. regex_t re_jump;
  30. regex_t re_react;
  31. regex_t re_elemlist;
  32. char RE_COMMENT[] = "^\\s*\\([^()]+\\)\\s*$";
  33. char RE_STEP[] = "^Step\\s+([0-9]+):\\s*$";
  34. char RE_JUMP[] = "^([A-Z][a-z]+)\\?\\s*([0-9]+)\\s*$";
  35. char RE_REACT[] = "^([A-Z][a-z]+)(.+)\\s+\\(([0-9]+)\\s+oz\\s+" \
  36. "(Alkahest|Water)\\):\\s+([A-Z][a-z]+)\\s*$";
  37. char RE_ELEMLIST[] ="([0-9]+)\\s+(dr|oz)\\s+([A-Z][a-z]+)";
  38. ReactionProcess string_to_process(const char *string) {
  39. if (strcmp(string, "Calcinate") == 0)
  40. return CALCINATE;
  41. if (strcmp(string, "Digest") == 0)
  42. return DIGEST;
  43. if (strcmp(string, "Ferment") == 0)
  44. return FERMENT;
  45. if (strcmp(string, "Coagulate") == 0)
  46. return COAGULATE;
  47. if (strcmp(string, "Fixate") == 0)
  48. return FIXATE;
  49. if (strcmp(string, "Cerate") == 0)
  50. return CERATE;
  51. if (strcmp(string, "Distill") == 0)
  52. return DISTILL;
  53. if (strcmp(string, "Sublimate") == 0)
  54. return SUBLIMATE;
  55. if (strcmp(string, "Filter") == 0)
  56. return FILTER;
  57. if (strcmp(string, "Fuse") == 0)
  58. return FUSE;
  59. if (strcmp(string, "Multiply") == 0)
  60. return MULTIPLY;
  61. if (strcmp(string, "Project") == 0)
  62. return PROJECT;
  63. return PROCESS_COUNT;
  64. }
  65. Solvent string_to_solvent(const char *string) {
  66. if (strcmp(string, "Alkahest") == 0)
  67. return ALKAHEST;
  68. return WATER;
  69. }
  70. /* ==================== Compilation Helpers ==================== */
  71. FINLINE uint16_t string_to_num(const char *label) {
  72. return strtoul(label, NULL, 10);
  73. }
  74. /* Return the array index for a given Element name, or if it doesn't exist,
  75. create it and return that index. If the Element fails to be initialized, a
  76. -1 will be returned instead.
  77. */
  78. FINLINE int32_t element_index(CompileState *state, const char *name) {
  79. unsigned int index, *ptr;
  80. assert(state);
  81. assert(name && name[0] != '\0');
  82. ptr = (unsigned int *) ht_get(&state->element_map, name);
  83. if (!ptr) {
  84. if (state->program->num_elements >= state->max_elements) {
  85. state->max_elements += 16;
  86. state->program->elements = realloc(state->program->elements,
  87. sizeof(Element) * state->max_elements);
  88. }
  89. index = state->program->num_elements++;
  90. if (!element_init(&state->program->elements[index], name))
  91. return -1;
  92. ht_put(&state->element_map, name, (void *) (index + 1));
  93. }
  94. else
  95. index = ((unsigned int) ptr) - 1;
  96. return index;
  97. }
  98. FINLINE void write_int16(CompileState *state, uint16_t i) {
  99. Program *program;
  100. assert(state);
  101. assert(state->program);
  102. program = state->program;
  103. if (state->code_max <= program->code_size) {
  104. state->code_max += 128;
  105. program->code = realloc(program->code, state->code_max * 2);
  106. }
  107. program->code[program->code_size] = i;
  108. program->code_size++;
  109. }
  110. FINLINE Result compile_step(CompileState *state, char *line, regmatch_t *matches) {
  111. uint16_t i, number;
  112. number = string_to_num(line + matches[0].rm_so);
  113. if (state->program->num_steps >= state->max_steps) {
  114. state->max_steps += 16;
  115. state->program->steps = realloc(state->program->steps,
  116. sizeof(Step) * state->max_steps);
  117. }
  118. for (i = 0; i < state->program->num_steps; i++) {
  119. if (state->program->steps[i].number == number)
  120. return EXPL_AMBIGUOUS_STEP;
  121. }
  122. state->program->steps[i].number = number;
  123. state->program->steps[i].offset = state->program->code_size;
  124. state->program->num_steps++;
  125. return OK;
  126. }
  127. FINLINE Result compile_jump(CompileState *state, char *line, regmatch_t *matches) {
  128. char *name, *label;
  129. name = line + matches[0].rm_so;
  130. label = line + matches[1].rm_so;
  131. write_int16(state, element_index(state, name));
  132. write_int16(state, string_to_num(label));
  133. return OK;
  134. }
  135. FINLINE Result compile_react(CompileState *state, char *line, regmatch_t *matches) {
  136. ReactionProcess process;
  137. Solvent solvent;
  138. uint16_t amount, *prefix, num_reagents = 0;
  139. int32_t index;
  140. char *list, *product;
  141. process = string_to_process(line + matches[0].rm_so);
  142. if (process == PROCESS_COUNT)
  143. return EXPL_UNDEFINED_REACTION;
  144. prefix = state->program->code + state->program->code_size;
  145. write_int16(state, 0);
  146. list = line + matches[0].rm_eo + 1;
  147. while (regexec(&re_elemlist, list, 4, matches + 5, 0) == 0) {
  148. TERMINATE_MATCHES(list, matches + 5, 4);
  149. amount = string_to_num(list + matches[6].rm_so);
  150. write_int16(state, amount);
  151. /* TODO: Check units */
  152. index = element_index(state, list + matches[8].rm_so);
  153. if (index == -1)
  154. return EXPL_MALFORMED_ELEMENT;
  155. if ((state->program->elements[index].attributes & ATTR_PRODUCT))
  156. return EXPL_IRRATIONAL_REAGENT;
  157. write_int16(state, index);
  158. num_reagents++;
  159. list += matches[5].rm_eo + 1;
  160. }
  161. /* Pack the solvent into the complex prefix entry */
  162. solvent = string_to_solvent(line + matches[3].rm_so);
  163. *prefix = (process << 9) | (solvent << 8) | (num_reagents & 0xFF);
  164. /* The amount of solvent to react with */
  165. amount = string_to_num(line + matches[2].rm_so);
  166. write_int16(state, amount);
  167. /* The product element; make sure it's a valid product (not constant) */
  168. product = line + matches[4].rm_so;
  169. index = element_index(state, product);
  170. if (index == -1)
  171. return EXPL_MALFORMED_ELEMENT;
  172. if ((state->program->elements[index].attributes & ATTR_CONSTANT))
  173. return EXPL_ALTERING_REALITY;
  174. write_int16(state, index);
  175. return OK;
  176. }
  177. /* Iterate over the list of builtin Element definitions and add them into the
  178. program's Element list.
  179. */
  180. void add_builtin_elements(CompileState *state) {
  181. uint16_t i, index;
  182. for (i = 0; i < BUILTIN_COUNT; i++) {
  183. BuiltinElement *builtin = &builtin_elements[i];
  184. index = element_index(state, builtin->name);
  185. element_set_value(&state->program->elements[index], builtin->value);
  186. state->program->elements[index].attributes = builtin->attributes;
  187. }
  188. }
  189. /* ==================== External Data ==================== */
  190. /* Compile the regular expressions needed to match input.
  191. If there are issues with this, return 0; otherwise 1.
  192. */
  193. int init() {
  194. if (regcomp(&re_comment, RE_COMMENT, REG_EXTENDED) != 0 ||
  195. regcomp(&re_step, RE_STEP, REG_EXTENDED) != 0 ||
  196. regcomp(&re_jump, RE_JUMP, REG_EXTENDED) != 0 ||
  197. regcomp(&re_react, RE_REACT, REG_EXTENDED) != 0 ||
  198. regcomp(&re_elemlist, RE_ELEMLIST, REG_EXTENDED) != 0) {
  199. fprintf(stderr, "Failed to compile regexps\n");
  200. return 0;
  201. }
  202. return 1;
  203. }
  204. void cleanup() {
  205. regfree(&re_comment);
  206. regfree(&re_step);
  207. regfree(&re_jump);
  208. regfree(&re_react);
  209. regfree(&re_elemlist);
  210. }
  211. Result compile(FILE *file, Program *program) {
  212. CompileState state;
  213. char line[1024];
  214. regmatch_t matches[MAX_MATCHES];
  215. state.program = program;
  216. state.code_max = 0;
  217. state.max_elements = 0;
  218. state.max_steps = 0;
  219. ht_init(&state.element_map, 64);
  220. program->code = NULL;
  221. program->code_size = 0;
  222. program->elements = NULL;
  223. program->num_elements = 0;
  224. program->steps = NULL;
  225. program->num_steps = 0;
  226. program->line_num = 0;
  227. add_builtin_elements(&state);
  228. while (fgets(line, sizeof(line), file)) {
  229. program->line_num++;
  230. /* 1. Blank line or comment? */
  231. if (line[0] == '\n' || regexec(&re_comment, line, 1, matches, 0) == 0)
  232. continue;
  233. /* 2. Step declaration? */
  234. if (regexec(&re_step, line, 2, matches, 0) == 0) {
  235. Result result;
  236. TERMINATE_MATCHES(line, matches, 2);
  237. result = compile_step(&state, line, matches + 1);
  238. if (result != OK)
  239. CLEAN_RETURN(result);
  240. continue;
  241. }
  242. /* 3. Jump if not zero? */
  243. if (regexec(&re_jump, line, 3, matches, 0) == 0) {
  244. Result result;
  245. TERMINATE_MATCHES(line, matches, 3);
  246. write_int16(&state, PREFIX(JUMP));
  247. result = compile_jump(&state, line, matches + 1);
  248. if (result != OK)
  249. CLEAN_RETURN(result);
  250. continue;
  251. }
  252. /* 4. Perform reaction */
  253. if (regexec(&re_react, line, 6, matches, 0) == 0) {
  254. Result result;
  255. TERMINATE_MATCHES(line, matches, 6);
  256. write_int16(&state, PREFIX(REACT));
  257. result = compile_react(&state, line, matches + 1);
  258. if (result != OK)
  259. CLEAN_RETURN(result);
  260. continue;
  261. }
  262. /* Invalid syntax */
  263. CLEAN_RETURN(EXPL_SYNTAX_ERROR);
  264. }
  265. CLEAN_RETURN(OK);
  266. }