diff --git a/lab-4/README.md b/lab-4/README.md index e814109..b2d23a0 100644 --- a/lab-4/README.md +++ b/lab-4/README.md @@ -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 diff --git a/lab-4/main_test.c b/lab-4/main_test.c index 0e80882..057bed9 100644 --- a/lab-4/main_test.c +++ b/lab-4/main_test.c @@ -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); +} \ No newline at end of file diff --git a/lab-4/parser.bison b/lab-4/parser.bison index ddbc8b4..e4c4b6f 100644 --- a/lab-4/parser.bison +++ b/lab-4/parser.bison @@ -77,6 +77,7 @@ sect_data : TOKEN_PROGRAM_ID TOKEN_DOT TOKEN_IDENT TOKEN_DOT type : TOKEN_IDENTIFICATION | TOKEN_PROCEDURE | TOKEN_STOP + | TOKEN_KEYWORD_DATA ; simple_stmt : function ; diff --git a/lab-4/token.h b/lab-4/token.h new file mode 100644 index 0000000..fc7b161 --- /dev/null +++ b/lab-4/token.h @@ -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 . */ + +/* 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 */ diff --git a/lab-5/Makefile b/lab-5/Makefile new file mode 100644 index 0000000..fd9e075 --- /dev/null +++ b/lab-5/Makefile @@ -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 + diff --git a/lab-5/README.md b/lab-5/README.md new file mode 100644 index 0000000..b9e30d0 --- /dev/null +++ b/lab-5/README.md @@ -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. diff --git a/lab-5/expr.c b/lab-5/expr.c new file mode 100644 index 0000000..6b0e94d --- /dev/null +++ b/lab-5/expr.c @@ -0,0 +1,457 @@ + +#include "expr.h" + +#include +#include +#include + +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); } diff --git a/lab-5/expr.h b/lab-5/expr.h new file mode 100644 index 0000000..99d8461 --- /dev/null +++ b/lab-5/expr.h @@ -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 +#include + +// 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 diff --git a/lab-5/main.c b/lab-5/main.c new file mode 100644 index 0000000..63208e9 --- /dev/null +++ b/lab-5/main.c @@ -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 + +/* 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; + } +} diff --git a/lab-5/parser.bison b/lab-5/parser.bison new file mode 100644 index 0000000..0f901ba --- /dev/null +++ b/lab-5/parser.bison @@ -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 program statement_list statement print_statement assignment_statement if_statement block +%type expr term factor name sum array_subscript star_expr +%type declaration + +%{ +#define YYDEBUG 1 + +#include +#include +#include +#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); +} diff --git a/lab-5/samples/array_assignment.c b/lab-5/samples/array_assignment.c new file mode 100644 index 0000000..cce2d38 --- /dev/null +++ b/lab-5/samples/array_assignment.c @@ -0,0 +1 @@ +a = [ 4, 8, 12 ]; diff --git a/lab-5/samples/array_read.c b/lab-5/samples/array_read.c new file mode 100644 index 0000000..db30710 --- /dev/null +++ b/lab-5/samples/array_read.c @@ -0,0 +1,2 @@ +a = [ 3, 5 ]; +print a[0]; \ No newline at end of file diff --git a/lab-5/samples/array_write.c b/lab-5/samples/array_write.c new file mode 100644 index 0000000..4f81214 --- /dev/null +++ b/lab-5/samples/array_write.c @@ -0,0 +1,4 @@ +a = [ 3, 5, 7, 9, 11]; +print a[1]; +a[1] = 6; +print a[1]; diff --git a/lab-5/samples/assignment_statement.c b/lab-5/samples/assignment_statement.c new file mode 100644 index 0000000..a5a2277 --- /dev/null +++ b/lab-5/samples/assignment_statement.c @@ -0,0 +1 @@ +a = 5; \ No newline at end of file diff --git a/lab-5/samples/multiple_statements.c b/lab-5/samples/multiple_statements.c new file mode 100644 index 0000000..b31de4e --- /dev/null +++ b/lab-5/samples/multiple_statements.c @@ -0,0 +1,2 @@ +print 5; +print 6; diff --git a/lab-5/samples/print_if_equal.c b/lab-5/samples/print_if_equal.c new file mode 100644 index 0000000..e4a42e4 --- /dev/null +++ b/lab-5/samples/print_if_equal.c @@ -0,0 +1,6 @@ +if 3==3 then + print 3; +endif +if 3==4 then + print 4; +endif diff --git a/lab-5/samples/print_if_false.c b/lab-5/samples/print_if_false.c new file mode 100644 index 0000000..5340a03 --- /dev/null +++ b/lab-5/samples/print_if_false.c @@ -0,0 +1,5 @@ +if 0 then + print 5; + print 3; + print 1; +endif diff --git a/lab-5/samples/print_if_true.c b/lab-5/samples/print_if_true.c new file mode 100644 index 0000000..7848347 --- /dev/null +++ b/lab-5/samples/print_if_true.c @@ -0,0 +1,5 @@ +if 1 then + print 5; + print 3; + print 1; +endif diff --git a/lab-5/samples/print_statement.c b/lab-5/samples/print_statement.c new file mode 100644 index 0000000..1748ba7 --- /dev/null +++ b/lab-5/samples/print_statement.c @@ -0,0 +1 @@ +print 5; diff --git a/lab-5/samples/print_variable.c b/lab-5/samples/print_variable.c new file mode 100644 index 0000000..28aae29 --- /dev/null +++ b/lab-5/samples/print_variable.c @@ -0,0 +1,2 @@ +a = 4 + 4; +print a; diff --git a/lab-5/samples/program.c b/lab-5/samples/program.c new file mode 100644 index 0000000..f1f084a --- /dev/null +++ b/lab-5/samples/program.c @@ -0,0 +1,2 @@ +a=(101*20)+4; +print(a); diff --git a/lab-5/scanner.flex b/lab-5/scanner.flex new file mode 100644 index 0000000..69284c9 --- /dev/null +++ b/lab-5/scanner.flex @@ -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. +*/ diff --git a/lab-5/symbol_map.c b/lab-5/symbol_map.c new file mode 100644 index 0000000..f15b8a7 --- /dev/null +++ b/lab-5/symbol_map.c @@ -0,0 +1,74 @@ +#include +#include +#include +#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); +} diff --git a/lab-5/symbol_map.h b/lab-5/symbol_map.h new file mode 100644 index 0000000..7704fda --- /dev/null +++ b/lab-5/symbol_map.h @@ -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