/* Core Wars. * Copyright (C) 1999 Walter Hofmann * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as * published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ %{ #include #include #include #include #include #include #include #include "main.h" #include "program.h" #include "program-cw.h" #include "options.h" int current_line; struct cw_line *root_line; char cw_error_message[1024]; void cw_error (char *s); int cw_lex (void); void cw_restart(FILE *input_file); %} %union { int integer; struct cw_line *line; struct cw_operand *operand; char *name; } %token INTEGER %token IDENTIFIER %token WHITESPACE %token COMMENT %token STRING %token KEYWORD %type input line command instruction %type label %type operand %% /* Grammar rules and actions follow */ program: { current_line = 1; } input { root_line = $2; } input: /* empty */ { $$ = NULL; } | line input { if ($1) { $$ = $1; $1->next = $2; } else { $$ = $2; } } ; line: whitespace comment '\n' { $$ = NULL; current_line++; } | whitespace command comment '\n' { $$ = $2; $$->line_number = current_line++; } ; command: label instruction { $$ = $2; $$->label = $1; } ; label: /* empty */ { $$ = NULL; } | IDENTIFIER ':' whitespace { $$ = $1; } ; instruction: KEYWORD WHITESPACE operand whitespace ',' whitespace operand whitespace { $$ = malloc (sizeof (struct cw_line)); $$->command = $1; $$->operand1 = $3; $$->operand2 = $7; } | KEYWORD WHITESPACE operand whitespace { $$ = malloc (sizeof (struct cw_line)); $$->command = $1; $$->operand1 = $3; $$->operand2 = NULL; } ; operand: STRING { $$ = malloc (sizeof (struct cw_operand)); $$->accessmode = AM_STRING; $$->operand.name = $1; } | INTEGER { $$ = malloc (sizeof (struct cw_operand)); $$->accessmode = AM_CONSTANT; $$->operand.value = $1; } | '&' whitespace IDENTIFIER { $$ = malloc (sizeof (struct cw_operand)); $$->accessmode = AM_ADDRESS; $$->operand.name = $3; } | IDENTIFIER { $$ = malloc (sizeof (struct cw_operand)); $$->accessmode = AM_VALUE; $$->operand.name = $1; } | '[' IDENTIFIER ']' { $$ = malloc (sizeof (struct cw_operand)); $$->accessmode = AM_INDIRECT; $$->operand.name = $2; } ; comment: /* empty */ {} | COMMENT {} ; whitespace: /* empty */ {} | WHITESPACE {} ; %% void free_parsed_program (struct cw_line* l) { struct cw_line *next; while (l) { next = l->next; if (l->operand1) { if (l->operand1->accessmode!=AM_CONSTANT) free (l->operand1->operand.name); free (l->operand1); } if (l->operand2) { if (l->operand2->accessmode!=AM_CONSTANT) free (l->operand2->operand.name); free (l->operand2); } free (l); l = next; } } void cw_dump_program (struct program *pr) { printf ("Not implemented.\n"); exit (1); } void cw_dump_program_internal (struct program *pr) { printf ("Not implemented.\n"); exit (1); } /* Loads error and code into a struct program. Unloads old error and old code if neccessary. Only fails on fatal errors. */ int cw_load_program (struct program *p) { struct label { char *name; int address; } ; struct cw_line *l, *next; char error_message[1024]; int opcode_number, label_number; struct label *label_table; int i, n; FILE *f; unload_program (p); f = fopen (p->filename, "r"); if (!f) return FAIL; cw_restart (f); if (cw_parse()==0) { fclose (f); l = root_line; label_number = 0; while (l) { if (l->command==CMD_TITLE) if (p->title) { sprintf (error_message, "%s (%d): Second 'title' keyword", p->filename, l->line_number); p->error = strdup (error_message); free_parsed_program (root_line); return OK; } else if (l->operand1->accessmode!=AM_STRING) { sprintf (error_message, "%s (%d): 'title' has string argument", p->filename, l->line_number); p->error = strdup (error_message); free_parsed_program (root_line); return OK; } else if (l->operand2) { sprintf (error_message, "%s (%d): 'title' has one argument", p->filename, l->line_number); p->error = strdup (error_message); free_parsed_program (root_line); return OK; } else if (l->label) { sprintf (error_message, "%s (%d): 'title' cannot have a label", p->filename, l->line_number); p->error = strdup (error_message); free_parsed_program (root_line); return OK; } else p->title = strdup (l->operand1->operand.name); else if (l->command==CMD_AUTHOR) if (p->author) { sprintf (error_message, "%s (%d): Second 'author' keyword", p->filename, l->line_number); p->error = strdup (error_message); free_parsed_program (root_line); return OK; } else if (l->operand1->accessmode!=AM_STRING) { sprintf (error_message, "%s (%d): 'author' has string argument", p->filename, l->line_number); p->error = strdup (error_message); free_parsed_program (root_line); return OK; } else if (l->operand2) { sprintf (error_message, "%s (%d): 'author' has one argument", p->filename, l->line_number); p->error = strdup (error_message); free_parsed_program (root_line); return OK; } else if (l->label) { sprintf (error_message, "%s (%d): 'author' cannot have a label", p->filename, l->line_number); p->error = strdup (error_message); free_parsed_program (root_line); return OK; } else p->author = strdup (l->operand1->operand.name); else { if (l->label) label_number++; switch (l->command) { case CMD_DATA: case CMD_JUMP: case CMD_FORK: case CMD_OWN: if (l->operand2) { sprintf (error_message, "%s (%d): One operand expected", p->filename, l->line_number); p->error = strdup (error_message); free_parsed_program (root_line); return OK; } break; case CMD_MOVE: case CMD_ADD: case CMD_NEG: case CMD_AND: case CMD_NOT: case CMD_MUL: case CMD_DIV: case CMD_MOD: case CMD_EQUAL: case CMD_LESS: case CMD_INFO: case CMD_SYSTEM: case CMD_LOOP: case CMD_MOVEI: if (!l->operand2) { sprintf (error_message, "%s (%d): Two operands expected", p->filename, l->line_number); p->error = strdup (error_message); free_parsed_program (root_line); return OK; } break; } switch (l->command) { case CMD_DATA: if (l->operand1->accessmode!=AM_CONSTANT && l->operand1->accessmode!=AM_ADDRESS) { sprintf (error_message, "%s (%d): 'data' expects constant or address", p->filename, l->line_number); p->error = strdup (error_message); free_parsed_program (root_line); return OK; } break; case CMD_MOVE: case CMD_ADD: case CMD_NEG: case CMD_AND: case CMD_NOT: case CMD_MUL: case CMD_DIV: case CMD_MOD: if (l->operand2->accessmode!=AM_VALUE && l->operand2->accessmode!=AM_INDIRECT) { sprintf (error_message, "%s (%d): 2nd operand must be variable or indirect", p->filename, l->line_number); p->error = strdup (error_message); free_parsed_program (root_line); return OK; } break; case CMD_JUMP: case CMD_FORK: case CMD_OWN: if (l->operand1->accessmode!=AM_VALUE && l->operand1->accessmode!=AM_INDIRECT) { sprintf (error_message, "%s (%d): operand must be a label or indirect", p->filename, l->line_number); p->error = strdup (error_message); free_parsed_program (root_line); return OK; } break; case CMD_INFO: if (l->operand1->accessmode!=AM_CONSTANT) { sprintf (error_message, "%s (%d): 1st operand must be constant", p->filename, l->line_number); p->error = strdup (error_message); free_parsed_program (root_line); return OK; } if (l->operand2->accessmode!=AM_VALUE && l->operand2->accessmode!=AM_INDIRECT) { sprintf (error_message, "%s (%d): 2nd operand must be variable or indirect", p->filename, l->line_number); p->error = strdup (error_message); free_parsed_program (root_line); return OK; } break; case CMD_SYSTEM: if (l->operand2->accessmode!=AM_CONSTANT) { sprintf (error_message, "%s (%d): 2nd operand must be constant", p->filename, l->line_number); p->error = strdup (error_message); free_parsed_program (root_line); return OK; } break; case CMD_LOOP: if (l->operand1->accessmode!=AM_VALUE && l->operand1->accessmode!=AM_INDIRECT) { sprintf (error_message, "%s (%d): 1st operand must be variable or indirect", p->filename, l->line_number); p->error = strdup (error_message); free_parsed_program (root_line); return OK; } if (l->operand2->accessmode!=AM_VALUE && l->operand2->accessmode!=AM_INDIRECT) { sprintf (error_message, "%s (%d): 2nd operand must be a label or indirect", p->filename, l->line_number); p->error = strdup (error_message); free_parsed_program (root_line); return OK; } break; case CMD_MOVEI: if (l->operand1->accessmode!=AM_VALUE) { sprintf (error_message, "%s (%d): 1st operand must be variable", p->filename, l->line_number); p->error = strdup (error_message); free_parsed_program (root_line); return OK; } if (l->operand2->accessmode!=AM_VALUE) { sprintf (error_message, "%s (%d): 2nd operand must be variable", p->filename, l->line_number); p->error = strdup (error_message); free_parsed_program (root_line); return OK; } break; } } l = l->next; } if (!p->title) p->title = strdup (p->filename); if (!p->author) p->author = strdup ("unknown author"); label_table = malloc (sizeof (struct label)*label_number+1); l = root_line; opcode_number = 0; n = 0; while (l) { if (l->label) { for (i=0; ilabel, label_table[i].name)==0) { snprintf (error_message, 1024, "%s (%d): Duplicate label (%s)", p->filename, l->line_number, l->label); error_message[1023] = '\0'; p->error = strdup (error_message); free (label_table); free_parsed_program (root_line); return OK; } label_table[n].name = l->label; label_table[n++].address = opcode_number; } if (l->command!=CMD_TITLE && l->command!=CMD_AUTHOR) opcode_number++; l = l->next; } if (!opcode_number) { sprintf (error_message, "%s: Program contains no code", p->filename); p->error = strdup (error_message); free (label_table); free_parsed_program (root_line); return OK; } l = root_line; while (l) { switch (l->operand1->accessmode) { case AM_ADDRESS: case AM_VALUE: case AM_INDIRECT: for (i=0; ioperand1->operand.name, label_table[i].name)==0) break; if (i==label_number) { snprintf (error_message, 1024, "%s (%d): Undefined label (%s)", p->filename, l->line_number, l->operand1->operand.name); error_message[1023] = '\0'; p->error = strdup (error_message); free (label_table); free_parsed_program (root_line); return OK; } } if (l->operand2) switch (l->operand2->accessmode) { case AM_ADDRESS: case AM_VALUE: case AM_INDIRECT: for (i=0; ioperand2->operand.name, label_table[i].name)==0) break; if (i==label_number) { snprintf (error_message, 1024, "%s (%d): Undefined label (%s)", p->filename, l->line_number, l->operand2->operand.name); error_message[1023] = '\0'; p->error = strdup (error_message); free (label_table); free_parsed_program (root_line); return OK; } } l = l->next; } if (opcode_number>MAX_LENGTH) { snprintf (error_message, 1024, "%s: Program exceeds maximum length (by %d instructions)", p->filename, opcode_number-MAX_LENGTH); error_message[1023] = '\0'; p->error = strdup (error_message); free (label_table); free_parsed_program (root_line); return OK; } p->lang.cw.opcode_number = opcode_number; p->lang.cw.code = malloc (sizeof (struct line *)*opcode_number); opcode_number = 0; l = root_line; while (l) { next = l->next; if (l->command==CMD_TITLE || l->command==CMD_AUTHOR) { free (l->operand1->operand.name); free (l->operand1); free (l); } else { switch (l->operand1->accessmode) { case AM_ADDRESS: case AM_VALUE: case AM_INDIRECT: for (i=0; ioperand1->operand.name, label_table[i].name)==0) { free (l->operand1->operand.name); l->operand1->operand.value = label_table[i].address-opcode_number; break; } } if (l->operand2) switch (l->operand2->accessmode) { case AM_ADDRESS: case AM_VALUE: case AM_INDIRECT: for (i=0; ioperand2->operand.name, label_table[i].name)==0) { free (l->operand2->operand.name); l->operand2->operand.value = label_table[i].address-opcode_number; break; } } l->next = NULL; l->label = NULL; p->lang.cw.code[opcode_number] = l; opcode_number++; } l = next; } for (i=0; ifilename, current_line); p->error = strdup (error_message); } return OK; } /* Unloads error and code from struct program. */ void cw_free_program (struct program *p) { if (p->lang.cw.code) free (p->lang.cw.code); p->lang.cw.code = NULL; } void cw_error (char *s) { }