This commit is contained in:
Preston Carman 2024-10-31 11:08:06 -07:00
parent 047ede4fd0
commit 347f1dd763
20 changed files with 1127 additions and 0 deletions

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