Merge branch 'josh_11_7' into 'main'

add bison interpreter

See merge request lustje/language-interpreter-lab!10
This commit is contained in:
Riley Smith 2024-11-14 11:24:40 -08:00
commit 0f4472a555
22 changed files with 765 additions and 306 deletions

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/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);
}
}

View File

@ -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"},

View File

@ -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.

BIN
lab-3/scanner Executable file

Binary file not shown.

View File

@ -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,8 +30,8 @@ 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; }
END-IF { return TOKEN_END_IF; }
SPACE { return TOKEN_SPACE; }
PIC { return TOKEN_PICTURE; }
OCCURS { return TOKEN_KEYWORD_OCCURS; }
@ -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; }

View File

@ -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,

View File

@ -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

View File

@ -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.

View File

@ -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.

View File

@ -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.

View File

@ -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;
}
@ -123,6 +125,7 @@ void stmt_print(struct stmt *s) {
if (!s)
return;
switch (s->kind) {
case STMT_DECL:
decl_print(s->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);

View File

@ -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;

View File

@ -4,13 +4,19 @@ Simply invoke the parser generated by bison, and then display the output.
*/
#include "expr.h"
#include "token.h"
#include <stdio.h>
/* 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");
@ -19,11 +25,13 @@ int main(int argc, char *argv[]) {
if (yyparse() == 0) {
printf("Parse successful: ");
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");

View File

@ -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) {
char* read_file_dynamic(const char* filename) {
FILE* file = fopen(filename, "r");
if (file == NULL) {
perror("fopen");
exit(EXIT_FAILURE);
return NULL;
}
size_t n =
fread(expected_output, 1, sizeof(expected_output) - 1, expected_file);
expected_output[n] = '\0';
fclose(expected_file);
// 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";
}

View File

@ -1,10 +1,10 @@
%{
#define YYDEBUG 1
#include <stdio.h>
#include <math.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#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
{$$ = $1;}
| if_branch
| else_parts
| perform_stmt
;
cbl_func_stmt : cbl_function
| 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,7 +239,6 @@ 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);

117
lab-5/parser.bison.new Normal file
View File

@ -0,0 +1,117 @@
%{
#define YYDEBUG 1
#include <stdio.h>
#include <math.h>
#include <stdio.h>
#include <stdlib.h>
#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);
}

123
lab-5/parser_test.c Normal file
View File

@ -0,0 +1,123 @@
// https://github.com/sheredom/utest.h/blob/master/utest.h
#include "utest.h"
#include "expr.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[] = "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);
}

View File

@ -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.

View File

@ -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

View File

@ -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; }

126
lab-5/token.h Normal file
View File

@ -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 <https://www.gnu.org/licenses/>. */
/* 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 */

View File

@ -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<char, false> {
static void _(const char c) {
if (std::is_signed<decltype(c)>::value) {
UTEST_PRINTF("%d", static_cast<int>(c));
} else {
UTEST_PRINTF("%u", static_cast<unsigned int>(c));
}
}
};
template <> struct utest_type_deducer<signed char, false> {
static void _(const signed char c) {
UTEST_PRINTF("%d", static_cast<int>(c));
@ -512,6 +531,10 @@ template <> struct utest_type_deducer<unsigned long long, false> {
static void _(const unsigned long long i) { UTEST_PRINTF("%llu", i); }
};
template <> struct utest_type_deducer<bool, false> {
static void _(const bool i) { UTEST_PRINTF(i ? "true" : "false"); }
};
template <typename T> struct utest_type_deducer<const T *, false> {
static void _(const T *t) {
UTEST_PRINTF("%p", static_cast<void *>(const_cast<T *>(t)));
@ -528,6 +551,12 @@ template <typename T> struct utest_type_deducer<T, true> {
}
};
template <> struct utest_type_deducer<std::nullptr_t, false> {
static void _(std::nullptr_t t) {
UTEST_PRINTF("%p", static_cast<void *>(t));
}
};
template <typename T>
UTEST_WEAK UTEST_OVERLOADABLE void utest_type_printer(const T t) {
utest_type_deducer<T>::_(t);
@ -626,23 +655,22 @@ 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")), \
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
/*
@ -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,15 +1170,21 @@ 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) { \
} else { \
if (utest_state.tests) { \
free(utest_state.tests); \
utest_state.tests = NULL; \
} \
if (name) { \
free(name); \
} \
} \
} \
UTEST_SURPRESS_WARNINGS_END \
void utest_run_##SET##_##NAME(int *utest_result)
@ -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,14 +1224,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_f_##FIXTURE##_##NAME; \
utest_state.tests[index].name = name; \
UTEST_SNPRINTF(name, name_size, "%s", name_part); \
} else if (name) { \
} else { \
if (utest_state.tests) { \
free(utest_state.tests); \
utest_state.tests = NULL; \
} \
if (name) { \
free(name); \
} \
} \
} \
UTEST_SURPRESS_WARNINGS_END \
void utest_run_##FIXTURE##_##NAME(int *utest_result, \
struct FIXTURE *utest_fixture)
@ -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,17 +1278,23 @@ 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) { \
} else { \
if (utest_state.tests) { \
free(utest_state.tests); \
utest_state.tests = NULL; \
} \
if (name) { \
free(name); \
} \
} \
} \
} \
UTEST_SURPRESS_WARNINGS_END \
void utest_run_##FIXTURE##_##NAME##_##INDEX(int *utest_result, \
struct FIXTURE *utest_fixture)