Merge branch 'main' of gitlab.cs.wallawalla.edu:lustje/language-interpreter-lab

This commit is contained in:
Jenessy Lustre 2024-10-31 11:34:09 -07:00
commit 9e3f11ad40
26 changed files with 1387 additions and 47 deletions

View File

@ -38,6 +38,7 @@ UTEST(scanner, hello) {
{TOKEN_EOF, ""},
};
yyin = fopen("samples/hello-world.cbl", "r");
ASSERT_TRUE(yyin);
int index = 0;

View File

@ -21,7 +21,6 @@ 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**.
@ -38,7 +37,6 @@ For example here is a command for `samples/program.c`
./parser.out < samples/program.c
```
### Testing the Parser
Then build the `main_test.c`, `parcer.c` and `scanner.c` using **gcc**.
@ -54,43 +52,41 @@ Then execute the test suite using `./parser_test.out`.
./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`
- 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
- Write test for various statements in your language
- assignment - Riley
- print - Josh (write test)
- mathmatical expressions - Jenessy
- boolean expressions - Jenessy
- branching - Josh
- looping - Riley
- Write a test for each sample test file.
- Hello World! - Josh (already done)
- Quadratic - Jenessy
- Sorting - Riley
## 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
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

View File

@ -43,9 +43,9 @@ UTEST(parser, missing_semi_colon) {
ASSERT_EQ(result, 1);
}
UTEST(parser, sample) {
UTEST(parser, hello) {
// Read sample file as input
yyin = fopen("samples/program.c", "r");
yyin = fopen("samples/hello-world.cbl", "r");
yyrestart(yyin);
ASSERT_TRUE(yyin);
@ -55,3 +55,31 @@ UTEST(parser, sample) {
// 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 THEN 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);
}

View File

@ -58,9 +58,33 @@ int yylex();
%token TOKEN_GREATER_THAN
%token TOKEN_LESS_THAN
%token TOKEN_EXPONENTIAL
%token TOKEN_DISPLAY
%%
file : statements
statements : statements statement
| statement
;
statement : section
| sect_data
| simple_stmt
;
section : type TOKEN_KEYWORD_DIVISION TOKEN_DOT
| type TOKEN_RUN TOKEN_DOT
;
sect_data : TOKEN_PROGRAM_ID TOKEN_DOT TOKEN_IDENT TOKEN_DOT
;
type : TOKEN_IDENTIFICATION
| TOKEN_PROCEDURE
| TOKEN_STOP
| TOKEN_KEYWORD_DATA
;
simple_stmt : function
;
function : TOKEN_DISPLAY parms
;
parms : TOKEN_STRING
;
%%

View File

@ -1,25 +1,64 @@
%{
#include "token.h"
%}
%option nounput
%option noinput
%option yylineno
DIGIT [0-9]
LETTER [a-zA-Z]
NAME [a-zA-Z]([a-zA-Z0-9_-]*[a-zA-Z0-9])?
DIGIT [0-9]+
%%
(" "|\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; }
%%
\*>\ ?.* { return TOKEN_COMMENT; }
IDENTIFICATION { return TOKEN_IDENTIFICATION; }
DIVISION { return TOKEN_KEYWORD_DIVISION; }
PROGRAM-ID { return TOKEN_PROGRAM_ID; }
PROCEDURE { return TOKEN_PROCEDURE; }
DATA { return TOKEN_KEYWORD_DATA; }
SECTION { return TOKEN_KEYWORD_SECTION; }
WORKING-STORAGE { return TOKEN_WORKING_STORAGE; }
DISPLAY { return TOKEN_DISPLAY; }
STOP { return TOKEN_STOP; }
RUN { return TOKEN_RUN; }
MOVE { return TOKEN_MOVE; }
TO { return TOKEN_KEYWORD_TO; }
VARYING { return TOKEN_VARYING; }
FROM { return TOKEN_KEYWORD_FROM; }
BY { return TOKEN_KEYWORD_BY; }
UNTIL { return TOKEN_UNTIL; }
PERFORM { return TOKEN_PERFORM; }
END-PERFORM { return TOKEN_END_PERFORM; }
IF { return TOKEN_IF; }
END-IF { return TOKEN_END_IF; }
SPACE { return TOKEN_SPACE; }
PIC { return TOKEN_PICTURE; }
OCCURS { return TOKEN_KEYWORD_OCCURS; }
VALUE { return TOKEN_KEYWORD_VALUE; }
COMPUTE { return TOKEN_KEYWORD_COMPUTE; }
FUNCTION { return TOKEN_KEYWORD_FUNCTION; }
X { return TOKEN_ALPHANUMERIC; }
S9 { return TOKEN_SIGNED_NUMERIC; }
9 { return TOKEN_NUMERIC; }
V9 { return TOKEN_IMPLIED_DECIMAL; }
COMP { return TOKEN_COMPUTATION_LEVEL_0; }
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; }
\* { return TOKEN_MULTIPLY; }
\/ { return TOKEN_DIVIDE; }
\> { return TOKEN_GREATER_THAN; }
\< { return TOKEN_LESS_THAN; }
\= { return TOKEN_EQUAL;}
"\""[^"]*"\"" { return TOKEN_STRING; }
"\'"[^']*"\'" { return TOKEN_STRING; }
"(" { return TOKEN_LEFT_PARENTHESIS; }
")" { return TOKEN_RIGHT_PARENTHESIS; }
\. { return TOKEN_DOT; }
%%
int yywrap() { return 1; }

125
lab-4/token.h Normal file
View File

@ -0,0 +1,125 @@
/* 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_IDENTIFICATION = 259, /* TOKEN_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_END_IF = 277, /* TOKEN_END_IF */
TOKEN_SPACE = 278, /* TOKEN_SPACE */
TOKEN_KEYWORD_OCCURS = 279, /* TOKEN_KEYWORD_OCCURS */
TOKEN_KEYWORD_VALUE = 280, /* TOKEN_KEYWORD_VALUE */
TOKEN_KEYWORD_COMPUTE = 281, /* TOKEN_KEYWORD_COMPUTE */
TOKEN_KEYWORD_FUNCTION = 282, /* TOKEN_KEYWORD_FUNCTION */
TOKEN_IDENT = 283, /* TOKEN_IDENT */
TOKEN_STRING = 284, /* TOKEN_STRING */
TOKEN_INTEGER = 285, /* TOKEN_INTEGER */
TOKEN_PICTURE = 286, /* TOKEN_PICTURE */
TOKEN_ALPHANUMERIC = 287, /* TOKEN_ALPHANUMERIC */
TOKEN_NUMERIC = 288, /* TOKEN_NUMERIC */
TOKEN_SIGNED_NUMERIC = 289, /* TOKEN_SIGNED_NUMERIC */
TOKEN_IMPLIED_DECIMAL = 290, /* TOKEN_IMPLIED_DECIMAL */
TOKEN_COMPUTATION_LEVEL_0 = 291, /* TOKEN_COMPUTATION_LEVEL_0 */
TOKEN_COMPUTATION_LEVEL_1 = 292, /* TOKEN_COMPUTATION_LEVEL_1 */
TOKEN_COMPUTATION_LEVEL_2 = 293, /* TOKEN_COMPUTATION_LEVEL_2 */
TOKEN_COMPUTATION_LEVEL_3 = 294, /* TOKEN_COMPUTATION_LEVEL_3 */
TOKEN_LEFT_PARENTHESIS = 295, /* TOKEN_LEFT_PARENTHESIS */
TOKEN_RIGHT_PARENTHESIS = 296, /* TOKEN_RIGHT_PARENTHESIS */
TOKEN_DOT = 297, /* TOKEN_DOT */
TOKEN_COMMENT = 298, /* TOKEN_COMMENT */
TOKEN_ADD = 299, /* TOKEN_ADD */
TOKEN_SUB = 300, /* TOKEN_SUB */
TOKEN_MULTIPLY = 301, /* TOKEN_MULTIPLY */
TOKEN_DIVIDE = 302, /* TOKEN_DIVIDE */
TOKEN_EQUAL = 303, /* TOKEN_EQUAL */
TOKEN_GREATER_THAN = 304, /* TOKEN_GREATER_THAN */
TOKEN_LESS_THAN = 305, /* TOKEN_LESS_THAN */
TOKEN_EXPONENTIAL = 306, /* TOKEN_EXPONENTIAL */
TOKEN_DISPLAY = 307 /* 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 */

26
lab-5/Makefile Normal file
View File

@ -0,0 +1,26 @@
# The top level rule indicates how to link everything together into calc
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
# 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 $@
# 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=parser.h --output=parser.c -v parser.bison -Wconflicts-sr -Wcounterexamples
# clean causes all intermediate files to be deleted.
clean:
rm -f parser.c parser.output parser.h scanner.c *.o interpreter.out

107
lab-5/README.md Normal file
View File

@ -0,0 +1,107 @@
# Bison Interpreter
Create an interpreter for your sample programs.
See the [example lab for Python](https://gitlab.cs.wallawalla.edu/cptr354/language-interpreter-lab-python).
In this lab, you will focus on basic operations that support your sample code.
* Arithmetic operations
* add
* subtract
* multiple
* divide
* Functions (but implemented directly in the language)
* Printing
* Square root
* Variable for numbers
* Variable for lists (or arrays)
* Branching statements
* Looping statements
Notice you will find a new file `expr.c`.
The file is used to implement the parser rule.
Inside these functions, you will create the runtime code that is the interpreter.
## Build Process
The process is similar to prior labs.
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 parser.bison
```
Then build the `main.c`, `expr.c`, `parcer.c` and `scanner.c` using **gcc**.
Note that you may get a few warnings that can be ignored.
```sh
gcc *.c -o interpreter.out
```
Then execute the test suite using `./interpreter.out` and pass in the input from your sample program.
For example here is a command for `samples/program.c`
```sh
./interpreter.out < samples/program.c
```
Single line command:
```sh
flex -o scanner.c scanner.flex && \
bison --defines=token.h --output=parser.c parser.bison && \
gcc *.c -o interpreter.out && ./interpreter.out < samples/program.c
```
## Building with Make
To simplify the process a Makefile has been supplied.
```sh
make && ./interpreter.out < samples/program.c
```
To remove the build files, use make clean.
```sh
make clean
```
## Assignment Steps
1. Start by confirming these commands work for the sample code provided.
1. Next copy over your `scanner.flex`, `parser.bison` and sample programs from lab 5.
1. Start working on your bison `parser.bison` and `expr.c` files to implement the interpreter part of your parser.
## Grading Rubric
The grading will be over the following categories.
Points Description
----------- ------------------------------------
30 points Ability to interpret — Hello world
30 points Ability to interpret — Quadratic equation
30 points Ability to interpret — Arithmetic operations
30 points Ability to interpret — Integer Sort example
20 points Ability to interpret — Looping operations
20 points Ability to interpret — Branching operations
20 points Ability to interpret — Array variables
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-5` folder to have your report and code snippets before the due date.

457
lab-5/expr.c Normal file
View File

@ -0,0 +1,457 @@
#include "expr.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
static struct SymbolMap *symbol_table = NULL;
/*
Create one node in an expression tree and return the structure.
*/
struct type *type_create(type_t kind, struct type *sub) {
struct type *t = malloc(sizeof(*t));
t->kind = kind;
t->subtype = sub;
return t;
}
struct decl *decl_create(struct expr *name, struct type *type,
struct expr *value, struct stmt *code,
struct decl *next) {
struct decl *d = malloc(sizeof(*d));
d->name = name;
d->type = type;
d->value = value;
d->code = code;
d->next = next;
return d;
}
struct stmt *stmt_create(stmt_t kind, struct decl *decl, struct expr *init_expr,
struct expr *expr, struct expr *next_expr,
struct stmt *body, struct stmt *else_body,
struct stmt *next) {
struct stmt *s = malloc(sizeof(*s));
s->kind = kind;
s->decl = decl;
s->init_expr = init_expr;
s->expr = expr;
s->next_expr = next_expr;
s->body = body;
s->else_body = else_body;
s->next = next;
return s;
}
struct expr *expr_create(expr_t kind, struct expr *left, struct expr *right) {
/* Shortcut: sizeof(*e) means "the size of what e points to" */
struct expr *e = malloc(sizeof(*e));
e->kind = kind;
e->left = left;
e->right = right;
e->name = "";
e->integer_value = 0;
e->float_value = 0.0;
e->string_literal = "";
return e;
}
struct expr *expr_create_name(const char *value) {
struct expr *e = expr_create(EXPR_NAME, 0, 0);
char *dest = malloc(sizeof(*value));
strcpy(dest, value); // copy contents of source to dest
e->name = dest;
return e;
}
struct expr *expr_create_integer_literal(int value) {
struct expr *e = expr_create(EXPR_INTEGER_LITERAL, 0, 0);
e->integer_value = value;
return e;
}
struct expr *expr_create_float_literal(float value) {
struct expr *e = expr_create(EXPR_FLOAT_LITERAL, 0, 0);
e->float_value = value;
return e;
}
struct expr *expr_create_string_literal(const char *value) {
struct expr *e = expr_create(EXPR_STRING_LITERAL, 0, 0);
e->string_literal = value;
return e;
}
/*
Recursively delete an expression tree.
*/
void expr_delete(struct expr *e) {
/* Careful: Stop on null pointer. */
if (!e)
return;
expr_delete(e->left);
expr_delete(e->right);
free(e);
}
void scope_bind(const char *name, struct expr *value) {
if (symbol_table == NULL) {
symbol_table = createSymbolMap();
}
insertSymbol(symbol_table, name, value);
}
struct expr *scope_lookup(const char *name) {
return getSymbol(symbol_table, name);
}
/*
Recursively print an expression tree by performing an
in-order traversal of the tree, printing the current node
between the left and right nodes.
*/
void stmt_print(struct stmt *s) {
if (!s)
return;
switch (s->kind) {
case STMT_DECL:
decl_print(s->decl);
printf(";\n");
break;
case STMT_EXPR:
expr_print(s->expr);
break;
case STMT_IF:
printf("if ");
expr_print(s->expr);
printf(" then\n");
stmt_print(s->body);
printf("endif\n");
break;
case STMT_PRINT:
printf("print ");
expr_print(s->expr);
printf(";\n");
break;
case STMT_BLOCK:
stmt_print(s->body);
break;
}
stmt_print(s->next);
}
void decl_print(struct decl *d) {
expr_print(d->name);
printf(" = ");
expr_print(d->value);
}
void expr_print(struct expr *e) {
/* Careful: Stop on null pointer. */
if (!e)
return;
const char *close = "";
if (e->kind == EXPR_ARRAY) {
printf("[");
close = "]";
} else if (e->kind != EXPR_NAME && e->kind != EXPR_SUBSCRIPT &&
e->kind != EXPR_INTEGER_LITERAL && e->kind != EXPR_FLOAT_LITERAL &&
e->kind != EXPR_STRING_LITERAL) {
printf("(");
close = ")";
}
expr_print(e->left);
switch (e->kind) {
case EXPR_NAME:
printf("%s", e->name);
break;
case EXPR_ARRAY:
if (e->right) {
printf(", ");
}
break;
case EXPR_SUBSCRIPT:
printf("[");
close = "]";
break;
case EXPR_ADD:
printf("+");
break;
case EXPR_SUBTRACT:
printf("-");
break;
case EXPR_MULTIPLY:
printf("*");
break;
case EXPR_DIVIDE:
printf("/");
break;
case EXPR_EQUAL_EQUAL:
printf("==");
break;
case EXPR_NOT_EQUAL:
printf("!=");
break;
case EXPR_GREATER_THAN:
printf(">");
break;
case EXPR_GREATER_THAN_OR_EQUAL:
printf(">=");
break;
case EXPR_LESS_THAN:
printf("<");
break;
case EXPR_LESS_THAN_OR_EQUAL:
printf("<=");
break;
case EXPR_INTEGER_LITERAL:
printf("%d", e->integer_value);
break;
case EXPR_FLOAT_LITERAL:
printf("%f", e->float_value);
break;
case EXPR_STRING_LITERAL:
printf("%s", e->string_literal);
break;
}
expr_print(e->right);
// Options for closing
printf("%s", close);
}
/*
Recursively evaluate an expression by performing
the desired operation and returning it up the tree.
*/
void stmt_evaluate(struct stmt *s) {
if (!s)
return;
switch (s->kind) {
case STMT_DECL:
decl_evaluate(s->decl);
break;
case STMT_EXPR:
expr_evaluate(s->expr);
break;
case STMT_IF:
if (expr_evaluate(s->expr)) {
stmt_evaluate(s->body);
}
break;
case STMT_PRINT:
if (s->expr->kind == EXPR_STRING_LITERAL) {
printf("%s", expr_string_evaluate(s->expr));
} else if (s->expr->kind == EXPR_FLOAT_LITERAL) {
printf("%f", expr_evaluate(s->expr));
} else if (s->expr->kind == EXPR_INTEGER_LITERAL ||
s->expr->kind == EXPR_NAME || s->expr->kind == EXPR_SUBSCRIPT) {
printf("%.0f", expr_evaluate(s->expr));
} else {
printf("runtime error: print expression is not literal\n");
}
break;
case STMT_BLOCK:
stmt_evaluate(s->body);
break;
}
stmt_evaluate(s->next);
}
void decl_evaluate(struct decl *d) {
if (!d)
return;
if (d->name->kind == EXPR_NAME && d->value->kind == EXPR_ARRAY) {
struct expr *e = expr_sub_evaluate(d->value);
scope_bind(d->name->name, e);
} else if (d->name->kind == EXPR_NAME) {
float value = expr_evaluate(d->value);
struct expr *e = expr_create_float_literal(value);
scope_bind(d->name->name, e);
} else if (d->name->kind == EXPR_SUBSCRIPT) {
float value = expr_evaluate(d->value);
decl_subscript_evaluate(d->name, value);
} else {
printf("Error: expression type unknown for name");
exit(1);
}
}
void decl_subscript_evaluate(struct expr *e, float value) {
/* Careful: Return zero on null pointer. */
if (!e)
return;
if (e->kind != EXPR_SUBSCRIPT) {
printf("runtime error: not a subscript expression\n");
exit(1);
}
if (e->left->kind != EXPR_NAME) {
printf("runtime error: subscript has no name\n");
exit(1);
}
// Get array expresion
struct expr *a = scope_lookup(e->left->name);
float index = expr_evaluate(e->right);
// Find the right offset
while (index > 0) {
a = a->right;
index--;
}
expr_delete(a->left);
a->left = expr_create_float_literal(value);
}
const char *expr_string_evaluate(struct expr *e) {
/* Careful: Return zero on null pointer. */
if (!e)
return 0;
switch (e->kind) {
case EXPR_NAME:
case EXPR_ARRAY:
case EXPR_SUBSCRIPT:
case EXPR_ADD:
case EXPR_SUBTRACT:
case EXPR_MULTIPLY:
case EXPR_DIVIDE:
case EXPR_EQUAL_EQUAL:
case EXPR_NOT_EQUAL:
case EXPR_GREATER_THAN:
case EXPR_GREATER_THAN_OR_EQUAL:
case EXPR_LESS_THAN:
case EXPR_LESS_THAN_OR_EQUAL:
case EXPR_INTEGER_LITERAL:
case EXPR_FLOAT_LITERAL:
printf("runtime error: not a string expression\n");
exit(1);
case EXPR_STRING_LITERAL:
return e->string_literal;
}
return "";
}
struct expr *expr_sub_evaluate(struct expr *e) {
/* Careful: Return zero on null pointer. */
if (!e)
return 0;
// TODO evaluate each item in the array and save the result
return e;
}
/*
Recursively evaluate an expression by performing
the desired operation and returning it up the tree.
*/
float expr_subscript_evaluate(struct expr *e) {
/* Careful: Return zero on null pointer. */
if (!e)
return 0;
if (e->kind != EXPR_SUBSCRIPT) {
printf("runtime error: not a subscript expression\n");
exit(1);
}
if (e->left->kind != EXPR_NAME) {
printf("runtime error: subscript has no name\n");
exit(1);
}
// Get array expresion
struct expr *a = scope_lookup(e->left->name);
float index = expr_evaluate(e->right);
// Find the right offset
while (index > 0) {
a = a->right;
index--;
}
return expr_evaluate(a->left);
}
float expr_evaluate(struct expr *e) {
/* Careful: Return zero on null pointer. */
if (!e)
return 0;
if (e->kind == EXPR_SUBSCRIPT)
return expr_subscript_evaluate(e);
float l = expr_evaluate(e->left);
float r = expr_evaluate(e->right);
switch (e->kind) {
case EXPR_NAME:
// Get the variable expression and then evaluate it.
return expr_evaluate(scope_lookup(e->name));
case EXPR_ARRAY:
printf("runtime error: array in expression\n");
exit(1);
case EXPR_SUBSCRIPT:
printf(
"runtime error: subscript should be processed earlier in expression\n");
exit(1);
case EXPR_ADD:
return l + r;
case EXPR_SUBTRACT:
return l - r;
case EXPR_MULTIPLY:
return l * r;
case EXPR_DIVIDE:
if (r == 0) {
printf("runtime error: divide by zero\n");
exit(1);
}
return l / r;
case EXPR_EQUAL_EQUAL:
return l == r;
case EXPR_NOT_EQUAL:
return l != r;
case EXPR_GREATER_THAN:
return l > r;
case EXPR_GREATER_THAN_OR_EQUAL:
return l >= r;
case EXPR_LESS_THAN:
return l < r;
case EXPR_LESS_THAN_OR_EQUAL:
return l <= r;
case EXPR_INTEGER_LITERAL:
return e->integer_value;
case EXPR_FLOAT_LITERAL:
return e->float_value;
case EXPR_STRING_LITERAL:
printf("runtime error: string in expression\n");
exit(1);
}
return 0;
}
void close_parser() { destroySymbolMap(symbol_table); }

108
lab-5/expr.h Normal file
View File

@ -0,0 +1,108 @@
/*
expr.h defines the structure of an expression node,
and the operations that can be performed upon it.
Note some things about this file that you should emulate:
- Every symbol in expr.[ch] begins with expr_.
- Use enumerations to define variant types.
- Build complex trees one node at a time.
- Define methods with recurse over those trees.
*/
#ifndef EXPR_H
#define EXPR_H
#include "symbol_map.h"
#include <stdio.h>
#include <stdlib.h>
// Expressions
typedef enum {
EXPR_ADD,
EXPR_ARRAY,
EXPR_DIVIDE,
EXPR_EQUAL_EQUAL,
EXPR_FLOAT_LITERAL,
EXPR_GREATER_THAN,
EXPR_GREATER_THAN_OR_EQUAL,
EXPR_INTEGER_LITERAL,
EXPR_LESS_THAN,
EXPR_LESS_THAN_OR_EQUAL,
EXPR_MULTIPLY,
EXPR_NAME,
EXPR_NOT_EQUAL,
EXPR_STRING_LITERAL,
EXPR_SUBSCRIPT,
EXPR_SUBTRACT,
} expr_t;
struct expr {
expr_t kind;
struct expr *left;
struct expr *right;
const char *name;
int integer_value;
float float_value;
const char *string_literal;
};
typedef enum { TYPE_ARRAY, TYPE_FLOAT, TYPE_INTEGER, TYPE_STRING } type_t;
struct type {
type_t kind;
struct type *subtype;
};
struct decl {
struct expr *name;
struct type *type;
struct expr *value;
struct stmt *code;
struct decl *next;
};
typedef enum { STMT_BLOCK, STMT_DECL, STMT_EXPR, STMT_IF, STMT_PRINT } stmt_t;
struct stmt {
stmt_t kind;
struct decl *decl;
struct expr *init_expr;
struct expr *expr;
struct expr *next_expr;
struct stmt *body;
struct stmt *else_body;
struct stmt *next;
};
struct stmt *stmt_create(stmt_t kind, struct decl *decl, struct expr *init_expr,
struct expr *expr, struct expr *next_expr,
struct stmt *body, struct stmt *else_body,
struct stmt *next);
struct decl *decl_create(struct expr *name, struct type *type,
struct expr *value, struct stmt *code,
struct decl *next);
struct expr *expr_create_name(const char *name);
struct expr *expr_create_integer_literal(int i);
struct expr *expr_create_float_literal(float f);
struct expr *expr_create_string_literal(const char *str);
struct expr *expr_create(expr_t kind, struct expr *left, struct expr *right);
struct type *type_create(type_t kind, struct type *sub);
void scope_bind(const char *name, struct expr *sym);
struct expr *scope_lookup(const char *name);
void stmt_print(struct stmt *e);
void decl_print(struct decl *d);
void expr_print(struct expr *e);
void stmt_evaluate(struct stmt *e);
void decl_evaluate(struct decl *e);
void decl_subscript_evaluate(struct expr *e, float value);
float expr_evaluate(struct expr *e);
struct expr *expr_sub_evaluate(struct expr *e);
float expr_subscript_evaluate(struct expr *e);
const char *expr_string_evaluate(struct expr *e);
void expr_delete(struct expr *e);
void close_parser();
#endif

32
lab-5/main.c Normal file
View File

@ -0,0 +1,32 @@
/*
Main program of calculator example.
Simply invoke the parser generated by bison, and then display the output.
*/
#include "expr.h"
#include <stdio.h>
/* Clunky: Declare the parse function generated from parser.bison */
extern int yyparse();
/* Clunky: Declare the result of the parser from parser.bison */
extern 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");
return 0;
} else {
printf("Parse failed!\n");
return 1;
}
}

189
lab-5/parser.bison Normal file
View File

@ -0,0 +1,189 @@
/*
Declare token types at the top of the bison file,
causing them to be automatically generated in parser.tab.h
for use by scanner.c.
*/
%debug
%define parse.error detailed
%token TOKEN_ASSIGNMENT
%token TOKEN_COMMA
%token TOKEN_DIV
%token TOKEN_EQUAL_EQUAL
%token TOKEN_GREATER_THAN
%token TOKEN_GREATER_THAN_OR_EQUAL
%token TOKEN_ID
%token TOKEN_INT
%token TOKEN_INTEGER
%token TOKEN_KEYWORD_ENDIF
%token TOKEN_KEYWORD_IF
%token TOKEN_KEYWORD_PRINT
%token TOKEN_KEYWORD_THEN
%token TOKEN_LBRACKET
%token TOKEN_LESS_THAN
%token TOKEN_LESS_THAN_OR_EQUAL
%token TOKEN_LPAREN
%token TOKEN_MINUS
%token TOKEN_MUL
%token TOKEN_NOT_EQUAL
%token TOKEN_PLUS
%token TOKEN_RBRACKET
%token TOKEN_RPAREN
%token TOKEN_SEMI
%union {
struct stmt *stmt;
struct expr *expr;
struct decl *decl;
};
%type <stmt> program statement_list statement print_statement assignment_statement if_statement block
%type <expr> expr term factor name sum array_subscript star_expr
%type <decl> declaration
%{
#define YYDEBUG 1
#include <math.h>
#include <stdio.h>
#include <stdlib.h>
#include "expr.h"
/*
Clunky: Manually declare the interface to the scanner generated by flex.
*/
extern int yylineno;
extern char *yytext;
extern int yylex();
extern void yyerror(const char*);
/*
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;
%}
%%
/* Here is the grammar: program is the start symbol. */
program : statement_list
{ parser_result = $1; return 0; }
;
statement_list : statement
{ $$ = $1; }
| statement statement_list
{ $$ = $1; $1->next = $2; }
;
block : statement
{ $$ = $1; }
| statement block
{ $$ = $1; $1->next = $2; }
;
statement : assignment_statement TOKEN_SEMI
{ $$ = $1; }
| print_statement TOKEN_SEMI
{ $$ = $1; }
| if_statement
{ $$ = $1; }
;
assignment_statement : declaration
{ $$ = stmt_create(STMT_DECL,$1,0,0,0,0,0,0); }
;
print_statement : TOKEN_KEYWORD_PRINT expr
{ $$ = stmt_create(STMT_PRINT,0,0,$2,0,0,0,0); }
;
if_statement : TOKEN_KEYWORD_IF expr TOKEN_KEYWORD_THEN block TOKEN_KEYWORD_ENDIF
{ $$ = stmt_create(STMT_IF,0,0,$2,0,$4,0,0); }
;
declaration : name TOKEN_ASSIGNMENT expr
{ $$ = decl_create($1,0,$3,0,0); }
| array_subscript TOKEN_ASSIGNMENT expr
{ $$ = decl_create($1,0,$3,0,0); }
;
name : TOKEN_ID
{ $$ = expr_create_name(yytext); }
;
array_subscript : name TOKEN_LBRACKET expr TOKEN_RBRACKET
{ $$ = expr_create(EXPR_SUBSCRIPT,$1,$3); }
;
star_expr : expr
{ $$ = expr_create(EXPR_ARRAY,$1,0); }
| expr TOKEN_COMMA star_expr
{ $$ = expr_create(EXPR_ARRAY,$1,$3); }
;
expr : sum TOKEN_LESS_THAN sum
{ $$ = expr_create(EXPR_LESS_THAN,$1,$3); }
| sum TOKEN_LESS_THAN_OR_EQUAL sum
{ $$ = expr_create(EXPR_LESS_THAN_OR_EQUAL,$1,$3); }
| sum TOKEN_GREATER_THAN sum
{ $$ = expr_create(EXPR_GREATER_THAN,$1,$3); }
| sum TOKEN_GREATER_THAN_OR_EQUAL sum
{ $$ = expr_create(EXPR_GREATER_THAN_OR_EQUAL,$1,$3); }
| sum TOKEN_EQUAL_EQUAL sum
{ $$ = expr_create(EXPR_EQUAL_EQUAL,$1,$3); }
| sum TOKEN_NOT_EQUAL sum
{ $$ = expr_create(EXPR_NOT_EQUAL,$1,$3); }
| sum
{ $$ = $1; }
;
sum : sum TOKEN_PLUS term
{ $$ = expr_create(EXPR_ADD,$1,$3); }
| sum TOKEN_MINUS term
{ $$ = expr_create(EXPR_SUBTRACT,$1,$3); }
| term
{ $$ = $1; }
;
term : term TOKEN_MUL factor
{ $$ = expr_create(EXPR_MULTIPLY,$1,$3); }
| term TOKEN_DIV factor
{ $$ = expr_create(EXPR_DIVIDE,$1,$3); }
| factor
{ $$ = $1; }
;
factor : TOKEN_LPAREN expr TOKEN_RPAREN
{ $$ = $2; }
| TOKEN_MINUS factor
{ $$ = expr_create(EXPR_SUBTRACT,expr_create_integer_literal(0),$2); }
| TOKEN_INT
{ $$ = expr_create_integer_literal(atoi(yytext)); }
| TOKEN_LBRACKET star_expr TOKEN_RBRACKET
{ $$ = $2; }
| array_subscript
{ $$ = $1; }
| name
{ $$ = $1; }
;
%%
/*
This function will be called by bison if the parse should
encounter an error. In principle, "str" will contain something
useful. In practice, it often does not.
*/
void yyerror(const char* msg) {
fprintf(stderr, "Error | Line: %d\n%s\n", yylineno, msg);
}

View File

@ -0,0 +1 @@
a = [ 4, 8, 12 ];

View File

@ -0,0 +1,2 @@
a = [ 3, 5 ];
print a[0];

View File

@ -0,0 +1,4 @@
a = [ 3, 5, 7, 9, 11];
print a[1];
a[1] = 6;
print a[1];

View File

@ -0,0 +1 @@
a = 5;

View File

@ -0,0 +1,2 @@
print 5;
print 6;

View File

@ -0,0 +1,6 @@
if 3==3 then
print 3;
endif
if 3==4 then
print 4;
endif

View File

@ -0,0 +1,5 @@
if 0 then
print 5;
print 3;
print 1;
endif

View File

@ -0,0 +1,5 @@
if 1 then
print 5;
print 3;
print 1;
endif

View File

@ -0,0 +1 @@
print 5;

View File

@ -0,0 +1,2 @@
a = 4 + 4;
print a;

2
lab-5/samples/program.c Normal file
View File

@ -0,0 +1,2 @@
a=(101*20)+4;
print(a);

47
lab-5/scanner.flex Normal file
View File

@ -0,0 +1,47 @@
%{
#include "parser.h"
%}
%option nounput
%option noinput
%option noyywrap
%option yylineno
DIGIT [0-9]
LETTER [a-zA-Z_]
%%
[ \n\r\t]* /* skip whitespace */;
"print" return TOKEN_KEYWORD_PRINT;
"if" return TOKEN_KEYWORD_IF;
"then" return TOKEN_KEYWORD_THEN;
"endif" return TOKEN_KEYWORD_ENDIF;
{DIGIT}+ return TOKEN_INT;
{LETTER}+ return TOKEN_ID;
"," return TOKEN_COMMA;
"]" return TOKEN_RBRACKET;
"[" return TOKEN_LBRACKET;
"==" return TOKEN_EQUAL_EQUAL;
"!=" return TOKEN_NOT_EQUAL;
"<=" return TOKEN_LESS_THAN_OR_EQUAL;
">=" return TOKEN_GREATER_THAN_OR_EQUAL;
"<" return TOKEN_LESS_THAN;
">" return TOKEN_GREATER_THAN;
\= return TOKEN_ASSIGNMENT;
\* return TOKEN_MUL;
\+ return TOKEN_PLUS;
\- return TOKEN_MINUS;
\/ return TOKEN_DIV;
\( return TOKEN_LPAREN;
\) return TOKEN_RPAREN;
\; return TOKEN_SEMI;
. { printf("scan error: bad token: %c\n", yytext[0]); }
%%
/*
flex calls yywrap() whenever it reaches the end of the current file.
If yywrap returns false to indicate the end of the program.
It could alternatively open up another file and return true,
so that flex would keep going.
*/

74
lab-5/symbol_map.c Normal file
View File

@ -0,0 +1,74 @@
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "symbol_map.h"
// Function to create a new symbol map
struct SymbolMap *createSymbolMap(struct SymbolMap *map) {
map = (struct SymbolMap *)malloc(sizeof(struct SymbolMap));
if (map == NULL) {
// Handle memory allocation failure
exit(EXIT_FAILURE);
}
map->head = NULL;
return map;
}
// Function to insert a symbol into the map
void insertSymbol(struct SymbolMap *map, const char *key, struct expr *value) {
struct Node *newNode = (struct Node *)malloc(sizeof(struct Node));
if (newNode == NULL) {
// Handle memory allocation failure
exit(EXIT_FAILURE);
}
newNode->key = key;
newNode->value = value;
newNode->next = map->head;
map->head = newNode;
}
// Function to get a symbol from the map
struct expr *getSymbol(struct SymbolMap *map, const char *key) {
struct Node *current = map->head;
while (current != NULL) {
if (strcmp(current->key, key) == 0) {
return current->value;
}
current = current->next;
}
return NULL; // Key not found
}
// Function to delete a symbol from the map
void deleteSymbol(struct SymbolMap *map, const char *key) {
struct Node *current = map->head;
struct Node *prev = NULL;
while (current != NULL) {
if (strcmp(current->key, key) == 0) {
if (prev == NULL) {
map->head = current->next;
} else {
prev->next = current->next;
}
free(current);
return;
}
prev = current;
current = current->next;
}
}
// Function to destroy the symbol map and free memory
void destroySymbolMap(struct SymbolMap *map) {
struct Node *current = map->head;
struct Node *next;
while (current != NULL) {
next = current->next;
free(current);
current = next;
}
free(map);
}

56
lab-5/symbol_map.h Normal file
View File

@ -0,0 +1,56 @@
/*
* symbol_map.h
*
* Header file for a simple symbol map implementation in C.
* This file defines structures and functions to create, manipulate,
* and destroy a map with keys of type 'const char *' and values of type 'struct expr *'.
*
* Author: ChatGPT
* Date: November 16, 2023
*
* Structures:
* - struct expr: Represents a symbol with fields for its value and other relevant information.
* - struct Node: Represents a node in the linked list used for the symbol map implementation.
* - struct SymbolMap: Represents the symbol map itself.
*
* Functions:
* - createSymbolMap: Creates a new symbol map and initializes it.
* - insertSymbol: Inserts a key-value pair into the symbol map.
* - getSymbol: Retrieves the value associated with a given key from the symbol map.
* - deleteSymbol: Removes a key-value pair from the symbol map.
* - destroySymbolMap: Frees memory allocated for the symbol map and its nodes.
*
* Usage:
* 1. Include this header file in your C program: #include "symbol_map.h"
* 2. Implement the functions in a corresponding C file.
* 3. Use the functions to manage symbols in your program.
*
* Note: This implementation assumes each key in the map is unique.
* If needed, explore other data structures for more complex use cases.
*/
#ifndef SYMBOL_MAP_H
#define SYMBOL_MAP_H
// Define the symbol structure
#include "expr.h"
// Define the node structure for the linked list
struct Node {
const char *key;
struct expr *value;
struct Node *next;
};
// Define the symbol map structure
struct SymbolMap {
struct Node *head;
};
// Function declarations
struct SymbolMap *createSymbolMap();
void insertSymbol(struct SymbolMap *map, const char *key, struct expr *value);
struct expr *getSymbol(struct SymbolMap *map, const char *key);
void deleteSymbol(struct SymbolMap *map, const char *key);
void destroySymbolMap(struct SymbolMap *map);
#endif // SYMBOL_MAP_H