Mercurial > repos > rhope
view parser.c @ 121:1af91ceaaf49
Fixed memory leak when an output is only attached to a single null input
author | Mike Pavone <pavone@retrodev.com> |
---|---|
date | Thu, 28 Oct 2010 21:04:12 -0400 |
parents | 9749109b3198 |
children |
line wrap: on
line source
#include "structs.h" #include "visuality.h" #include "debugmacros.h" #ifdef WIN32 #include "windows.h" #endif #include <stdio.h> #include "interp.h" #include "parser.h" #include <string.h> #include <stdlib.h> extern int num_workers; extern int num_wires; /*#ifdef SEGA #define NUM_WORKERS_START 40 #define NUM_WIRES_START 80 #else #define NUM_WORKERS_START 1024 #define NUM_WIRES_START 2048 #endif*/ #define NUM_WORKERS_START 20 #define NUM_WIRES_START 40 //TODO: Figure out why setting those defines to 16 and 32 respectively causes problems BOOL is_whitespace(char c) { if(c == ' ' || c == '\t' || c == '\n' || c == '\r') return TRUE; return FALSE; } company * create_company(program * prog, char * name, int num_workers, int num_rooms, BOOL buildable) { company * this_comp = &(prog->companylist[prog->num_companies]); DEBUGPRINTF("create_company %s with %d workers and %d rooms\n", name, num_workers, num_rooms); this_comp->type_id = prog->num_companies++; strncpy(this_comp->name, name, 256); this_comp->name[255] = '\0'; if(num_workers) this_comp->methodlist = MALLOC(sizeof(worker_def *) * num_workers, "company methodlist"); else this_comp->methodlist = NULL; this_comp->num_methods = 0;//num_workers; this_comp->method_storage = num_workers; if(num_rooms) { this_comp->room_list = MALLOC(sizeof(company_room) * num_rooms, "company roomlist"); DEBUGPRINTF("Allocated %d bytes at %X for room_list\n", sizeof(company_room) * num_rooms, this_comp->room_list); } else this_comp->room_list = NULL; this_comp->num_rooms = 0;//num_rooms; this_comp->room_storage = num_rooms; if(buildable) this_comp->build_size = 0; else this_comp->build_size = -1; VIS_InitializeCriticalSection(this_comp->lock); return this_comp; } int add_method(company * this_comp, worker_def * def) { worker_def ** temp; int i; DEBUGPUTS("add_method: "); DEBUGPRINTF("%s\n", def->name); VIS_EnterCriticalSection(this_comp->lock); if(this_comp->method_storage - this_comp->num_methods <= 0) { if(this_comp->method_storage < 2) this_comp->method_storage = 4; else this_comp->method_storage = this_comp->method_storage + ((this_comp->method_storage) >> 1); temp = MALLOC(sizeof(worker_def *) * this_comp->method_storage, "company method storage"); for(i = 0; i < this_comp->num_methods; ++i) temp[i] = this_comp->methodlist[i]; VIS_FREE(this_comp->methodlist, "company methodlist"); this_comp->methodlist = temp; } i = this_comp->num_methods; this_comp->methodlist[this_comp->num_methods++] = def; DEBUGPRINTF("%s now has %d methods\n", this_comp->name, this_comp->num_methods); VIS_LeaveCriticalSection(this_comp->lock); return i; } int room_build_sizes[] = {0, sizeof(char), sizeof(short), sizeof(long), sizeof(float), sizeof(double), -1, -1, -1, -1, -1, sizeof(datum *), 0}; int add_comp_room(company * this_comp, char * name, int set_func, int get_func, short set_func_type, short get_func_type) { company_room * temp; int i; int min_build_size; VIS_EnterCriticalSection(this_comp->lock); DEBUGPRINTF("add_comp_room: %s", name); DEBUGPRINTF(", num_rooms: %d, room_storage: %d\n", this_comp->num_rooms, this_comp->room_storage); if(this_comp->room_storage - this_comp->num_rooms <= 0) { if(this_comp->room_storage < 2) this_comp->room_storage = 4; else this_comp->room_storage = this_comp->room_storage + ((this_comp->room_storage) >> 1); temp = MALLOC(sizeof(company_room) * this_comp->room_storage, "company room storage"); memcpy(temp, this_comp->room_list, sizeof(company_room)*this_comp->num_rooms); VIS_FREE(this_comp->room_list, "company room list"); this_comp->room_list = temp; } if(this_comp->build_size >= 0) { DEBUGPRINTF("get_func_type: %d, room_build_sizes[%d] = %d\n", get_func_type, get_func_type, room_build_sizes[get_func_type]); if(get_func_type && get_func_type != ROOM_WORKER && get_func == -1) get_func = this_comp->build_size; if(set_func_type && set_func_type != ROOM_WORKER && set_func == -1) set_func = this_comp->build_size; if(room_build_sizes[set_func_type]) if(room_build_sizes[set_func_type] > room_build_sizes[get_func_type]) min_build_size = room_build_sizes[set_func_type] + set_func; else min_build_size = room_build_sizes[get_func_type] + get_func; else if(room_build_sizes[get_func_type]) min_build_size = room_build_sizes[get_func_type] + get_func; if(min_build_size > this_comp->build_size) this_comp->build_size = min_build_size; } i = this_comp->num_rooms; DEBUGPUTS("Copying name\n"); this_comp->room_list[this_comp->num_rooms++].name = MALLOC(strlen(name) + 1, "company room name"); strcpy(this_comp->room_list[i].name, name); DEBUGPUTS("Setting func types\n"); this_comp->room_list[i].set_func_type = set_func_type; this_comp->room_list[i].get_func_type = get_func_type; DEBUGPRINTF("Setting funcs: get = %d, set = %d\n", get_func, set_func); //TODO: Change the signature of this function so that get_func and set_func are void * so we can safely pass in pointers for future get_func types this_comp->room_list[i].get_func = (void *)get_func; this_comp->room_list[i].set_func = (void *)set_func; DEBUGPRINTF("Build size is now: %d\n", this_comp->build_size); DEBUGPUTS("before return\n"); VIS_LeaveCriticalSection(this_comp->lock); return i; } worker_def * create_worker(program * prog, char * name, int num_inputs, int num_outputs, short type) { custom_worker * aworker; int i, j; int thisdef; worker_def * deflist; DEBUGPRINTF("create_worker: %s with %d inputs and %d outputs\n", name, num_inputs, num_outputs); if(!strcmp(name, "Main")) { thisdef = 0; deflist = prog->defs->deflist; } else { if(prog->current->num_defs >= prog->current->defs_storage) { prog->current->next = MALLOC(sizeof(defchunk) + (START_DEF_STORAGE - 1) * sizeof(worker_def), "worker def storage"); prog->current = prog->current->next; prog->current->defs_storage = START_DEF_STORAGE; prog->current->num_defs = 0; prog->current->next = NULL; } if(prog->current == prog->defs && prog->current->num_defs == 0) prog->current->num_defs = 1; thisdef = prog->current->num_defs; deflist = prog->current->deflist; } DEBUGPRINTF("new deflist index: %d\n", thisdef); if(type & USER_FLAG && (type & TYPE_MASK) == WORKER_TYPE) { //puts("Creating custom worker."); aworker = MALLOC(sizeof(custom_worker), "custom worker implementation"); aworker->num_workers = 0; aworker->num_wires = 0; aworker->workerlist = MALLOC(sizeof(worker)*NUM_WORKERS_START, "custom worker workerlist"); aworker->worker_storage = NUM_WORKERS_START; aworker->wirelist = MALLOC(sizeof(wire)*NUM_WIRES_START, "custom worker wirelist"); aworker->wire_storage = NUM_WIRES_START; aworker->workers_to_wires_up = MALLOC(sizeof(int)*(NUM_WIRES_START+1),"custom worker workers to wires up"); aworker->workers_to_wires_down = MALLOC(sizeof(int)*(NUM_WIRES_START+1),"custom worker workers to wires down"); aworker->dirty = TRUE; VIS_InitializeCriticalSection(aworker->lock); deflist[thisdef].implement_func = aworker; } DEBUGPUTS("Setting properties\n"); deflist[thisdef].num_inputs = num_inputs; deflist[thisdef].num_outputs = num_outputs; deflist[thisdef].type = type; DEBUGPUTS("Calling malloc\n"); deflist[thisdef].name = MALLOC(strlen(name)+1, "worker def name"); DEBUGPRINTF("malloc return: %X, calling strcpy\n", deflist[thisdef].name); strcpy(deflist[thisdef].name, name); DEBUGPUTS("strcpy done\n"); if(num_inputs) deflist[thisdef].input_types = MALLOC(sizeof(short) * num_inputs, "worker def input types"); else deflist[thisdef].input_types = NULL; if(num_outputs) deflist[thisdef].output_types = MALLOC(sizeof(short) * num_outputs, "worker def output types"); else deflist[thisdef].output_types = NULL; for(i = 0; i < num_inputs; ++i) deflist[thisdef].input_types[i] = ANY_TYPE; deflist[thisdef].num_stores = 0; deflist[thisdef].uses_stores = NULL; deflist[thisdef].transaction_flags = TRANSACTION_RETRY; if(thisdef >= prog->current->num_defs) prog->current->num_defs = thisdef+1; for(i = 0; name[i] != '\0'; ++i) { if(name[i] == '@') { DEBUGPRINTF("Name ends in %s\n", name+i); for(j = 0; j < prog->num_companies; ++j) { if(!strcmp(name+i+1, prog->companylist[j].name)) { DEBUGPRINTF("Worker is a method of company %d, ", j); DEBUGPRINTF("%s\n", prog->companylist[j].name); add_method(prog->companylist+j, deflist+thisdef); break; } } break; } } deflist[thisdef].program = prog; return deflist + thisdef; } int find_worker(char * name, int * num_inputs, int * num_outputs, program * prog, worker_def ** def) { int i; int term; int len; defchunk * current; worker_def * temp; DEBUGPRINTF("Calling strlen on name: %X\n", name); len = strlen(name); DEBUGPRINTF("find_worker: %s\n", name); if(len >= 2 && !strcmp(name+len-2, ">>")) { DEBUGPUTS("Get property\n"); term = len-2; for(i = term-1; i > 0; --i) { if(name[i] == ' ') term = i; else break; } DEBUGPRINTF("term: %d\n", term); name[term] = '\0'; DEBUGPRINTF("name: %s\n", name); if(num_inputs); *num_inputs = 1; if(num_outputs) *num_outputs = 1; return -1; } if(len >= 2 && !strcmp(name+len-2, "<<")) { DEBUGPUTS("Set property\n"); term = len-2; for(i = term-1; i > 0; --i) { if(name[i] == ' ') term = i; else break; } DEBUGPRINTF("term: %d\n", term); name[term] = '\0'; DEBUGPRINTF("name: %s\n", name); if(num_inputs); *num_inputs = 2; if(num_outputs) *num_outputs = 1; return -2; } current = prog->defs; while(current) { for(i = 0; i < current->num_defs; ++i) { if(current->deflist[i].name && !strcmp(current->deflist[i].name, name)) { DEBUGPRINTF("Found worker #%d\n", i); if(num_inputs) *num_inputs = current->deflist[i].num_inputs; if(num_outputs) *num_outputs = current->deflist[i].num_outputs; if(def) *def = current->deflist + i; return i; } } current = current->next; } if(num_inputs && num_outputs) { for(i = 1; i < prog->num_companies; ++i) { temp = find_method(i, name, *num_inputs, prog); if(temp && temp->num_outputs >= *num_outputs) { *def = create_worker(prog, name, temp->num_inputs, temp->num_outputs, MAGIC_TYPE); return 1; } } } else { for(i = 1; i < prog->num_companies; ++i) { temp = find_method_noinputcheck(i, name, prog); if(temp) { *def = create_worker(prog, name, temp->num_inputs, temp->num_outputs, MAGIC_TYPE); return 1; } } } DEBUGPUTS("Could not find worker\n"); return -3; } int generic_add_to_def(worker_def * parent, char * name,int display_type, int type, int num_inputs, int num_outputs, double xpos, double ypos) { int returnval; worker * temp; VIS_EnterCriticalSection(parent->implement_func->lock); if(parent->implement_func->num_workers >= parent->implement_func->worker_storage) { parent->implement_func->worker_storage = parent->implement_func->num_workers + (parent->implement_func->num_workers >> 1); parent->implement_func->workerlist = realloc(parent->implement_func->workerlist, parent->implement_func->worker_storage * sizeof(worker)); } strcpy(parent->implement_func->workerlist[parent->implement_func->num_workers].name, name); parent->implement_func->workerlist[parent->implement_func->num_workers].type=type; parent->implement_func->workerlist[parent->implement_func->num_workers].num_inputs = num_inputs; parent->implement_func->workerlist[parent->implement_func->num_workers].num_outputs = num_outputs; parent->implement_func->workerlist[parent->implement_func->num_workers].null_input = FALSE; parent->implement_func->workerlist[parent->implement_func->num_workers].magic_cache_implement = NULL; parent->implement_func->workerlist[parent->implement_func->num_workers].magic_cache_type = 0; VIS_InitializeCriticalSection(parent->implement_func->workerlist[parent->implement_func->num_workers].lock); parent->implement_func->dirty = TRUE; //puts("end generic_add_to_def"); DEBUGPRINTF("generic_add_to_def: %s with type %d and num_inputs %d and num_outputs %d returned %d\n", name, type, num_inputs, num_outputs, parent->implement_func->num_workers); returnval = parent->implement_func->num_workers++; VIS_LeaveCriticalSection(parent->implement_func->lock); return returnval; } int add_worker_to_def(worker_def * parent, worker_def * worker, double xpos, double ypos) { int list_index; list_index = generic_add_to_def(parent, worker->name, TRAPEZOID, WORKER, worker->num_inputs, worker->num_outputs, xpos, ypos); parent->implement_func->workerlist[list_index].value_index = worker; return list_index; } int add_get_comp_room(worker_def * parent, char * name, double xpos, double ypos) { return generic_add_to_def(parent, name, RECTANGLE, GET_COMP, 1, 2, xpos, ypos); } int add_set_comp_room(worker_def * parent, char * name, double xpos, double ypos) { return generic_add_to_def(parent, name, RECTANGLE, SET_COMP, 2, 1, xpos, ypos); } int add_global(worker_def * parent, char * name, double xpos, double ypos, int type) { int i,j; int thisdef; int num_in, num_out; if(type == SET_GLOBAL) { num_in = 1; num_out = 0; parent->transaction_flags |= TRANSACTION_WRITE; } else { num_in = 0; num_out = 1; } thisdef = generic_add_to_def(parent, name, RECTANGLE, type, num_in, num_out, xpos, ypos); for(i = 0; name[i+1] != '\0'; ++i) { if(name[i] == ':' && name[i+1] == ':') { parent->implement_func->workerlist[thisdef].value_index = (void *)-1; if(i > 0) { for(j = 0; j < parent->num_stores; ++j) { if(strlen(parent->uses_stores[j]) == i && !memcmp(parent->uses_stores[j], name, i)) { parent->implement_func->workerlist[thisdef].value_index = (void *)j; break; } } if(parent->implement_func->workerlist[thisdef].value_index < 0) { name[i] = '\0'; printf("Error: Worker %s is not declared to use global store %s but references it.\n", parent->name, name); exit(-1); } } else if(type == SET_GLOBAL) parent->implement_func->workerlist[thisdef].value_index = 0; parent->implement_func->workerlist[thisdef].io_num = i+2; break; } } return thisdef; } int add_global_get(worker_def * parent, char * name, double xpos, double ypos) { return add_global(parent, name, xpos, ypos, GET_GLOBAL); } int add_global_set(worker_def * parent, char * name, double xpos, double ypos) { return add_global(parent, name, xpos, ypos, SET_GLOBAL); } int find_object(worker_def * def, char * name, int type) { int i; for(i = 0; i < def->implement_func->num_workers; ++i) if(!strcmp(def->implement_func->workerlist[i].name, name) && def->implement_func->workerlist[i].type == 1) break; if(i < def->implement_func->num_workers) return i; return -1; } int create_find_room(worker_def * def, char * name, double xpos, double ypos) { int found = find_object(def, name, ROOM); if(found >= 0) return found; return generic_add_to_def(def, name, RECTANGLE, ROOM, 1, 1, xpos, ypos); } int add_constant(worker_def * def, char * value, double xpos, double ypos) { int index = generic_add_to_def(def, value, RECTANGLE, CONSTANT, 0, 1, xpos, ypos); def->implement_func->workerlist[index].value_index = get_constant(value, -1, def->program); return index; } int add_input_num(worker_def * def, char * name, int input_num, double xpos, double ypos) { int i; unsigned short * temp_types; int found; if(def->num_inputs <= input_num) { temp_types = def->input_types; def->input_types = MALLOC(sizeof(short)*(input_num+1), "worker def input types"); if(temp_types) { memcpy(def->input_types, temp_types, sizeof(short)*def->num_inputs); VIS_FREE(temp_types, "temp types?"); } for(i = def->num_inputs; i <= input_num; ++i) def->input_types[i] = ANY_TYPE; def->num_inputs = input_num+1; } found = generic_add_to_def(def, name, RECTANGLE, INPUT, 0, 1, xpos, ypos); def->implement_func->workerlist[found].io_num = input_num; return found; } int add_input(worker_def * def, char * name, double xpos, double ypos) { int input_num; int i; for(i = 0; name[i] != 0; ++i) if(name[i] == '(') { input_num = atol(name+i+1); break; } return add_input_num(def, name, input_num, xpos, ypos); } int add_output_num(worker_def * def, char * name, int output_num, double xpos, double ypos) { int i; unsigned short * temp_types; int found; if(def->num_outputs <= output_num) { temp_types = def->output_types; def->output_types = MALLOC(sizeof(short)*(output_num+1), "worker def output types"); if(temp_types) { memcpy(def->output_types, temp_types, sizeof(short)*def->num_outputs); VIS_FREE(temp_types, "temp types?"); } for(i = def->num_outputs; i <= output_num; ++i) def->output_types[i] = ANY_TYPE; def->num_outputs = output_num+1; } found = generic_add_to_def(def, name, RECTANGLE, OUTPUT, 1, 0, xpos, ypos); def->implement_func->workerlist[found].io_num = output_num; return found; } int add_output(worker_def * def, char * name, double xpos, double ypos) { int output_num; int i; for(i = 0; name[i] != 0; ++i) if(name[i] == '(') { output_num = atol(name+i+1); break; } return add_output_num(def, name, output_num, xpos, ypos); } int create_find_output(worker_def * def, char * name, double xpos, double ypos) { int output_num; int i; unsigned short * temp_types; int found = find_object(def, name, OUTPUT); if(found >= 0) return found; return add_output(def, name, xpos, ypos); } void add_wire(worker_def * def, int start, int output_num, int end, int input_num) { VIS_EnterCriticalSection(def->implement_func->lock); if(def->implement_func->num_wires >= def->implement_func->wire_storage) { def->implement_func->wire_storage = def->implement_func->num_wires + (def->implement_func->num_wires >> 1); def->implement_func->wirelist = realloc(def->implement_func->wirelist, def->implement_func->wire_storage * sizeof(wire)); def->implement_func->workers_to_wires_up = realloc(def->implement_func->workers_to_wires_up, (def->implement_func->wire_storage+1) * sizeof(int)); def->implement_func->workers_to_wires_down = realloc(def->implement_func->workers_to_wires_down, (def->implement_func->wire_storage+1) * sizeof(int)); } def->implement_func->wirelist[def->implement_func->num_wires].start_worker = start; def->implement_func->wirelist[def->implement_func->num_wires].end_worker = end; def->implement_func->wirelist[def->implement_func->num_wires].output_num = output_num; def->implement_func->wirelist[def->implement_func->num_wires].input_num = input_num; def->implement_func->dirty = TRUE; ++def->implement_func->num_wires; if(input_num == -1) { def->implement_func->workerlist[end].null_input = TRUE; } VIS_LeaveCriticalSection(def->implement_func->lock); } int process_expression(worker_def * def, int num_outputs, char ** outvars, int num_inputs, char ** inputs, char * workername, BOOL worker_expr, int block_depth, int * block_workers, int * block_output, int last_worker) { int i,j; int current; int this_worker;// = ++return_worker; int expected_in, expected_out; int input_num=-1; int worker_num; worker_def * call_def; BOOL block_attach=FALSE; BOOL room = FALSE; BOOL global_flag; if(worker_expr) { DEBUGPUTS("calling find_worker\n"); expected_in = num_inputs; expected_out = num_outputs; worker_num = find_worker(workername, &expected_in, &expected_out, def->program, &call_def); DEBUGPRINTF("\nWorker %s with %d inputs and %d outputs expects %d inputs and %d outputs\n", workername, num_inputs, num_outputs, expected_in, expected_out); if(worker_num >= 0) this_worker = add_worker_to_def(def, call_def, 1.0, 1.0); else if(worker_num == -1) this_worker = add_get_comp_room(def, workername, 1.0, 1.0); else if(worker_num == -2) this_worker = add_set_comp_room(def, workername, 1.0, 1.0); else { ERRORPRINTF("Could not find a worker named %s or a method of the same name with %d inputs and at least %d outputs\n", workername, num_inputs, num_outputs); for(i = 0; i < num_inputs; ++i) { ERRORPRINTF("Input %d was %s\n", i, inputs[i]); } exit(-1); } } else { if(workername[0] == '{' || workername[0] == '"' || (workername[0] >= '0' && workername[0] <= '9') || strcmp(workername, "Yes") == 0 || strcmp(workername, "No") == 0) { //Constant expression this_worker = add_constant(def, workername, 1.0, 1.0); } /*else if(workername[0] == '"') { //printf("\nString %s\n", workername); workername[strlen(workername)-1] = '\0'; this_worker = add_constant(def, workername+1, 1.0, 1.0); } else if(workername[0] >= '0' && workername[0] <= '9') { //printf("\nNumber %s\n", workername); this_worker = add_constant(def, workername, 1.0, 1.0); } else if(strcmp(workername, "Yes") == 0 || strcmp(workername, "No") == 0) { this_worker = add_constant(def, workername, 1.0, 1.0); }*/ else { for(i = 0; workername[i] != 0; ++i) if(workername[i] == '(') { input_num = atol(workername + i+1); break; } if(input_num >= 0) { //printf("\nInput %d %s\n", input_num, workername); this_worker = add_input(def, workername, 1.0, 1.0); } else { room = TRUE; //printf("\nRoom %s\n", workername); for(i = 0; i < strlen(workername)-1; ++i) { if(workername[i] == ':' && workername[i+1] == ':') { room = FALSE; break; } } if(room) this_worker = create_find_room(def, workername, 1.0, 1.0); else this_worker = add_global_get(def, workername, 1.0, 1.0); } } } for(i = 0; i < num_inputs; ++i) if(inputs[i]) { if(!strcmp(inputs[i], "~")) { //printf("Input %d = ~ (parent block)\n", i); if(!block_depth) { printf("Error: Reference to parent block (~) for input %d of worker %s, but block depth is 0", i, workername); exit(-2); } add_wire(def, block_workers[block_depth-1], block_output[block_depth-1], this_worker, i); block_attach = TRUE; } else { DEBUGPRINTF("Processing input %d (%s)\n", i, inputs[i]); if(block_depth) current = parse_body(def, inputs[i], strlen(inputs[i]), block_depth, block_output, block_workers); else current = parse_body(def, inputs[i], strlen(inputs[i]), 0, NULL, NULL); add_wire(def, current, 0, this_worker, i); } } else { //printf("Input %d = last_worker(%d)\n", i, last_worker); add_wire(def, last_worker, 0, this_worker, i); } for(i = 0; i < num_outputs; ++i) { global_flag = FALSE; //printf("Output %d = %s\n", i, outvars[i]); for(j = 0; outvars[i][j] != '\0'; ++j) if(outvars[i][j] == '(') break; else if(outvars[i][j] == ':' && outvars[i][j+1] == ':') break; if(outvars[i][j] == '\0') current = create_find_room(def, outvars[i], 2.0, 2.0); else if(outvars[i][j] == ':') current = add_global_set(def, outvars[i], 2.0, 2.0); else current = create_find_output(def, outvars[i], 2.0, 2.0); add_wire(def, this_worker, i, current, 0); } //printf("Current blockdepth: %d\n", block_depth); if(block_depth > 0) { //printf("Block Worker: %d\nBlock Output: %d\n", block_workers[block_depth-1], block_output[block_depth-1]); if(worker_expr && num_inputs < expected_in) { if(block_attach) printf("Warning: Worker %s is attached to block both explicitly and implicitly (i.e. at least one input was stated as ~, but there are still unsatisfied inputs)\n", workername); if(expected_in - num_inputs > 1) printf("Warning: More than one input of worker %s implicitly tied to block (%d inputs implicitly tied)", workername, expected_in - num_inputs); for(i = num_inputs; i < expected_in; ++i) add_wire(def, block_workers[block_depth-1], block_output[block_depth-1], this_worker, i); } else if(!block_attach && !room) { add_wire(def, block_workers[block_depth-1], block_output[block_depth-1], this_worker, -1); //def->implement_func->workerlist[this_worker].null_input = TRUE; } } else if(worker_expr && num_inputs < expected_in) { ERRORPRINTF("Error: Worker %s expects %d input(s), but was only given %d input(s)\n", workername, expected_in, num_inputs); exit(-1); } //printf("Returning %d\n\n", this_worker); return this_worker; } typedef enum {NULL_STATE, OUT_STATE, BETWEEN_OUT, BEFORE_WORK, PRE_IN_STATE, BETWEEN_PRE_IN, WORK_STATE, IN_STATE, BETWEEN_IN, AFTER_IN, BLOCK_FIND, LINE_COMMENT_STATE} parse_state; char state_txt[12][20] = {"Null","Out","Between Out","Before Work","Pre Input","Between Pre-Input","Worker","Input","Between Input","After Input","Block Find"}; int parse_body(worker_def * def, char * code, int len, int prev_block_depth, int * prev_block_output, int * prev_block_workers) { char * outputs[32]; char * inputs[32]; char * worker; short saw_line=0; int num_inputs=0, num_outputs=0; int left_bracket=0; int left_curly=0; int block_depth = 0; int block_workers[32]; int block_output[32]; int last_worker = 0; BOOL worker_expr = FALSE; BOOL in_string = FALSE; BOOL literal = FALSE; BOOL do_curly = FALSE; BOOL saw_newline = FALSE; int i,j; int start; int line_comment_start; parse_state state = NULL_STATE; parse_state old_state; DEBUGPRINTF("code: %X\n", code); //printf("prev_block_depth: %d\n", prev_block_depth); for(i=0; i < len; ++i) { //printf("i: %d, code[i]: '%c', state: %s(%d)\n", i, code[i], state_txt[state],state); DEBUGPRINTF("i: %d, code[i]: '%c', state: %s(%d), left_bracket: %d, num_inputs: %d\n", i, code[i], state_txt[state],state, left_bracket, num_inputs); if(!in_string) { if(state != LINE_COMMENT_STATE) { if(code[i] == '/' && code[i+1] == '/') { old_state = state; state = LINE_COMMENT_STATE; line_comment_start = i; i += 2; } else if(code[i] == '[') ++left_bracket; else if(code[i] == ']') --left_bracket; else if(code[i] == '{') do_curly = TRUE; else if(code[i] == '}') --left_curly; else if(code[i] == '"') { in_string = TRUE; //continue; } } if(left_curly == 0) { switch(state) { case NULL_STATE: if(code[i] == '[') { //puts("worker_expr = TRUE"); worker_expr = TRUE; state = BETWEEN_PRE_IN; } else if(code[i] == ':' && code[i+1] == '|') { ++i; state = BLOCK_FIND; } else if(code[i] == '|' && code[i+1] == ':') { block_workers[block_depth] = last_worker; block_output[block_depth++] = 0; //puts("Found |: increasing block depth"); ++i; } else if(!is_whitespace(code[i])) { start = i; for(j = i; j < len-1; ++j) if(code[j] == '<' && code[j+1] == '-') { state = OUT_STATE; --i; break; } else if(code[j] == '[' || code[j] == '\n' || (code[j] == '|' && code[j+1] == ':') || code[j] == '#' || code[j] == '"') { state = WORK_STATE; break; } if(state == NULL_STATE) state = WORK_STATE; } break; case OUT_STATE: if(code[i] == ',' || (code[i] == '<' && code[i+1] == '-')) { outputs[num_outputs++] = code + start; for(j = i-1; j > start; --j) if(!is_whitespace(code[j])) break; if(code[i] == ',') state = BETWEEN_OUT; else { state = BEFORE_WORK; ++i; } code[j+1] = '\0'; } break; case BETWEEN_OUT: if(!is_whitespace(code[i])) { start = i; state = OUT_STATE; --i; } break; case BEFORE_WORK: if(code[i] == '[') { //puts("worker_expr = TRUE"); worker_expr = TRUE; state = BETWEEN_PRE_IN; } else if(code[i] == ':' && code[i+1] == '|') { start = i; while(i < len && (code[i] != '\n' || saw_line < 5)) { if(code[i] == '\n') ++saw_line; ++i; } code[i] = '\0'; ERRORPRINTF("Error: Expected a worker name, but found a closing bracket (:|) instead at:\n%s", code + start); exit(-1); } else if(!is_whitespace(code[i])) { start = i; state = WORK_STATE; } break; case PRE_IN_STATE: if((code[i] == ',' && left_bracket == 1) || (code[i] == ']' && left_bracket == 0) ) { inputs[num_inputs++] = code + start; for(j = i-1; j > start; --j) if(!is_whitespace(code[j])) break; if(code[i] == ',') state = BETWEEN_PRE_IN; else state = BEFORE_WORK; code[j+1] = '\0'; } break; case BETWEEN_PRE_IN: if(code[i] == ']') { state = BEFORE_WORK; } else if(!is_whitespace(code[i])) { start = i; state = PRE_IN_STATE; } break; case WORK_STATE: if(code[i] == '[' || code[i] == '#' || (code[i] == '|' && code[i+1] == ':') || (code[i] == ':' && code[i+1] == '|') || code[i] == '\n') { for(j = i-1; j > start; --j) if(!is_whitespace(code[j])) break; worker = code+start; if(code[i] == '[') { code[j+1] = '\0'; // puts("Worker to Between Input"); // puts("worker_expr = TRUE;"); worker_expr = TRUE; state = BETWEEN_IN; } else if(code[i] == '#' || code[i] == '\n') { code[j+1] = '\0'; // puts("Worker to Null State(#)"); //printf("worker_expr: %d\n", worker_expr); if(block_depth) last_worker = process_expression(def, num_outputs, outputs, num_inputs, inputs, worker, worker_expr, block_depth, block_workers, block_output, last_worker); else last_worker = process_expression(def, num_outputs, outputs, num_inputs, inputs, worker, worker_expr, prev_block_depth, prev_block_workers, prev_block_output, last_worker); num_inputs = 0; num_outputs = 0; worker_expr = FALSE; state = NULL_STATE; } else if(code[i] == ':') { code[j+1] = '\0'; if(block_depth) last_worker = process_expression(def, num_outputs, outputs, num_inputs, inputs, worker, worker_expr, block_depth, block_workers, block_output, last_worker); else last_worker = process_expression(def, num_outputs, outputs, num_inputs, inputs, worker, worker_expr, prev_block_depth, prev_block_workers, prev_block_output, last_worker); num_inputs = 0; num_outputs = 0; worker_expr = FALSE; state = BLOCK_FIND; ++i; } else { //puts("Worker to Null State(else)"); code[j+1] = '\0'; if(block_depth) last_worker = block_workers[block_depth] = process_expression(def, num_outputs, outputs, num_inputs, inputs, worker, worker_expr, block_depth, block_workers, block_output, last_worker); else last_worker = block_workers[block_depth] = process_expression(def, num_outputs, outputs, num_inputs, inputs, worker, worker_expr, prev_block_depth, prev_block_workers, prev_block_output, last_worker); block_output[block_depth++] = 0; //puts("Found |:, increasinb block depth"); num_inputs = 0; num_outputs = 0; worker_expr = FALSE; state = NULL_STATE; ++i; } } break; case BETWEEN_IN: if(code[i] == ']') { state = AFTER_IN; } else if(!is_whitespace(code[i])) { start = i; state = IN_STATE; } break; case IN_STATE: if((code[i] == ',' && left_bracket == 1) || (code[i] == ']' && left_bracket == 0) ) { inputs[num_inputs++] = code + start; for(j = i-1; j > start; --j) if(!is_whitespace(code[j])) break; if(code[i] == ',') state = BETWEEN_IN; else state = AFTER_IN; code[j+1] = '\0'; } break; case AFTER_IN: //puts("AFTER_IN test"); if(code[i] == '\n') saw_newline = TRUE; if(code[i] == '|' && code[i+1] == ':') { if(block_depth) last_worker = block_workers[block_depth] = process_expression(def, num_outputs, outputs, num_inputs, inputs, worker, worker_expr, block_depth, block_workers, block_output, last_worker); else last_worker = block_workers[block_depth] = process_expression(def, num_outputs, outputs, num_inputs, inputs, worker, worker_expr, prev_block_depth, prev_block_workers, prev_block_output, last_worker); block_output[block_depth++] = 0; //puts("Found |: increasing block depth"); state = NULL_STATE; num_inputs = 0; num_outputs = 0; worker_expr = FALSE; ++i; } else if(code[i] == '#') { if(block_depth) last_worker = process_expression(def, num_outputs, outputs, num_inputs, inputs, worker, worker_expr, block_depth, block_workers, block_output, last_worker); else last_worker = process_expression(def, num_outputs, outputs, num_inputs, inputs, worker, worker_expr, prev_block_depth, prev_block_workers, prev_block_output, last_worker); state = NULL_STATE; num_inputs = 0; num_outputs = 0; worker_expr = FALSE; } else if(code[i] == '[') { if(saw_newline) { if(block_depth) last_worker = process_expression(def, num_outputs, outputs, num_inputs, inputs, worker, worker_expr, block_depth, block_workers, block_output, last_worker); else last_worker = process_expression(def, num_outputs, outputs, num_inputs, inputs, worker, worker_expr, prev_block_depth, prev_block_workers, prev_block_output, last_worker); saw_newline = FALSE; state = NULL_STATE; num_inputs = 0; num_outputs = 0; worker_expr = FALSE; --left_bracket; --i; } else { puts("Error: Too many input blocks at"); code[i+1] = '\0'; for(j = i-1; j > 0; --j) if(code[j] == '\n') { ++j; break; } puts(code + j); exit(-1); } } else if(!is_whitespace(code[i])) { if(block_depth) last_worker = process_expression(def, num_outputs, outputs, num_inputs, inputs, worker, worker_expr, block_depth, block_workers, block_output, last_worker); else last_worker = process_expression(def, num_outputs, outputs, num_inputs, inputs, worker, worker_expr, prev_block_depth, prev_block_workers, prev_block_output, last_worker); if(saw_newline) { saw_newline = FALSE; num_inputs = 0; } else { num_inputs = 1; inputs[0] = NULL; } state = NULL_STATE; num_outputs = 0; worker_expr = FALSE; --i; } break; case BLOCK_FIND: if(code[i] == '|' && code[i+1] == ':') { //puts("Found |: increasing output number (not block depth)"); ++block_output[block_depth-1]; ++i; state = NULL_STATE; } else if(!is_whitespace(code[i])) { //puts("Found :| without another |: following; decreasing block depth"); --block_depth; if(code[i] == '[') --left_bracket; --i; num_inputs = 0; state = NULL_STATE; } break; case LINE_COMMENT_STATE: if(code[i+1] == '\n') { for(;line_comment_start <= i; ++line_comment_start) code[line_comment_start] = ' '; state = old_state; } break; } } if(do_curly) { ++left_curly; do_curly = FALSE; } } else if(literal) literal = FALSE; else if(code[i] == '"') in_string = FALSE; else if(code[i] == '\\') literal = TRUE; } //printf("State at end of code chunk: %s(%d)\n", state_txt[state], state); if((state != BLOCK_FIND && block_depth != 0) || (state == BLOCK_FIND && block_depth > 1)) { ERRORPRINTF("Syntax Error: Missing %d block close symbol(s) (:|)\n", block_depth); exit(-1); } else { switch(state) { case WORK_STATE: for(j = i-1; j > start; --j) if(!is_whitespace(code[j])) break; worker = code+start; code[j+1] = '\0'; case AFTER_IN: if(block_depth) last_worker = process_expression(def, num_outputs, outputs, num_inputs, inputs, worker, worker_expr, block_depth, block_workers, block_output, last_worker); else last_worker = process_expression(def, num_outputs, outputs, num_inputs, inputs, worker, worker_expr, prev_block_depth, prev_block_workers, prev_block_output, last_worker); break; case BLOCK_FIND: case NULL_STATE: break; default: printf("Syntax Error: State is %s but there are no more characters left to process.\n", state_txt[state]); puts(code + start); exit(-1); } } return last_worker; } typedef struct parse_worker { char * worker_name; int num_inputs; int num_outputs; worker_def * def; char * block; int block_len; struct parse_worker * next; } parse_worker; void parse_company(char * name, char * code, int len, program * prog) { char * type_name; char * field_name; int i; int part_start; int part_end; int type; company * comp; BOOL saw_newline; field_name = type_name = NULL; comp = create_company(prog, name, 0, 0, TRUE); i = 0; while(i < len) { saw_newline = FALSE; part_start = -1; for(; i < len; ++i) { DEBUGPRINTF("i: %d, code[i]: %c ", i, code[i]); if(part_start < 0) { if(!is_whitespace(code[i])) part_end = part_start = i; } else if(code[i] == ':' && code[i-1] == ':') { type_name = code + part_start; type_name[part_end - part_start-1] = '\0'; part_start = -1; ++i; break; } else if(!is_whitespace(code[i])) { DEBUGPUTS("Not whitespace\n"); if(saw_newline) { code[part_end] = '\0'; add_comp_room(comp, code + part_start, -1, -1, ROOM_VIS_REF, ROOM_VIS_REF); --i; part_start = -1; saw_newline = FALSE; } else part_end = i+1; } else if(code[i] == '\n') { DEBUGPUTS("saw newline\n"); saw_newline = TRUE; } } if(i >= len && part_start >= 0 && part_end > part_start) { code[part_end] = '\0'; add_comp_room(comp, code + part_start, -1, -1, ROOM_VIS_REF, ROOM_VIS_REF); part_start = -1; } for(; i < len; ++i) { DEBUGPRINTF("i: %d, code[i]: %c ", i, code[i]); if(part_start < 0) { if(!is_whitespace(code[i])) part_end = part_start = i; DEBUGPRINTF("part_start: %d\n", part_start); } else if(!is_whitespace(code[i])) { DEBUGPRINTF("part_end: %d\n", part_end); part_end = i+1; } else if(code[i] == '\n') { DEBUGPRINTF("saw newline, type name: %s\n", type_name); if(!strcmp("Byte", type_name)) type = ROOM_BYTE; else if(!strcmp("Word", type_name)) type = ROOM_SHORT; else if(!strcmp("Long", type_name)) type = ROOM_LONG; else type = ROOM_VIS_REF; code[part_end] = '\0'; add_comp_room(comp, code + part_start, -1, -1, type, type); break; } } } if(part_start >= 0 && part_end > part_start) { if(!strcmp("Byte", type_name)) type = ROOM_BYTE; else if(!strcmp("Word", type_name)) type = ROOM_SHORT; else if(!strcmp("Long", type_name)) type = ROOM_LONG; else type = ROOM_VIS_REF; code[part_end] = '\0'; add_comp_room(comp, code + part_start, -1, -1, type, type); } } void import(char * filename, program * prog) { int size; char * code; FILE * imp; imp = fopen(filename, "rb"); if(imp) { fseek(imp, 0, SEEK_END); size = ftell(imp); fseek(imp, 0, SEEK_SET); code = MALLOC(size, "code from imported file"); fread(code, 1, size, imp); DEBUGPRINTF("Read %d bytes\n", size); parse(code, size, prog); DEBUGPUTS("Finished parsing import file\n"); VIS_FREE(code, "code buffer"); fclose(imp); } else { ERRORPRINTF("Warning: Could not open import file: %s\n", filename); } } typedef enum {NULL_PARSE, WORKER_STATE, BLOCK_STATE, STRING_STATE, LITERAL_STATE, LIST_STATE, LIST_STRING, LIST_LITERAL, LINE_COMMENT} super_state; char super_state_txt[9][30] = {"Null", "Worker", "Block", "String", "String Literal", "List", "String in List", "String Literal in List", "Single Line Comment"}; void parse(char * code, int len, program * prog) { parse_worker *list=NULL, *current=NULL, *temp; short saw_line = 0; int i,j,k; int block_count = 0; int start; int end; int num_inputs; int num_outputs; int company_len, import_len; char * worker_name; int left_curly=0; int block_done = -1; int comp_start, comp_end; char * company_name; char * comp_block; int comp_block_len; int num_uses; char * importfiles[32]; int num_imported = 0; super_state old_state; char ** uses_names = NULL; BOOL lastwasspace = FALSE; super_state state = NULL_PARSE; company_len = strlen("Company"); import_len = strlen("Import"); for(i = 0; i < len; ++i) { DEBUGPRINTF("i: %d, code[i]: %c, state: %s(%d)\n", i, code[i], super_state_txt[state], state); if(code[i] == '/' && code[i+1] == '/' && state != STRING_STATE && state != LIST_STRING) { old_state = state; state = LINE_COMMENT; i += 2; } switch(state) { case NULL_PARSE: if(len - i >= import_len && !memcmp("Import", code + i, import_len)) { start = end = -1; for(j = i+import_len; j < len && code[j] != '\n'; ++j) { if(start < 0) { if(!is_whitespace(code[j])) start = j; } else { if(!is_whitespace(code[j])) end = j; } } if(end < 0) { puts("Error: Import statement with no file name"); exit(-1); } ++end; code[end] = '\0'; i = end; if(num_imported < 32) importfiles[num_imported++] = code+start; else { puts("Error: You cannot have more than 32 import statements in the same file"); exit(-1); } } else if(code[i] == ':' && code[i+1] == '|') { start = i; while((code[i] != '\n' || saw_line < 5) && i < len) { if(code[i] == '\n') ++saw_line; ++i; } code[i] = '\0'; ERRORPRINTF("Error: Unexpected closing bracket (:|) found:\n%s", code + start); exit(-1); } else if(!is_whitespace(code[i])) { start = i; state = WORKER_STATE; } break; case WORKER_STATE: if(is_whitespace(code[i])) lastwasspace = TRUE; else if(code[i] == '|' && code[i+1] == ':') { if(!strncmp(code+start, "Company",company_len) && is_whitespace(code[start+company_len])) { comp_start = -1; comp_end = 0; for(j = start+company_len+1; j < len-1; ++j) { if(comp_start >= 0) { if(code[j] == '|' && code[j+1] == ':') break; else if(!is_whitespace(code[j])) comp_end = j+1; } else { if(!is_whitespace(code[j])) comp_start = j; } } if(comp_start >= 0 && comp_end > comp_start) { company_name = code + comp_start; code[comp_end] = '\0'; comp_start = -1; for(j = comp_end+1; j < len-1; ++j) { if(comp_start < 0) { if(code[j] == '|' && code[j+1] == ':') comp_start = j+2; } else { if(code[j] == ':' && code[j+1] == '|') { comp_end = j; break; } } } if(comp_start > 0) { comp_block = code+comp_start; comp_block_len = comp_end-comp_start; DEBUGPRINTF("Company block start: %d, end: %d, length: %d\n", comp_start, comp_end, comp_block_len); code[comp_end] = '\0'; i = comp_end+1; parse_company(company_name, comp_block, comp_block_len, prog); } else { printf("Error parsing Company, comp_start is %d\n", comp_start); } } else { puts("Error parsing Company\n"); } state = NULL_STATE; } else { for(j = i-1; j > start; --j) if(code[j] != ' ' && code[j] != '\t' && code[j] != '\n' && code[j] != '\r') break; for(k = start; k <= j; ++k) if(code[k] == '(') { code[k] = '\0'; num_inputs = atol(code + k + 1); } else if(code[k] == ',') num_outputs = atol(code + k + 1); worker_name = code + start; //printf("Found worker def: %s with %d outputs and %d inputs\n", worker_name, num_outputs, num_inputs); start = i+2; ++i; num_uses = 0; state = BLOCK_STATE; } } else if(lastwasspace && !strncmp(code+i, "uses", 4)) { for(j = i-1; j > start; --j) if(code[j] != ' ' && code[j] != '\t' && code[j] != '\n' && code[j] != '\r') break; for(k = start; k <= j; ++k) if(code[k] == '(') { code[k] = '\0'; num_inputs = atol(code + k + 1); } else if(code[k] == ',') num_outputs = atol(code + k + 1); worker_name = code + start; num_uses = 1; for(j = i + strlen("uses"); code[j] != '|' && j < len; ++j) if(code[j] == ',') ++num_uses; DEBUGPRINTF("num_uses: %d\n", num_uses); i += strlen("uses"); uses_names = MALLOC(sizeof(char *) * num_uses, "uses stores names"); end = -1; start = i; j = 0; while(code[i] != '|' && i < len) { if(code[i] == ',') { uses_names[j] = MALLOC(sizeof(char) * (end-start+1), "global store name for uses stores"); memcpy(uses_names[j], code+start, end-start); uses_names[j][end-start] = '\0'; DEBUGPRINTF("uses: %s\n", uses_names[j]); end = -1; ++j; } else if(code[i] != ' ' && code[i] != '\t' && code[i] != '\r' && code[i] != '\n') end = i+1; else if(end < 0) start = i+1; ++i; } if(end >= 0) { uses_names[j] = MALLOC(sizeof(char) * (end-start+1), "global store name for uses stores"); memcpy(uses_names[j], code+start, end-start); uses_names[j][end-start] = '\0'; DEBUGPRINTF("uses: %s\n", uses_names[j]); end = -1; ++j; } start = i+2; ++i; state = BLOCK_STATE; } break; case BLOCK_STATE: //printf("Block count: %d\n", block_count); if((i-block_done)>1 && code[i] == ':' && code[i-1] == '|') { ++block_count; } else if(code[i] == '"') { state = STRING_STATE; } else if(code[i] == '{') { state = LIST_STATE; } else if(code[i] == '|' && code[i-1] == ':') { if(block_count == 0) { if(current) { current->next = MALLOC(sizeof(parse_worker),"parse worker"); current = current->next; } else { list = current = MALLOC(sizeof(parse_worker),"parse worker"); } current->num_inputs = num_inputs; current->num_outputs = num_outputs; current->worker_name = worker_name; current->block = code + start; current->block_len = i-start-1; current->next = NULL; current->def = create_worker(prog, current->worker_name, current->num_inputs, current->num_outputs, USER_FLAG | WORKER_TYPE); current->def->uses_stores = uses_names; current->def->num_stores = num_uses; uses_names = NULL; num_uses = 0; state = NULL_PARSE; block_done = -1; } else { block_done = i; --block_count; } } break; case STRING_STATE: if(code[i] == '\\') state = LITERAL_STATE; else if(code[i] == '"') state = BLOCK_STATE; break; case LITERAL_STATE: state = STRING_STATE; break; case LIST_STATE: if(code[i] == '{') ++left_curly; else if(code[i] == '}') if(left_curly) --left_curly; else state = BLOCK_STATE; else if(code[i] == '"') state = LIST_STRING; break; case LIST_STRING: if(code[i] == '\\') state = LIST_LITERAL; else if(code[i] == '"') state = LIST_STATE; break; case LIST_LITERAL: state = LIST_STRING; break; case LINE_COMMENT: if(code[i+1] == '\n') state = old_state; break; } } if(state != NULL_PARSE) { printf("Error: Current parse state is %s(%d) in first pass but there are no more characters to parse.\n", super_state_txt[state], state); if(state == BLOCK_STATE) { printf("Worker %s appears to be missing a closing bracket (:|)\n", worker_name); } exit(-3); } for(i = 0; i < num_imported; ++i) { import(importfiles[i], prog); } current = list; while(current) { DEBUGPRINTF("Processing worker %s with %d inputs and %d outputs\n\n", current->worker_name, current->num_inputs, current->num_outputs); parse_body(current->def, current->block, current->block_len, 0, NULL, NULL); temp = current; current = current->next; free(temp); } }