0
\$\begingroup\$

My program , developed with C using win_flex , implements lexical, syntatic and semantic analysis , given defined grammar rules.

I would like to know if you any suggestion for improving or refactoring this.

Here is my code:

main.c

#define _CRT_SECURE_NO_DEPRECATE
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "LEXYY.h"
#include "PARSER.h"
#include "semantic.h"

char *yytext;
FILE *yyin;
int yylex();

void printToken() {
    const char *TokenNames[11] = {
        "PROGRAM", "END",
        "INTEGER", "REAL", "INT_NUMBER", "REAL_NUMBER",
        "ID", "VOID", "RETURN",
        "COMMA", "SEMICOLON",
    };
    char buffer[] = { (char)token->kind, '\0' };
    const char *token_name = token->kind >= PROGRAM ? TokenNames[token->kind - PROGRAM] : buffer;
    token->count = strlen(token->lexeme);

    fprintf(yyout, "Token of kind '%s' was found at line: %d, lexeme: '%s'\n", token_name, token->line, token->lexeme);
}

int main(int argc, char *argv[]) {
    int current_token;

    for (int i = 1; i < 3; i++) {
        if (i == 1) {
            yyin = fopen("C:\\temp\\test1.txt", "r");
            yyout = fopen("C:\\temp\\test1_200419513_lex.txt", "w");
        }
        else {
            yyin = fopen("C:\\temp\\test2.txt", "r");
            yyout = fopen("C:\\temp\\test2_200419513_lex.txt", "w");
        }
        if (yyin == NULL) {
            printf("Cannot find file.\nPlease use standard input.\n");
            yyin = stdin;
        }
        if (yyout == NULL) {
            printf("Cannot open output file.\n");
            yyout = stdout;
        }
        initLexer();
        while ((current_token = yylex()) != 0) {
            create_and_store_token(current_token, (char *)yytext, line_number);
            printToken();
        }

        if (i == 1) {
            yyout = fopen("C:\\temp\\test1_200419513_syntatic.txt", "w");
        }
        else {
            yyout = fopen("C:\\temp\\test2_200419513_syntatic.txt", "w");
        }
        if (yyout == NULL) {
            printf("Cannot open output file.\n");
            yyout = stdout;
        }
        if (lexer_errors) {
            fprintf(yyout, "Detected Errors by lexical analysis.\nPlease first fix lexical error.\n");
            continue;
        }
        parser();

        if (i == 1) {
            yyout = fopen("C:\\temp\\test1_200419513_semantic.txt", "w");
        }
        else {
            yyout = fopen("C:\\temp\\test2_200419513_semantic.txt", "w");
        }
        if (yyout == NULL) {
            printf("Cannot open output file.\n");
            yyout = stdout;
        }
        if (parser_errors) {
            fprintf(yyout, "Detected Errors by syntatic analysis.\nPlease first fix syntax error.\n");
            continue;
        }
        semantic();
    }
    return 0;

}

LEXYY.Y

program : PROGRAM var_definitions ';' statements END func_definitions
        ;

var_definitions : var_definition
                | var_definition ';' var_definitions
                ;

var_definition : type variables_list
               ;

type : REAL
     | INTEGER
     ;

variables_list : variable
               | variables_list ',' variable
               ;

variable : ID
         | ID '[' INT_NUMBER ']'
         ;

func_definitions : func_definition
                 | func_definitions func_definition
                 ;

func_definition : returned_type ID '(' param_definitions ')' block
                ;

returned_type : VOID
              | type
              ;

param_definitions : /* empty */
                  | var_definitions
                  ;

block : '{' var_definitions ';' statements '}'
      ;

statements : statement ';'
           | statement ';' statements
           ;

statement : variable '=' expression
          | block
          | RETURN
          | RETURN expression
          | function_call
          ;

function_call : ID '(' parameters_list ')'
              ;

parameters_list : /* empty */
                | variables_list
                ;

expression : INT_NUMBER
           | REAL_NUMBER
           | variable
           | ID ar_op expression
           ;

ar_op : '*'
      | '/'
      ;

LEXYY.h

#pragma once

#include <stdio.h>

typedef struct YYTYPE {
    int kind;
    char *lexeme;
    int line;
    int count;
    struct YYTYPE *next;
    struct YYTYPE *prev;
} YYSTYPE;

YYSTYPE *token;
YYSTYPE *tokens;
int line_number;
int lexer_errors;

void initLexer();
void create_and_store_token(int, char *, int);

#define PROGRAM 256
#define END 257
#define INTEGER 258
#define REAL 259
#define INT_NUMBER 260
#define REAL_NUMBER 261
#define ID 262
#define VOID 263
#define RETURN 264
#define COMMA 265
#define SEMICOLON 266

YYSTYPE *next_token();
YYSTYPE *back_token();
int match(int);

int type();
int ar_op();
int returned_type();
int not_var_definitions();
int not_statements();

LEXYY.C

%option nounistd
%option noyywrap

%{
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <io.h>
#include "LEXYY.h"
#define isatty _isatty
#define fileno _fileno
void initLexer();
void create_and_store_token(int, char *, int);
void errorPrint(char);
void illegalError(const char *, const char *);
%}

WHITESPACE          ([ \t]+)
NEWLINE             (\r|\n|\r\n)
COMMENT             ("--"[^\r\n]*)
ID                  ([A-Za-z]([_]?[A-Za-z0-9]+)*)
ILLEGALID           ([_][A-Za-z0-9_]*|[A-Za-z][A-Za-z0-9_]*[_]|[0-9][A-Za-z0-9]*|[A-Za-z]([_]*[A-Za-z0-9]*)*)
INTEGER             (0|[0-9]+)
REAL                (0\.[0-9]+|[0-9]+\.[0-9]+)
WRONGNUMBER         ([0][0-9]+|[0][0-9]+[.][0-9]*|[.][0-9]+|[0-9]+[.])
OPERATOR            ([*/=])
SEPARATION          ([[\]{}\(\)])

%%

{NEWLINE}           { line_number++; }
{WHITESPACE}+       {}
{COMMENT}           {}

"program"           { return PROGRAM; }
"end"               { return END; }
"real"              { return REAL; }
"integer"           { return INTEGER; }
"void"              { return VOID; }
"return"            { return RETURN; }
{INTEGER}           { return INT_NUMBER; }
{REAL}              { return REAL_NUMBER; }
{WRONGNUMBER}       { illegalError(yytext, "Number"); }
","                 { return COMMA; }
";"                 { return SEMICOLON; }

{ID}                { return ID; }
{ILLEGALID}         { illegalError(yytext, "ID"); }

{OPERATOR}          { return yytext[0]; }
{SEPARATION}        { return yytext[0]; }

.                   { errorPrint(yytext[0]); }

%%

void initLexer() {
    line_number = 1;
    lexer_errors = 0;
    tokens = NULL;
    token = NULL;
}

void create_and_store_token(int kind, char *lexeme, int line) {
    if (token == NULL) {
        tokens = (YYSTYPE *)malloc(sizeof(YYSTYPE));
        token = tokens;
        token->next = NULL;
        token->prev = NULL;
    } else {
        token->next = (YYSTYPE *)malloc(sizeof(YYSTYPE));
        token->next->next = NULL;
        token->next->prev = token;
        token = token->next;
    }
    token->kind = kind;
    token->line = line;
    token->lexeme = (char *)malloc(sizeof(lexeme) + 1);
#ifdef _WIN32
    strcpy_s(token->lexeme, strlen(lexeme) + 1, lexeme);
#else
    strcpy(token->lexeme, lexeme);
#endif
}

YYSTYPE *next_token() {
    if (token) 
        return (token = token->next);
    return NULL;
}

YYSTYPE *back_token() {
    if (token) 
        return (token = token->prev);
    return NULL;
}

int match(int kind) {
    if (token && token->kind == kind) 
        return kind;
    return 0;
}

int type() {
    return (match(REAL) ? REAL : match(INTEGER));
}

int ar_op() {
    if (match('*') || match('/')) 
        return 1;
    return 0;
}

int returned_type() {
    return (match(VOID) ? VOID : type());
}

int not_var_definitions() {
    int flag = 0;
    if (!token) 
        flag = 1;
    else if (match(ID)) {
        next_token();
        if (match('=') || match('('))
            flag = 2;

        if (match('[')) {
            next_token();
            next_token();
            next_token();
            if (match('=') || match('('))
                flag = 2;

            back_token();
            back_token();
            back_token();
        }
        back_token();
    }

    else 
        flag = 3;
    return flag;

}

int not_statements() {
    if (!token) 
        return 1;
    if (!match(ID) && !match(RETURN) && !match('{')) 
        return 1;
    return 0;
}

void errorPrint(char ch) {
    fprintf(yyout, "The character '%c' at line: %d does not begin any legal token in the language.\n", ch, line_number);
}

void illegalError(const char *text, const char *type) {
    fprintf(yyout, "Illegal %s '%s' was found at line %d\n", type, text, line_number);
}

parser.c

#pragma once

#include "LEXYY.h"

int parser_errors, recovery;
FILE *yyout;

void error_handle(char *, YYSTYPE *);
void print_token(char *, YYSTYPE *);

int parser();
void var_definitions();
void var_definition();
void variables_list();
void variable();
void statements();
void statement();
void function_call();
void parameters_list();
int expression();
void block();
void func_definitions();
void func_definition();
void param_definitions();

parser.c

#include <stdio.h>
#include "PARSER.h"

void error_handle(char *expected, YYSTYPE *token) {
    if (token) {
        fprintf(yyout, "Error: Expected token is { %s }, but actual token is [ %s ] at line %d\n", expected, token->lexeme, token->line);
        parser_errors++;
        recovery = 1;
    }
}

void print_token(char *str, YYSTYPE *token) {
    if (token) 
        fprintf(yyout, str, token->lexeme, token->line);
}

int parser() {
    parser_errors = 0;
    recovery = 0;
    token = tokens;

    if (!token) 
        return 0;

    fprintf(yyout, "Starting Program...\n");

    if (!match(PROGRAM)) {
        error_handle("program", token);
        recovery = 0;
    }
    else 
        print_token("[ %s ] at line %d\n", token);

    next_token();

    var_definitions();

    statements();

    if (!match(END)) {
        error_handle("end", token);
        recovery = 0;
    }
    else 
        print_token("[ %s ] at line %d\n", token);

    next_token();

    func_definitions();

    return parser_errors;
}

void var_definitions() {
    if (!type()) 
        if (not_var_definitions()) 
            return;

    fprintf(yyout, "Variable Definitions...\n");

    while (token) {
        if (!type()) {
            if (not_var_definitions()) {
                recovery = 0;
                break;
            }
        }
        var_definition();
        if (match(')'))
            break;
        if (!match(SEMICOLON))
            error_handle(";", token);
        next_token();
    }
}

void var_definition() {
    if (type()) 
        print_token("Variable Type [ %s ] at line %d\n", token);
    else if (!type()) 
        error_handle("integer, real", token);
    next_token();
    variables_list();
}

void variables_list() {
    int error;
    do {
        error = not_var_definitions();
        variable();
        if (error == 2) {
            error_handle(",", token);
            while (!match(COMMA) && !match(SEMICOLON)) next_token();
            recovery = 0;
        }
    } while (match(COMMA) && next_token());
}

void variable() {
    token->lexeme[token->count] = '\0';
    if (!match(ID)) {
        if (recovery) {
            while (!match(ID) && !match(COMMA) && !match(SEMICOLON)) next_token();
            recovery = 0;
            if (!match(ID)) return;
        }
        else error_handle("Variable Name(id)", token);
    }
    next_token();
    if (match('[')) {
        if (!recovery) {
            back_token();
            if (token) fprintf(yyout, "Array Variable %s\n", token->lexeme);
            next_token();
        }
        next_token();
        if (!match(INT_NUMBER)) {
            error_handle("int number", token);
        }
        else print_token("Array Size [%s] at line %d\n", token);
        next_token();
        if (!match(']') && token) {
            if (recovery) {
                while (!match(']')) next_token();
                recovery = 0;
            }
            else error_handle("]", token);
        }
        next_token();
    }
    else {
        if (!recovery) {
            back_token();
            print_token("Variable [ %s ] at line %d\n", token);
            next_token();
        }
    }
}

void statements() {
    if (not_statements()) return;

    fprintf(yyout, "Statement List...\n");

    while (!not_statements()) {
        statement();
        if (!match(SEMICOLON)) {
            if (recovery) {
                while (!match(SEMICOLON) && !match('{') && !match(END))
                    if (!next_token()) 
                        break;
                recovery = 0;
            }
            else 
                error_handle(";", token);
        }
        if (match(END)) 
            break;
        if (match('{')) 
            continue;
        next_token();
        if (match(SEMICOLON)) 
            next_token();
    }
}

void statement() {
    fprintf(yyout, "Statement...\n");

    if (match(ID)) {
        next_token();
        if (match('(')) {
            back_token();
            function_call();
        }
        else {
            back_token();
            fprintf(yyout, "Variable Assignment...\n");

            variable();

      if (!match('=')) {
        if (!match(SEMICOLON)) 
            error_handle("=", token);
        if (recovery) {
            while (!match('=') && !match(SEMICOLON) && !match('{') && !match(END)) 
                next_token();
                recovery = 0;
            if (match(SEMICOLON) || match('{') || match(END)) 
                return;
            print_token("Assign Operator [ %s ] at line %d\n", token);
        }
     } 
     else 
        print_token("Assign Operator [ %s ] at line %d\n", token);

            next_token();
            if (!expression()) {
                if (match(END) || match('{')) 
                    return;
                error_handle("int number, real number, id", token);
                next_token();
            }
        }
    }
    else if (match(RETURN)) {
        fprintf(yyout, "Return Statement...\n");
        next_token();
        if (match(SEMICOLON)) fprintf(yyout, "Empty Statement\n");
        else {
            if (!expression()) 
                error_handle("ID, int number, real number", token);
            expression();
        }
    }
    else if (match('{')) {
        block();
    }
}

void function_call() {
    fprintf(yyout, "Function Call...\n");
    print_token("Function Name [ %s ] at line %d\n", token);

    next_token(); 
    next_token();
    parameters_list();

    if (!match(')')) {
        error_handle(", or )", token);
        next_token();
        while (!match(')') && !match(SEMICOLON)) 
            next_token();
        back_token();
    }
    next_token();
}

void parameters_list() {
    fprintf(yyout, "Parameters List...\n");

    if (match(')')) {
        fprintf(yyout, "Empty Parameter\n");
    }
    else 
        variables_list();
}

int expression() {
    fprintf(yyout, "Expression...\n");

    if (recovery) {
        while (!match(INT_NUMBER) && !match(REAL_NUMBER) && !match(ID) && !match('{')) next_token();
        recovery = 0;
        if (match('{')) 
            return 0;
    }

    if (match(INT_NUMBER)) {
        print_token("int number [ %s ] at line %d\n", token);
        next_token();
    }
    else if (match(REAL_NUMBER)) {
        print_token("real number [ %s ] at line %d\n", token);
        next_token();
    }
    else if (match(ID)) {
        next_token();
        if (ar_op()) {
            back_token();
            print_token("ID [ %s ] at line %d\n", token);
            next_token();
            print_token("Arithmetic Operator [ %s ] at line %d\n", token);
            next_token();
            if (!expression()) {
                error_handle("int number, real number, id", token);
                next_token();
            }
        }
        else {
            back_token();
            variable();
        }
    }
    else 
        return 0;
    return 1;
}

void block() {
    fprintf(yyout, "Block Statement...\n");

    if (!match('{')) {
        if (match(END)) 
            return;
        error_handle("{", token);
    }
    next_token();

    var_definitions();

    back_token();
    if (!match(SEMICOLON) && !match('{')) {
        if (match(END)) 
            return;
        error_handle(";", token);
    }

    next_token();

    statements();

    if (!match('}')) {
        if (match(END)) 
            return;
        error_handle("}", token);
    }

    next_token();
}

void func_definitions() {
    fprintf(yyout, "Function Definitions List...\n");

    while (token) 
        func_definition();
}

void func_definition() {
    if (!returned_type()) {
        error_handle("integer, real, void", token);
        while (!returned_type() && !match(ID)) 
            next_token();
    }

    recovery = 0;

    fprintf(yyout, "Function Definition...\n");
    if (returned_type()) 
        print_token("Return Type [ %s ] at line %d\n", token);
    else 
        error_handle("integer, real, void", token);
    next_token();

    if (!match(ID)) 
        error_handle("id", token);
    else 
        print_token("Function Name [ %s ] at line %d\n", token);
    next_token();

    if (!match('(')) 
        error_handle("(", token);
    next_token();

    param_definitions();

    if (!match(')')) 
        error_handle(")", token);
    next_token();

    block();
    if (recovery) {
        while (!match('}')) next_token();
        next_token();
        recovery = 0;
    }
}

void param_definitions() {
    fprintf(yyout, "Parameter definitions List...\n");
    if (match(')')) 
        fprintf(yyout, "Empty Parameter\n");
    else 
        var_definitions();

semantic.h

#pragma once

#include "LEXYY.h"

typedef struct SCOPES {
    struct SCOPES *parent;
    struct SCOPES *first_child;     /*for right */
    struct SCOPES *next_sibling;    /*for left*/
    struct SYMBOLS *symbols;
} SCOPE;

typedef struct SYMBOLS {
    char *name;
    int type;
    int size;
    int initialized;
    int used;
    int line;
    struct SYMBOLS *next;
    struct SYMBOLS *prev;
} SYMBOL;

typedef struct FUNCTIONS {
    char *name;
    int returnd_type;
    int param_number;
    int line;
    struct SYMBOLS *params;
    struct SCOPES *scope;
    struct FUNCTIONS *prev_func;
    struct FUNCTIONS *next_func;
} FUNCTION;

SCOPE *root;

FUNCTION *func;

int semantic_errors, semantic_warnings;
FILE *yyout;

int return_statement;

FUNCTION *find_func(char *);
SYMBOL *find_symbol(SCOPE *, char *, SYMBOL *);

void semantic_error_handle(char *, char *, int);

int semantic();

FUNCTION *get_function_list();
FUNCTION *get_function();

SYMBOL *semantic_var_definitions(SCOPE *, SYMBOL *, int);
SYMBOL *semantic_var_definition(SCOPE *);
SYMBOL *semantic_vars_list(SCOPE *, int);
SYMBOL *semantic_var(SCOPE *, int);
SCOPE *semantic_statements(SCOPE *, int, SYMBOL *);
void semantic_statement(SCOPE *, int, SYMBOL *);
void semantic_function_call(SCOPE *, SYMBOL *);
int semantic_expression(SCOPE *, SYMBOL *);
SCOPE *semantic_block(SCOPE *, int, SYMBOL *);
void semantic_func_definitions();
void semantic_func_definition(FUNCTION *f);

void check_unused_in_scope(SCOPE *);
void check_unused_in_function(FUNCTION *);

semantic.c

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "semantic.h"

/*find function definition*/
FUNCTION *find_func(char *name) {
    FUNCTION *f = func;
    while (f && strcmp(f->name, name))
        f = f->prev_func;
    return f;
}

/*finding variable definition*/
SYMBOL *find_symbol(SCOPE *scope, char *name, SYMBOL *params) {
    SYMBOL *symbols;
    /*if function definition, searching in function parameters*/
    if (params) {
        symbols = params;
        while (symbols) {
            if (!strcmp(symbols->name, name)) 
                return symbols;
            symbols = symbols->next;
        }
    }
    if (!scope) 
        return NULL;
    /*searchinf in scope variables*/
    symbols = scope->symbols;
    while (symbols) {
        if (!strcmp(symbols->name, name)) 
            return symbols;
        symbols = symbols->next;
    }
    return find_symbol(scope->parent, name, NULL);
}

void semantic_error_handle(char *str, char *name, int line) {
    fprintf(yyout, str, name, line);
    semantic_errors++;
}

/*processing semantic analysis*/
int semantic() {
    semantic_errors = semantic_warnings = 0;
    token = tokens;
    if (!token) 
        return 0;

    //preload function definitions for function call
    while (!match(END)) 
        next_token();

    func = get_function_list();

    token = tokens;
    next_token();

    /*creating new scope for main program*/
    root = (SCOPE *)malloc(sizeof(SCOPE));
    root->parent = root->first_child = root->next_sibling = NULL;
    root->symbols = NULL;

    /*get variable definitions in current scope*/
    root->symbols = semantic_var_definitions(root, NULL, 1);

    /*analysing statements and get sub scope*/
    root->first_child = semantic_statements(root, VOID, NULL);

    next_token();

    check_unused_in_scope(root);

    semantic_func_definitions();

    if (!semantic_errors) 
        fprintf(yyout, "Semantic analysis success!\n");

    return semantic_errors;
}

/*getting function definition list before start analysis*/
FUNCTION *get_function_list() {
    FUNCTION *functions = NULL;
    next_token();
    while (token) {
        if (functions) {
            functions->next_func = get_function();
            functions->next_func->prev_func = functions;
            functions = functions->next_func;
        }
        else 
            functions = get_function();
    }
    return functions;
}

/*getting function definition before start of analysis*/
FUNCTION *get_function() {
    FUNCTION *f = (FUNCTION *)malloc(sizeof(FUNCTION));
    SYMBOL *temp;
    f->next_func = f->prev_func = NULL;
    f->scope = NULL;
    int block_number = 0;
    f->returnd_type = returned_type();
    next_token();
    f->name = token->lexeme;
    f->line = token->line;
    next_token();
    next_token();

    /*getting function parameters list and number*/
    f->params = semantic_var_definitions(NULL, NULL, 0);
    f->param_number = 0;
    temp = f->params;
    while (temp) {
        f->param_number++;
        temp = temp->next;
    }
    next_token();

    do {
        if (match('{')) 
            block_number++;
        if (match('}')) 
            block_number--;
        next_token();
    } while (block_number);

    return f;
}

/*get variables definitions*/
SYMBOL *semantic_var_definitions(SCOPE *scope, SYMBOL *params, int check) {
    SYMBOL *symbols = NULL, *temp = NULL, *temp1;

    /*creating symbol table for current scope*/
    while (token) {
        if (!type()) 
            break;
        if (symbols) {
            while (temp->next) temp = temp->next;
            temp->next = semantic_var_definition(scope);
            temp->next->prev = temp;
        }
        else {
            temp = symbols = semantic_var_definition(scope);
            while (temp->next) temp = temp->next;
        }
        if (match(')')) 
            break;
        next_token();
    }
    /*after getting variables, checking for duplicated definitions. if function definition, checking function parameters*/
    if (params) {
        temp = symbols;
        while (temp) {
            /*if varialble name is function name, then error*/
            if (find_func(temp->name)) {
                semantic_error_handle("Error: Cannot use function name [ %s ] with varaible at line [ %d ]\n", temp->name, temp->line);
                if (scope) {
                    if (temp->prev) {
                        temp->prev->next = temp->next;
                        if (temp->next) 
                            temp->next->prev = temp->prev;
                        temp1 = temp;
                        temp = temp->next;
                        free(temp1);
                    }
                    else {
                        symbols = temp->next;
                        if (symbols) 
                            symbols->prev = NULL;
                        free(temp);
                        temp = symbols;
                    }
                }
                continue;
            }
            temp1 = params;
            /*check in parameter definitions*/
            while (temp1) {
                if (!strcmp(temp->name, temp1->name)) 
                    break;
                temp1 = temp1->next;
            }
            /*if duplicated definition*/
            if (temp1) {
                semantic_error_handle("Error: Duplicated variable [ %s ] declaration with parameters at line %d\n", temp->name, temp->line);
                if (scope) {
                    if (temp->prev) {
                        temp->prev->next = temp->next;
                        if (temp->next) 
                            temp->next->prev = temp->prev;
                        temp1 = temp;
                        temp = temp->next;
                        free(temp1);
                    }
                    else {
                        symbols = temp->next;
                        if (symbols) 
                            symbols->prev = NULL;
                        free(temp);
                        temp = symbols;
                    }
                }
                else
                    temp = temp->next;
            }
            else 
                temp = temp->next;
        }
    }
    /*checking scope variables*/
    if (check) {
        temp = symbols;
        while (temp) {
            /*if varialble name is function name -  error*/
            if (find_func(temp->name)) {
                semantic_error_handle("Error: Cannot use function name [ %s ] with varaible at line [ %d ]\n", temp->name, temp->line);
                if (scope) {
                    if (temp->prev) {
                        temp->prev->next = temp->next;
                        if (temp->next) 
                            temp->next->prev = temp->prev;
                        temp1 = temp;
                        temp = temp->next;
                        free(temp1);
                    }
                    else {
                        symbols = temp->next;
                        if (symbols) 
                            symbols->prev = NULL;
                        free(temp);
                        temp = symbols;
                    }
                }
                continue;
            }
            if (!temp->size) 
                semantic_error_handle("Error: Array size cannot be zero at line %s%d\n", "", temp->line);
            temp1 = temp->prev;

            while (temp1) {
                if (!strcmp(temp->name, temp1->name)) 
                    break;
                temp1 = temp1->prev;
            }

            if (temp1) {
                semantic_error_handle("Error: Duplicated variable [ %s ] declaration within same scope at line %d\n", temp->name, temp->line);
                if (scope) {
                    if (temp->prev) {
                        temp->prev->next = temp->next;
                        if (temp->next) 
                            temp->next->prev = temp->prev;
                        temp1 = temp;
                        temp = temp->next;
                        free(temp1);
                    }
                    else {
                        symbols = temp->next;
                        if (symbols) 
                            symbols->prev = NULL;
                        free(temp);
                        temp = symbols;
                    }
                }
                else 
                    temp = temp->next;
            }
            else 
                temp = temp->next;
        }
    }
    return symbols;
}

/*getting variable definition*/
SYMBOL *semantic_var_definition(SCOPE *scope) {
    int var_type = type();
    next_token();

    return semantic_vars_list(scope, var_type);
}

/*getting variables list, each variable separated by comma, and insert into symbol table*/
SYMBOL *semantic_vars_list(SCOPE *scope, int var_type) {
    SYMBOL *symbols = NULL, *temp = NULL;

    do {
        if (symbols) {
            temp->next = semantic_var(scope, var_type);
            temp->next->prev = temp;
            temp = temp->next;
        }
        else 
            temp = symbols = semantic_var(scope, var_type);
    } while (match(COMMA) && next_token());

    return symbols;
}

/*getting variables separated by comma and make symbol item for symbol table*/
SYMBOL *semantic_var(SCOPE *scope, int var_type) {
    SYMBOL *symbol;
    if (!match(ID))
        return NULL;

    symbol = (SYMBOL *)malloc(sizeof(SYMBOL));
    symbol->type = var_type;
    symbol->name = token->lexeme;
    symbol->line = token->line;
    symbol->initialized = 0;
    symbol->used = 0;
    next_token();

    /*check if array varaible*/
    if (match('[')) {
        next_token();
        symbol->size = atoi(token->lexeme);
        next_token(); next_token();
    }
    else 
        symbol->size = -1;

    symbol->next = symbol->prev = NULL;

    return symbol;
}

/*analysis statements and get sub scopes*/
SCOPE *semantic_statements(SCOPE *parent, int return_type, SYMBOL *params) {
    SCOPE *scope = NULL, *temp = NULL;

    while (!not_statements()) {
        /* new scope for sub scope*/
        if (!scope) {
            scope = (SCOPE *)malloc(sizeof(SCOPE));
            scope->parent = parent;
            scope->first_child = scope->next_sibling = NULL;
            scope->symbols = NULL;
        }

        /*analysising each statementand get symbols for sub scope*/
        semantic_statement(scope, return_type, params);
        if (match(END)) 
            break;
        if (match('{')) 
            continue;
        next_token();
    }
    return scope;
}

/*analysinf each statement*/
void semantic_statement(SCOPE *scope, int return_type, SYMBOL *params) {
    SYMBOL *symbol, *cur_symbol;
    FUNCTION *f_temp = NULL;
    int type, line;

    /*if current statement is assignment or function call   */
    if (match(ID)) {
        next_token();
        /*if function call*/
        if (match('(')) {
            back_token();

            semantic_function_call(scope, params);
        }

        /*assignment*/
        else {
            back_token();
            /*getting variable for assignment*/
            symbol = semantic_var(scope, 0);
            /*check if current variable is declaration*/
            cur_symbol = find_symbol(scope, symbol->name, params);

            /*if current variable is declaration*/
            if (cur_symbol) {
                cur_symbol->initialized = 1;
                cur_symbol->used = 1;
                symbol->type = cur_symbol->type;

                /*if variable is arra*/
                if (cur_symbol->size > -1) {
                    /*check array size for array index  */
                    if (symbol->size > cur_symbol->size) 
                        semantic_error_handle("Error: Exceed bound of array [ %s ] at line %d\n", symbol->name, symbol->line);
                    else if (symbol->size <  0) 
                        semantic_error_handle("Error: Expressions can't refer to entire array [ %s ] at line %d\n", symbol->name, symbol->line);
                }

                else if (symbol->size > -1) 
                    semantic_error_handle("Error: Not array variable [ %s ] at line %d\n", symbol->name, symbol->line);
            }
            /*if current variable is not declared*/
            else {
                f_temp = find_func(symbol->name);
                /*if symbole is function*/
                if (f_temp) 
                    semantic_error_handle("Error: Cannot assign to function name [ %s ] at line %d\n", symbol->name, symbol->line);
                /*undefined variable*/
                else
                    semantic_error_handle("Error: Undefined variable [ %s ] at line %d\n", symbol->name, symbol->line);
            }

            next_token();
            type = semantic_expression(scope, params);
            /*if erro type, then error*/
            if (!type) 
                semantic_error_handle("Error: Cannot eval expression type [ %s ] at line %d\n", "error_type", symbol->line);
            /*if type mismatch,  then error */
            if (!f_temp && (!type || (symbol->type != type && !(symbol->type == REAL && type == INTEGER)))) 
                semantic_error_handle("Error: Cannot assign variable [ %s ] type mismatch at line %d\n", symbol->name, symbol->line);
        }
    }
    /*return statement*/
    else if (match(RETURN)) {
        next_token();
        line = token->line;
        if (match(SEMICOLON)) {
            if (return_type != VOID) 
                semantic_error_handle("Error: Return type mismatch %s at line %d\n", "", line);
        }
        /*if return type mismatch   */
        else {
            type = semantic_expression(scope, params);
            if (return_type == VOID || (return_type != type && return_type != REAL && type != INTEGER)) 
                semantic_error_handle("Error: Return type mismatch %s at line %d\n", "", line);
        }
        return_statement = 1;
    }
    /*block statement*/
    else if (match('{')) 
        semantic_block(scope, return_type, params);
}

/*analysis function call*/
void semantic_function_call(SCOPE *scope, SYMBOL *params) {
    /*check function definition*/
    FUNCTION *f = find_func(token->lexeme), *f_temp;
    SYMBOL *actual_params, *func_params = NULL, *cur_symbol;
    int param_number = 0, call_line = token->line;

    if (!f) 
        semantic_error_handle("Error: Undefined function [ %s ] at line %d\n", token->lexeme, token->line);
    next_token(); next_token();
    /*getting parameters list of function call*/
    actual_params = semantic_vars_list(NULL, 0);
    /*get parameters list of function definition*/
    if (f) 
        func_params = f->params;
    /*compare function call parameters with function definition parameters*/
    while (actual_params) {
        param_number++;
        /*checking parameter of funciton call in scopes*/
        cur_symbol = find_symbol(scope, actual_params->name, params);
        /*if found*/
        if (cur_symbol) {
            cur_symbol->used = 1;
            /*if variable is array*/
            if (cur_symbol->size > -1) {
                if (actual_params->size > cur_symbol->size) 
                    semantic_error_handle("Error: Exceed bound of array [ %s ] at line %d\n", actual_params->name, actual_params->line);
                else if (actual_params->size < -1) 
                    semantic_error_handle("Error: Expressions can't refer to entire array [ %s ] at line %d\n", actual_params->name, actual_params->line);
            }
            /*if variable is not array but it's as sucj, then error*/
            else if (actual_params->size > -1) 
                semantic_error_handle("Error: Not array variable [ %s ] at line %d\n", actual_params->name, actual_params->line);

            /*if variable type of function call doesn't mismatch with varaible type of function definition*/
            if (func_params && cur_symbol->type != func_params->type && !(cur_symbol->type == INTEGER && func_params->type == REAL)) 
                semantic_error_handle("Error: Variable type [ %s ] does not match with function parameters at line %d\n", cur_symbol->type == INTEGER ? "integer" : "real", actual_params->line);
        }
        /*if not found*/
        else {
            f_temp = find_func(actual_params->name);
            /*if symbole is function*/
            if (f_temp) 
                semantic_error_handle("Error: Cannot use function name as variable [ %s ] at line %d\n", actual_params->name, actual_params->line);
            /*undefined variable*/
            else 
                semantic_error_handle("Error: Undefined variable [ %s ] at line %d\n", actual_params->name, actual_params->line);
        }
        actual_params = actual_params->next;
        if (func_params) 
            func_params = func_params->next;
        if (actual_params) 
            free(actual_params->prev);
    }
    /*check function parameters number*/
    if (f && param_number > f->param_number) 
        semantic_error_handle("Error: Too much parameters for [ %s ] at line %d\n", f->name, call_line);
    else if (f && param_number < f->param_number) 
        semantic_error_handle("Error: Too few parameters for [ %s ] at line %d\n", f->name, call_line);
    next_token();
}

/*analysis expression and get its type*/
int semantic_expression(SCOPE *scope, SYMBOL *params) {
    SYMBOL *symbol, *cur_symbol;
    FUNCTION *f_temp;
    int type = 0;
    /*int number is integer*/

    if (match(INT_NUMBER)) {
        next_token();
        type = INTEGER;
    }
    /*real number is real*/
    else if (match(REAL_NUMBER)) {
        next_token();
        type = REAL;
    }
    else if (match(ID)) {
        symbol = semantic_var(scope, 0);
        cur_symbol = find_symbol(scope, symbol->name, params);
        /*if it is declared*/
        if (cur_symbol) {
            cur_symbol->used = 1;
            symbol->type = cur_symbol->type;
            type = symbol->type;
            /*if variable is array*/
            if (cur_symbol->size > -1) {
                /*checking array size*/
                if (symbol->size > cur_symbol->size) 
                    semantic_error_handle("Error: Exceed bound of array [ %s ] at line %d\n", symbol->name, symbol->line);
                else if (symbol->size < 0) 
                    semantic_error_handle("Error: Expressions can't refer to entire array [ %s ] at line %d\n", symbol->name, symbol->line);
            }
            /*if variable is not array but it's used with as such- then error*/
            else if (symbol->size > -1) 
                semantic_error_handle("Error: Not array variable [ %s ] at line %d\n", symbol->name, symbol->line);

        }
        /*if  declaration of variable is not found*/
        else {
            f_temp = find_func(symbol->name);
            if (f_temp) 
                semantic_error_handle("Error: Cannot use function name as variable [ %s ] at line %d\n", symbol->name, symbol->line);
            else 
                semantic_error_handle("Error: Undefined variable [ %s ] at line %d\n", symbol->name, symbol->line);
        }
        /*if arithmetic operation, geting next expression's type*/
        if (ar_op()) {
            next_token();
            type = semantic_expression(scope, params);
            if (type && (symbol->type == REAL || !symbol->type)) 
                type = symbol->type;
        }
    }
    return type;
}

/*analysing block and get sub scope */
SCOPE *semantic_block(SCOPE *parent, int return_type, SYMBOL *params) {
    /*createing new scope for sub scope*/
    SCOPE *scope = (SCOPE *)malloc(sizeof(SCOPE)), *temp = parent;
    scope->parent = parent;
    scope->first_child = scope->next_sibling = NULL;
    next_token();
    /*getting variables list in sub scope*/
    scope->symbols = semantic_var_definitions(scope, params, 1);
    /*analysing statements in sub scope*/
    scope->first_child = semantic_statements(scope, return_type, params);
    next_token();
    /*linking with parent scope*/
    if (temp) {
        if (temp->first_child) {
            while (temp->next_sibling->next_sibling) temp = temp->next_sibling;
            temp->next_sibling = scope;
        }
        else 
            temp->first_child = scope;
    }
    return scope;
}

/*analysing function definitions*/
void semantic_func_definitions() {
    FUNCTION *f;
    func = NULL;

    /*get function definitions list*/
    while (token) {
        f = (FUNCTION *)malloc(sizeof(FUNCTION));
        f->next_func = f->prev_func = NULL;
        if (func) {
            func->next_func = f;
            func->next_func->prev_func = func;
            func = func->next_func;
        }
        else 
            func = f;
        semantic_func_definition(f);
    }
}

/*analysing function definition and getting scopes for each function*/
void semantic_func_definition(FUNCTION *f) {
    FUNCTION *f_temp = func->prev_func;
    SYMBOL *temp;
    /*type of function*/
    f->returnd_type = returned_type();
    next_token();
    f->name = token->lexeme;
    f->line = token->line;
    /*checking if  definition deplicated*/
    while (f_temp) {
        if (!strcmp(f->name, f_temp->name))
            break;
        f_temp = f_temp->prev_func;
    }
    if (f_temp) 
        semantic_error_handle("Error: Function [ %s ] overloading at line %d\n", f->name, f->line);

    next_token(); 
    next_token();
    if (token->line == 100)
        token->line = token->line;
    f->params = semantic_var_definitions(NULL, NULL, 1);
    f->param_number = 0;
    temp = f->params;
    while (temp) {
        f->param_number++;
        temp = temp->next;
    }
    next_token();

    return_statement = 0;
    /*sub scope of this function*/
    f->scope = semantic_block(NULL, f->returnd_type, f->params);

    if (!return_statement && f->returnd_type != VOID)
        semantic_error_handle("Error: No return statement in function [ %s ]\n", f->name, 0);


    /*checking if any variable is not used in function*/
    check_unused_in_function(f);
}

/*check if any variables are not declared in scope*/
void check_unused_in_scope(SCOPE *root) {
    if (!root) 
        return;
    while (root->symbols) {
        if (!root->symbols->used) {
            semantic_error_handle("Error: Unused variable [ %s ] at line %d\n", root->symbols->name, root->symbols->line);
        }
        root->symbols = root->symbols->next;
    }
    if (root->first_child) 
        check_unused_in_scope(root->first_child);
    if (root->next_sibling) 
        check_unused_in_scope(root->next_sibling);
}

/*checking if any variables are not declared in function*/
void check_unused_in_function(FUNCTION *func) {
    if (func->params) {
        SYMBOL *symbol = func->params, *temp;
        while (symbol) {
            temp = symbol->prev;
            while (temp) {
                if (!strcmp(temp->name, symbol->name))
                    break;
                temp = temp->prev;
            }
            if (!temp && !symbol->used) 
                semantic_error_handle("Error: Unused variable [ %s ] at line %d\n", symbol->name, symbol->line);
            symbol = symbol->next;
        }
    }
    if (func->scope) 
        check_unused_in_scope(func->scope);
}
\$\endgroup\$
4
  • 2
    \$\begingroup\$ Welcome to Code Review! Was any of the code generated by tools such as Lex or Flex for the lexical analyzer or YACC or Bison for the parser? If so please indicte which parts. \$\endgroup\$ Commented May 19, 2020 at 13:15
  • 1
    \$\begingroup\$ @pacmaninbw flex \$\endgroup\$ Commented May 19, 2020 at 13:25
  • \$\begingroup\$ Does this program build properly for you? Are you using Visual Studio? \$\endgroup\$ Commented May 19, 2020 at 19:58
  • \$\begingroup\$ @pacmaninbw yes \$\endgroup\$ Commented May 19, 2020 at 20:30

0

You must log in to answer this question.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.