This is readjug.c in view mode; [Download] [Up]
/* (c) 1991 S.Hawtin Permission is granted to make any use of this file provided 1) It is not used for commercial gain without my permission 2) This notice is included in all copies 3) Altered copies are marked as such No liability is accepted for the contents of the file. */ /* This file reads in the description of a juggle and converts it to a set of instructions */ #include <stdio.h> #include <stdlib.h> #include <ctype.h> #include "jug.h" #include "readjug.h" /* External varibales */ extern Ball balls[]; extern Hand hands[]; extern MoveInst *moves; extern int num_moves; extern int size_moves; extern Hand ***hand_table; extern int max_hand; extern Ball ***ball_table; extern int max_ball; /* Table to translate between moves and strings */ extern void move_nop(); extern void move_throw(); extern void move_catch(); extern void move_swap(); extern void move_loop(); extern void move_move(); MoveAction move_table[] = {/* Each entry contains a table to determine what parameters can be passed to moves */ {"nothing",move_nop, {0,0,0,0,0,0,0,0,0}}, {"throw",move_throw, {1,1,1,1,1,1,1,1,0}}, {"catch",move_catch, {1,1,1,1,1,0,0,0,0}}, {"move",move_move, {1,0,1,1,1,0,0,0,0}}, {"swap",move_swap, {1,1,0,0,0,0,0,0,0}}, {"loop",move_loop, {0,0,0,0,0,0,0,0,1}}, {"goto",move_loop, {0,0,0,0,0,0,0,0,1}}, /* Punctuation with text commands, indicated by NULL functions */ /* I originally used "to" as a position specifier but it means different things in "move to" and "throw to" so I dropped it */ {"hand",NULL, {1,0,0,0,0,0,0,0,0}}, {"ball",NULL, {0,1,0,0,0,0,0,0,0}}, {"position",NULL, {0,0,1,1,1,0,0,0,0}}, {"at",NULL, {0,0,1,1,1,0,0,0,0}}, {"from",NULL, {0,0,1,1,1,0,0,0,0}}, {"speed",NULL, {0,0,0,0,0,1,1,1,0}}, {"towards",NULL, {0,0,0,0,0,1,1,1,0}}, {"time",NULL, {0,0,0,0,0,0,0,0,1}}, {"label",NULL, {0,0,0,0,0,0,0,0,1}}, {"start",NULL, {1,1,1,1,1,0,0,0,0}}, }; extern char pattern_name[]; extern char pat_dir[]; extern int max_time; extern int loop_count; extern int gravity; /* Commands to set global things */ SetGlobal globals[] = { {"name",GLOBAL_STR,(void *)pattern_name}, {"max-moves",GLOBAL_INT,(void *)&max_time}, {"loop-count",GLOBAL_INT,(void *)&loop_count}, {"gravity",GLOBAL_INT,(void *)&gravity}, }; static int line_num = 1; static int num_errors = 0; /********************************************************************************/ static void error() {/* Tell the user where the error was */ fprintf(stderr,"Error in line %d: ",line_num); num_errors++; } static void do_global(in_file,str) FILE *in_file; char *str; {/* Process a global setting */ char temp[128]; int count; for(count=0;count<arraysize(globals);++count) {if(strcmp(globals[count].str,str) == 0) break; } if(count >= arraysize(globals)) {error(); fprintf(stderr,"Unknown global \"%s\"\n",str); return; } fgets(temp,128,in_file); line_num++; switch(globals[count].type) {case GLOBAL_STR: strcpy(globals[count].value,temp); break; case GLOBAL_INT: *(int *)(globals[count].value) = atoi(temp); break; case GLOBAL_FLOAT: *(double *)(globals[count].value) = (double)atof(temp); break; } } static int scan_str(in_file,str) FILE *in_file; char *str; {/* Scan the next delimited string from the file, put in "EOF" for end of file. Use a simple state based parser */ int state,next,count; int negative; state = STATE_START; count = 0; next = fgetc(in_file); while(next != EOF) { if(next == '\n') line_num++; again: switch(state) {case STATE_COMMENT: if(next == '\n') state = STATE_START; break; case STATE_GLOBAL: /* Set a global variable */ if(isalpha(next) || next=='_' || next=='-') {if(isupper(next)) str[count++] = tolower(next); else str[count++] = next; } else {/* We should have a string in the str variable use this to descide what to do next */ str[count] = '\0'; do_global(in_file,str); /* Carry on scanning from where we were */ state = STATE_START; count = 0; } break; case STATE_START: if(next == '!') state = STATE_GLOBAL; else if(next == '#') state = STATE_COMMENT; else if (isalpha(next)) {state = STATE_STR; goto again; } else if (next == '-') {state = STATE_NUM; str[0] = '-'; count++; } else if (isdigit(next)) {state = STATE_NUM; goto again; } else if (!isspace(next)) {/* All punctuation is single characters */ str[0] = next; str[1] = '\0'; return(TOK_PUNCT); } break; case STATE_STR: if(isalpha(next)) {if(isupper(next)) str[count++] = tolower(next); else str[count++] = next; } else {ungetc(next,in_file); if(next == '\n') line_num--; str[count] = '\0'; return(TOK_STR); } break; case STATE_NUM: if(isdigit(next)) {str[count++] = next; } else {ungetc(next,in_file); if(next == '\n') line_num--; str[count] = '\0'; return(TOK_NUM); } break; } next = fgetc(in_file); } return(TOK_EOF); } static void clasify_str(tok) Token *tok; {/* We have a token that is a string, what does it represent? */ int count; /* First guess is a command */ for(count=0;count<arraysize(move_table);++count) {if(strcmp(tok->str,move_table[count].name)==0) {tok->tok_type = TOK_CMD; tok->value = count+1; return; } } /* Next a ball */ for(count=0;count<MAX_BALLS;++count) {if(strcmp(tok->str,balls[count].alias) == 0) {tok->tok_type = TOK_BALL; tok->value = count+1; return; } } /* Finally a hand */ for(count=0;count<MAX_HANDS;++count) {if(strcmp(tok->str,hands[count].alias) == 0) {tok->tok_type = TOK_HAND; tok->value = count+1; return; } } } static void get_token(in_file,next) FILE *in_file; Token *next; {/* Read the next string and convert it to a token type */ char str[128]; next->tok_type = TOK_NULL; next->sub_type = SUB_NULL; /* Read the next element */ switch(scan_str(in_file,str)) {case TOK_PUNCT: next->tok_type = TOK_PUNCT; next->str[0] = str[0]; switch(str[0]) {case ':': next->sub_type = SUB_COLON; break; case ',': next->sub_type = SUB_COMMA; break; default: error(); fprintf(stderr,"character \"%c\"\n",str[0]); } break; case TOK_STR: next->tok_type = TOK_STR; strcpy(next->str,str); clasify_str(next); return; case TOK_NUM: next->tok_type = TOK_NUM; sscanf(str,"%d",&next->value); return; default: error(); fprintf(stderr,"Unknown token\n"); case TOK_EOF: next->tok_type = TOK_EOF; return; } } void print_token(token) Token *token; {/* Print out the value of a token */ switch(token->tok_type) {case TOK_CMD: fprintf(stderr," %s ", move_table[token->value - 1].name); break; case TOK_BALL: fprintf(stderr," <Ball %d> ",token->value); break; case TOK_HAND: fprintf(stderr," <Hand %d> ",token->value); break; case TOK_NUM: fprintf(stderr," %d ",token->value); break; case TOK_PUNCT: fprintf(stderr," \"%c\" ",token->str[0]); break; case TOK_STR: fprintf(stderr," |Nasty token %s| ",token->str); break; default: fprintf(stderr," |Nasty token| "); } } /********************************************************************************/ static int set_value(param_num,new_val,params,move) int param_num,new_val; int *params; MoveInst *move; {/* Put a parameter into the current move */ Hand *cur_hand; int count; /* Are we setting up? */ if(move->time == TIME_START) {/* Valid things to do at the start are the hands ball and position */ if(param_num != OFF_HAND && param_num != OFF_BALL) {if(params[param_num] & PARAM_SET) {error(); fprintf(stderr,"Attempt to set start param twice\n"); } params[param_num] |= PARAM_SET; } switch(param_num) { case OFF_HAND: /* Talk about the new hand */ move->param[OFF_HAND] = new_val; hands[new_val-1].num_balls = 0; hands[new_val-1].x = 0; /* Just so long as it is not DEFAULT */ /* Reset the parameters */ for(count=0;count<OFF_LAST;count++) params[count] &= ~PARAM_SET; return(CMD_START); case OFF_BALL: /* Add the ball to the hand */ cur_hand = &hands[move->param[OFF_HAND]-1]; cur_hand->balls[cur_hand->num_balls++] = &balls[new_val-1]; balls[new_val-1].y = cur_hand->y; /* Just so long as it is not DEFAULT */ return(CMD_START); case OFF_X: cur_hand = &hands[move->param[OFF_HAND]-1]; cur_hand->x = new_val; return(CMD_START); case OFF_Y: cur_hand = &hands[move->param[OFF_HAND]-1]; cur_hand->y = new_val; return(CMD_START); case OFF_Z: cur_hand = &hands[move->param[OFF_HAND]-1]; cur_hand->z = new_val; return(CMD_START); default: error(); fprintf(stderr,"Illegal parameter in startup\n"); return(CMD_DONE); } } if(params[param_num] & PARAM_SET) {error(); fprintf(stderr,"Attempt to set parameter twice\n"); } params[param_num] |= PARAM_SET; /* Check to see that we can set it at all!! */ move->param[param_num] = new_val; } int read_cmd(in_file,cmd_time) FILE *in_file; int cmd_time; {/* Read a command from the file, return the time of the next command */ static Token next_tok,last_tok,build_tok; static int use_last = 0; int state,last_num,count; int param[OFF_LAST]; MoveInst next_move; /* Initialise the state of everything */ state = CMD_START; next_move.time = cmd_time; next_move.what = MOVE_NOP; for(count=0;count<OFF_LAST;count++) {next_move.param[count] = DEFAULT; param[count] = 0; } next_move.cmnd = NULL; while(state != CMD_DONE) {/* Keep processing tokens until we have a complete command */ if(use_last) {memcpy(&next_tok,&last_tok,sizeof(Token)); use_last = 0; } else get_token(in_file,&next_tok); again: switch(next_tok.tok_type) {case TOK_EOF: state = CMD_DONE; cmd_time = TIME_INVALID; break; case TOK_HAND: if(next_move.time != TIME_START) {/* Convert to a logical hand number */ int num; if(hand_table == NULL) get_hand(1); for(num = 0;num<max_hand;num++) {if(hand_table[0][num] == &hands[next_tok.value - 1]) {next_tok.value = num + 1; break; } } } set_value(OFF_HAND,next_tok.value,param,&next_move); break; case TOK_BALL: if(next_move.time != TIME_START) {/* Convert to a logical ball number */ int num; if(ball_table == NULL) get_ball(1); for(num = 0;num<max_ball;num++) {if(ball_table[0][num] == &balls[next_tok.value - 1]) {next_tok.value = num + 1; break; } } } set_value(OFF_BALL,next_tok.value,param,&next_move); break; case TOK_CMD: /* This is either the start of a new command, or telling us to expect a parameter */ if(move_table[next_tok.value - 1].fun) {/* This is a real function, copy it into the command */ if(next_move.time == TIME_START) {error(); fprintf(stderr,"Cannot perform op in startup\n"); state = CMD_DONE; } else if(next_move.what != MOVE_NOP) {/* That must be the end of the previous move */ memcpy(&last_tok,&next_tok,sizeof(Token)); use_last = 1; state = CMD_DONE; } else {/* OK a valid move, put it in the next_move */ next_move.what = next_tok.value; next_move.cmnd = &move_table[next_tok.value - 1]; /* Keep note of valid parameters */ for(count=0;count<OFF_LAST;++count) {param[count] &= ~PARAM_SET; if(move_table[next_tok.value - 1].param[count]) param[count] |= PARAM_VALID; else param[count] &= ~PARAM_VALID; } } } else {if(next_move.time == TIME_START || next_move.what != MOVE_NOP) {/* Check if this is a valid param spec at the moment */ for(count=0;count<OFF_LAST;++count) {if(move_table[next_tok.value - 1].param[count]) {if((next_move.time == TIME_START) || (param[count] & PARAM_VALID)) param[count] |= PARAM_IMMEDIATE; else {error(); fprintf(stderr,"Cannot set here\n"); state = CMD_DONE; } } else { param[count] &= ~PARAM_IMMEDIATE; } } } } break; case TOK_NUM: /* Some sort of parameter, the next token will tell us what it means */ get_token(in_file,&last_tok); if(last_tok.tok_type == TOK_PUNCT) {/* So this is either a parameter or a time */ if(last_tok.sub_type == SUB_COMMA) {/* A Parameter, look at the parameter list */ int last_good; put_param: last_good = -1; for(count=0;count<OFF_LAST;++count) {if((last_good == -1) && (param[count] & PARAM_VALID) && !(param[count] & PARAM_SET)) last_good = count; if((param[count] & PARAM_IMMEDIATE) && !(param[count] & PARAM_SET)) { set_value(count,next_tok.value,param,&next_move); break; } } if(count>=OFF_LAST) {if(last_good != -1) set_value(last_good,next_tok.value,param,&next_move); else {error(); fprintf(stderr,"Too many params\n"); } } } else if (last_tok.sub_type == SUB_COLON) {/* A Time */ if(next_move.time > next_tok.value) {error(); fprintf(stderr,"Time is going backwards!!\n"); } cmd_time = next_tok.value; state = CMD_DONE; } else {error(); fprintf(stderr,"Invalid punctuation\n"); } } else {/* Put this on the "to be processed list" */ use_last = 1; /* Now get the parameter out */ goto put_param; } break; case TOK_PUNCT: default: error(); print_token(&next_tok); fprintf(stderr," not processed\n"); state = CMD_DONE; break; } } /* We have ended this command, did we get anything that needs dealing with? */ if(next_move.what != MOVE_NOP) {/* Move the current command into the instruction stream */ if(num_moves >= size_moves) {/* Need more space for moves */ if(size_moves == 0) {size_moves = 10; moves = (MoveInst *)malloc(size_moves * sizeof(MoveInst)); } else {/* Grab a few more moves */ size_moves += 10; moves = (MoveInst *)realloc(moves,size_moves * sizeof(MoveInst)); } } moves[num_moves].time = next_move.time; moves[num_moves].what = next_move.what; moves[num_moves].cmnd = next_move.cmnd; for(count=0;count<OFF_LAST;++count) {if(!(param[count] & PARAM_SET)) moves[num_moves].param[count] = DEFAULT; else moves[num_moves].param[count] = next_move.param[count]; } /* Special cases */ if(strcmp(next_move.cmnd->name,"swap") == 0) {/* This allows commands such as "swap hand" to work even if the value of the swap is not set */ if(param[OFF_HAND] & PARAM_IMMEDIATE && moves[num_moves].param[OFF_HAND] == DEFAULT) moves[num_moves].param[OFF_HAND] = 1; if(param[OFF_BALL] & PARAM_IMMEDIATE && moves[num_moves].param[OFF_BALL] == DEFAULT) moves[num_moves].param[OFF_BALL] = 1; } num_moves++; } /* Now tell the caller we have done */ return(cmd_time); } void read_file(str) char *str; {/* Read a file of juggling instructions */ int cmd_time; char long_str[128]; FILE *input; if(strcmp(str,"-") == 0) {input = stdin; if(loop_count == 0) loop_count = 1; } else input = fopen(str,"r"); if(input == NULL) {/* If we failed to open that file, try the same thing with a ".jug" extension */ strcpy(long_str,pat_dir); strcat(long_str,str); strcat(long_str,".jug"); input = fopen(long_str,"r"); if(input == NULL) {/* No such file found, try without the subdir */ strcpy(long_str,str); strcat(long_str,".jug"); input = fopen(long_str,"r"); if(input == NULL) {fprintf(stderr,"Cannot open \"%s\"\n",str); exit(20); } } } cmd_time = TIME_START; while(cmd_time != TIME_INVALID) {/* Read the next instruction */ cmd_time = read_cmd(input,cmd_time); } if(input != stdin) fclose(input); if(num_errors != 0) exit(20); }
These are the contents of the former NiCE NeXT User Group NeXTSTEP/OpenStep software archive, currently hosted by Netfuture.ch.