This commit is contained in:
Josh 2024-10-24 11:39:57 -07:00
commit a43c72a7c6
15 changed files with 2080 additions and 39 deletions

View File

@ -1,3 +1,4 @@
---
lab_3_tests:
stage: test
image: gitlab.cs.wallawalla.edu:5050/cs_department/docker-images/cpp
@ -12,3 +13,15 @@ lab_3_tests:
- gcc main_test.c scanner.c -o scanner_test.out
- echo "Lab 3 - Run Tests"
- ./scanner_test.out
lab_4_tests:
stage: test
image: gitlab.cs.wallawalla.edu:5050/cs_department/docker-images/cpp
script:
- apt update
- apt install -y flex bison
- bison --version
- cd lab-4
- echo "Lab 4 - Bison Parser"
- make test
- ./parser_test.out

View File

@ -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/hello-world.cbl"; // Default filename
// Check if a filename is provided as a command-line argument
if (argc > 1) {

View File

@ -18,30 +18,6 @@ struct token_st {
char *p;
};
// UTEST(scanner, identifier) {
// token_t t;
// // Must include the null character to terminate input
// char string[] = "test\0";
// YY_BUFFER_STATE buffer = yy_scan_buffer(string, sizeof(string));
// ASSERT_EQ(TOKEN_EOF, (t = yylex()));
// ASSERT_STREQ("", yytext);
// yy_delete_buffer(buffer);
// }
// UTEST(scanner, assignment) {
// token_t t;
// // Must include the null character to terminate input
// char string[] = "=\0";
// YY_BUFFER_STATE buffer = yy_scan_buffer(string, sizeof(string));
// ASSERT_EQ(TOKEN_EOF, (t = yylex()));
// ASSERT_STREQ("", yytext);
// yy_delete_buffer(buffer);
// }
UTEST(scanner, hello) {
struct token_st tokens[] = {
{TOKEN_IDENTIFICATION, "IDENTIFICATION"},
@ -64,12 +40,11 @@ UTEST(scanner, hello) {
yyin = fopen("samples/hello-world.cbl", "r");
yyrestart(yyin);
ASSERT_TRUE(yyin);
int index = 0;
token_t t;
do {
printf("index: %d token: %d text: %s\n", index, t, yytext);
ASSERT_EQ(tokens[index].t, (t = yylex()));
ASSERT_STREQ(tokens[index].p, yytext);
++index;

View File

@ -3,7 +3,6 @@
%}
NAME [a-zA-Z]([a-zA-Z0-9_-]*[a-zA-Z0-9])?
DIGIT [0-9]+
%%
(" "|\t|\n) /* skip whitespace */

View File

@ -1,6 +1,5 @@
typedef enum {
TOKEN_EOF = 0,
// Identification Keywords
TOKEN_IDENTIFICATION,
TOKEN_KEYWORD_DIVISION,
TOKEN_KEYWORD_DATA,
@ -8,8 +7,6 @@ typedef enum {
TOKEN_PROGRAM_ID,
TOKEN_WORKING_STORAGE,
TOKEN_PROCEDURE,
// Program Keywords
TOKEN_DISPLAY,
TOKEN_STOP,
TOKEN_RUN,
@ -28,10 +25,7 @@ typedef enum {
TOKEN_KEYWORD_VALUE,
TOKEN_KEYWORD_COMPUTE,
TOKEN_KEYWORD_FUNCTION,
// Identifiers
TOKEN_IDENT,
// Data types
TOKEN_STRING,
TOKEN_INTEGER,
TOKEN_PICTURE,
@ -39,19 +33,14 @@ typedef enum {
TOKEN_NUMERIC,
TOKEN_SIGNED_NUMERIC,
TOKEN_IMPLIED_DECIMAL,
// https://ibmmainframes.com/about393.html
TOKEN_COMPUTATION_LEVEL_0,
TOKEN_COMPUTATION_LEVEL_1,
TOKEN_COMPUTATION_LEVEL_2,
TOKEN_COMPUTATION_LEVEL_3,
// Grammar
TOKEN_LEFT_PARENTHESIS,
TOKEN_RIGHT_PARENTHESIS,
TOKEN_DOT,
TOKEN_COMMENT,
// Operators
TOKEN_ADD,
TOKEN_SUB,
TOKEN_MULTIPLY,

29
lab-4/Makefile Normal file
View File

@ -0,0 +1,29 @@
# The top level rule indicates how to link everything together into main
main: main.o scanner.o parser.o
gcc main.o scanner.o parser.o -o parser.out -lm
test: main_test.o scanner.o parser.o
gcc main_test.o scanner.o parser.o -o parser_test.out -lm
# Only the files generated by flex and bison need explicit rules.
scanner.c: scanner.flex parser.h
flex -oscanner.c scanner.flex
parser.c parser.h: parser.bison
bison --defines=token.h --output=parser.c -v parser.bison
# 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 $@
# clean causes all intermediate files to be deleted.
clean:
rm -f parser.c parser.output token.h scanner.c *.o parser.out parser_test.out

98
lab-4/README.md Normal file
View File

@ -0,0 +1,98 @@
# Bison Parser With Tests
Create a scanner for your sample programs with tests.
The tests should include basic statements and the sample files for your language.
See the [example lab for Python](https://gitlab.cs.wallawalla.edu/cptr354/language-interpreter-lab-python).
## Build Process
To build the `scanner.c` run the following **flex** command.
```sh
flex -o scanner.c scanner.flex
```
The parser is built using **bison**.
Note this command also builds the `token.h`.
```sh
bison --defines=token.h --output=parser.c -v parser.bison
```
THe `-v` flag will create a `parser.output` which represents the grammar in text form.
### Running the Parser
Then build the `main.c`, `parcer.c` and `scanner.c` using **gcc**.
Note that you may get a few warnings that can be ignored.
```sh
gcc main.c scanner.c parser.c -o parser.out
```
Then execute the test suite using `./parser.out` and pass in the input from your sample program.
For example here is a command for `samples/program.c`
```sh
./parser.out < samples/program.c
```
### Testing the Parser
Then build the `main_test.c`, `parcer.c` and `scanner.c` using **gcc**.
Note that you may get a few warnings that can be ignored.
```sh
gcc main_test.c scanner.c parser.c -o parser_test.out
```
Then execute the test suite using `./parser_test.out`.
```sh
./parser_test.out
```
### Using Make
Alternatively you can use the supplied make file.
* Build main program - `make`
* Build test program - `make test`
* Clean up build files - `make clean`
## Tests
* Write test for various statements in your language
* assignment
* print
* mathmatical expressions
* boolean expressions
* branching
* looping
* Write a test for each sample test file.
* Hello World!
* Quadratic
* Sorting
## Grading Rubric
The grading will be over the following categories.
Points Description
----------- ------------------------------------
60 points Ability to parse — Six different language statements
10 points Ability to parse — Hello world
10 points Ability to parse — Quadratic equation
10 points Ability to parse — Integer sorting
10 points Report formatting and readability
## Turn In
Please submit the URL of your gitlab project to D2L's Brightspace with your report.
On gitlab update the `lab-4` folder to have your report and code snippets before the due date.

11
lab-4/main.c Normal file
View File

@ -0,0 +1,11 @@
#include <stdio.h>
extern int yyparse();
int main() {
if (yyparse() == 0) {
printf("Parse successful!\n");
} else {
printf("Parse failed.\n");
}
}

57
lab-4/main_test.c Normal file
View File

@ -0,0 +1,57 @@
// https://github.com/sheredom/utest.h/blob/master/utest.h
#include "utest.h"
#include <stdio.h>
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[] = "1+8/4-3;\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, missing_semi_colon) {
// 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));
yylineno = 1;
int result = yyparse();
yy_delete_buffer(buffer);
// Assert the result to test correctness
ASSERT_EQ(result, 1);
}
UTEST(parser, sample) {
// Read sample file as input
yyin = fopen("samples/program.c", "r");
yyrestart(yyin);
ASSERT_TRUE(yyin);
yylineno = 1;
int result = yyparse();
// Assert the result to test correctness
ASSERT_EQ(result, 0);
}

82
lab-4/parser.bison Normal file
View File

@ -0,0 +1,82 @@
%{
#define YYDEBUG 1
#include <stdio.h>
extern int yylineno;
void yyerror(const char*);
int yylex();
%}
%debug
%define parse.error detailed
%token TOKEN_EOF
%token TOKEN_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_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_COMMENT
%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
%%
program : expr TOKEN_SEMI;
expr : expr TOKEN_PLUS term
| expr TOKEN_MINUS term
| term
;
term : term TOKEN_MUL factor
| term TOKEN_DIV factor
| factor
;
factor: TOKEN_MINUS factor
| TOKEN_LPAREN expr TOKEN_RPAREN
| TOKEN_INT
;
%%
void yyerror(const char* msg) {
fprintf(stderr, "Error | Line: %d\n%s\n",yylineno,msg);
}

View File

@ -0,0 +1,5 @@
IDENTIFICATION DIVISION.
PROGRAM-ID. HELLO-WORLD.
PROCEDURE DIVISION.
DISPLAY 'Hello World!'
STOP RUN.

View File

@ -0,0 +1,35 @@
*> Code altered from https://www.quora.com/What-is-a-COBOL-program-that-will-solve-a-quadratic-equation
*> Program finds the roots to a simple quadratic equation
IDENTIFICATION DIVISION.
PROGRAM-ID. QuadraticSolver.
DATA DIVISION.
WORKING-STORAGE SECTION.
77 a PIC S9(5)V9(5) COMP-3 VALUE 1.
77 b PIC S9(5)V9(5) COMP-3 VALUE 5.
77 c PIC S9(5)V9(5) COMP-3 VALUE 6.
77 discriminant PIC S9(5)V9(5) COMP-3.
77 root1 PIC S9(5)V9(5) COMP-3.
77 root2 PIC S9(5)V9(5) COMP-3.
77 square-root-discriminant PIC S9(5)V9(5) COMP-3.
PROCEDURE DIVISION. *> program begins here
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
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.

View File

@ -0,0 +1,55 @@
IDENTIFICATION DIVISION.
PROGRAM-ID. sorting.
DATA DIVISION.
WORKING-STORAGE SECTION.
01 WS-SORT-AREA.
05 WS-SORT-TABLE.
10 WS-SORT-ROW PIC X(10) OCCURS 100.
05 WS-TEMP-ROW PIC X(10).
05 WS-ROW-MAX PIC S9(4) COMP VALUE 100.
05 WS-SORT-MAX PIC S9(4) COMP.
05 WS-I PIC S9(4) COMP.
05 WS-J PIC S9(4) COMP.
05 WS-INDEX PIC S9(4) COMP.
PROCEDURE DIVISION.
*> Initialize test data
MOVE "30" TO WS-SORT-ROW(1)
MOVE "10" TO WS-SORT-ROW(2)
MOVE "50" TO WS-SORT-ROW(3)
MOVE "20" TO WS-SORT-ROW(4)
MOVE "40" TO WS-SORT-ROW(5)
MOVE 5 TO WS-SORT-MAX
*> * Display original array
DISPLAY "Original Array Contents:"
DISPLAY "---------------------"
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
DISPLAY SPACE
*> * Simplified bubble sort
PERFORM VARYING WS-I FROM 1 BY 1
UNTIL WS-I > WS-SORT-MAX - 1
PERFORM VARYING WS-J FROM 1 BY 1
UNTIL WS-J > WS-SORT-MAX - WS-I
IF WS-SORT-ROW(WS-J) > WS-SORT-ROW(WS-J + 1)
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
*> * Display sorted array
DISPLAY "Sorted Array Contents:"
DISPLAY "--------------------"
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.
STOP RUN.

25
lab-4/scanner.flex Normal file
View File

@ -0,0 +1,25 @@
%{
#include "token.h"
%}
%option nounput
%option noinput
%option yylineno
DIGIT [0-9]
LETTER [a-zA-Z]
%%
(" "|\t|\n) /* skip whitespace */
\+ { return TOKEN_PLUS; }
\- { return TOKEN_MINUS; }
\* { return TOKEN_MUL; }
\/ { return TOKEN_DIV; }
\( { return TOKEN_LPAREN; }
\) { return TOKEN_RPAREN; }
\; { return TOKEN_SEMI; }
{DIGIT}+ { return TOKEN_INT; }
. { return TOKEN_ERROR; }
%%
int yywrap() { return 1; }

1668
lab-4/utest.h Normal file

File diff suppressed because it is too large Load Diff