/compiler.c
C | 312 lines | 240 code | 46 blank | 26 comment | 71 complexity | 8fdfecf13fe71f1ee9cf45253eb4c0ad MD5 | raw file
- #include <stdlib.h>
- #include <stdio.h>
- #include <string.h>
- #include <stdint.h>
- #include <ctype.h>
- #include <assert.h>
- #include <regex.h>
- #include "compiler.h"
- #include "program.h"
- #include "element.h"
- #include "hash.h"
- #define MAX_MATCHES 16
- /* Macro(s) for the compile function and its helpers */
- #define CLEAN_RETURN(RETVAL) \
- { ht_end(&state.element_map); \
- return (RETVAL); }
- #define PREFIX(INST) \
- ((program->line_num << 1) | ((INST) & 1))
- #define TERMINATE_MATCHES(L, M, J) \
- { unsigned int _i; \
- for (_i = 1; _i < (J); _i++) \
- (L)[(M)[_i].rm_eo] = '\0'; } \
- /* ==================== Static Data ==================== */
- /* When faced with the problem of matching lines, I thought "I know! I'll use
- regular expressions!" Now there are many problems.
- */
- regex_t re_comment;
- regex_t re_step;
- regex_t re_jump;
- regex_t re_react;
- regex_t re_elemlist;
- char RE_COMMENT[] = "^\\s*\\([^()]+\\)\\s*$";
- char RE_STEP[] = "^Step\\s+([0-9]+):\\s*$";
- char RE_JUMP[] = "^([A-Z][a-z]+)\\?\\s*([0-9]+)\\s*$";
- char RE_REACT[] = "^([A-Z][a-z]+)(.+)\\s+\\(([0-9]+)\\s+oz\\s+" \
- "(Alkahest|Water)\\):\\s+([A-Z][a-z]+)\\s*$";
- char RE_ELEMLIST[] ="([0-9]+)\\s+(dr|oz)\\s+([A-Z][a-z]+)";
- ReactionProcess string_to_process(const char *string) {
- if (strcmp(string, "Calcinate") == 0)
- return CALCINATE;
- if (strcmp(string, "Digest") == 0)
- return DIGEST;
- if (strcmp(string, "Ferment") == 0)
- return FERMENT;
- if (strcmp(string, "Coagulate") == 0)
- return COAGULATE;
- if (strcmp(string, "Fixate") == 0)
- return FIXATE;
- if (strcmp(string, "Cerate") == 0)
- return CERATE;
- if (strcmp(string, "Distill") == 0)
- return DISTILL;
- if (strcmp(string, "Sublimate") == 0)
- return SUBLIMATE;
- if (strcmp(string, "Filter") == 0)
- return FILTER;
- if (strcmp(string, "Fuse") == 0)
- return FUSE;
- if (strcmp(string, "Multiply") == 0)
- return MULTIPLY;
- if (strcmp(string, "Project") == 0)
- return PROJECT;
- return PROCESS_COUNT;
- }
- Solvent string_to_solvent(const char *string) {
- if (strcmp(string, "Alkahest") == 0)
- return ALKAHEST;
- return WATER;
- }
- /* ==================== Compilation Helpers ==================== */
- FINLINE uint16_t string_to_num(const char *label) {
- return strtoul(label, NULL, 10);
- }
- /* Return the array index for a given Element name, or if it doesn't exist,
- create it and return that index. If the Element fails to be initialized, a
- -1 will be returned instead.
- */
- FINLINE int32_t element_index(CompileState *state, const char *name) {
- unsigned int index, *ptr;
- assert(state);
- assert(name && name[0] != '\0');
- ptr = (unsigned int *) ht_get(&state->element_map, name);
- if (!ptr) {
- if (state->program->num_elements >= state->max_elements) {
- state->max_elements += 16;
- state->program->elements = realloc(state->program->elements,
- sizeof(Element) * state->max_elements);
- }
- index = state->program->num_elements++;
- if (!element_init(&state->program->elements[index], name))
- return -1;
- ht_put(&state->element_map, name, (void *) (index + 1));
- }
- else
- index = ((unsigned int) ptr) - 1;
- return index;
- }
- FINLINE void write_int16(CompileState *state, uint16_t i) {
- Program *program;
- assert(state);
- assert(state->program);
- program = state->program;
- if (state->code_max <= program->code_size) {
- state->code_max += 128;
- program->code = realloc(program->code, state->code_max * 2);
- }
- program->code[program->code_size] = i;
- program->code_size++;
- }
- FINLINE Result compile_step(CompileState *state, char *line, regmatch_t *matches) {
- uint16_t i, number;
- number = string_to_num(line + matches[0].rm_so);
- if (state->program->num_steps >= state->max_steps) {
- state->max_steps += 16;
- state->program->steps = realloc(state->program->steps,
- sizeof(Step) * state->max_steps);
- }
- for (i = 0; i < state->program->num_steps; i++) {
- if (state->program->steps[i].number == number)
- return EXPL_AMBIGUOUS_STEP;
- }
- state->program->steps[i].number = number;
- state->program->steps[i].offset = state->program->code_size;
- state->program->num_steps++;
- return OK;
- }
- FINLINE Result compile_jump(CompileState *state, char *line, regmatch_t *matches) {
- char *name, *label;
- name = line + matches[0].rm_so;
- label = line + matches[1].rm_so;
- write_int16(state, element_index(state, name));
- write_int16(state, string_to_num(label));
- return OK;
- }
- FINLINE Result compile_react(CompileState *state, char *line, regmatch_t *matches) {
- ReactionProcess process;
- Solvent solvent;
- uint16_t amount, *prefix, num_reagents = 0;
- int32_t index;
- char *list, *product;
-
- process = string_to_process(line + matches[0].rm_so);
- if (process == PROCESS_COUNT)
- return EXPL_UNDEFINED_REACTION;
- prefix = state->program->code + state->program->code_size;
- write_int16(state, 0);
-
- list = line + matches[0].rm_eo + 1;
- while (regexec(&re_elemlist, list, 4, matches + 5, 0) == 0) {
- TERMINATE_MATCHES(list, matches + 5, 4);
- amount = string_to_num(list + matches[6].rm_so);
- write_int16(state, amount);
- /* TODO: Check units */
- index = element_index(state, list + matches[8].rm_so);
- if (index == -1)
- return EXPL_MALFORMED_ELEMENT;
- if ((state->program->elements[index].attributes & ATTR_PRODUCT))
- return EXPL_IRRATIONAL_REAGENT;
- write_int16(state, index);
- num_reagents++;
- list += matches[5].rm_eo + 1;
- }
-
- /* Pack the solvent into the complex prefix entry */
- solvent = string_to_solvent(line + matches[3].rm_so);
- *prefix = (process << 9) | (solvent << 8) | (num_reagents & 0xFF);
-
- /* The amount of solvent to react with */
- amount = string_to_num(line + matches[2].rm_so);
- write_int16(state, amount);
-
- /* The product element; make sure it's a valid product (not constant) */
- product = line + matches[4].rm_so;
- index = element_index(state, product);
- if (index == -1)
- return EXPL_MALFORMED_ELEMENT;
- if ((state->program->elements[index].attributes & ATTR_CONSTANT))
- return EXPL_ALTERING_REALITY;
- write_int16(state, index);
- return OK;
- }
- /* Iterate over the list of builtin Element definitions and add them into the
- program's Element list.
- */
- void add_builtin_elements(CompileState *state) {
- uint16_t i, index;
- for (i = 0; i < BUILTIN_COUNT; i++) {
- BuiltinElement *builtin = &builtin_elements[i];
- index = element_index(state, builtin->name);
- element_set_value(&state->program->elements[index], builtin->value);
- state->program->elements[index].attributes = builtin->attributes;
- }
- }
- /* ==================== External Data ==================== */
- /* Compile the regular expressions needed to match input.
- If there are issues with this, return 0; otherwise 1.
- */
- int init() {
- if (regcomp(&re_comment, RE_COMMENT, REG_EXTENDED) != 0 ||
- regcomp(&re_step, RE_STEP, REG_EXTENDED) != 0 ||
- regcomp(&re_jump, RE_JUMP, REG_EXTENDED) != 0 ||
- regcomp(&re_react, RE_REACT, REG_EXTENDED) != 0 ||
- regcomp(&re_elemlist, RE_ELEMLIST, REG_EXTENDED) != 0) {
- fprintf(stderr, "Failed to compile regexps\n");
- return 0;
- }
- return 1;
- }
- void cleanup() {
- regfree(&re_comment);
- regfree(&re_step);
- regfree(&re_jump);
- regfree(&re_react);
- regfree(&re_elemlist);
- }