/* Author: Jose Romeo Vela Date: May 4, 2002 email: jrvela@aristasol.com http://www.aristasol.com/ This software is licensed under GNU's GPL. For details see http://www.gnu.org/ Copyright (c) 2002, Jose Romeo Vela ============================================================================== Purpose: This bison grammer is used to generate parser for casm (cardiac assembler). The grammer also has a syntax directed translator to generate cardiac code. This grammer file (casm.y) is used in combination with a lexical analyzer created from the flex code casm.l. ============================================================================== Modifications: 5/20/2002 - jrv1 Fixed a bug in error in validation. Negative values in EQU were not detected, thus potentially generating invalid code. 5/24/2002 - jrv2 Fixed bug in cygwin when using stdin as a variable. oops. It seems like some *nix implement stdin differently. This might fix the issue with MacOSX. */ %{ #include #include #include #include #include "casm.data.h" #include "casm.h" #define EMPTY 0 #define SYMBOLIC 1 #define NUMERIC 2 #define VERSION "2.1.3" FILE * out; /* jrv2 */ extern FILE * yyin; struct sym_table * sym_ptr; struct sym_table * search_ptr; struct sym_table * proc_sym_ptr; struct cardiac_code * start_ptr; struct cardiac_code * end_ptr; struct proc_stack * top_proc_stack; int line ; /* Tracks input lines. Used to display error messages */ int loc = 1; /* Tracks mempry locations during code generation */ int value; /* used in different contexts as a utility variable */ int optype; /* Operator type */ int labeltype; /* Label type */ int casm_error = FALSE; /* Error indicator */ int rc; /* return codes */ char * temp_str; /* a temporary string variable */ void yyerror(const char *str) { fprintf(stderr,"casm:%s at line %d\n\n",str,line); casm_error = TRUE; } int yywrap() { return 1; } /* These functions should probably go in casmutls.c. It was easier easier to debug with the code here, eventually I will move this code over. When these functions are move, they must be fully parametrize to avoid accessing external variables. */ /* Generate instructions JMP MAIN and HRS */ int proc_main() { int value; /* Unresolved JMP, to be resolved by MAIN: PROC */ value=800; /* load return address */ code_add(&start_ptr,&end_ptr,loc,value); proc_symbol(&sym_ptr,"MAIN",end_ptr); loc ++; /* HRS */ value=900; code_add(&start_ptr,&end_ptr,loc,value); loc++; } int proc_stack_push(struct proc_stack * * top, struct sym_table * sym_ptr) { struct proc_stack * temp; temp = malloc(sizeof( struct proc_stack ) ); temp->sym_ptr = sym_ptr; temp->next = *top; *top = temp; } int proc_stack_pop(struct proc_stack * * top, struct sym_table * * sym_ptr) { struct proc_stack * temp; struct proc_stack * t; struct sym_table * s_ptr; t = *top; if ( t != NULL ) { *sym_ptr = t->sym_ptr; temp = t; *top = t->next; free(temp); } else { *sym_ptr = NULL; } } int add_label(struct sym_table * *s,char * label,int value) { int rc = 0; /* add label to symbol table */ search_ptr = *s; sym_table_search(&search_ptr,label); if (search_ptr != NULL) { if (search_ptr->backpatch_ptr != NULL) { /* Symbol existed, but was unresolved, we now have resolution */ search_ptr->value=value; bkpatch(search_ptr); rc = 2; } else { /* Duplicate symbol put error message */ fprintf(stderr,"Duplicate symbol \"%s\"\n",label); yyerror("parser error"); rc = 1; } } else { /* New symbol must be added */ sym_table_add(s,label,value); } return(rc); } int proc_symbol(struct sym_table **s,char * symbol,struct cardiac_code * code_ptr) { search_ptr = *s; sym_table_search(&search_ptr,symbol); if (search_ptr == NULL) { /* Symbol is undefined */ sym_table_unresolved_new(s,symbol,code_ptr); end_ptr->unresolved_symbol_ptr = *s; } else { /* Symbol is defined */ if (search_ptr->backpatch_ptr == NULL){ /* Bingo! The symbol is defined and has resolution */ code_ptr->cell = code_ptr->cell + search_ptr->value; } else { /* The symbol is defined, but unresolved */ sym_table_unresolved_add(search_ptr,code_ptr); code_ptr->unresolved_symbol_ptr = search_ptr; } } } %} %union { char * sval; int ival; }; %token NUMBER OPER INSTRUCTION LABEL LABTERM EOL GARBAGE %type operation %type instruction %type label %type operand %type input %type lines %% input: /*empty*/ { $$ = "" ; } | input lines | input error EOL { line++; fprintf(stderr,"Unrecognizible string. \n"); yyerror("parse error"); yyerrok; } ; lines: EOL {$$ = ""; line++;} | instruction EOL {line++;} | operation EOL {line++;} ; /* This syntax rule for instruction is the most complex since it handles a variaty of unique instructions. The code could be simplified if the syntax rules get redesigned to work in a different way. I plan to review this code in later releases. In the mean time just bare with it :) */ instruction: label INSTRUCTION operand { rc = 0; if ( ! strcmp($2,"EQU") ) { if ( optype != NUMERIC ) { fprintf(stderr,"Numeric operand expected\n"); yyerror("parse error"); YYERROR; } else { /* jrv1 value should be between 1 and 99 */ if ( ($3 < 0) || ($3 > 99) ) { fprintf(stderr,"Value out of bounds\n"); yyerror("parse error"); YYERROR; } else { if ( labeltype == EMPTY ) { fprintf(stderr,"Label expected\n"); yyerror("parse error"); YYERROR; } else { /* EQU instruction just creates a table entry */ rc = add_label(&sym_ptr,$1,$3); if ( rc == 1 ) { /* add_label calls yyerror however, the YYERROR macro is called here since it cannot be outside the scope of the Bison grammer section. */ YYERROR; } } } } } if ( ! strcmp($2,"CEL") ) { /* Generate code for CEL */ code_add(&start_ptr,&end_ptr,loc,$3); if ( labeltype == EMPTY || optype == EMPTY) { fprintf(stderr,"Label and operand expected\n"); yyerror("parse error"); YYERROR; } else { rc = add_label(&sym_ptr,$1,loc); loc++; } } if ( ! strcmp($2,"PROC") ) { /* Generate the cardiac code for PROC and setup the return stuff. */ /* One PROC instruction actully generates code for two cardiac instructions. One to load the return instruction to the accumulator (199) and one to store the return instruction at a location to be determine by a matching ENDP instruction. This allows casm to generate code for procedures. It is one of the percs that the assembler offers over machine code programming. */ value=199; /* load return address */ code_add(&start_ptr,&end_ptr,loc,value); rc = add_label(&sym_ptr,$1,loc); if ( rc != 1 ) { loc ++; /* Find the entry for the label in the symbol table and push this pointer into a stack so that it can late be mateched to an ENDP instruction. */ search_ptr = sym_ptr; sym_table_search(&search_ptr,$1); proc_stack_push(&top_proc_stack,search_ptr); /* A STO operation is created to an unresolved symbol so that when the symbol gets resolved, the generated code will be 6xx where xx is the return address for the symbol. */ value=600; /* store to undefined symbol */ code_add(&start_ptr,&end_ptr,loc,value); loc++; /* Create an unresolved symbol name the same as the label with a # at the end. This unique symbol is for internal use only. The purpose is to generate code that will allow us to return to the calling procedure. */ $1 = strcat($1,"#"); proc_symbol(&sym_ptr,$1,end_ptr); } } if ( ! strcmp($2,"ENDP")) { /* Generate code for ENDP */ value = 800; code_add(&start_ptr,&end_ptr,loc,800); rc = 0; if (labeltype != EMPTY) { /* add label to symbol table */ rc = add_label(&sym_ptr,$1,loc); } proc_sym_ptr = NULL; proc_stack_pop(&top_proc_stack,&proc_sym_ptr); if ( proc_sym_ptr == NULL ) { /* Error unmatched ENDP */ fprintf(stderr,"Unmatched ENDP instruction\n"); yyerror("parse error"); YYERROR; } else { /* OK, now generate a resolution for the internal label created by PROC (the one with the # at the end). */ temp_str = strdup(proc_sym_ptr->symbol); temp_str = strcat(temp_str,"#"); rc = add_label(&sym_ptr,temp_str,loc); free(temp_str); } if ( optype != EMPTY || rc == 1 ) { yyerror("parse error"); YYERROR; } loc++; } } ; operation: label OPER operand { code_add(&start_ptr,&end_ptr,loc,$2); if (labeltype != EMPTY) { /* add label to symbol table */ rc = add_label(&sym_ptr,$1,loc); } if (optype == EMPTY) { fprintf(stderr,"Operand expected\n"); yyerror("parse error"); YYERROR; } if (optype == SYMBOLIC) { proc_symbol(&sym_ptr,$3,end_ptr); } if (optype == NUMERIC) { if ( value < 0 || value > 99 ) { fprintf(stderr,"Value out of bounds\n"); yyerror("parse error"); YYERROR; } else { end_ptr->cell=end_ptr->cell+value; } } loc++; } ; label: /* empty */ { $$ = ""; labeltype=EMPTY} | LABEL LABTERM {labeltype=SYMBOLIC;} ; operand: /* empty */ { $$=""; optype=EMPTY; value=0; } | LABEL { $$ = $1; optype = SYMBOLIC; value = 0; } | NUMBER { $$ = $1; optype = NUMERIC; value=$1; } ; %% main(int argc,char * argv[]) { int option_o = FALSE; int casmrun = TRUE; int opt; /* set yydebug = 1 use the -t option in bison to debug the parsing */ yydebug = 0; while ((opt = getopt(argc,argv,"vho:")) != -1 ) { switch (opt) { case 'o': out=fopen(optarg,"w"); if (out==NULL) { casmrun=FALSE; } else { option_o = TRUE; } break; case 'v' : printf("casm v %s - CARDIAC Assembler\n",VERSION); casmrun = FALSE; break; case '?': printf("\nFor help, try: casm -h\n"); casmrun = FALSE; break; case 'h': fprintf(stderr,"Usage: casm [-vh] [-o ] \n"); casmrun = FALSE; break; } } /* jrv2 use yyin from flex instead of stdin */ yyin = NULL; if ( casmrun && optind > 0 && argv[optind] != '\0' ) { yyin = fopen(argv[optind],"r"); } if ( casmrun && yyin == NULL ) { fprintf(stderr,"error: invalid source file \"%s\"\n",argv[optind]); casmrun = FALSE; } if ( casmrun ) { if (! option_o) { out = fopen("a.cinc","w"); if ( out == NULL ) { fprintf(stderr,"error creating a.cinc\n"); return (1); } } line = 1; sym_ptr = NULL; start_ptr = NULL; end_ptr = NULL; top_proc_stack = NULL; /* proc_main initilizes the cardiac code with jump to main (JMP MAIN) and HRS */ proc_main(); yyparse(); value = sym_table_check(sym_ptr); if ( ! value ) { if ( ! casm_error) { code_out(start_ptr); } } else { fprintf(stderr,"casm error: There were %d unresolved symbols (including user and casm internal symbols)\n\n",value); } code_destroy(start_ptr); sym_table_destroy(sym_ptr); if ( top_proc_stack != NULL) { fprintf(stderr,"casm error: Unmatched PROC/ENDP instructions encountered\n\n"); while ( top_proc_stack != NULL ) { proc_stack_pop(&top_proc_stack,&proc_sym_ptr); } } } };