Skip to content
Snippets Groups Projects
symbol_table_parse.c 12.9 KiB
Newer Older
Clemens Paumgarten's avatar
Clemens Paumgarten committed

#include <stdio.h>
#include <stdlib.h>
#include <assert.h>
#include "mcc/ast.h"
#include "mcc/ast_print.h"
Clemens Paumgarten's avatar
Clemens Paumgarten committed
#include "mcc/symbol_table_parse.h"
#include "utils/unused.h"

// ------------------------------------------------------------ Variable

int mcc_symbol_table_add_variable_declaration(
        struct mcc_ast_declaration *declaration,
        struct mcc_symbol_table *symbol_table,
        struct mcc_symbol_table_error_collector *ec) {
    assert(declaration);
    assert(symbol_table);
    assert(ec);

    struct mcc_symbol *vs = mcc_symbol_new_symbol_variable(declaration->ident->i_value, declaration->type);

    // check if already declared
Clemens Paumgarten's avatar
Clemens Paumgarten committed
    if(mcc_symbol_table_get_symbol(symbol_table, vs->variable_name, false) == NULL) {
Clemens Paumgarten's avatar
Clemens Paumgarten committed
        mcc_symbol_table_insert_symbol(symbol_table, vs);
        return 0;
    } else {
        mcc_symbol_table_add_error(ec, mcc_symbol_table_new_error(&(declaration->node.sloc),
                                                                  MCC_SEMANTIC_ERROR_VARIABLE_ALREADY_DECLARED));
        return 1;
    }
}

// ------------------------------------------------------------ Array

// returns 1 if error
int mcc_symbol_table_check_array_declaration_size(
        struct mcc_ast_declaration *declaration,
        struct mcc_symbol_table_error_collector *ec) {
    if(declaration->arr_literal != NULL) {
        // check if size is int
        if(declaration->arr_literal->type != MCC_AST_DATA_TYPE_INT) {
            mcc_symbol_table_add_error(
                    ec,
                    mcc_symbol_table_new_error(&(declaration->node.sloc), MCC_SEMANTIC_ERROR_ARRAY_SIZE_DEFINITION));

            return 1;
        }
    }

    return 0;
}

int mcc_symbol_table_add_array_declaration(
        struct mcc_ast_declaration *declaration,
        struct mcc_symbol_table *symbol_table,
        struct mcc_symbol_table_error_collector *ec) {
    assert(declaration);
    assert(symbol_table);
    assert(ec);

    if(mcc_symbol_table_check_array_declaration_size(declaration, ec) == 1) {
        return 1;
    }

    struct mcc_symbol *vs = mcc_symbol_new_symbol_array(
            declaration->ident->i_value,
            declaration->type,
            declaration->arr_literal->i_value);

    vs->symbol_type = MCC_SYMBOL_TYPE_ARRAY;
    // check if already declared
Clemens Paumgarten's avatar
Clemens Paumgarten committed
    if(mcc_symbol_table_get_symbol(symbol_table, vs->variable_name, false) == NULL) {
Clemens Paumgarten's avatar
Clemens Paumgarten committed
        // add array declaration to symbol table
        mcc_symbol_table_insert_symbol(symbol_table, vs);

        return 0;
    } else {
        mcc_symbol_table_add_error(ec, mcc_symbol_table_new_error(&(declaration->node.sloc),
                                                                  MCC_SEMANTIC_ERROR_ARRAY_ALREADY_DECLARED));
        return 1;
    }
}

// ---------------------------------------------------------- Statement

int mcc_symbol_table_parse_compound_statement(
        struct mcc_ast_statement *statement,
        struct mcc_symbol_table *symbol_table,
        struct mcc_symbol_table_error_collector *ec
) {
    assert(statement);
    assert(statement->type == MCC_AST_STATEMENT_TYPE_COMPOUND);
    assert(symbol_table);
    assert(ec);

    struct mcc_ast_statement_list *stl = statement->statement_list;

    if(stl == NULL){
        return 0;
    }

    while(stl->statement != NULL ) {
        int create_new = 0;
        if (stl -> statement -> type == MCC_AST_STATEMENT_TYPE_COMPOUND) {
            create_new = 1;
        }
       
        int statement_result = mcc_symbol_table_parse_statement(
                stl->statement,
                symbol_table,
                ec,
                create_new
        );

        if(statement_result == 1) {
            return 1;
        }
        if (stl->next == NULL){
            break;
        } else{
            stl = stl->next;
        }
       
    } 

    return 0;
}

int mcc_symbol_table_parse_statement(
        struct mcc_ast_statement *statement,
        struct mcc_symbol_table *symbol_table,
        struct mcc_symbol_table_error_collector *ec,
        int create_new) {
    assert(statement);
    assert(symbol_table);
    assert(ec);

    struct mcc_symbol_table *table = symbol_table;

    if (create_new) {
        table = mcc_symbol_table_create_inner_table(symbol_table,"COMPOUND_STMT");
Clemens Paumgarten's avatar
Clemens Paumgarten committed
    }

    switch(statement->type) {
        case MCC_AST_STATEMENT_TYPE_RETURN:
        case MCC_AST_STATEMENT_TYPE_EXPRESSION:
Clemens Paumgarten's avatar
Clemens Paumgarten committed
            return mcc_symbol_table_validate_expression(statement->expression, table, ec);
Clemens Paumgarten's avatar
Clemens Paumgarten committed
        case MCC_AST_STATEMENT_TYPE_WHILE:
            if(mcc_symbol_table_validate_expression(
Clemens Paumgarten's avatar
Clemens Paumgarten committed
                    statement->while_condition, table, ec) == 0) {
Clemens Paumgarten's avatar
Clemens Paumgarten committed
                if (mcc_symbol_table_validate_condition_to_type_bool(
                        statement -> while_condition,
Clemens Paumgarten's avatar
Clemens Paumgarten committed
                        table,
Clemens Paumgarten's avatar
Clemens Paumgarten committed
                        ec
                ) == 0) {
                    return mcc_symbol_table_parse_statement(
                            statement->while_stmt, table, ec, 1);
                } else {
                    return 1;
                }
            } else {
                return 1;
            }
        case MCC_AST_STATEMENT_TYPE_IF:
Clemens Paumgarten's avatar
Clemens Paumgarten committed
            if(mcc_symbol_table_validate_expression(
Clemens Paumgarten's avatar
Clemens Paumgarten committed
                    statement->if_condition, table, ec) == 0) {
Clemens Paumgarten's avatar
Clemens Paumgarten committed
                if (mcc_symbol_table_validate_condition_to_type_bool(
                        statement -> if_condition,
Clemens Paumgarten's avatar
Clemens Paumgarten committed
                        table,
Clemens Paumgarten's avatar
Clemens Paumgarten committed
                        ec
                ) == 0) {
                    return mcc_symbol_table_parse_statement(
Clemens Paumgarten's avatar
Clemens Paumgarten committed
                            statement->if_stmt, table, ec, 1);
Clemens Paumgarten's avatar
Clemens Paumgarten committed
                } else {
                    return 1;
                }
            } else {
                return 1;
            }
        case MCC_AST_STATEMENT_TYPE_DECL:
            if (statement->declaration->arr_literal != NULL) {
                // array declaration
Clemens Paumgarten's avatar
Clemens Paumgarten committed
                return mcc_symbol_table_add_array_declaration(statement->declaration, table, ec);
Clemens Paumgarten's avatar
Clemens Paumgarten committed
            } else {
                // variable declaration
Clemens Paumgarten's avatar
Clemens Paumgarten committed
                return mcc_symbol_table_add_variable_declaration(statement->declaration, table, ec);
Clemens Paumgarten's avatar
Clemens Paumgarten committed
            }
        case MCC_AST_STATEMENT_TYPE_ASSGN:
Clemens Paumgarten's avatar
Clemens Paumgarten committed
            return mcc_symbol_table_validate_assignment_semantic(statement->assignment, table, ec);
        case MCC_AST_STATEMENT_TYPE_ASSGN_ARR:
Clemens Paumgarten's avatar
Clemens Paumgarten committed
            return mcc_symbol_table_validate_assignment_array(statement->assignment, table, ec);
Clemens Paumgarten's avatar
Clemens Paumgarten committed
        case MCC_AST_STATEMENT_TYPE_COMPOUND:
            return mcc_symbol_table_parse_compound_statement(
                    statement,
Clemens Paumgarten's avatar
Clemens Paumgarten committed
                    table,
Clemens Paumgarten's avatar
Clemens Paumgarten committed
                    ec
            );
        default:
            return 0;
    }
}

// ---------------------------------------------------------- Function

int mcc_symbol_table_parse_function(
        struct mcc_ast_function *func_def,
        struct mcc_symbol_table *symbol_table,
        struct mcc_symbol_table_error_collector *ec
) {
    assert(func_def);
    assert(symbol_table);
    assert(ec);

    // create new scope and parse function
    struct mcc_symbol_table *sub_table = mcc_symbol_table_create_inner_table(symbol_table,func_def->identifier->i_value);
    // add params to sub table
    if (func_def -> parameter != NULL && func_def -> parameter -> parameters -> size > 0) {
Clemens Paumgarten's avatar
Clemens Paumgarten committed
        struct mcc_ast_parameter *p = func_def -> parameter;

        for (int i = 0; i < p -> parameters -> size; i++) {
            struct mcc_ast_declaration *declaration = p -> parameters -> arr[i];
Clemens Paumgarten's avatar
Clemens Paumgarten committed

            // at this point symbol table is still empty -> adding won't result in an error
            if (declaration -> arr_literal != NULL) {
                // array declaration
                mcc_symbol_table_add_array_declaration(declaration, sub_table, ec);
            } else {
                // variable declaration
                mcc_symbol_table_add_variable_declaration(declaration, sub_table, ec);
            }
        }
    }

    int valid_function_body = mcc_symbol_table_parse_statement(func_def -> statement, sub_table, ec, 0);
    if (valid_function_body == 0) {
        // valid function body - check if return type of non-void function is correct
        if (func_def -> return_type != MCC_AST_DATA_TYPE_VOID) {
            valid_function_body = mcc_symbol_table_validate_statement_return(
                    func_def -> statement,
                    func_def -> return_type,
                    sub_table,
Clemens Paumgarten's avatar
Clemens Paumgarten committed
                    ec
            );
        }
    }

    return valid_function_body;
}

int mcc_symbol_table_add_function_declaration(
        struct mcc_ast_function *func_def,
        struct mcc_symbol_table *symbol_table,
        struct mcc_symbol_table_error_collector *ec) {
    assert(symbol_table);
    assert(ec);
    assert(func_def);

Clemens Paumgarten's avatar
Clemens Paumgarten committed
    struct mcc_symbol *fs = mcc_symbol_new_symbol_function(
            func_def->identifier->i_value,
            func_def->return_type,
            func_def->parameter);

    // check if already declared
Clemens Paumgarten's avatar
Clemens Paumgarten committed
    if(mcc_symbol_table_get_symbol(symbol_table, fs -> variable_name, true) == NULL) {
Clemens Paumgarten's avatar
Clemens Paumgarten committed
        mcc_symbol_table_insert_symbol(symbol_table, fs);
        return 0;
    } else {
        // already declared - create already declared error message
        mcc_symbol_table_add_error(ec, mcc_symbol_table_new_error(&(func_def->node.sloc),
                                                                  MCC_SEMANTIC_ERROR_FUNC_ALREADY_DECLARED));
        return 1;
    }
}

// ---------------------------------------------------------- Program

void mcc_add_builtin_function(
        struct mcc_symbol_table *symbol_table,
        char *variable_name,
        enum mcc_ast_data_type return_type,
        enum mcc_ast_data_type param_type) {
   struct mcc_symbol *symbol = malloc(sizeof(*symbol));

    symbol -> variable_name = variable_name;
    symbol -> data_type = return_type;
    symbol -> symbol_type = MCC_SYMBOL_TYPE_BUILTIN_FUNCTION;
    symbol -> func_arguments = mcc_create_dynamic_array(5);

    if (param_type != MCC_AST_DATA_TYPE_VOID) {
        struct mcc_symbol_function_argument *fp  = malloc(sizeof(*fp));

        fp -> arg_type = param_type;

        mcc_add_to_array(symbol -> func_arguments, fp);
    }

    symbol_table -> inner_tables -> arr[0] = NULL;

    mcc_symbol_table_insert_symbol(symbol_table,symbol);
}

void mcc_symbol_table_add_builtins(struct mcc_symbol_table *symbol_table) {
    mcc_add_builtin_function(symbol_table,"print_nl",MCC_AST_DATA_TYPE_VOID,MCC_AST_DATA_TYPE_VOID);
    mcc_add_builtin_function(symbol_table,"print",MCC_AST_DATA_TYPE_VOID,MCC_AST_DATA_TYPE_STRING);
    mcc_add_builtin_function(symbol_table,"print_int",MCC_AST_DATA_TYPE_VOID,MCC_AST_DATA_TYPE_INT);
    mcc_add_builtin_function(symbol_table,"print_float",MCC_AST_DATA_TYPE_VOID,MCC_AST_DATA_TYPE_FLOAT);
    mcc_add_builtin_function(symbol_table,"read_int",MCC_AST_DATA_TYPE_INT,MCC_AST_DATA_TYPE_VOID);
    mcc_add_builtin_function(symbol_table,"read_float",MCC_AST_DATA_TYPE_FLOAT,MCC_AST_DATA_TYPE_VOID);
}

int mcc_symbol_table_parse_program(
        struct mcc_ast_program *program,
        struct mcc_symbol_table *symbol_table,
        struct mcc_symbol_table_error_collector *ec) {
    assert(program);

    int function_parse = 0;
    for(int i = 0; i < program -> function_def -> size; i++) {
        function_parse = mcc_symbol_table_add_function_declaration(program -> function_def -> arr[i], symbol_table, ec);
Clemens Paumgarten's avatar
Clemens Paumgarten committed

        if (function_parse) break;
    }

    // if everything so far ok
    if (function_parse == 0) {

        // parse all functions
        for(int i = 0; i < program-> function_def -> size; i++) {
            function_parse = mcc_symbol_table_parse_function(program->function_def -> arr[i], symbol_table, ec);
Clemens Paumgarten's avatar
Clemens Paumgarten committed

            if (function_parse) break;
        }
    }

    return function_parse;
}

struct mcc_symbol_table *mcc_symbol_table_build_program(struct mcc_ast_program *program, struct mcc_symbol_table_error_collector *ec) {
    assert(program);

    struct mcc_symbol_table *st = mcc_symbol_table_new_table(NULL);
    st->sym_table_name = "functions";
Clemens Paumgarten's avatar
Clemens Paumgarten committed

    mcc_symbol_table_add_builtins(st);

    if (mcc_symbol_table_parse_program(program, st, ec) == 0) {
Clemens Paumgarten's avatar
Clemens Paumgarten committed
        // check if main exists
        if (mcc_symbol_table_validate_main(program, st, ec) == 0) {
            return st;
        } else {
            return NULL;
        }

    } else {
        return NULL;
    }
}

struct mcc_symbol_table *mcc_symbol_table_build_function(struct mcc_ast_function *function, struct mcc_symbol_table_error_collector *ec) {
    assert(function);

    struct mcc_symbol_table *st = mcc_symbol_table_new_table(NULL);
    st->sym_table_name = "functions";
Clemens Paumgarten's avatar
Clemens Paumgarten committed
    mcc_symbol_table_add_builtins(st);

    int parse_function = mcc_symbol_table_add_function_declaration(function, st, ec);
    if (parse_function == 0) {
        if (mcc_symbol_table_parse_function(function, st, ec) == 0) {
            return st;
        } else {
            // handle error collection
            return NULL;
        }
    } else {
        // handle error collection
        return NULL;
    }
}