/* Syntax Analyzer with Bison (3.8.2) */ /* The Translators - Spring 2025 */ // ----- THIS FILE MUST BE FORMATTED CORRECTLY FOR READABILITY ----- // // ✏️ FORMATTING RULES: // 1️⃣ Use 4 spaces for indentation. // 2️⃣ Grammar rules (terminals and nonterminals) should always be on their own line. // 3️⃣ Grammar rules and C-blocks should always begin 8 spaces in. // 4️⃣ Rule end-markers (;, |) should always be 4 spaces in. // 5️⃣ C-blocks should always be clearly defined and follow clang formatting rules. // 6️⃣ 1-line if/for/while statements must be wrapped in curly braces. // 7️⃣ DO NOT USE TABS. EVER. // Please ask Scarlett if you are unsure of how to format something. Thanks! 😀 %{ #include #include "../src/symbol_table.c" #include extern int yylex(void); void yyerror(const char *err); extern char* yytext; extern int yyleng; extern int yychar; extern SymbolTable * cur; //char* cur_value; //char* cur_type; int token_tracker; extern int line_number; extern int column_number; extern FILE * yyin; extern TableNode* funprime; extern TableNode* arrayprim; extern TableNode* recprime; extern TableNode* funtypeprime; extern TableNode* integ; extern TableNode* addr; extern TableNode* chara; extern TableNode* stri; extern TableNode* boo; TableNode * tn; %} %union { int integ; char* words; void* tn; } %locations %type idlist %type assignable %type expression %type constant %type id_or_types %type types %type argument_list %type ablock %token ID 101 %token T_INTEGER 201 %token T_ADDRESS 202 %token T_BOOLEAN 203 %token T_CHARACTER 204 %token T_STRING 205 %token C_INTEGER 301 %token C_NULL 302 %token C_CHARACTER 303 %token C_STRING 304 %token C_TRUE 305 %token C_FALSE 306 %token WHILE 401 %token IF 402 %token THEN 403 %token ELSE 404 %token TYPE 405 %token FUNCTION 406 %token RETURN 407 %token EXTERNAL 408 %token AS 409 %token L_PAREN 501 %token R_PAREN 502 %token L_BRACKET 503 %token R_BRACKET 504 %token L_BRACE 505 %token R_BRACE 506 %token SEMI_COLON 507 %token COLON 508 %token COMMA 509 %token ARROW 510 %token MUL 603 %token DIV 604 %token REM 605 %token ADD 601 %token LESS_THAN 606 %token EQUAL_TO 607 %token AND 610 %token OR 611 %token ASSIGN 608 %token SUB_OR_NEG 602 %token NOT 609 %token DOT 612 %token RESERVE 613 %token RELEASE 614 %token COMMENT 700 //precedence order %left ASSIGN %left OR %left AND %left EQUAL_TO %left LESS_THAN %left ADD SUB_OR_NEG %left MUL DIV REM %precedence NOT %precedence UMINUS %precedence DOT %precedence RESERVE RELEASE %% program: prototype_or_definition_list ; prototype_or_definition_list: prototype prototype_or_definition_list | definition prototype_or_definition_list | prototype | definition ; prototype: L_PAREN EXTERNAL R_PAREN FUNCTION ID COLON ID; definition: TYPE ID COLON { printdebug("Currently see a record definition for %s", $2); tn = CreateEntry(getAncestor(cur),TYPE_RECORD_TYPE, recprime, $2, CreateRecordInfo(0, cur = CreateScope(cur, 0, 0))); if (look_up(cur, $2) == undefined) { printdebug("rec not found"); } } dblock { //We are scanning through the dblock scope to get the length of the dblock (num of elements) from getRecSize //and then putting it in the entry that we created above. setRecSize(look_up(getParent(cur), $2), getRecSize(cur)); cur = getParent(cur); } | TYPE ID COLON C_INTEGER ARROW id_or_types { printdebug("Currently see a array definition of name %s,storing type %s, of dimensions %d", $2, getName($6), $4); CreateEntry(cur,TYPE_ARRAY_TYPE, arrayprim, $2, CreateArrayInfo($4, $6)); printdebug("%sID: %s, dimensions: %d, typeOfArray: %s", COLOR_GREEN, $2, $4, getName($6)); } | function_declaration | TYPE ID COLON id_or_types ARROW id_or_types { printdebug("Currently see a function type definition of name %s,parameter type %s, of return type %s", $2, getName($4), getName($6)); CreateEntry(cur,TYPE_FUNCTION_TYPE,funtypeprime,$2,CreateFunctionTypeInfo($4 ,$6)); } | ID { TableNode *node = table_lookup(getAncestor(cur), $1); if (node == undefined) { printdebug("function not declared at line %d, column %d", @1.first_line, @1.first_column); } else if(getAdInfoType(node) != TYPE_FUNCTION_DECLARATION) { printdebug("function not declared at line %d, column %d", @1.first_line, @1.first_column); } else { setStartLine(node, @1.first_line); setAsKeyword(node, false); } cur = CreateScope(cur, 0, 0); } L_PAREN ID { printdebug("Currently see a function definition taking only one parameter (no as) of name %s and argument name %s", $1,$4); TableNode* type_of_param = getParameter(table_lookup(getAncestor(cur), getType(table_lookup(getAncestor(cur), $1)))); int type_of_param_type = getAdInfoType(type_of_param); if( type_of_param_type == TYPE_UNDEFINED || type_of_param_type == TYPE_FUNCTION_DECLARATION || type_of_param_type == TYPE_ARRAY || type_of_param_type == TYPE_ALL_ELSE || type_of_param_type == TYPE_SYSTEM_DEFINED || type_of_param_type == TYPE_STRING){ // note that strings are actually arrays so this is unused printdebug("type of parameter is undefined or invalid at line %d, column %d", @4.first_line, @4.first_column); type_of_param_type = TYPE_UNDEFINED; // setting tag as undefined in these cases }else{ printdebug("type of parameter is %s at line %d, column %d", getName(type_of_param), @4.first_line, @4.first_column); } CreateEntry(cur,type_of_param_type, type_of_param, $4, getAdInfo(type_of_param)); } R_PAREN ASSIGN sblock //leaving scope is being taken care of in sblock | ID { TableNode *node = table_lookup(getAncestor(cur), $1); if (node == undefined) { printdebug(" undefined nodedeclared at line %d, column %d", @1.first_line, @1.first_column); }else if(getAdInfoType(node) != TYPE_FUNCTION_DECLARATION){ printdebug("not a valid function declaration at line %d, column %d", @1.first_line, @1.first_column); } else { printdebug("setting as keyword to true"); setStartLine(node, @1.first_line); setAsKeyword(node, true); } cur = CreateScope(cur, 0, 0); }AS L_PAREN { TableNode *parameter = getParameter(table_lookup(getAncestor(cur), getType(table_lookup(getAncestor(cur), $1)))); printdebug("parameter type: %s", getType(parameter)); if (parameter == undefined) { printdebug("function defined with as, but parameter is undefined at line %d, column %d", @1.first_line, @1.first_column); }else if(getAdInfoType(parameter) != TYPE_RECORD){ printdebug("record: %s., primitive: %s.", getType(parameter), getName(recprime)); printdebug("function defined with as, but parameter is type %s at line %d, column %d", getType(parameter),@1.first_line, @1.first_column); }else { for (TableNode* entry = getFirstEntry(getRecList(parameter)); entry!= NULL; entry = getNextEntry(entry)){ int type_of_param_type = getAdInfoType(entry); if( type_of_param_type == TYPE_UNDEFINED || type_of_param_type == TYPE_FUNCTION_DECLARATION || type_of_param_type == TYPE_ARRAY || type_of_param_type == TYPE_ALL_ELSE || type_of_param_type == TYPE_SYSTEM_DEFINED || type_of_param_type == TYPE_STRING){ // note that strings are actually arrays so this is unused printdebug("type of parameter being passed in to AS function definition is %s which is invalid", getName(entry)); type_of_param_type = TYPE_UNDEFINED; // setting tag as undefined in these cases }else{ printdebug("type of parameter correctly being passed in to AS function definition is %s which is valid", getName(entry)); } if(type_of_param_type == TYPE_UNDEFINED){ CreateEntry(cur,type_of_param_type, undefined, NULL, NULL); } else { CreateEntry(cur,type_of_param_type, entry, NULL, getAdInfo(entry)); /*printdebug("creating entry of type %s for function", getType(entry)); CreateEntry(cur, getTypeEntry(entry), "undefined", NULL);*/ } } } } idlist {printdebug("Currently see a function definition taking one paramter (with as) of name %s and number of arguments %d", $1,$6);} R_PAREN ASSIGN sblock //check sblock type ; function_declaration: FUNCTION ID COLON ID { CreateEntry(cur,TYPE_FUNCTION_DECLARATION, look_up(cur, $4), $2, CreateFunctionDeclarationInfo(-1, false)); } | EXTERNAL FUNCTION ID COLON ID { CreateEntry(cur,TYPE_FUNCTION_DECLARATION, look_up(cur, $5), $3, NULL); } ; idlist: ID { TableNode *entry = getFirstEntry(cur); while (strcmp(getName(entry),"undefined") != 0) { entry = getNextEntry(entry); } if (getNextEntry(entry) == NULL) { printdebug("too many parameters at line %d column %d", @1.first_line, @1.first_column); } addName(entry, $1); } COMMA idlist { $$ = $4 + 1; } | ID { TableNode *entry = getFirstEntry(cur); while (strcmp(getName(entry),"undefined") != 0) { entry = getNextEntry(entry); } if (getNextEntry(entry) != NULL) { printdebug("too many parameters at line %d column %d", @1.first_line, @1.first_column); } addName(entry, $1); $$ = 1; } ; sblock: L_BRACE { if (getLine(cur) != 0) { cur = CreateScope(cur,@1.first_line,@1.first_column); } else { setLineNumber(cur, @1.first_line); setColumnNumber(cur,@1.first_line); } } statement_list { cur = getParent(cur); } R_BRACE | L_BRACE { if (getLine(cur) != 0) { cur = CreateScope(cur,@1.first_line,@1.first_column); } else { setLineNumber(cur, @1.first_line); setColumnNumber(cur,@1.first_line); } } dblock { printdebug("seen sblock with dblock"); } statement_list { cur = getParent(cur); } R_BRACE ; dblock: L_BRACKET {if(getLine(cur)==0){ setLineNumber(cur, @1.first_line); setColumnNumber(cur,@1.first_line);} else{ cur = CreateScope(cur,@1.first_line,@1.first_column); } } declaration_list R_BRACKET; declaration_list: declaration SEMI_COLON declaration_list | declaration ; declaration: id_or_types COLON ID { printdebug("ID/TYPE: %s, ID: %s", getName($1), $3) ; CreateEntry(cur,getAdInfoType($1),$1,$3,getAdInfo($1)); } ; id_or_types: ID { printdebug("string of id is %s in ID pattern of id_or_type rule.", $1); $$ = table_lookup(getAncestor(cur), $1); } | types { printdebug("string of type is %s in types pattern of id_or_type rule.",getName($1)); $$ = $1; } ; statement_list: compound_statement statement_list | compound_statement | simple_statement SEMI_COLON statement_list | simple_statement SEMI_COLON ; compound_statement: WHILE L_PAREN expression R_PAREN sblock | IF L_PAREN expression R_PAREN THEN sblock ELSE sblock | sblock ; simple_statement: assignable ASSIGN expression { if(strcmp(getName($1), getName($3)) == 0) { printdebug("Passed standard type check; assignable = expression"); } else if((strcmp(getType($1), "array") == 0) && (strcmp(getName($3), "address") == 0)) { printdebug("%s[☺] Passed array type check; %s = %s", COLOR_GREEN, getName($1), getName($3)); } else if((strcmp(getType($1), "record") == 0) && (strcmp(getName($3), "address") == 0)) { printdebug("%s[☺] Passed address type check; %s = %s", COLOR_GREEN, getName($1), getName($3)); } else if((strcmp(getType($1), "function type primitive") == 0) && (strcmp(getName($3), "address") == 0)) { printdebug("%s[☺] Passed function type primitive type check; %s = %s", COLOR_GREEN, getName($1), getName($3)); // } else if () { // } else if(strcmp(getType(table_lookup(cur, $1)), getType(table_lookup(cur, $3))) == 0) { // printdebug("%s[] Passed double lookup type check; %s = %s", COLOR_GREEN, $1, $3); } else { printdebug("%s[TYPE ERROR] %sMismatch at %sline %d and column %d%s", COLOR_ORANGE, COLOR_WHITE, COLOR_YELLOW, @2.first_line, @2.first_column, COLOR_WHITE); printdebug(" - Invalid types %s$1: %s and $3: %s%s", COLOR_YELLOW, getType($1), getType($3), COLOR_WHITE); printdebug(" - %sgetType for address: %s", COLOR_YELLOW, getType($1)); } } | RETURN expression ; rec_op: DOT ablock: L_PAREN argument_list R_PAREN { $$ = $2; printdebug("ablock is %d", $$); } ; argument_list: expression COMMA argument_list { CreateEntry(cur,getAdInfoType($1), $1, getName($1), NULL); $$ = $3 + 1; printdebug("[ARGUMENT_LIST] argument list is %d", $$); } | expression { CreateEntry(cur,getAdInfoType($1),$1, getName($1), NULL); $$ = 1; printdebug("[ARGUMENT_LIST] argument list is %d", $$); } ; // will ALWAYS be a TYPE expression: constant { printdebug("constant expression"); $$ = $1; } | SUB_OR_NEG expression %prec UMINUS { printdebug("negative expression"); if($2 != integ) { printdebug("cant negate something not an integer at line %d and column %d",@2.first_line,@2.first_column); $$=undefined; } else { $$=$2; } } | NOT expression { printdebug("not expression"); if($2 == boo) { $$=$2; } else { $$=undefined; printdebug("mismatch at line %d and column %d. Invalid type being negated is %s", @1.first_line,@1.first_column,getName($2)); } } | expression ADD expression { printdebug("add expression"); if($1 == $3 && $1 == integ) { $$=$1; } else { printdebug("mismatch at line %d and column %d. Invalid types %s and %s.", @2.first_line,@2.first_column,getName($1),getName($3)); $$=undefined; } } | expression SUB_OR_NEG expression { printdebug("sub or neg expression"); if($1 == $3 && $1 == integ) { $$=$1; } else { printdebug("mismatch at line %d and column %d. Invalid types %s and %s.", @2.first_line,@2.first_column,getName($1),getName($3)); $$=undefined; } } | expression MUL expression { printdebug("multiply expression"); if($1 == $3 && $1 == integ) { $$=$1; } else{ printdebug("mismatch at line %d and column %d. Invalid types %s and %s.", @2.first_line,@2.first_column,getName($1),getName($3)); $$=undefined; } } | expression DIV expression { printdebug("divide expression"); if((strcmp(getName($1),getName($3))==0) && ($1 == integ)) { $$=$1; } else { printdebug("mismatch at line %d and column %d. Invalid types %s and %s.", @2.first_line,@2.first_column,getName($1),getName($3)); $$=undefined; } } | expression REM expression { printdebug("remainder expression"); if($1 == $3 && $1 == integ) { $$=$1; } else { printdebug("mismatch at line %d and column %d. Invalid types %s and %s.", @2.first_line,@2.first_column,getName($1),getName($3)); $$=undefined; } } | expression AND expression { printdebug("AND expression"); if($1 == $3 && $1 == boo){ $$=$1; } else{ printdebug("mismatch at line %d and column %d. Invalid types %s and %s.", @2.first_line,@2.first_column,getName($1),getName($3)); $$=undefined; } } | expression OR expression { printdebug("OR"); if((strcmp(getName($1),getName($3))==0) && $1 == boo) { $$=$1; } else { printdebug("mismatch at line %d and column %d. Invalid types %s and %s.", @2.first_line,@2.first_column,getName($1),getName($3)); $$=undefined; } } | expression LESS_THAN expression { printdebug("less than expression"); if($1 == $3 && $1==integ) { $$=boo; } else { printdebug("mismatch at line %d and column %d. Invalid types %s and %s.", @2.first_line,@2.first_column,getName($1),getName($3)); $$=undefined; } } | expression EQUAL_TO expression { printdebug("equals check expression"); if($1 == $3 && $1 != undefined) { $$=boo; } else { printdebug("mismatch at line %d and column %d. Invalid types %s and %s", @2.first_line,@2.first_column,getName($1),getName($3)); $$ = undefined; } } | assignable { printdebug("assignable expression. current type is %s",$1); $$= getTypeEntry($1); } | L_PAREN expression R_PAREN { printdebug("paren expression. current type is %s",getName($2)); $$=$2; } | memOp assignable { $$ = addr; } ; //UPDATED $$ for tablenodes to this point // prolly right, check back with me later // add array case // include type check for ablock in arrays - ablock is always the int of the elements in array/rec assignable: ID { $$ = look_up(cur,$1); printdebug("[ASSIGNABLE - RULE 1] assignable = type: %s | ID = %s", $$, $1); } | assignable { printdebug("%sBeginning rule 2 of assignable.", COLOR_CYAN); cur = CreateScope(cur, -1,-1); } ablock { int type = getAdInfoType(look_up(getParent(cur), getName($1))); printdebug("%stype is %d", COLOR_PURPLE, type); printdebug("%s", $1); if (type == TYPE_FUNCTION_DECLARATION) { printdebug("%sEntering function call", COLOR_LIGHTGREEN); if (look_up(getParent(cur), getName($1))->additionalinfo->FunDecAdInfo->regularoras) { printdebug("as function"); //char *funtype = getType(look_up(cur, $1)); printdebug("%s", getType(look_up(cur, getName($1)))); TableNode *param = getParameter(look_up(getParent(cur), getName($1))); SymbolTable *recList = getRecList(param); TableNode *lastCheckedRef = getFirstEntry(recList); TableNode *lastCheckedAct = getFirstEntry(cur); while (getNextEntry(lastCheckedRef) != NULL) { lastCheckedRef = getNextEntry(lastCheckedRef); } //this isn't very efficient, but will hopefully work while (lastCheckedAct != NULL && lastCheckedRef != NULL) { if (strcmp(getName(lastCheckedAct), getName(lastCheckedRef)) != 0) { printdebug("expected %s. expression in function call got %s. at line %d and column %d",getType(lastCheckedRef), getName(lastCheckedAct), @3.first_line, @3.first_column); printdebug("%d", strcmp(getName(lastCheckedAct), getName(lastCheckedRef))); } lastCheckedAct = getNextEntry(lastCheckedAct); TableNode *tn = getFirstEntry(recList); if (tn != lastCheckedRef) { while (getNextEntry(tn) != lastCheckedRef) { tn = getNextEntry(tn); } lastCheckedRef = tn; } else {break;} } } else { char *expected = getName(getParameter(look_up(getParent(cur), getName($1)))); char *actual = getType(getFirstEntry(cur)); if (strcmp(expected, actual) != 0) { printdebug("expected %s expression in function call but got %s at line %d and column %d",expected, actual, @3.first_line, @3.first_column); } } $$ = getReturn((table_lookup(getAncestor(cur), getType($1)))); printdebug("[ASSIGNABLE - RULE 2] assignable = type: %s | name_func = %s", getName($$), getName($1)); } else if (type == TYPE_ARRAY_TYPE) { printdebug("%sEntering array call", COLOR_LIGHTGREEN); if (getNumArrDim(look_up(getParent(cur), getName($1))) != $2) { printdebug("expected %d arguments but had %d at line %d and column %d\n", getNumArrDim(look_up(cur, getName($1))), $2, @2.first_line, @2.first_column); } $$ = getArrType(look_up(getParent(cur), getName($1))); printdebug("[ASSIGNABLE - RULE 2] assignable = type: %s | name_func = %s", getName($$), getName($1)); } cur = getParent(cur); } | assignable rec_op ID { if(undefined != table_lookup(getRecList(table_lookup(getAncestor(cur), getName($1))), $3)) { $$ = table_lookup(getRecList(table_lookup(getAncestor(cur), getName($1))), $3); } printdebug("[ASSIGNABLE - RULE 3] assignable = type: %s | ID = %s", getName($$), $1); } ; memOp: RESERVE { printdebug("reserve expression"); } | RELEASE { printdebug("release expression"); } ; constant: C_STRING { $$ = $1; printdebug("string of C_STRING in constant is %s",$1); } | C_INTEGER { $$ = integ; printdebug("string of C_INTEGER in constant is integer"); } | C_NULL { $$ = $1; printdebug("string of C_NULL in constant is %s",$1); } | C_CHARACTER { $$ = $1; printdebug("string of C_CHARACTER in constant is %s",$1); } | C_TRUE { $$ = $1; printdebug("string of C_TRUE in constant is %s",$1); } | C_FALSE { $$ = $1; printdebug("string of C_FALSE in constant is %s",$1); } ; types: T_INTEGER { $$ = $1; printdebug("string of T_INTEGER in types is %s",$1); } | T_ADDRESS { $$ = $1; printdebug("string of T_ADDRESS in types is %s",$1); } | T_CHARACTER { $$ = $1; printdebug("string of T_CHARACTER in types is %s",$1); } | T_BOOLEAN { $$ = $1; printdebug("string of T_BOOLEAN in types is %s",$1); } ; %% void yyerror(const char *err) { fprintf(stderr, "ERROR: %s at token %s at line number %d,column number %d", err,yytext,yylloc.first_line,yylloc.first_column); }