/* File Name: parser.c * The PLATYPUS parsing program for the final assignment of How To Train Your Dragon (AKA Compilers) * Compiler: GNU GCC 6 * Author: Victor Fernandes, 040772243 * Course: CST8152 - Compilers, Lab Section: 011 * Date: April 21, 2017 * Professor: Svillen Ravev * Version: 0.1 */ #include "parser.h" /* Global variables for the parser */ Token lookahead; int synerrno; /* Error counter */ Buffer *sc_buf; /* Scanner buffer */ extern char *kw_table[]; /* Keyword table with matching enum */ extern STD sym_table; /* The symbol table */ extern Buffer *str_LTBL; /* String literal table */ extern int line; /* Line position of the Scanner */ extern Token malar_next_token(Buffer *); /* Scanner function to get the next token */ /* Begins the source file parsing * Author: Victor Fernandes, 040772243 * Version: 0.0.1 * Called functions: malar_next_token, program, match, gen_incode * Parameters: pBuffer - The input buffer * Return value: N/A */ void parser(pBuffer in_buf) { synerrno = 0; sc_buf = in_buf; lookahead = malar_next_token(sc_buf); program(); match(SEOF_T, NO_ATTR); gen_incode("PLATY: Source file parsed"); } /* Matches the given token code+attribute pair to the current lookahead token * Author: Victor Fernandes, 040772243 * Version: 0.0.1 * Called functions: syn_eh, syn_printe, malar_next_token * Parameters: pr_token_code, pr_token_attribute * Return value: * Algorithm: */ void match(int pr_token_code, int pr_token_attribute) { if (pr_token_code != lookahead.code) { syn_eh(pr_token_code); return; } switch (pr_token_code) { case ART_OP_T: case REL_OP_T: case LOG_OP_T: case KW_T: if (pr_token_attribute != lookahead.attribute.get_int) { break; } default: if (lookahead.code == SEOF_T) return; lookahead = malar_next_token(sc_buf); if (lookahead.code == ERR_T) { ++synerrno; syn_printe(); lookahead = malar_next_token(sc_buf); } return; } syn_eh(pr_token_code); } /* Error "recovery" function for the parser * Author: Victor Fernandes, 040772243 * Version: 0.0.1 * Called functions: syn_printe, malar_next_token, exit * Parameters: int pr_token_code * Return value: * Algorithm: Outputs a standard syn_printe error, and steps through the source file with the Scanner until it finds the next matching code needed by pr_token_code. */ void syn_eh(int pr_token_code) { syn_printe(); ++synerrno; while (lookahead.code != SEOF_T) { lookahead = malar_next_token(sc_buf); if (lookahead.code == pr_token_code) { if (lookahead.code != SEOF_T) { lookahead = malar_next_token(sc_buf); } return; } } if (pr_token_code != SEOF_T) exit(synerrno); } /* Outputs an error message to stdout using the current lookahead token * Author: Victor Fernandes, 040772243 * Version: 0.0.1 * Called functions: printf, b_setmark * Parameters: N/A */ void syn_printe() { printf("PLATY: Syntax error: Line:%3d\n***** Token code: %3d Attribute: ", line, lookahead.code); switch (lookahead.code) { case ERR_T: printf("%s\n", lookahead.attribute.err_lex); break; case SEOF_T: printf("NA\n"); break; case AVID_T: case SVID_T: printf("%s\n", sym_table.pstvr[lookahead.attribute.get_int].plex); break; case FPL_T: printf("%5.1f\n", lookahead.attribute.flt_value); break; case INL_T: printf("%d\n", lookahead.attribute.get_int); break; case STR_T: printf("%s\n", b_setmark(str_LTBL, lookahead.attribute.str_offset)); break; case SCC_OP_T: printf("NA\n"); break; case ASS_OP_T: printf("NA\n"); break; case ART_OP_T: printf("%d\n", lookahead.attribute.get_int); break; case REL_OP_T: printf("%d\n", lookahead.attribute.get_int); break; case LOG_OP_T: printf("%d\n", lookahead.attribute.get_int); break; case LPR_T: printf("NA\n"); break; case RPR_T: printf("NA\n"); break; case LBR_T: printf("NA\n"); break; case RBR_T: printf("NA\n"); break; case KW_T: printf("%s\n", kw_table[lookahead.attribute.get_int]); break; case COM_T: printf("NA\n"); break; case EOS_T: printf("NA\n"); break; default: printf("PLATY: Scanner error: invalid token code: %d\n", lookahead.code); } } /* Generates a message to stdout (can be later modified to generate C code) * Author: Victor Fernandes, 040772243 * Version: 0.0.1 * Called functions: printf * Parameters: char* code * Return value: N/A */ void gen_incode(char *code) { printf("%s\n", code); } /* * Grammar functions ahoy */ /* * -> FIRST Set = {AVID_T, FPL_T, INL_T, (} */ void additive_arithmetic_expression() { multiplicative_arithmetic_expression(); additive_arithmetic_expression_prime(); } /* * -> FIRST Set = {AVID_T, FPL_T, INL_T, (} */ void additive_arithmetic_expression_prime() { if (lookahead.code == ART_OP_T && lookahead.attribute.arr_op != MULT && lookahead.attribute.arr_op != DIV) { match(lookahead.code, lookahead.attribute.arr_op); multiplicative_arithmetic_expression(); additive_arithmetic_expression_prime(); gen_incode("PLATY: Additive arithmetic expression parsed"); } } /* * | FIRST Set = {-, +, AVID_T, FPL_T, INL_T, (} */ void arithmetic_expression() { /* GCC complains that PLUS and MINUS enums aren't handled, but this is automatically handled in additive_arithmetic_expression() */ switch (lookahead.code) { case ART_OP_T: switch (lookahead.attribute.arr_op) { case MULT: case DIV: syn_printe(); return; } unary_arithmetic_expression(); break; case AVID_T: case FPL_T: case INL_T: case LPR_T: additive_arithmetic_expression(); break; case EOS_T: return; default: syn_printe(); return; } gen_incode("PLATY: Arithmetic expression parsed"); } /* * -> AVID = | SVID = FIRST Set = {AVID, SVID} */ void assignment_expression() { switch (lookahead.code) { case AVID_T: match(AVID_T, NO_ATTR); match(ASS_OP_T, NO_ATTR); arithmetic_expression(); gen_incode("PLATY: Assignment expression (arithmetic) parsed"); break; case SVID_T: match(SVID_T, NO_ATTR); match(ASS_OP_T, NO_ATTR); string_expression(); gen_incode("PLATY: Assignment expression (string) parsed"); break; default: syn_printe(); } } /* * -> ; FIRST Set = {AVID, SVID} */ void assignment_statement() { assignment_expression(); match(EOS_T, NO_ATTR); gen_incode("PLATY: Assignment statement parsed"); } /* * -> FIRST Set = {AVID_T, SVID_T, INL_T, SVID_T, STR_T} */ void conditional_expression() { logical_or_expression(); gen_incode("PLATY: Conditional expression parsed"); } /* * -> INPUT ( ); FIRST Set = {INPUT} */ void input_statement() { match(KW_T, INPUT); match(LPR_T, NO_ATTR); variable_list(); match(RPR_T, NO_ATTR); match(EOS_T, NO_ATTR); gen_incode("PLATY: Input statement parsed"); } /* * -> USING ( , , ) REPEAT { } ; FIRST Set = {USING} */ void iteration_statement() { match(KW_T, USING); match(LPR_T, NO_ATTR); assignment_expression(); match(COM_T, NO_ATTR); assignment_expression(); match(RPR_T, NO_ATTR); match(KW_T, REPEAT); match(LBR_T, NO_ATTR); opt_statements(); match(RBR_T, NO_ATTR); match(EOS_T, NO_ATTR); gen_incode("PLATY: USING statement parsed"); } /* * -> FIRST Set = {AVID_T, FPL_T, INL_T, SVID_T, STR_T} */ void logical_and_expression() { relational_expression(); logical_and_expression_prime(); } /* * -> .AND. | E FIRST Set = { .AND., E} */ void logical_and_expression_prime() { if (lookahead.code == LOG_OP_T && lookahead.attribute.log_op == AND) { match(LOG_OP_T, AND); relational_expression(); logical_and_expression_prime(); gen_incode("PLATY: Logical AND expression parsed"); } } /* * -> FIRST Set = {AVID_T, FPL_T, INL_T, SVID_T, STR_T} */ void logical_or_expression() { logical_and_expression(); logical_or_expression_prime(); } /* * -> .OR. FIRST Set = { .OR., E } */ void logical_or_expression_prime() { if (lookahead.code == LOG_OP_T && lookahead.attribute.log_op == OR) { logical_and_expression(); logical_or_expression_prime(); gen_incode("PLATY: Logical OR expression parsed"); } } /* * -> FIRST Set = {AVID_T, FPL_T, INL_T, (} */ void multiplicative_arithmetic_expression() { primary_arithmetic_expression(); multiplicative_arithmetic_expression_prime(); } /* * -> FIRST Set = {*, /, E} */ void multiplicative_arithmetic_expression_prime() { if (lookahead.code == ART_OP_T && lookahead.attribute.arr_op != PLUS && lookahead.attribute.arr_op != MINUS) { match(lookahead.code, lookahead.attribute.arr_op); primary_arithmetic_expression(); multiplicative_arithmetic_expression_prime(); gen_incode("PLATY: Multiplicative arithmetic expression parsed"); } } /* * -> | E FIRST Set = {AVID_T, SVID_T, IF, USING, INPUT, OUTPUT, E} */ void opt_statements() { switch (lookahead.code) { case KW_T: switch (lookahead.attribute.kwt_idx) { case PLATYPUS: case ELSE: case THEN: case REPEAT: gen_incode("PLATY: Opt_statements parsed"); return; } case AVID_T: case SVID_T: statements(); break; default: gen_incode("PLATY: Opt_statements parsed"); } } /* * -> OUTPUT ( ); -> | FIRST() = {OUTPUT} FIRST Set = {AVID_T, SVID_T, E} This can be done in one function */ void output_statement() { match(KW_T, OUTPUT); match(LPR_T, NO_ATTR); switch (lookahead.code) { case AVID_T: case SVID_T: variable_list(); break; case STR_T: match(STR_T, NO_ATTR); gen_incode("PLATY: Output list (string literal) parsed"); break; default: gen_incode("PLATY: Output list (empty) parsed"); } match(RPR_T, NO_ATTR); match(EOS_T, NO_ATTR); gen_incode("PLATY: OUTPUT statement parsed"); } /* * -> AVID_T | FPL_T | INL_T FIRST Set = {AVID_T, FPL_T, INL_T} */ void primary_a_relational_expression() { switch (lookahead.code) { case AVID_T: case FPL_T: case INL_T: match(lookahead.code, NO_ATTR); break; case LOG_OP_T: break; default: syn_printe(); } gen_incode("PLATY: Primary a_relational expression parsed"); } /* * -> AVID_T | FPL_T | INL_T | ( ) FIRST Set = {AVID_T, FPL_T, INL_T, (} */ void primary_arithmetic_expression() { switch (lookahead.code) { case AVID_T: case FPL_T: case INL_T: match(lookahead.code, lookahead.attribute.arr_op); break; case LPR_T: match(lookahead.code, lookahead.attribute.arr_op); arithmetic_expression(); match(RPR_T, NO_ATTR); default: syn_printe(); return; } gen_incode("PLATY: Primary arithmetic expression parsed"); } /* * -> FIRST Set = {SVID_T, STR_T} */ void primary_s_relational_expression() { switch (lookahead.code) { case SVID_T: case AVID_T: primary_string_expression(); break; default: syn_printe(); } gen_incode("PLATY: Primary s_relational expression parsed"); } /* * -> SVID_T | STR_T FIRST Set = {SVID_T, STR_T} */ void primary_string_expression() { switch (lookahead.code) { case SVID_T: case STR_T: match(lookahead.code, NO_ATTR); break; default: syn_printe(); } gen_incode("PLATY: Primary string expression parsed"); } /* * -> PLATYPUS { } FIRST Set = {PLATYPUS} */ void program() { match(KW_T, PLATYPUS); match(LBR_T, NO_ATTR); opt_statements(); match(RBR_T, NO_ATTR); gen_incode("PLATY: Program parsed"); } /* * -> | FIRST Set = {AVID_T, FPL_T, INL_T, SVID_T, STR_T} */ void relational_expression() { switch (lookahead.code) { case AVID_T: case FPL_T: case INL_T: primary_a_relational_expression(); relational_expression_prime(); break; case SVID_T: case STR_T: primary_s_relational_expression(); relational_expression_prime_string(); break; default: syn_printe(); } gen_incode("PLATY: Relational expression parsed"); } /* * -> == | <> | > | < FIRST Set = { ==, <>, >, <} */ void relational_expression_prime() { if (lookahead.code == REL_OP_T) { switch (lookahead.attribute.rel_op) { case EQ: case NE: case GT: case LT: match(lookahead.code, lookahead.attribute.arr_op); primary_a_relational_expression(); return; } } syn_printe(); } /* * -> == | <> | > | < FIRST Set = {==, <>, >, <} */ void relational_expression_prime_string() { if (lookahead.code == REL_OP_T) { switch (lookahead.attribute.rel_op) { case EQ: case NE: case GT: case LT: match(lookahead.code, lookahead.attribute.arr_op); primary_s_relational_expression(); return; } } syn_printe(); } /* * -> IF ( ) THEN ELSE { } ; FIRST Set = {IF} */ void selection_statement() { match(KW_T, IF); match(LPR_T, NO_ATTR); conditional_expression(); match(RPR_T, NO_ATTR); match(KW_T, THEN); opt_statements(); match(KW_T, ELSE); match(LBR_T, NO_ATTR); opt_statements(); match(RBR_T, NO_ATTR); match(EOS_T, NO_ATTR); gen_incode("PLATY: IF statement parsed"); } /* * -> | | | | FIRST Set = {AVID_T, SVID_T, IF, USING, INPUT, OUTPUT} */ void statement() { switch (lookahead.code) { case AVID_T: case SVID_T: assignment_statement(); break; case KW_T: switch (lookahead.attribute.kwt_idx) { case IF: selection_statement(); break; case USING: iteration_statement(); break; case INPUT: input_statement(); break; case OUTPUT: output_statement(); break; default: syn_printe(); } break; default: syn_printe(); } } /* * -> FIRST Set = {AVID_T, SVID_T, IF, USING, INPUT, OUTPUT} */ void statements() { statement(); statements_prime(); } /* * -> | E FIRST Set = {AVID_T, SVID_T, IF, USING, INPUT, OUTPUT} */ void statements_prime() { switch (lookahead.code) { case KW_T: switch (lookahead.attribute.kwt_idx) { case PLATYPUS: case ELSE: case THEN: case REPEAT: return; } case AVID_T: case SVID_T: statement(); statements_prime(); break; } } /* * -> FIRST Set = {SVID_T, STR_T} */ void string_expression() { primary_string_expression(); string_expression_prime(); gen_incode("PLATY: String expression parsed"); } /* * << | E FIRST Set = {<< , E} */ void string_expression_prime() { if (lookahead.code == SCC_OP_T) { match(SCC_OP_T, NO_ATTR); primary_string_expression(); string_expression_prime(); } } /* * -> - | + FIRST Set = {-, +} */ void unary_arithmetic_expression() { /* Again, GCC complains about PLUS and MINUS enums */ switch (lookahead.code) { case ART_OP_T: switch (lookahead.attribute.arr_op) { case MULT: case DIV: syn_printe(); return; } match(lookahead.code, lookahead.attribute.arr_op); primary_arithmetic_expression(); gen_incode("PLATY: Unary arithmetic expression parsed"); break; default: syn_printe(); return; } } /* * -> AVID_T | SVID_T FIRST Set = {AVID_T, SVID_T} */ void variable_identifier() { switch (lookahead.code) { case AVID_T: case SVID_T: match(lookahead.code, NO_ATTR); break; default: syn_printe(); } } /* * -> FIRST Set = {AVID_T, SVID_T} */ void variable_list() { variable_identifier(); variable_list_prime(); gen_incode("PLATY: Variable list parsed"); } /* * -> , | E FIRST Set = {AVID_T, SVID_T, E} */ void variable_list_prime() { if (lookahead.code != COM_T) return; match(COM_T, NO_ATTR); variable_identifier(); variable_list_prime(); }