Merge branch 'main' of gitlab.cs.wallawalla.edu:lustje/language-interpreter-lab
This commit is contained in:
commit
9e3f11ad40
|
|
@ -38,6 +38,7 @@ UTEST(scanner, hello) {
|
|||
{TOKEN_EOF, ""},
|
||||
};
|
||||
|
||||
|
||||
yyin = fopen("samples/hello-world.cbl", "r");
|
||||
ASSERT_TRUE(yyin);
|
||||
int index = 0;
|
||||
|
|
|
|||
|
|
@ -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,44 +52,42 @@ 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
|
||||
|
||||
|
||||
## Turn In
|
||||
|
||||
Please submit the URL of your gitlab project to D2L's Brightspace with your report.
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
|
|
@ -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
|
||||
;
|
||||
|
||||
|
||||
%%
|
||||
|
|
|
|||
|
|
@ -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; }
|
||||
|
|
|
|||
|
|
@ -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 */
|
||||
|
|
@ -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
|
||||
|
||||
|
|
@ -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.
|
||||
|
|
@ -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); }
|
||||
|
|
@ -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
|
||||
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
@ -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);
|
||||
}
|
||||
|
|
@ -0,0 +1 @@
|
|||
a = [ 4, 8, 12 ];
|
||||
|
|
@ -0,0 +1,2 @@
|
|||
a = [ 3, 5 ];
|
||||
print a[0];
|
||||
|
|
@ -0,0 +1,4 @@
|
|||
a = [ 3, 5, 7, 9, 11];
|
||||
print a[1];
|
||||
a[1] = 6;
|
||||
print a[1];
|
||||
|
|
@ -0,0 +1 @@
|
|||
a = 5;
|
||||
|
|
@ -0,0 +1,2 @@
|
|||
print 5;
|
||||
print 6;
|
||||
|
|
@ -0,0 +1,6 @@
|
|||
if 3==3 then
|
||||
print 3;
|
||||
endif
|
||||
if 3==4 then
|
||||
print 4;
|
||||
endif
|
||||
|
|
@ -0,0 +1,5 @@
|
|||
if 0 then
|
||||
print 5;
|
||||
print 3;
|
||||
print 1;
|
||||
endif
|
||||
|
|
@ -0,0 +1,5 @@
|
|||
if 1 then
|
||||
print 5;
|
||||
print 3;
|
||||
print 1;
|
||||
endif
|
||||
|
|
@ -0,0 +1 @@
|
|||
print 5;
|
||||
|
|
@ -0,0 +1,2 @@
|
|||
a = 4 + 4;
|
||||
print a;
|
||||
|
|
@ -0,0 +1,2 @@
|
|||
a=(101*20)+4;
|
||||
print(a);
|
||||
|
|
@ -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.
|
||||
*/
|
||||
|
|
@ -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);
|
||||
}
|
||||
|
|
@ -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
|
||||
Loading…
Reference in New Issue