diff --git a/lab-3/main.c b/lab-3/main.c index 1187003..78770a1 100644 --- a/lab-3/main.c +++ b/lab-3/main.c @@ -8,7 +8,7 @@ extern char *yytext; int main(int argc, char *argv[]) { FILE *file; - const char *filename = "samples/quadratic_snippet.cbl"; // Default filename + const char *filename = "samples/quadratic-snippet.cbl"; // Check if a filename is provided as a command-line argument if (argc > 1) { @@ -25,6 +25,6 @@ int main(int argc, char *argv[]) { token_t t = yylex(); if (t == TOKEN_EOF) break; - printf("token: %d text: %s\n", t, yytext); + printf("token: %d, text: %s\n", t, yytext); } } diff --git a/lab-3/main_test.c b/lab-3/main_test.c index 1eb729a..8f22d24 100644 --- a/lab-3/main_test.c +++ b/lab-3/main_test.c @@ -20,7 +20,7 @@ struct token_st { UTEST(scanner, hello) { struct token_st tokens[] = { - {TOKEN_IDENTIFICATION, "IDENTIFICATION"}, + {TOKEN_KEYWORD_IDENTIFICATION, "IDENTIFICATION"}, {TOKEN_KEYWORD_DIVISION, "DIVISION"}, {TOKEN_DOT, "."}, {TOKEN_PROGRAM_ID, "PROGRAM-ID"}, @@ -53,10 +53,7 @@ UTEST(scanner, hello) { UTEST(scanner, quadratic) { struct token_st tokens[] = { - {TOKEN_COMMENT, "*> Code altered from https://www.quora.com/What-is-a-COBOL-program-that-will-solve-a-quadratic-equation"}, - {TOKEN_COMMENT, "*> Program finds the roots to a simple quadratic equation"}, - - {TOKEN_IDENTIFICATION, "IDENTIFICATION"}, + {TOKEN_KEYWORD_IDENTIFICATION, "IDENTIFICATION"}, {TOKEN_KEYWORD_DIVISION, "DIVISION"}, {TOKEN_DOT, "."}, {TOKEN_PROGRAM_ID, "PROGRAM-ID"}, @@ -180,9 +177,8 @@ UTEST(scanner, quadratic) { {TOKEN_PROCEDURE, "PROCEDURE"}, {TOKEN_KEYWORD_DIVISION, "DIVISION"}, {TOKEN_DOT, "."}, - {TOKEN_COMMENT, "*> program begins here"}, {TOKEN_DISPLAY, "DISPLAY"}, - {TOKEN_STRING, "'EQUATION: (1x^2) + 5x + 6 = 0'"}, + {TOKEN_STRING, "\"EQUATION: (1x^2) + 5x + 6 = 0\""}, {TOKEN_KEYWORD_COMPUTE, "COMPUTE"}, {TOKEN_IDENT, "discriminant"}, {TOKEN_EQUAL, "="}, @@ -250,14 +246,14 @@ UTEST(scanner, quadratic) { {TOKEN_RIGHT_PARENTHESIS, ")"}, {TOKEN_DISPLAY, "DISPLAY"}, - {TOKEN_STRING, "'The equation has two distinct real roots: '"}, + {TOKEN_STRING, "\"The equation has two distinct real roots: \""}, {TOKEN_DISPLAY, "DISPLAY"}, - {TOKEN_STRING, "'Root 1: '"}, + {TOKEN_STRING, "\"Root 1: \""}, {TOKEN_IDENT, "root1"}, {TOKEN_DISPLAY, "DISPLAY"}, - {TOKEN_STRING, "'Root 2: '"}, + {TOKEN_STRING, "\"Root 2: \""}, {TOKEN_IDENT, "root2"}, // {TOKEN_EOF, ""}, @@ -282,17 +278,18 @@ UTEST(scanner, quadratic) { {TOKEN_DISPLAY, "DISPLAY"}, - {TOKEN_STRING, "'The equation has one real root: '"}, + {TOKEN_STRING, "\"The equation has one real root: \""}, {TOKEN_DISPLAY, "DISPLAY"}, - {TOKEN_STRING, "'Root: '"}, + {TOKEN_STRING, "\"Root: \""}, {TOKEN_IDENT, "root1"}, {TOKEN_ELSE, "ELSE"}, {TOKEN_DISPLAY, "DISPLAY"}, - {TOKEN_STRING, "'The equation has no real roots.'"}, + {TOKEN_STRING, "\"The equation has no real roots.\""}, + {TOKEN_END_IF, "END-IF"}, // {TOKEN_EOF, ""}, @@ -316,7 +313,7 @@ UTEST(scanner, quadratic) { UTEST(scanner, sorting) { struct token_st tokens[] = { - {TOKEN_IDENTIFICATION, "IDENTIFICATION"}, + {TOKEN_KEYWORD_IDENTIFICATION, "IDENTIFICATION"}, {TOKEN_KEYWORD_DIVISION, "DIVISION"}, {TOKEN_DOT, "."}, {TOKEN_PROGRAM_ID, "PROGRAM-ID"}, @@ -403,7 +400,6 @@ UTEST(scanner, sorting) { {TOKEN_PROCEDURE, "PROCEDURE"}, {TOKEN_KEYWORD_DIVISION, "DIVISION"}, {TOKEN_DOT, "."}, - {TOKEN_COMMENT, "*> Initialize test data"}, {TOKEN_MOVE, "MOVE"}, {TOKEN_STRING, "\"30\""}, {TOKEN_KEYWORD_TO, "TO"}, @@ -443,7 +439,6 @@ UTEST(scanner, sorting) { {TOKEN_INTEGER, "5"}, {TOKEN_KEYWORD_TO, "TO"}, {TOKEN_IDENT, "WS-SORT-MAX"}, - {TOKEN_COMMENT, "*> * Display original array"}, {TOKEN_DISPLAY, "DISPLAY"}, {TOKEN_STRING, "\"Original Array Contents:\""}, {TOKEN_DISPLAY, "DISPLAY"}, @@ -470,7 +465,6 @@ UTEST(scanner, sorting) { {TOKEN_END_PERFORM, "END-PERFORM"}, {TOKEN_DISPLAY, "DISPLAY"}, {TOKEN_SPACE, "SPACE"}, - {TOKEN_COMMENT, "*> * Simplified bubble sort"}, {TOKEN_PERFORM, "PERFORM"}, {TOKEN_VARYING, "VARYING"}, {TOKEN_IDENT, "WS-I"}, @@ -540,7 +534,6 @@ UTEST(scanner, sorting) { {TOKEN_END_IF, "END-IF"}, {TOKEN_END_PERFORM, "END-PERFORM"}, {TOKEN_END_PERFORM, "END-PERFORM"}, - {TOKEN_COMMENT, "*> * Display sorted array"}, {TOKEN_DISPLAY, "DISPLAY"}, {TOKEN_STRING, "\"Sorted Array Contents:\""}, {TOKEN_DISPLAY, "DISPLAY"}, diff --git a/lab-3/samples/quadratic-snippet.cbl b/lab-3/samples/quadratic-snippet.cbl index 18ba158..00eb26f 100644 --- a/lab-3/samples/quadratic-snippet.cbl +++ b/lab-3/samples/quadratic-snippet.cbl @@ -14,22 +14,21 @@ 77 square-root-discriminant PIC S9(5)V9(5) COMP-3. PROCEDURE DIVISION. *> program begins here - DISPLAY 'EQUATION: (1x^2) + 5x + 6 = 0' + DISPLAY "EQUATION: (1x^2) + 5x + 6 = 0" COMPUTE discriminant = (b ** 2) - (4 * a * c) IF discriminant > 0 COMPUTE square-root-discriminant = FUNCTION SQRT(discriminant) COMPUTE root1 = (-b + square-root-discriminant) / (2 * a) COMPUTE root2 = (-b - square-root-discriminant) / (2 * a) - DISPLAY 'The equation has two distinct real roots: ' - DISPLAY 'Root 1: ' root1 - DISPLAY 'Root 2: ' root2 - + DISPLAY "The equation has two distinct real roots: " + DISPLAY "Root 1: " root1 + DISPLAY "Root 2: " root2 ELSE IF discriminant = 0 COMPUTE root1 = -b / (2 * a) - DISPLAY 'The equation has one real root: ' - DISPLAY 'Root: ' root1 + DISPLAY "The equation has one real root: " + DISPLAY "Root: " root1 ELSE - DISPLAY 'The equation has no real roots.' - + DISPLAY "The equation has no real roots." + END-IF STOP RUN. diff --git a/lab-3/scanner b/lab-3/scanner new file mode 100755 index 0000000..28ce88d Binary files /dev/null and b/lab-3/scanner differ diff --git a/lab-3/scanner.flex b/lab-3/scanner.flex index cc22816..262c437 100644 --- a/lab-3/scanner.flex +++ b/lab-3/scanner.flex @@ -1,13 +1,17 @@ %{ #include "token.h" %} +%option warn +%option nodefault +%option yylineno + NAME [a-zA-Z]([a-zA-Z0-9_-]*[a-zA-Z0-9])? DIGIT [0-9]+ %% (" "|\t|\n) /* skip whitespace */ -\*>\ ?.* { return TOKEN_COMMENT; } -IDENTIFICATION { return TOKEN_IDENTIFICATION; } +\*>\ ?.* +IDENTIFICATION { return TOKEN_KEYWORD_IDENTIFICATION; } DIVISION { return TOKEN_KEYWORD_DIVISION; } PROGRAM-ID { return TOKEN_PROGRAM_ID; } PROCEDURE { return TOKEN_PROCEDURE; } @@ -26,9 +30,9 @@ UNTIL { return TOKEN_UNTIL; } PERFORM { return TOKEN_PERFORM; } END-PERFORM { return TOKEN_END_PERFORM; } IF { return TOKEN_IF; } -END-IF { return TOKEN_END_IF; } ELSE { return TOKEN_ELSE; } -SPACE { return TOKEN_SPACE; } +END-IF { return TOKEN_END_IF; } +SPACE { return TOKEN_SPACE; } PIC { return TOKEN_PICTURE; } OCCURS { return TOKEN_KEYWORD_OCCURS; } VALUE { return TOKEN_KEYWORD_VALUE; } @@ -44,8 +48,6 @@ COMP-2 { return TOKEN_COMPUTATION_LEVEL_2; } COMP-3 { return TOKEN_COMPUTATION_LEVEL_3; } -{DIGIT} { return TOKEN_INTEGER; } -{NAME} { return TOKEN_IDENT; } \+ { return TOKEN_ADD; } \- { return TOKEN_SUB; } \*\* { return TOKEN_EXPONENTIAL; } @@ -55,13 +57,13 @@ COMP-3 { return TOKEN_COMPUTATION_LEVEL_3; } \< { return TOKEN_LESS_THAN; } \= { return TOKEN_EQUAL;} - "\""[^"]*"\"" { return TOKEN_STRING; } "\'"[^']*"\'" { return TOKEN_STRING; } "(" { return TOKEN_LEFT_PARENTHESIS; } ")" { return TOKEN_RIGHT_PARENTHESIS; } +{NAME} { return TOKEN_IDENT; } +{DIGIT} { return TOKEN_INTEGER; } \. { return TOKEN_DOT; } - %% int yywrap() { return 1; } diff --git a/lab-3/token.h b/lab-3/token.h index e64609e..47ce470 100644 --- a/lab-3/token.h +++ b/lab-3/token.h @@ -1,6 +1,6 @@ typedef enum { TOKEN_EOF = 0, - TOKEN_IDENTIFICATION, + TOKEN_KEYWORD_IDENTIFICATION, TOKEN_KEYWORD_DIVISION, TOKEN_KEYWORD_DATA, TOKEN_KEYWORD_SECTION, @@ -21,6 +21,7 @@ typedef enum { TOKEN_IF, TOKEN_ELSE, TOKEN_END_IF, + TOKEN_ELSE_IF, TOKEN_SPACE, TOKEN_KEYWORD_OCCURS, TOKEN_KEYWORD_VALUE, diff --git a/lab-4/parser.bison b/lab-4/parser.bison index c0c71ca..2585a15 100644 --- a/lab-4/parser.bison +++ b/lab-4/parser.bison @@ -9,57 +9,58 @@ int yylex(); %debug %define parse.error detailed -%token TOKEN_EOF -%token TOKEN_KEYWORD_IDENTIFICATION -%token TOKEN_KEYWORD_DIVISION -%token TOKEN_KEYWORD_DATA -%token TOKEN_KEYWORD_SECTION -%token TOKEN_PROGRAM_ID -%token TOKEN_WORKING_STORAGE -%token TOKEN_PROCEDURE -%token TOKEN_STOP -%token TOKEN_RUN -%token TOKEN_MOVE -%token TOKEN_KEYWORD_TO -%token TOKEN_PERFORM -%token TOKEN_VARYING -%token TOKEN_KEYWORD_FROM -%token TOKEN_KEYWORD_BY -%token TOKEN_UNTIL -%token TOKEN_END_PERFORM -%token TOKEN_IF -%token TOKEN_ELSE_IF -%token TOKEN_ELSE -%token TOKEN_END_IF -%token TOKEN_SPACE -%token TOKEN_KEYWORD_OCCURS -%token TOKEN_KEYWORD_VALUE -%token TOKEN_KEYWORD_COMPUTE -%token TOKEN_KEYWORD_FUNCTION -%token TOKEN_IDENT -%token TOKEN_STRING -%token TOKEN_INTEGER -%token TOKEN_PICTURE +%token TOKEN_ADD %token TOKEN_ALPHANUMERIC -%token TOKEN_NUMERIC -%token TOKEN_SIGNED_NUMERIC -%token TOKEN_IMPLIED_DECIMAL %token TOKEN_COMPUTATION_LEVEL_0 %token TOKEN_COMPUTATION_LEVEL_1 %token TOKEN_COMPUTATION_LEVEL_2 %token TOKEN_COMPUTATION_LEVEL_3 -%token TOKEN_LEFT_PARENTHESIS -%token TOKEN_RIGHT_PARENTHESIS -%token TOKEN_DOT -%token TOKEN_ADD -%token TOKEN_SUB -%token TOKEN_MULTIPLY -%token TOKEN_DIVIDE -%token TOKEN_EQUAL -%token TOKEN_GREATER_THAN -%token TOKEN_LESS_THAN -%token TOKEN_EXPONENTIAL %token TOKEN_DISPLAY +%token TOKEN_DIVIDE +%token TOKEN_DOT +%token TOKEN_ELSE +%token TOKEN_ELSE_IF +%token TOKEN_END_IF +%token TOKEN_END_PERFORM +%token TOKEN_EQUAL +%token TOKEN_EXPONENTIAL +%token TOKEN_GREATER_THAN +%token TOKEN_IDENT +%token TOKEN_IF +%token TOKEN_IMPLIED_DECIMAL +%token TOKEN_INTEGER +%token TOKEN_KEYWORD_BY +%token TOKEN_KEYWORD_COMPUTE +%token TOKEN_KEYWORD_DATA +%token TOKEN_KEYWORD_DIVISION +%token TOKEN_KEYWORD_FROM +%token TOKEN_KEYWORD_FUNCTION +%token TOKEN_KEYWORD_IDENTIFICATION +%token TOKEN_KEYWORD_OCCURS +%token TOKEN_KEYWORD_SECTION +%token TOKEN_KEYWORD_TO +%token TOKEN_KEYWORD_VALUE +%token TOKEN_LEFT_PARENTHESIS +%token TOKEN_LESS_THAN +%token TOKEN_MOVE +%token TOKEN_MULTIPLY +%token TOKEN_NUMERIC +%token TOKEN_PERFORM +%token TOKEN_PICTURE +%token TOKEN_PROGRAM_ID +%token TOKEN_PROCEDURE +%token TOKEN_RIGHT_PARENTHESIS +%token TOKEN_RUN +%token TOKEN_SIGNED_NUMERIC +%token TOKEN_SPACE +%token TOKEN_STOP +%token TOKEN_STRING +%token TOKEN_SUB +%token TOKEN_UNTIL +%token TOKEN_VARYING +%token TOKEN_WORKING_STORAGE +%token TOKEN_EOF + %% @@ -88,55 +89,65 @@ type : TOKEN_KEYWORD_IDENTIFICATION ; simple_stmt : cbl_func_stmt | if_branch + | else_parts | perform_stmt ; cbl_func_stmt : cbl_function | cbl_function op_parms | cbl_function assignment_stmt - | cbl_function op_parms assignment_stmt + | cbl_function op_parm assignment_stmt ; -assignment_stmt : TOKEN_EQUAL ext_function - | TOKEN_EQUAL function +assignment_stmt : TOKEN_EQUAL op_parms | TOKEN_KEYWORD_TO op_parms ; -op_parms : op_parms TOKEN_ADD op_parms - | op_parms TOKEN_SUB op_parms - | op_parms TOKEN_MULTIPLY op_parms - | op_parms TOKEN_DIVIDE op_parms - | op_parms TOKEN_EXPONENTIAL op_parms - | op_parms TOKEN_LESS_THAN op_parms - | op_parms TOKEN_GREATER_THAN op_parms - | op_parms TOKEN_EQUAL op_parms - | TOKEN_SUB op_parms - | TOKEN_LEFT_PARENTHESIS op_parms TOKEN_RIGHT_PARENTHESIS - | expr - | op_parms op_parms +op_parms : op_parm + | op_parms op_parm ; -expr : TOKEN_IDENT +op_parm : mathmaticalexpr + | booleanexpr + ; +term : mathmaticalexpr + ; +math_op : TOKEN_ADD + | TOKEN_SUB + | TOKEN_MULTIPLY + | TOKEN_DIVIDE + | TOKEN_EXPONENTIAL + ; +mathmaticalexpr : type_expr + | mathmaticalexpr math_op term + | container_expr + | type_expr container_expr + ; +container_expr : TOKEN_LEFT_PARENTHESIS mathmaticalexpr TOKEN_RIGHT_PARENTHESIS + ; +booleanexpr : mathmaticalexpr TOKEN_LESS_THAN term + | mathmaticalexpr TOKEN_GREATER_THAN term + | mathmaticalexpr TOKEN_EQUAL term + ; +type_expr : TOKEN_IDENT | TOKEN_INTEGER | TOKEN_STRING | TOKEN_SPACE - ; -function : op_parms + | TOKEN_SUB TOKEN_IDENT + | ext_function ; ext_function : TOKEN_KEYWORD_FUNCTION TOKEN_IDENT TOKEN_LEFT_PARENTHESIS TOKEN_IDENT TOKEN_RIGHT_PARENTHESIS ; cbl_function : TOKEN_DISPLAY | TOKEN_MOVE | TOKEN_KEYWORD_COMPUTE - | TOKEN_PERFORM ; -if_branch : TOKEN_IF op_parms - | TOKEN_ELSE_IF op_parms - | TOKEN_ELSE statement +if_branch : TOKEN_IF booleanexpr + ; +else_parts : TOKEN_ELSE_IF booleanexpr simple_stmt + | TOKEN_ELSE simple_stmt | TOKEN_END_IF ; perform_stmt : TOKEN_PERFORM TOKEN_VARYING TOKEN_IDENT TOKEN_KEYWORD_FROM TOKEN_INTEGER TOKEN_KEYWORD_BY TOKEN_INTEGER TOKEN_UNTIL op_parms | TOKEN_END_PERFORM ; -data_space : TOKEN_WORKING_STORAGE - | TOKEN_KEYWORD_SECTION - | TOKEN_DOT +data_space : TOKEN_WORKING_STORAGE TOKEN_KEYWORD_SECTION TOKEN_DOT ; data_category : TOKEN_ALPHANUMERIC | TOKEN_NUMERIC diff --git a/lab-4/samples/quadratic-snippet.cbl b/lab-4/samples/quadratic-snippet.cbl index ab76e40..7f27b4d 100644 --- a/lab-4/samples/quadratic-snippet.cbl +++ b/lab-4/samples/quadratic-snippet.cbl @@ -24,12 +24,10 @@ DISPLAY "The equation has two distinct real roots: " DISPLAY "Root 1: " root1 DISPLAY "Root 2: " root2 - ELSE IF discriminant = 0 COMPUTE root1 = -b / (2 * a) DISPLAY "The equation has one real root: " DISPLAY "Root: " root1 ELSE DISPLAY "The equation has no real roots." - STOP RUN. diff --git a/lab-4/samples/sorting-snippet.cbl b/lab-4/samples/sorting-snippet.cbl index 8324e47..7019d00 100644 --- a/lab-4/samples/sorting-snippet.cbl +++ b/lab-4/samples/sorting-snippet.cbl @@ -39,7 +39,6 @@ PROCEDURE DIVISION. MOVE WS-SORT-ROW(WS-J) TO WS-TEMP-ROW MOVE WS-SORT-ROW(WS-J + 1) TO WS-SORT-ROW(WS-J) MOVE WS-TEMP-ROW TO WS-SORT-ROW(WS-J + 1) - END-IF END-PERFORM END-PERFORM @@ -49,7 +48,7 @@ PROCEDURE DIVISION. PERFORM VARYING WS-INDEX FROM 1 BY 1 UNTIL WS-INDEX > WS-SORT-MAX DISPLAY "Element " WS-INDEX ": " WS-SORT-ROW(WS-INDEX) - END-PERFORM. + END-PERFORM STOP RUN. \ No newline at end of file diff --git a/lab-5/Makefile b/lab-5/Makefile index dfc7723..4c4f16d 100644 --- a/lab-5/Makefile +++ b/lab-5/Makefile @@ -2,17 +2,20 @@ # The top level rule indicates how to link everything together into main main: main.o symbol_map.o expr.o scanner.o parser.o - gcc main.o symbol_map.o expr.o scanner.o parser.o -o interpreter.out -lm + gcc -g3 main.o symbol_map.o expr.o scanner.o parser.o -o interpreter.out -lm test: main_test.o symbol_map.o expr.o scanner.o parser.o gcc main_test.o symbol_map.o expr.o scanner.o parser.o -o interpreter_test.out -lm +parser_test: parser_test.o symbol_map.o expr.o scanner.o parser.o + gcc parser_test.o symbol_map.o expr.o scanner.o parser.o -o parser_test.out -lm + # This pattern indicates that any .o file depends # upon the .c file of the same name, and all of the .h files. # So, if a .o file is needed, it is built automatically. %.o: %.c *.h - gcc -Wall -c $< -o $@ + gcc -g3 -Wall -c $< -o $@ # Only the files generated by flex and bison need explicit rules. @@ -20,7 +23,7 @@ scanner.c: scanner.flex parser.h flex -oscanner.c scanner.flex parser.c parser.h: parser.bison - bison --defines=parser.h --output=parser.c -v parser.bison -Wconflicts-sr -Wcounterexamples + bison --defines=parser.h --output=parser.c -v parser.bison # clean causes all intermediate files to be deleted. diff --git a/lab-5/expr.c b/lab-5/expr.c index 6b0e94d..1bbd990 100644 --- a/lab-5/expr.c +++ b/lab-5/expr.c @@ -85,7 +85,9 @@ struct expr *expr_create_float_literal(float value) { struct expr *expr_create_string_literal(const char *value) { struct expr *e = expr_create(EXPR_STRING_LITERAL, 0, 0); - e->string_literal = value; + char *dest = malloc(sizeof(*value)); + strcpy(dest, value); // copy contents of source to dest + e->string_literal = dest; return e; } @@ -122,6 +124,7 @@ between the left and right nodes. void stmt_print(struct stmt *s) { if (!s) return; + switch (s->kind) { case STMT_DECL: @@ -146,6 +149,10 @@ void stmt_print(struct stmt *s) { case STMT_BLOCK: stmt_print(s->body); break; + // we haven't implemented sections yet + case STMT_SECTION: + printf("section\n"); + break; } stmt_print(s->next); @@ -271,6 +278,8 @@ void stmt_evaluate(struct stmt *s) { case STMT_BLOCK: stmt_evaluate(s->body); break; + case STMT_SECTION: + break; } stmt_evaluate(s->next); diff --git a/lab-5/expr.h b/lab-5/expr.h index 99d8461..bf404b4 100644 --- a/lab-5/expr.h +++ b/lab-5/expr.h @@ -20,6 +20,7 @@ typedef enum { EXPR_ADD, EXPR_ARRAY, EXPR_DIVIDE, + EXPR_EXPONENTIAL, EXPR_EQUAL_EQUAL, EXPR_FLOAT_LITERAL, EXPR_GREATER_THAN, @@ -60,7 +61,16 @@ struct decl { struct decl *next; }; -typedef enum { STMT_BLOCK, STMT_DECL, STMT_EXPR, STMT_IF, STMT_PRINT } stmt_t; +typedef enum { + STMT_BLOCK, + STMT_DECL, + STMT_EXPR, + STMT_IF, + STMT_PRINT, + STMT_SECTION, + STMT_COMPUTE, + STMT_MOVE +} stmt_t; struct stmt { stmt_t kind; diff --git a/lab-5/main.c b/lab-5/main.c index 63208e9..c0ba9d7 100644 --- a/lab-5/main.c +++ b/lab-5/main.c @@ -4,26 +4,34 @@ Simply invoke the parser generated by bison, and then display the output. */ #include "expr.h" +#include "token.h" #include /* Clunky: Declare the parse function generated from parser.bison */ +// extern int yyparse(struct stmt *parser_result); extern int yyparse(); +extern char *yytext; + +extern int yylex(); /* Clunky: Declare the result of the parser from parser.bison */ -extern struct stmt *parser_result; + +struct stmt *parser_result; int main(int argc, char *argv[]) { printf("Lab 6 Example Interpreter Compiler\n"); printf( "Enter an infix expression using the operators +-*/() ending with ;\n\n"); - + if (yyparse() == 0) { printf("Parse successful: "); - stmt_print(parser_result); - printf("\n"); - printf("Running the program, results in: "); - stmt_evaluate(parser_result); - printf("\n"); + if (parser_result != NULL) { + stmt_print(parser_result); + printf("\n"); + printf("Running the program, results in: "); + stmt_evaluate(parser_result); + printf("\n"); + } return 0; } else { printf("Parse failed!\n"); diff --git a/lab-5/main_test.c b/lab-5/main_test.c index d40e918..4b1ec9f 100644 --- a/lab-5/main_test.c +++ b/lab-5/main_test.c @@ -19,20 +19,45 @@ extern int yylineno; UTEST_MAIN(); -void read_file(const char *filename, char *expected_output) { - // Read the expected output from a file - FILE *expected_file = fopen(filename, "r"); - if (expected_file == NULL) { - perror("fopen"); - exit(EXIT_FAILURE); - } - size_t n = - fread(expected_output, 1, sizeof(expected_output) - 1, expected_file); - expected_output[n] = '\0'; - fclose(expected_file); +char* read_file_dynamic(const char* filename) { + FILE* file = fopen(filename, "r"); + if (file == NULL) { + perror("fopen"); + return NULL; + } + + // Seek to the end of the file to determine its size + fseek(file, 0, SEEK_END); + long file_size = ftell(file); + fseek(file, 0, SEEK_SET); + + // Allocate a buffer to hold the file contents + char* buffer = (char*)malloc(file_size + 1); + if (buffer == NULL) { + perror("malloc"); + fclose(file); + return NULL; + } + + // Read the entire file into the buffer + size_t read_size = fread(buffer, 1, file_size, file); + if (read_size != file_size) { + perror("fread"); + free(buffer); + fclose(file); + return NULL; + } + + // Null-terminate the buffer + buffer[file_size] = '\0'; + + fclose(file); + return buffer; } void redirect_stdout(const char *filename, int evalutate) { + fflush(stdout); + // Redirect stdout to a temporary file FILE *temp_file = fopen(filename, "w"); if (temp_file == NULL) { @@ -56,42 +81,18 @@ void redirect_stdout(const char *filename, int evalutate) { close(stdout_fd); } -UTEST(interpreter, print) { +struct InterpreterTestFile { + const char *evaluated_file; + const char *print_file; + const char *test_file; +}; - yyin = fopen("samples/multiple_statements.c", "r"); - yyrestart(yyin); - ASSERT_TRUE(yyin); - -// yylineno = 1; - yylineno = 1; - int result = yyparse(); - - if (result == 0) { - // Catch the standard output and compare with expected test result - redirect_stdout("test_print.txt", 0); - redirect_stdout("test_evaluate.txt", 1); - } - - // Assert the result to test correctness - ASSERT_EQ(result, 0); - - char actual_print[1024]; - read_file("test_print.txt", actual_print); - char expected_print[1024]; - read_file("samples/multiple_statements_print.txt", expected_print); - ASSERT_STREQ(actual_print, expected_print); - - char actual_evaluate[1024]; - read_file("test_evaluate.txt", actual_evaluate); - char expected_evaluate[1024]; - read_file("samples/multiple_statements_evaluate.txt", expected_evaluate); - ASSERT_STREQ(actual_evaluate, expected_evaluate); +UTEST_F_SETUP(InterpreterTestFile) { } - - -UTEST(interpreter, program) { - yyin = fopen("samples/program.c", "r"); +UTEST_F_TEARDOWN(InterpreterTestFile) { + // and also assert and expect in teardown! + yyin = fopen(utest_fixture->test_file, "r"); yyrestart(yyin); ASSERT_TRUE(yyin); @@ -107,55 +108,23 @@ UTEST(interpreter, program) { // Assert the result to test correctness ASSERT_EQ(result, 0); - char actual_print[1024]; - read_file("test_print.txt", actual_print); - char expected_print[1024]; - read_file("samples/program_print.txt", expected_print); + char *actual_print = read_file_dynamic("test_print.txt"); + char *expected_print = read_file_dynamic(utest_fixture->print_file); ASSERT_STREQ(actual_print, expected_print); - char actual_evaluate[1024]; - read_file("test_evaluate.txt", actual_evaluate); - char expected_evaluate[1024]; - read_file("samples/program_evaluate.txt", expected_evaluate); + char *actual_evaluate = read_file_dynamic("test_evaluate.txt"); + char *expected_evaluate = read_file_dynamic(utest_fixture->evaluated_file); ASSERT_STREQ(actual_evaluate, expected_evaluate); } -// UTEST(parser, missing_new_line) { -// // Must include the null character to terminate input -// char string[] = "1+8/4-3\0"; -// YY_BUFFER_STATE buffer = yy_scan_buffer(string, sizeof(string)); +UTEST_F(InterpreterTestFile, print) { + utest_fixture->test_file = "samples/multiple_statements.c"; + utest_fixture->print_file = "samples/multiple_statements_print.txt"; + utest_fixture->evaluated_file = "samples/multiple_statements_evaluate.txt"; +} -// yylineno = 1; -// int result = yyparse(); - -// yy_delete_buffer(buffer); - -// // Assert the result to test correctness -// ASSERT_EQ(result, 1); -// } - -// UTEST(parser, hello_world) { -// // Read sample file as input -// yyin = fopen("samples/hello.py", "r"); -// yyrestart(yyin); -// ASSERT_TRUE(yyin); - -// yylineno = 1; -// int result = yyparse(); - -// // Assert the result to test correctness -// ASSERT_EQ(result, 0); -// } - -// UTEST(parser, quadratic) { -// // Read sample file as input -// yyin = fopen("samples/quadratic.py", "r"); -// yyrestart(yyin); -// ASSERT_TRUE(yyin); - -// yylineno = 1; -// int result = yyparse(); - -// // Assert the result to test correctness -// ASSERT_EQ(result, 0); -// } +UTEST_F(InterpreterTestFile, program_file) { + utest_fixture->test_file = "samples/program.c"; + utest_fixture->print_file = "samples/program_print.txt"; + utest_fixture->evaluated_file = "samples/program_evaluate.txt"; +} diff --git a/lab-5/parser.bison b/lab-5/parser.bison index e536a2c..79ffcd0 100644 --- a/lab-5/parser.bison +++ b/lab-5/parser.bison @@ -1,10 +1,10 @@ %{ #define YYDEBUG 1 #include - #include #include #include +#include #include "expr.h" /* @@ -12,7 +12,7 @@ YYSTYPE is the lexical value returned by each rule in a bison grammar. By default, it is an integer. In this example, we are returning a pointer to an expression. */ -#define YYSTYPE struct expr * +#define YYSTYPE struct stmt * /* Clunky: Manually declare the interface to the scanner generated by flex. @@ -20,22 +20,17 @@ Clunky: Manually declare the interface to the scanner generated by flex. extern char *yytext; extern int yylex(); -extern int yylineno; void yyerror(const char*); +extern int yylineno; -/* -Clunky: Keep the final result of the parse in a global variable, -so that it can be retrieved by main(). -*/ +struct stmt *parser_result = 0; -struct expr * parser_result = 0; %} %debug %define parse.error detailed - %token TOKEN_EOF %token TOKEN_KEYWORD_IDENTIFICATION %token TOKEN_KEYWORD_DIVISION @@ -88,82 +83,127 @@ struct expr * parser_result = 0; %token TOKEN_EXPONENTIAL %token TOKEN_DISPLAY - %% -file : statements -statements : statement_list +file : statement_list stop_run + {parser_result = $1; return 0;} ; -statement_list : statement_list statement +statement_list : statement statement_list + { $$ = $1; $1->next = $2;} | statement + { $$ = $1; } ; statement : section + {$$ = $1;} | sect_data + {$$ = $1;} | simple_stmt + {$$ = stmt_create(STMT_BLOCK, NULL, NULL, NULL, NULL, $1, NULL, NULL);} | data_space + {$$ = stmt_create(STMT_SECTION, NULL, NULL, NULL, NULL, NULL, NULL, NULL);} | data_declaration + {$$ = stmt_create(STMT_BLOCK, NULL, NULL, NULL, NULL, $1, NULL, NULL);} ; - section : type TOKEN_KEYWORD_DIVISION TOKEN_DOT - | type TOKEN_RUN TOKEN_DOT + {$$ = stmt_create(STMT_SECTION, NULL, NULL, NULL, NULL, NULL, NULL, NULL);} + ; +stop_run : TOKEN_STOP TOKEN_RUN TOKEN_DOT + {$$ = stmt_create(STMT_SECTION, NULL, NULL, NULL, NULL, NULL, NULL, NULL);} ; sect_data : TOKEN_PROGRAM_ID TOKEN_DOT TOKEN_IDENT TOKEN_DOT + {$$ = stmt_create(STMT_SECTION, NULL, NULL, NULL, NULL, NULL, NULL, NULL);} ; type : TOKEN_KEYWORD_IDENTIFICATION | TOKEN_PROCEDURE - | TOKEN_STOP | TOKEN_KEYWORD_DATA ; simple_stmt : cbl_func_stmt - | if_branch + {$$ = $1;} + | if_branch + | else_parts | perform_stmt ; cbl_func_stmt : cbl_function - | cbl_function op_parms + | cbl_function op_parms + {$$ = stmt_create($1->kind, NULL, NULL, $2, NULL, NULL, NULL, NULL);} | cbl_function assignment_stmt - | cbl_function op_parms assignment_stmt + | cbl_function op_parm assignment_stmt ; -assignment_stmt : TOKEN_EQUAL ext_function - | TOKEN_EQUAL function +assignment_stmt : TOKEN_EQUAL op_parms | TOKEN_KEYWORD_TO op_parms ; -op_parms : op_parms TOKEN_ADD op_parms - | op_parms TOKEN_SUB op_parms - | op_parms TOKEN_MULTIPLY op_parms - | op_parms TOKEN_DIVIDE op_parms - | op_parms TOKEN_EXPONENTIAL op_parms - | op_parms TOKEN_LESS_THAN op_parms - | op_parms TOKEN_GREATER_THAN op_parms - | op_parms TOKEN_EQUAL op_parms - | TOKEN_SUB op_parms - | TOKEN_LEFT_PARENTHESIS op_parms TOKEN_RIGHT_PARENTHESIS - | expr - | op_parms op_parms +op_parms : op_parm + {$$ = $1;} + | op_parm op_parms + {$$ = $1; $1->next_expr = $2;} ; -expr : TOKEN_IDENT +op_parm : mathmaticalexpr + {$$ = $1;} + | booleanexpr + {$$ = $1;} + ; +term : mathmaticalexpr + {$$ = $1;} + ; +math_op : TOKEN_ADD + {$$ = expr_create(EXPR_ADD, NULL, NULL);} + | TOKEN_SUB + {$$ = expr_create(EXPR_SUBTRACT, NULL, NULL);} + | TOKEN_MULTIPLY + {$$ = expr_create(EXPR_MULTIPLY, NULL, NULL);} + | TOKEN_DIVIDE + {$$ = expr_create(EXPR_DIVIDE, NULL, NULL);} + | TOKEN_EXPONENTIAL + {$$ = expr_create(EXPR_EXPONENTIAL, NULL, NULL);} + ; +mathmaticalexpr : type_expr + {$$ = $1;} + | mathmaticalexpr math_op term + + | container_expr + {$$ = $1;} + | type_expr container_expr + {$$ = $1; $1->next_expr = $2;} + ; +container_expr : TOKEN_LEFT_PARENTHESIS mathmaticalexpr TOKEN_RIGHT_PARENTHESIS + ; +booleanexpr : mathmaticalexpr TOKEN_LESS_THAN term + {$$ = expr_create(EXPR_LESS_THAN, $1, $3);} + | mathmaticalexpr TOKEN_GREATER_THAN term + {$$ = expr_create(EXPR_GREATER_THAN, $1, $3);} + | mathmaticalexpr TOKEN_EQUAL term + {$$ = expr_create(EXPR_EQUAL_EQUAL, $1, $3);} + ; +type_expr : TOKEN_IDENT + {$$ = expr_create_name(yytext);} | TOKEN_INTEGER + {$$ = expr_create_integer_literal(atoi(yytext));} | TOKEN_STRING + {$$ = expr_create_string_literal(yytext);} | TOKEN_SPACE - ; -function : op_parms + {$$ = expr_create_integer_literal(0);} + | TOKEN_SUB TOKEN_IDENT + | ext_function ; ext_function : TOKEN_KEYWORD_FUNCTION TOKEN_IDENT TOKEN_LEFT_PARENTHESIS TOKEN_IDENT TOKEN_RIGHT_PARENTHESIS + ; cbl_function : TOKEN_DISPLAY + {$$ = stmt_create(STMT_PRINT, NULL, NULL, NULL, NULL, NULL, NULL, NULL);} | TOKEN_MOVE + {$$ = stmt_create(STMT_MOVE, NULL, NULL, NULL, NULL, NULL, NULL, NULL);} | TOKEN_KEYWORD_COMPUTE - | TOKEN_PERFORM + {$$ = stmt_create(STMT_COMPUTE, NULL, NULL, NULL, NULL, NULL, NULL, NULL);} ; -if_branch : TOKEN_IF op_parms - | TOKEN_ELSE_IF op_parms - | TOKEN_ELSE statement +if_branch : TOKEN_IF booleanexpr + ; +else_parts : TOKEN_ELSE_IF booleanexpr simple_stmt + | TOKEN_ELSE simple_stmt | TOKEN_END_IF ; perform_stmt : TOKEN_PERFORM TOKEN_VARYING TOKEN_IDENT TOKEN_KEYWORD_FROM TOKEN_INTEGER TOKEN_KEYWORD_BY TOKEN_INTEGER TOKEN_UNTIL op_parms | TOKEN_END_PERFORM ; -data_space : TOKEN_WORKING_STORAGE - | TOKEN_KEYWORD_SECTION - | TOKEN_DOT +data_space : TOKEN_WORKING_STORAGE TOKEN_KEYWORD_SECTION TOKEN_DOT ; data_category : TOKEN_ALPHANUMERIC | TOKEN_NUMERIC @@ -199,8 +239,7 @@ data_clauses : full_data_clause data_declaration: simple_decl | complex_decl ; - %% void yyerror(const char* msg) { fprintf(stderr, "Error | Line: %d\n%s\n",yylineno,msg); -} +} \ No newline at end of file diff --git a/lab-5/parser.bison.new b/lab-5/parser.bison.new new file mode 100644 index 0000000..50bf300 --- /dev/null +++ b/lab-5/parser.bison.new @@ -0,0 +1,117 @@ +%{ +#define YYDEBUG 1 +#include +#include +#include +#include +#include "expr.h" + +/* +YYSTYPE is the lexical value returned by each rule in a bison grammar. +By default, it is an integer. In this example, we are returning a pointer to an expression. +*/ + +#define YYSTYPE struct stmt * + +/* +Clunky: Manually declare the interface to the scanner generated by flex. +*/ + +extern char *yytext; +extern int yylex(); +// void yyerror(struct stmt *parser_result, const char*); +void yyerror(const char*); + +extern int yylineno; + +struct stmt *parser_result = 0; + +%} + +// %parse-param {struct stmt *parser_result} + +%debug +%define parse.error detailed + +%token TOKEN_EOF +%token TOKEN_KEYWORD_IDENTIFICATION +%token TOKEN_KEYWORD_DIVISION +%token TOKEN_KEYWORD_DATA +%token TOKEN_KEYWORD_SECTION +%token TOKEN_PROGRAM_ID +%token TOKEN_WORKING_STORAGE +%token TOKEN_PROCEDURE +%token TOKEN_STOP +%token TOKEN_RUN +%token TOKEN_MOVE +%token TOKEN_KEYWORD_TO +%token TOKEN_PERFORM +%token TOKEN_VARYING +%token TOKEN_KEYWORD_FROM +%token TOKEN_KEYWORD_BY +%token TOKEN_UNTIL +%token TOKEN_END_PERFORM +%token TOKEN_IF +%token TOKEN_ELSE_IF +%token TOKEN_ELSE +%token TOKEN_END_IF +%token TOKEN_SPACE +%token TOKEN_KEYWORD_OCCURS +%token TOKEN_KEYWORD_VALUE +%token TOKEN_KEYWORD_COMPUTE +%token TOKEN_KEYWORD_FUNCTION +%token TOKEN_IDENT +%token TOKEN_STRING +%token TOKEN_INTEGER +%token TOKEN_PICTURE +%token TOKEN_ALPHANUMERIC +%token TOKEN_NUMERIC +%token TOKEN_SIGNED_NUMERIC +%token TOKEN_IMPLIED_DECIMAL +%token TOKEN_COMPUTATION_LEVEL_0 +%token TOKEN_COMPUTATION_LEVEL_1 +%token TOKEN_COMPUTATION_LEVEL_2 +%token TOKEN_COMPUTATION_LEVEL_3 +%token TOKEN_LEFT_PARENTHESIS +%token TOKEN_RIGHT_PARENTHESIS +%token TOKEN_DOT +%token TOKEN_ADD +%token TOKEN_SUB +%token TOKEN_MULTIPLY +%token TOKEN_DIVIDE +%token TOKEN_EQUAL +%token TOKEN_GREATER_THAN +%token TOKEN_LESS_THAN +%token TOKEN_EXPONENTIAL +%token TOKEN_DISPLAY + + +%% +file : statements + {parser_result = $1; return 0;} + ; +statements : statements statement + { $$ = $1; $1->next = $2; } + | statement + { $$ = $1; } + ; +statement : simple_stmt + {$$ = stmt_create(STMT_BLOCK, NULL, NULL, NULL, NULL, $1, NULL, NULL);} + ; +simple_stmt : cbl_func_stmt + {$$ = $1;} + ; +cbl_func_stmt : display_stmt + {$$ = $1;} + ; +display_stmt : TOKEN_DISPLAY expr + {$$ = stmt_create(STMT_PRINT, NULL, NULL, $2, NULL, NULL, NULL, NULL);} + ; +expr : TOKEN_STRING + { $$ = expr_create_string_literal(yytext);} + ; +%% + +void yyerror(const char* msg) { + fprintf(stderr, "Error | Line: %d\n%s\n",yylineno,msg); +} \ No newline at end of file diff --git a/lab-5/parser_test.c b/lab-5/parser_test.c new file mode 100644 index 0000000..c9c8fe6 --- /dev/null +++ b/lab-5/parser_test.c @@ -0,0 +1,123 @@ +// https://github.com/sheredom/utest.h/blob/master/utest.h +#include "utest.h" +#include "expr.h" +#include + +extern int yyparse(); + +typedef struct yy_buffer_state *YY_BUFFER_STATE; +extern int yyrestart(); +extern YY_BUFFER_STATE yy_scan_buffer(char *str, int i); +extern YY_BUFFER_STATE yy_scan_string(char *str); +extern void yy_delete_buffer(YY_BUFFER_STATE buffer); +extern FILE *yyin; +extern int yylineno; + +UTEST_MAIN(); + +UTEST(parser, math) { + // Must include the null character to terminate input + char string[] = "COMPUTE A = (b ** 2) - (4 * a * c)\0"; + YY_BUFFER_STATE buffer = yy_scan_buffer(string, sizeof(string)); + + yylineno = 1; + int result = yyparse(); + + yy_delete_buffer(buffer); + + // Assert the result to test correctness + ASSERT_EQ(result, 0); +} + +UTEST(parser, hello) { + // Read sample file as input + yyin = fopen("samples/hello-world.cbl", "r"); + yyrestart(yyin); + ASSERT_TRUE(yyin); + + yylineno = 1; + int result = yyparse(); + + // Assert the result to test correctness + ASSERT_EQ(result, 0); +} + +UTEST(parser, print) { + // Must include the null character to terminate input + char string[] = "DISPLAY 'Hello World!'\0"; + YY_BUFFER_STATE buffer = yy_scan_buffer(string, sizeof(string)); + + yylineno = 1; + int result = yyparse(); + + yy_delete_buffer(buffer); + + // Assert the result to test correctness + ASSERT_EQ(result, 0); +} + +UTEST(parser, branching) { + // Must include the null character to terminate input + char string[] = "IF A > B DISPLAY 'A is greater than B' ELSE DISPLAY 'B is greater than A'\0"; + YY_BUFFER_STATE buffer = yy_scan_buffer(string, sizeof(string)); + + yylineno = 1; + int result = yyparse(); + + yy_delete_buffer(buffer); + + // Assert the result to test correctness + ASSERT_EQ(result, 0); +} + +UTEST(parser, looping) { + char string[] = "PERFORM VARYING I FROM 1 BY 1 UNTIL I > 10\0"; + YY_BUFFER_STATE buffer = yy_scan_buffer(string, sizeof(string)); + + yylineno = 1; + int result = yyparse(); + + yy_delete_buffer(buffer); + + // Assert the result to test correctness + ASSERT_EQ(result, 0); +} + +UTEST(parser, assignment) { + char string[] = "MOVE I TO A(I) MOVE 1 TO I COMPUTE discriminant = (b ** 2) - (4 * a * c)\0"; + YY_BUFFER_STATE buffer = yy_scan_buffer(string, sizeof(string)); + + yylineno = 1; + int result = yyparse(); + + yy_delete_buffer(buffer); + + // Assert the result to test correctness + ASSERT_EQ(result, 0); +} + +UTEST(parser, sorting) { + // Read sample file as input + yyin = fopen("samples/sorting-snippet.cbl", "r"); + yyrestart(yyin); + ASSERT_TRUE(yyin); + + yylineno = 1; + int result = yyparse(); + + // Assert the result to test correctness + ASSERT_EQ(result, 0); +} + +UTEST(parser, quadratic) { + // Read sample file as input + yyin = fopen("samples/quadratic-snippet.cbl", "r"); + yyrestart(yyin); + ASSERT_TRUE(yyin); + + yylineno = 1; + int result = yyparse(); + + // Assert the result to test correctness + ASSERT_EQ(result, 0); +} diff --git a/lab-5/samples/quadratic-snippet.cbl b/lab-5/samples/quadratic-snippet.cbl index ab76e40..7f27b4d 100644 --- a/lab-5/samples/quadratic-snippet.cbl +++ b/lab-5/samples/quadratic-snippet.cbl @@ -24,12 +24,10 @@ DISPLAY "The equation has two distinct real roots: " DISPLAY "Root 1: " root1 DISPLAY "Root 2: " root2 - ELSE IF discriminant = 0 COMPUTE root1 = -b / (2 * a) DISPLAY "The equation has one real root: " DISPLAY "Root: " root1 ELSE DISPLAY "The equation has no real roots." - STOP RUN. diff --git a/lab-5/samples/sorting-snippet.cbl b/lab-5/samples/sorting-snippet.cbl index 8324e47..07b2ebd 100644 --- a/lab-5/samples/sorting-snippet.cbl +++ b/lab-5/samples/sorting-snippet.cbl @@ -39,7 +39,6 @@ PROCEDURE DIVISION. MOVE WS-SORT-ROW(WS-J) TO WS-TEMP-ROW MOVE WS-SORT-ROW(WS-J + 1) TO WS-SORT-ROW(WS-J) MOVE WS-TEMP-ROW TO WS-SORT-ROW(WS-J + 1) - END-IF END-PERFORM END-PERFORM diff --git a/lab-5/scanner.flex b/lab-5/scanner.flex index ca9acac..528650c 100644 --- a/lab-5/scanner.flex +++ b/lab-5/scanner.flex @@ -1,6 +1,10 @@ %{ -#include "parser.h" +#include "token.h" %} +%option warn +%option nodefault +%option yylineno + NAME [a-zA-Z]([a-zA-Z0-9_-]*[a-zA-Z0-9])? DIGIT [0-9]+ %% @@ -26,6 +30,7 @@ UNTIL { return TOKEN_UNTIL; } PERFORM { return TOKEN_PERFORM; } END-PERFORM { return TOKEN_END_PERFORM; } IF { return TOKEN_IF; } +ELSE { return TOKEN_ELSE; } END-IF { return TOKEN_END_IF; } SPACE { return TOKEN_SPACE; } PIC { return TOKEN_PICTURE; } @@ -42,8 +47,7 @@ COMP-1 { return TOKEN_COMPUTATION_LEVEL_1; } COMP-2 { return TOKEN_COMPUTATION_LEVEL_2; } COMP-3 { return TOKEN_COMPUTATION_LEVEL_3; } -{DIGIT} { return TOKEN_INTEGER; } -{NAME} { return TOKEN_IDENT; } + \+ { return TOKEN_ADD; } \- { return TOKEN_SUB; } \*\* { return TOKEN_EXPONENTIAL; } @@ -57,8 +61,9 @@ COMP-3 { return TOKEN_COMPUTATION_LEVEL_3; } "\'"[^']*"\'" { return TOKEN_STRING; } "(" { return TOKEN_LEFT_PARENTHESIS; } ")" { return TOKEN_RIGHT_PARENTHESIS; } +{NAME} { return TOKEN_IDENT; } +{DIGIT} { return TOKEN_INTEGER; } \. { return TOKEN_DOT; } - %% -int yywrap() { return 1; } +int yywrap() { return 1; } \ No newline at end of file diff --git a/lab-5/token.h b/lab-5/token.h new file mode 100644 index 0000000..ebb85f5 --- /dev/null +++ b/lab-5/token.h @@ -0,0 +1,126 @@ +/* A Bison parser, made by GNU Bison 3.8.2. */ + +/* Bison interface for Yacc-like parsers in C + + Copyright (C) 1984, 1989-1990, 2000-2015, 2018-2021 Free Software Foundation, + Inc. + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + 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, see . */ + +/* As a special exception, you may create a larger work that contains + part or all of the Bison parser skeleton and distribute that work + under terms of your choice, so long as that work isn't itself a + parser generator using the skeleton or a modified version thereof + as a parser skeleton. Alternatively, if you modify or redistribute + the parser skeleton itself, you may (at your option) remove this + special exception, which will cause the skeleton and the resulting + Bison output files to be licensed under the GNU General Public + License without this special exception. + + This special exception was added by the Free Software Foundation in + version 2.2 of Bison. */ + +/* DO NOT RELY ON FEATURES THAT ARE NOT DOCUMENTED in the manual, + especially those whose name start with YY_ or yy_. They are + private implementation details that can be changed or removed. */ + +#ifndef YY_YY_TOKEN_H_INCLUDED +# define YY_YY_TOKEN_H_INCLUDED +/* Debug traces. */ +#ifndef YYDEBUG +# define YYDEBUG 1 +#endif +#if YYDEBUG +extern int yydebug; +#endif + +/* Token kinds. */ +#ifndef YYTOKENTYPE +# define YYTOKENTYPE + enum yytokentype + { + YYEMPTY = -2, + YYEOF = 0, /* "end of file" */ + YYerror = 256, /* error */ + YYUNDEF = 257, /* "invalid token" */ + TOKEN_EOF = 258, /* TOKEN_EOF */ + TOKEN_KEYWORD_IDENTIFICATION = 259, /* TOKEN_KEYWORD_IDENTIFICATION */ + TOKEN_KEYWORD_DIVISION = 260, /* TOKEN_KEYWORD_DIVISION */ + TOKEN_KEYWORD_DATA = 261, /* TOKEN_KEYWORD_DATA */ + TOKEN_KEYWORD_SECTION = 262, /* TOKEN_KEYWORD_SECTION */ + TOKEN_PROGRAM_ID = 263, /* TOKEN_PROGRAM_ID */ + TOKEN_WORKING_STORAGE = 264, /* TOKEN_WORKING_STORAGE */ + TOKEN_PROCEDURE = 265, /* TOKEN_PROCEDURE */ + TOKEN_STOP = 266, /* TOKEN_STOP */ + TOKEN_RUN = 267, /* TOKEN_RUN */ + TOKEN_MOVE = 268, /* TOKEN_MOVE */ + TOKEN_KEYWORD_TO = 269, /* TOKEN_KEYWORD_TO */ + TOKEN_PERFORM = 270, /* TOKEN_PERFORM */ + TOKEN_VARYING = 271, /* TOKEN_VARYING */ + TOKEN_KEYWORD_FROM = 272, /* TOKEN_KEYWORD_FROM */ + TOKEN_KEYWORD_BY = 273, /* TOKEN_KEYWORD_BY */ + TOKEN_UNTIL = 274, /* TOKEN_UNTIL */ + TOKEN_END_PERFORM = 275, /* TOKEN_END_PERFORM */ + TOKEN_IF = 276, /* TOKEN_IF */ + TOKEN_ELSE_IF = 277, /* TOKEN_ELSE_IF */ + TOKEN_ELSE = 278, /* TOKEN_ELSE */ + TOKEN_END_IF = 279, /* TOKEN_END_IF */ + TOKEN_SPACE = 280, /* TOKEN_SPACE */ + TOKEN_KEYWORD_OCCURS = 281, /* TOKEN_KEYWORD_OCCURS */ + TOKEN_KEYWORD_VALUE = 282, /* TOKEN_KEYWORD_VALUE */ + TOKEN_KEYWORD_COMPUTE = 283, /* TOKEN_KEYWORD_COMPUTE */ + TOKEN_KEYWORD_FUNCTION = 284, /* TOKEN_KEYWORD_FUNCTION */ + TOKEN_IDENT = 285, /* TOKEN_IDENT */ + TOKEN_STRING = 286, /* TOKEN_STRING */ + TOKEN_INTEGER = 287, /* TOKEN_INTEGER */ + TOKEN_PICTURE = 288, /* TOKEN_PICTURE */ + TOKEN_ALPHANUMERIC = 289, /* TOKEN_ALPHANUMERIC */ + TOKEN_NUMERIC = 290, /* TOKEN_NUMERIC */ + TOKEN_SIGNED_NUMERIC = 291, /* TOKEN_SIGNED_NUMERIC */ + TOKEN_IMPLIED_DECIMAL = 292, /* TOKEN_IMPLIED_DECIMAL */ + TOKEN_COMPUTATION_LEVEL_0 = 293, /* TOKEN_COMPUTATION_LEVEL_0 */ + TOKEN_COMPUTATION_LEVEL_1 = 294, /* TOKEN_COMPUTATION_LEVEL_1 */ + TOKEN_COMPUTATION_LEVEL_2 = 295, /* TOKEN_COMPUTATION_LEVEL_2 */ + TOKEN_COMPUTATION_LEVEL_3 = 296, /* TOKEN_COMPUTATION_LEVEL_3 */ + TOKEN_LEFT_PARENTHESIS = 297, /* TOKEN_LEFT_PARENTHESIS */ + TOKEN_RIGHT_PARENTHESIS = 298, /* TOKEN_RIGHT_PARENTHESIS */ + TOKEN_DOT = 299, /* TOKEN_DOT */ + TOKEN_ADD = 300, /* TOKEN_ADD */ + TOKEN_SUB = 301, /* TOKEN_SUB */ + TOKEN_MULTIPLY = 302, /* TOKEN_MULTIPLY */ + TOKEN_DIVIDE = 303, /* TOKEN_DIVIDE */ + TOKEN_EQUAL = 304, /* TOKEN_EQUAL */ + TOKEN_GREATER_THAN = 305, /* TOKEN_GREATER_THAN */ + TOKEN_LESS_THAN = 306, /* TOKEN_LESS_THAN */ + TOKEN_EXPONENTIAL = 307, /* TOKEN_EXPONENTIAL */ + TOKEN_DISPLAY = 308 /* TOKEN_DISPLAY */ + }; + typedef enum yytokentype yytoken_kind_t; +#endif + +/* Value type. */ +#if ! defined YYSTYPE && ! defined YYSTYPE_IS_DECLARED +typedef int YYSTYPE; +# define YYSTYPE_IS_TRIVIAL 1 +# define YYSTYPE_IS_DECLARED 1 +#endif + + +extern YYSTYPE yylval; + + +int yyparse (void); + + +#endif /* !YY_YY_TOKEN_H_INCLUDED */ diff --git a/lab-5/utest.h b/lab-5/utest.h index 8767600..82de631 100644 --- a/lab-5/utest.h +++ b/lab-5/utest.h @@ -255,8 +255,8 @@ UTEST_C_FUNC __declspec(dllimport) int __stdcall QueryPerformanceFrequency( static void __cdecl f(void); \ UTEST_INITIALIZER_BEGIN_DISABLE_WARNINGS \ __pragma(comment(linker, "/include:" UTEST_SYMBOL_PREFIX #f "_")) \ - UTEST_C_FUNC __declspec(allocate(".CRT$XCU")) void(__cdecl * \ - f##_)(void) = f; \ + UTEST_C_FUNC \ + __declspec(allocate(".CRT$XCU")) void(__cdecl * f##_)(void) = f; \ UTEST_INITIALIZER_END_DISABLE_WARNINGS \ static void __cdecl f(void) #else @@ -321,22 +321,32 @@ static UTEST_INLINE void *utest_realloc(void *const pointer, size_t new_size) { void *const new_pointer = realloc(pointer, new_size); if (UTEST_NULL == new_pointer) { - free(new_pointer); + free(pointer); } return new_pointer; } +// Prevent 64-bit integer overflow when computing a timestamp by using a trick +// from Sokol: +// https://github.com/floooh/sokol/blob/189843bf4f86969ca4cc4b6d94e793a37c5128a7/sokol_time.h#L204 +static UTEST_INLINE utest_int64_t utest_mul_div(const utest_int64_t value, + const utest_int64_t numer, + const utest_int64_t denom) { + const utest_int64_t q = value / denom; + const utest_int64_t r = value % denom; + return q * numer + r * numer / denom; +} + static UTEST_INLINE utest_int64_t utest_ns(void) { #if defined(_MSC_VER) || defined(__MINGW64__) || defined(__MINGW32__) utest_large_integer counter; utest_large_integer frequency; QueryPerformanceCounter(&counter); QueryPerformanceFrequency(&frequency); - return UTEST_CAST(utest_int64_t, - (counter.QuadPart * 1000000000) / frequency.QuadPart); + return utest_mul_div(counter.QuadPart, 1000000000, frequency.QuadPart); #elif defined(__linux__) && defined(__STRICT_ANSI__) - return UTEST_CAST(utest_int64_t, clock()) * 1000000000 / CLOCKS_PER_SEC; + return utest_mul_div(clock(), 1000000000, CLOCKS_PER_SEC); #elif defined(__linux__) || defined(__FreeBSD__) || defined(__OpenBSD__) || \ defined(__NetBSD__) || defined(__DragonFly__) || defined(__sun__) || \ defined(__HAIKU__) @@ -447,6 +457,15 @@ struct utest_type_deducer final { static void _(const T t); }; +template <> struct utest_type_deducer { + static void _(const char c) { + if (std::is_signed::value) { + UTEST_PRINTF("%d", static_cast(c)); + } else { + UTEST_PRINTF("%u", static_cast(c)); + } + } +}; template <> struct utest_type_deducer { static void _(const signed char c) { UTEST_PRINTF("%d", static_cast(c)); @@ -512,6 +531,10 @@ template <> struct utest_type_deducer { static void _(const unsigned long long i) { UTEST_PRINTF("%llu", i); } }; +template <> struct utest_type_deducer { + static void _(const bool i) { UTEST_PRINTF(i ? "true" : "false"); } +}; + template struct utest_type_deducer { static void _(const T *t) { UTEST_PRINTF("%p", static_cast(const_cast(t))); @@ -528,6 +551,12 @@ template struct utest_type_deducer { } }; +template <> struct utest_type_deducer { + static void _(std::nullptr_t t) { + UTEST_PRINTF("%p", static_cast(t)); + } +}; + template UTEST_WEAK UTEST_OVERLOADABLE void utest_type_printer(const T t) { utest_type_deducer::_(t); @@ -626,24 +655,23 @@ utest_type_printer(long long unsigned int i) { !(defined(__MINGW32__) || defined(__MINGW64__)) || \ defined(__TINYC__) #define utest_type_printer(val) \ - UTEST_PRINTF(_Generic((val), signed char \ - : "%d", unsigned char \ - : "%u", short \ - : "%d", unsigned short \ - : "%u", int \ - : "%d", long \ - : "%ld", long long \ - : "%lld", unsigned \ - : "%u", unsigned long \ - : "%lu", unsigned long long \ - : "%llu", float \ - : "%f", double \ - : "%f", long double \ - : "%Lf", default \ - : _Generic((val - val), ptrdiff_t \ - : "%p", default \ - : "undef")), \ - (val)) + UTEST_PRINTF( \ + _Generic((val), \ + signed char: "%d", \ + unsigned char: "%u", \ + short: "%d", \ + unsigned short: "%u", \ + int: "%d", \ + long: "%ld", \ + long long: "%lld", \ + unsigned: "%u", \ + unsigned long: "%lu", \ + unsigned long long: "%llu", \ + float: "%f", \ + double: "%f", \ + long double: "%Lf", \ + default: _Generic((val - val), ptrdiff_t: "%p", default: "undef")), \ + (val)) #else /* we don't have the ability to print the values we got, so we create a macro @@ -724,10 +752,12 @@ utest_strncpy_gcc(char *const dst, const char *const src, const size_t size) { UTEST_AUTO(x) xEval = (x); \ UTEST_AUTO(y) yEval = (y); \ if (!((xEval)cond(yEval))) { \ + const char *const xAsString = #x; \ + const char *const yAsString = #y; \ _Pragma("clang diagnostic pop") \ UTEST_PRINTF("%s:%i: Failure\n", __FILE__, __LINE__); \ UTEST_PRINTF(" Expected : ("); \ - UTEST_PRINTF(#x ") " #cond " (" #y); \ + UTEST_PRINTF("%s) " #cond " (%s", xAsString, yAsString); \ UTEST_PRINTF(")\n"); \ UTEST_PRINTF(" Actual : "); \ utest_type_printer(xEval); \ @@ -751,9 +781,11 @@ utest_strncpy_gcc(char *const dst, const char *const src, const size_t size) { UTEST_AUTO(x) xEval = (x); \ UTEST_AUTO(y) yEval = (y); \ if (!((xEval)cond(yEval))) { \ + const char *const xAsString = #x; \ + const char *const yAsString = #y; \ UTEST_PRINTF("%s:%i: Failure\n", __FILE__, __LINE__); \ UTEST_PRINTF(" Expected : ("); \ - UTEST_PRINTF(#x ") " #cond " (" #y); \ + UTEST_PRINTF("%s) " #cond " (%s", xAsString, yAsString); \ UTEST_PRINTF(")\n"); \ UTEST_PRINTF(" Actual : "); \ utest_type_printer(xEval); \ @@ -1130,7 +1162,7 @@ utest_strncpy_gcc(char *const dst, const char *const src, const size_t size) { } \ UTEST_INITIALIZER(utest_register_##SET##_##NAME) { \ const size_t index = utest_state.tests_length++; \ - const char *name_part = #SET "." #NAME; \ + const char name_part[] = #SET "." #NAME; \ const size_t name_size = strlen(name_part) + 1; \ char *name = UTEST_PTR_CAST(char *, malloc(name_size)); \ utest_state.tests = UTEST_PTR_CAST( \ @@ -1138,13 +1170,19 @@ utest_strncpy_gcc(char *const dst, const char *const src, const size_t size) { utest_realloc(UTEST_PTR_CAST(void *, utest_state.tests), \ sizeof(struct utest_test_state_s) * \ utest_state.tests_length)); \ - if (utest_state.tests) { \ + if (utest_state.tests && name) { \ utest_state.tests[index].func = &utest_##SET##_##NAME; \ utest_state.tests[index].name = name; \ utest_state.tests[index].index = 0; \ UTEST_SNPRINTF(name, name_size, "%s", name_part); \ - } else if (name) { \ - free(name); \ + } else { \ + if (utest_state.tests) { \ + free(utest_state.tests); \ + utest_state.tests = NULL; \ + } \ + if (name) { \ + free(name); \ + } \ } \ } \ UTEST_SURPRESS_WARNINGS_END \ @@ -1178,7 +1216,7 @@ utest_strncpy_gcc(char *const dst, const char *const src, const size_t size) { } \ UTEST_INITIALIZER(utest_register_##FIXTURE##_##NAME) { \ const size_t index = utest_state.tests_length++; \ - const char *name_part = #FIXTURE "." #NAME; \ + const char name_part[] = #FIXTURE "." #NAME; \ const size_t name_size = strlen(name_part) + 1; \ char *name = UTEST_PTR_CAST(char *, malloc(name_size)); \ utest_state.tests = UTEST_PTR_CAST( \ @@ -1186,12 +1224,18 @@ utest_strncpy_gcc(char *const dst, const char *const src, const size_t size) { utest_realloc(UTEST_PTR_CAST(void *, utest_state.tests), \ sizeof(struct utest_test_state_s) * \ utest_state.tests_length)); \ - if (utest_state.tests) { \ + if (utest_state.tests && name) { \ utest_state.tests[index].func = &utest_f_##FIXTURE##_##NAME; \ utest_state.tests[index].name = name; \ UTEST_SNPRINTF(name, name_size, "%s", name_part); \ - } else if (name) { \ - free(name); \ + } else { \ + if (utest_state.tests) { \ + free(utest_state.tests); \ + utest_state.tests = NULL; \ + } \ + if (name) { \ + free(name); \ + } \ } \ } \ UTEST_SURPRESS_WARNINGS_END \ @@ -1226,7 +1270,7 @@ utest_strncpy_gcc(char *const dst, const char *const src, const size_t size) { utest_uint64_t iUp; \ for (i = 0; i < (INDEX); i++) { \ const size_t index = utest_state.tests_length++; \ - const char *name_part = #FIXTURE "." #NAME; \ + const char name_part[] = #FIXTURE "." #NAME; \ const size_t name_size = strlen(name_part) + 32; \ char *name = UTEST_PTR_CAST(char *, malloc(name_size)); \ utest_state.tests = UTEST_PTR_CAST( \ @@ -1234,14 +1278,20 @@ utest_strncpy_gcc(char *const dst, const char *const src, const size_t size) { utest_realloc(UTEST_PTR_CAST(void *, utest_state.tests), \ sizeof(struct utest_test_state_s) * \ utest_state.tests_length)); \ - if (utest_state.tests) { \ + if (utest_state.tests && name) { \ utest_state.tests[index].func = &utest_i_##FIXTURE##_##NAME##_##INDEX; \ utest_state.tests[index].index = i; \ utest_state.tests[index].name = name; \ iUp = UTEST_CAST(utest_uint64_t, i); \ UTEST_SNPRINTF(name, name_size, "%s/%" UTEST_PRIu64, name_part, iUp); \ - } else if (name) { \ - free(name); \ + } else { \ + if (utest_state.tests) { \ + free(utest_state.tests); \ + utest_state.tests = NULL; \ + } \ + if (name) { \ + free(name); \ + } \ } \ } \ } \ @@ -1665,4 +1715,4 @@ cleanup: return utest_main(argc, argv); \ } -#endif /* SHEREDOM_UTEST_H_INCLUDED */ +#endif /* SHEREDOM_UTEST_H_INCLUDED */ \ No newline at end of file