ftp.nice.ch/pub/next/unix/tools/jug.0.7.N.bs.tar.gz#/readjug.c

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.