| home | contents | previous | next page | send comment | send link | add bookmark |

PRS_STMT.CPP

statements

/*-------------------------------------------------------------------*
     Parsing routines for T statements.

     File:     prs_stmt.cpp

     Module:   parser

     by:  Stephen R. Schmitt
 *-------------------------------------------------------------------*/

#include "tpl_data.h"
#include <assert.h>
#include <stdlib.h>

/*
 *   External variables
 */
       /* in scn_main.cpp */
extern token_struct Token;
extern TOKEN_CODE Stmt_start_list[];
extern TOKEN_CODE Decl_start_list[];
extern TOKEN_CODE Stmt_end_list[];
extern TOKEN_CODE Data_decl_list[];
extern TOKEN_CODE Loop_end_list[];
extern TOKEN_CODE For_end_list[];
extern TOKEN_CODE If_end1_list[];
extern TOKEN_CODE If_end2_list[];
extern TOKEN_CODE Case_label_list[];
extern TOKEN_CODE Case_end_list[];
extern TOKEN_CODE Put_item_list[];
extern TOKEN_CODE Number_type_list[];
extern TOKEN_CODE String_type_list[];
       /* in sym_tabl.cpp */
extern int Scope;
extern SYMTAB_NODE_PTR Symbol_table[];
extern TYPE_STRUCT_PTR Integer_typep, Real_typep,
                       Boolean_typep, Char_typep,
                       String_typep,  Null_typep;
       /* in exc_util.cpp */
extern bool Routine_flag;
       /* in prs_decl.cpp */
extern SYMTAB_NODE_PTR Last_rtne_idp;
       /* in prs_rutn.cpp */
extern bool Result_flag;

/*
 *   Global declarations
 */
int Id_count = 0;
int Level = 0;
TOKEN_CODE Construct_Stack[NESTING_LEVELS];
int Continue_label[NESTING_LEVELS];
int Exit_label[NESTING_LEVELS];

/*-------------------------------------------------------------------*
                 Code Section
 *-------------------------------------------------------------------*/

/*
 *   "statement" calls the appropriate parsing procedure based on
 *   the statement's first token.
 *
 *   returns:  nothing
 */
void statement(SYMTAB_NODE_PTR rtn_idp)             // procedure or function
{
    SYMTAB_NODE_PTR idp;

    emit_statement_marker(Token.file, Token.line);

    switch (Token.code)
    {
    case ID_TOKEN:
        idp = search_symbol_tables(Token.lexeme);
        if (idp == NULL)
        {
            compile_error(M_UNDEFINED, M_IDENTIFIER, M_0);
            get_token();
            synchronize(Stmt_start_list, Data_decl_list, Stmt_end_list);
        }
        else if (idp->definition == PROC_DEFN)
        {
            get_token();                            // after <id>
            declared_routine_call(idp);
        }
        else if ((idp->definition == VAR_DEFN) ||
                 (idp->definition == VALPARM_DEFN) ||
                 (idp->definition == VARPARM_DEFN))
            assignment_statement(idp);
        else if (idp->definition == LABEL_DEFN)
            label_statement(idp);
        else
        {
            compile_error(M_INVALID, M_IDENTIFIER, M_USAGE, M_0);
            get_token();
            synchronize(Stmt_start_list, Data_decl_list, Stmt_end_list);
        }
        break;

    case PUTCH:
    case PUTSTR:
    case PUTLINE:
    case PUTPIXEL:
    case CURSOR:
    case LOCATE:
    case RANDOMIZE:
    case RANDSEED:
    case SCROLL:
    case SETVIDEO:
    case WATCH:
        standard_routine_call();
        break;

    case LOOP:
        loop_statement(rtn_idp);
        break;

    case FOR:
        for_statement(rtn_idp);
        break;

    case IF:
        if_statement(rtn_idp);
        break;

    case CASE:
        case_statement(rtn_idp);
        break;

    case ASSERT:
        assert_statement();
        break;

    case CONTINUE:
        continue_statement();
        break;

    case EXIT:
        exit_statement();
        break;

    case GOTO:
        goto_statement();
        break;

    case GET:
        get_statement();
        break;

    case PUT:
        put_statement();
        break;

    case RETURN:
        return_statement(rtn_idp);
        break;

    default:
        assert(0);
        break;
    }
}

/*
 *   "assignment_statement" parses:
 *
 *             <variable> := <expn>
 *
 *   returns:  nothing
 */
void assignment_statement(SYMTAB_NODE_PTR var_idp)  // target id
{
    TYPE_STRUCT_PTR var_tp, expr_tp;

    var_tp = target_variable(var_idp);              // and get next token

    if (Token.code == ASSIGN)
        get_token();                                // after ":="
    else                                            // don't process <expn>
    {
        compile_error(M_MISSING, M_ASSIGN, M_0);
        synchronize(Stmt_start_list, Data_decl_list, Stmt_end_list);
        return;
    }

    if (var_tp == Boolean_typep)
        expr_tp = boolean_expression();             // and get next token
    else if (var_tp->form == STRING_FORM)
        expr_tp = string_expression();              // and get next token
    else
        expr_tp = expression();                     // and get next token

    if (assign_type_compatible(var_tp, expr_tp))
    {
        if (var_idp->definition == VARPARM_DEFN)
            emit_var_item(POP, var_idp, var_tp);
        else
        {
            emit_mem_item(POP, var_idp, var_tp);
            var_idp->initialized = true;
        }
    }
    else
        compile_error(M_INCOMPATIBLE, M_ASSIGNMENT, M_0);
}

/*
 *   "target_variable" processes a variable to be assigned a value
 *
 *   returns:  a pointer to the type structure of the variable
 */
TYPE_STRUCT_PTR target_variable(SYMTAB_NODE_PTR idp)
{
    TYPE_STRUCT_PTR tp = idp->typep;

    get_token();                                    // after <id>

    // subscripts and/or field designators?
    if ((Token.code == L_PAREN)||(Token.code == PERIOD))
        emit_push_imm_index(0);

    if ((Token.code == L_PAREN)&&(tp->form == STRING_FORM))
        tp = target_string_char(tp);
    else while ((Token.code == L_PAREN)||(Token.code == PERIOD))
    {
        if ((Token.code == L_PAREN)&&(tp->form == ARRAY_FORM))
            tp = target_array_element(tp);
        else if ((Token.code == PERIOD)&&
                 ((tp->form == RECORD_FORM)||(tp->form == UNION_FORM)))
            tp = target_structure_field(tp);
        else
        {
            compile_error(M_INVALID, M_IDENTIFIER, M_USAGE, M_0);
            while ((Token.code != R_PAREN     ) &&
                   (!token_in(Decl_start_list)) &&
                   (!token_in(Stmt_start_list)))
                   get_token();                     // until synchronized

            tp = Real_typep;                        // avoid run time error
        }
    }

    return tp;
}

/*
 *   "target_string_char" processes a subscript following
 *   a string identifier.  The next "Token" is obtained.
 *
 *   returns:  a pointer to the type structure of the string element
 */
TYPE_STRUCT_PTR target_string_char(TYPE_STRUCT_PTR tp)
{
    get_token();                                    // after "("
    get_integer_expression();

    emit_op_reg_imm( MOV_ax_imm, tp->array.max_index );
    emit_op_reg_imm( MOV_bx_imm, tp->array.val_index );
    emit_operator( ADD_xi_offset, NO_CODE, 0, 0);

    if_code_get_token_else_error(R_PAREN);

    return tp->array.elmt_typep;
}

/*
 *   "target_array_element" processes a list of subscripts following
 *   an array identifier.  The next "Token" is obtained.
 *
 *   returns:  a pointer to the type structure of the array element
 */
TYPE_STRUCT_PTR target_array_element(TYPE_STRUCT_PTR tp)
{
    TYPE_STRUCT_PTR elmt_tp = tp->array.elmt_typep;

    do                                              // process a subscript list
    {
        if (tp->form == ARRAY_FORM)                 // if an indexed array
        {
            get_token();                            // after "(" or ","
            get_integer_expression();

            emit_op_reg_imm( MOV_ax_imm, tp->array.max_index );
            emit_op_reg_imm( MOV_bx_imm, tp->array.val_index );
            emit_operator( ADD_xi_offset, NO_CODE, 0, 0);
        }
        else
        {
            compile_error(M_TOO, M_MANY, M_SUBSCRIPTS, M_0);
            while ((Token.code != R_PAREN) &&
                  !token_in(Data_decl_list) &&
                  !token_in(Stmt_start_list))
                get_token();                        // until synchronized
        }

        // if the next subscript is valid,
        // tp->form will be set to ARRAY_FORM
        tp = tp->array.next_indexp;                 // for next dimension
    }
    while (Token.code == COMMA);

    if_code_get_token_else_error(R_PAREN);

    return elmt_tp;
}

/*
 *   "target_structure_field" processes a field designation following
 *   a structure identifier
 *
 *   returns:  a pointer to the type structure of the right id
 */
TYPE_STRUCT_PTR target_structure_field(TYPE_STRUCT_PTR tp)
{
    SYMTAB_NODE_PTR field_idp;
    TYPE_STRUCT_PTR field_tp;

    get_token();                                    // after "."

    if (((tp->form == RECORD_FORM)||(tp->form == UNION_FORM ))&&
        (Token.code == ID_TOKEN))
    {
        if (tp->form == RECORD_FORM)
        {
            field_idp = lookup(Token.lexeme, tp->record_struct.field_symtab);
        }
        else
        {
            field_idp = lookup(Token.lexeme, tp->union_struct.field_symtab);
        }

        if (field_idp == NULL)
        {
            compile_error(M_INVALID, M_FIELD, M_0);
            field_tp = Null_typep;
        }
        else
            field_tp = field_idp->typep;

        emit_operator(ADD_xi_imm, NO_CODE, 0, field_idp->data_offset);

        get_token();                                // after <id>
    }
    else
    {
        compile_error(M_INVALID, M_FIELD, M_0);
        field_tp = Null_typep;

        get_token();                                // after <id>
    }

    return field_tp;
}

/*
 *   "loop_statement" parses:
 *
 *             loop
 *                 <declarationsAndStatements>
 *             end loop
 *
 *   returns:       nothing
 */
void loop_statement(SYMTAB_NODE_PTR rtn_idp)        // subroutine id
{
    get_token();                                    // after "loop"

    int begin = next_code_label();
    int end = next_code_label();

    emit_code_label(begin);                         // jump here to continue
    incr_level(LOOP, begin, end);
    incr_scope();

    parse_block(rtn_idp, Data_decl_list, Stmt_start_list, Loop_end_list);

    emit_operator(JMP, NO_CODE, 0, begin);
    decr_level();
    decr_scope();

    if_code_get_token_else_error(END);

    if (Token.code == LOOP)
    {
        emit_code_label(end);                       // jump here to exit
        emit_statement_marker(Token.file, Token.line);
        get_token();                                // after "loop"
    }
    else
        compile_error(M_MISSING, M_LOOP, M_0);
}

/*
 *   "for_statement" parses:
 *
 *             for [decreasing] <id> := <expn> ... <expn> do
 *             <declarations and statements>
 *             end for
 *
 *   returns:  nothing
 */
void for_statement(SYMTAB_NODE_PTR rtn_idp)         // subroutine id
{
    int begin,                                      // loop top
        again,                                      // continue
        end;                                        // exit
    char buffer[8];
    SYMTAB_NODE_PTR count_idp, end_idp;
    TYPE_STRUCT_PTR count_tp, end_tp;
    bool count_ok = false;                          // default
    bool increasing = true;                         // default

    get_token();                                    // after "for"

    if (Token.code == DECREASING)
    {
        increasing = false;
        get_token();                                // after "decreasing"
    }

    // process count parameter
    if (Token.code == ID_TOKEN)
    {
        count_idp = search_symbol_tables(Token.lexeme);
        if (count_idp == NULL)
            compile_error(M_UNDEFINED, M_IDENTIFIER, M_0);
        else if ((count_idp->definition == VAR_DEFN    )||
                 (count_idp->definition == VALPARM_DEFN)||
                 (count_idp->definition == VARPARM_DEFN))
        {
            count_idp->initialized = true;
            count_tp = count_idp->typep;

            if (count_tp != Integer_typep)
                compile_error(M_INVALID, M_TYPE, M_0);
            else
                count_ok = true;
        }
        else
            compile_error(M_INVALID, M_IDENTIFIER, M_USAGE, M_0);

        get_token();                                // after <id>
    }
    else
        compile_error(M_MISSING, M_IDENTIFIER, M_0);

    // create internal idp for end value
    Id_count++;
    itoa(Id_count, buffer, 10);
    end_idp = insert(buffer, &Symbol_table[Scope]);
    end_idp->definition = CONST_DEFN;
    end_idp->typep = Integer_typep;
    end_tp = end_idp->typep;

    Last_rtne_idp->next = end_idp;
    end_idp->prev = Last_rtne_idp;
    Last_rtne_idp = end_idp;

    if (Token.code == ASSIGN)
        get_token();                                // after ":="
    else
    {
        compile_error(M_MISSING, M_ASSIGN, M_0);
        get_token();
    }

    // process count limits
    get_integer_expression();                       // beginning value

    if (count_ok)
    {
        if (count_idp->definition == VARPARM_DEFN)
            emit_var_item(POP, count_idp, count_tp);
        else
            emit_mem_item(POP, count_idp, count_tp);
    }

    if_code_get_token_else_error(ELLIPSES);

    get_integer_expression();                       // ending value

    emit_mem_item(POP, end_idp, end_tp);

    if_code_get_token_else_error(DO);

    begin = next_code_label();
    again = next_code_label();
    end = next_code_label();
    emit_code_label(begin);                         // jump here to loop

    // emit code to check for completion of loop
    if (count_ok)
    {
        if (count_idp->definition == VARPARM_DEFN)
            emit_var_item(PUSH, count_idp, count_tp);
        else
            emit_mem_item(PUSH, count_idp, count_tp);
    }

    emit_mem_item(PUSH, end_idp, end_tp);

    if (increasing)
        emit_operator(TEST_NUMBER, TEST_GT, 0, 0);
    else
        emit_operator(TEST_NUMBER, TEST_LT, 0, 0);

    emit_operator(JNZ, NO_CODE, 0, end);

    // emit code for block of statements
    incr_level(FOR, again, end);
    incr_scope();

    parse_block(rtn_idp, Data_decl_list, Stmt_start_list, For_end_list);

    emit_code_label(again);                         // jump here to continue

    // increment or decrement counter
    if (count_ok)
    {
        if (count_idp->definition == VARPARM_DEFN)
            emit_var_item(PUSH, count_idp, count_tp);
        else
            emit_mem_item(PUSH, count_idp, count_tp);
    }

    if (increasing)
        emit_push_imm_integer(+1L);                 // increment
    else
        emit_push_imm_integer(-1L);                 // decrement

    emit_operator(IADD, NO_CODE, 0, 0);

    if (count_ok)
    {
        if (count_idp->definition == VARPARM_DEFN)
            emit_var_item(POP, count_idp, count_tp);
        else
            emit_mem_item(POP, count_idp, count_tp);
    }

    emit_operator(JMP, NO_CODE, 0, begin);

    decr_level();
    decr_scope();

    if_code_get_token_else_error(END);

    if (Token.code == FOR)
    {
        emit_code_label(end);                       // jump here to exit
        emit_statement_marker(Token.file, Token.line);
        get_token();                                // after "for"
    }
    else
        compile_error(M_MISSING, M_FOR, M_0);
}

/*
 *   "if_statement" parses:
 *
 *             if <booleanExpn> then
 *             <declarationsAndStatements>
 *           { elsif <booleanExpn> then
 *             <declarationsAndStatements> }
 *           [else
 *             <declarationsAndStatements>]
 *             end if
 *
 *   returns:       nothing
 */
void if_statement(SYMTAB_NODE_PTR rtn_idp)          // subroutine id
{
    get_token();                                    // after "if"

    int if_exit = next_code_label();
    int if_false = next_code_label();

    boolean_expression();                           // and get next token
    emit_operator(JZ, NO_CODE, 0, if_false);

    if_code_get_token_else_error(THEN);

    incr_level(IF, 0, 0);
    incr_scope();

    parse_block(rtn_idp, Data_decl_list, Stmt_start_list, If_end1_list);

    decr_level();
    decr_scope();

    emit_operator(JMP, NO_CODE, 0, if_exit);
    emit_code_label(if_false);                      // jump here if false

    while (Token.code == ELSIF)
    {
        emit_statement_marker(Token.file, Token.line);
        get_token();                                // after "elsif"

        if_false = next_code_label();

        boolean_expression();                       // and get next token
        emit_operator(JZ, NO_CODE, 0, if_false);

        if_code_get_token_else_error(THEN);

        incr_level(ELSIF, 0, 0);
        incr_scope();

        parse_block(rtn_idp, Data_decl_list, Stmt_start_list, If_end1_list);

        decr_level();
        decr_scope();

        emit_operator(JMP, NO_CODE, 0, if_exit);
        emit_code_label(if_false);                  // jump here if false
    }

    if (Token.code == ELSE)
    {
        emit_statement_marker(Token.file, Token.line);
        get_token();                                // after "else"

        incr_level(ELSE, 0, 0);
        incr_scope();

        parse_block(rtn_idp, Data_decl_list, Stmt_start_list, If_end2_list);

        decr_level();
        decr_scope();
    }

    if_code_get_token_else_error(END);

    if (Token.code == IF)
    {
        emit_code_label(if_exit);                   // jump here when done
        emit_statement_marker(Token.file, Token.line);
        get_token();                                // after "if"
    }
    else
        compile_error(M_MISSING, M_IF, M_0);
}

/*
 *   "case_statement" parses:
 *
 *           case <expn> of
 *             value <const> {, <const> } :
 *        <declarations and statements>
 *           { value <const> {, <const> } :
 *        <declarations and statements> }
 *           [value :
 *             <declarations and statements>]
 *           end case
 *
 *   returns:  nothing
 */
void case_statement(SYMTAB_NODE_PTR rtn_idp)        // subroutine id
{
    SYMTAB_NODE_PTR expr_idp, idp;
    TYPE_STRUCT_PTR expr_tp, label_tp;
    bool another_branch, another_label, expr_ok;
    int case_exit, case_true, case_false;
    char buffer[8];

    get_token();                                    // after "case"

    if (Token.code == ID_TOKEN)                     // get expression's type
    {
        idp = search_symbol_tables(Token.lexeme);
        if (idp == NULL)
        {
            compile_error(M_UNDEFINED, M_IDENTIFIER, M_0);
            expr_tp = Integer_typep;                // default
            get_token();                            // after <id>
        }
        else
        {
            if (idp->typep->form == STRING_FORM)
                expr_tp = string_expression();      // and get next token
            else
                expr_tp = expression();             // and get next token
        }
    }
    else if (Token.code == STR_TOKEN)
        expr_tp = string_expression();              // and get next token
    else
        expr_tp = expression();                     // and get next token

    // create internal idp for case expression's value
    Id_count++;
    itoa(Id_count, buffer, 10);
    expr_idp = insert(buffer, &Symbol_table[Scope]);
    expr_idp->definition = CONST_DEFN;
    expr_idp->typep = expr_tp;

    Last_rtne_idp->next = expr_idp;
    expr_idp->prev = Last_rtne_idp;
    Last_rtne_idp = expr_idp;

    if ((expr_tp       == Integer_typep)||
        (expr_tp       == Char_typep   )||
        (expr_tp->form == STRING_FORM  )||
        (expr_tp->form == ENUM_FORM    ))
    {
        expr_ok = true;
        emit_mem_item(POP, expr_idp, expr_tp);
    }
    else
    {
        expr_ok = false;
        compile_error(M_INVALID, M_TYPE, M_0);
    }

    if_code_get_token_else_error(OF);

    another_branch = true;
    case_exit = next_code_label();

    while ((Token.code == VALUE) && another_branch)
    {
        get_token();                                // after label

        case_true = next_code_label();
        case_false = next_code_label();

        if (token_in(Case_label_list))              // valid label
        {
            // get label value(s) for a branch
            another_label = true;

            while (another_label)
            {
                label_tp = case_label();            // and get next token

                if (expr_tp != label_tp)
                    compile_error(M_INCOMPATIBLE, M_TYPES, M_0);
                else if (expr_ok)
                    emit_mem_item(PUSH, expr_idp, expr_tp);

                if (expr_tp == Integer_typep)
                    emit_operator(TEST_NUMBER, TEST_EQ, 0, 0);
                else if (expr_tp == Char_typep)
                    emit_operator(TEST_CHAR, TEST_EQ, 0, 0);
                else if (expr_tp->form == STRING_FORM)
                    emit_operator(TEST_STRING, TEST_EQ, 0, 0);
                else if (expr_tp->form == ENUM_FORM)
                    emit_operator(TEST_INDEX, TEST_EQ, 0, 0);
                else
                    assert(0);

                emit_operator(JNZ, NO_CODE, 0, case_true);

                if (Token.code == COMMA)
                {
                    get_token();                    // after ","

                    if (token_in(Case_label_list))
                        another_label = true;
                    else
                    {
                        compile_error(M_MISSING, M_CASE, M_VALUE, M_0);
                        another_label = false;
                    }
                }
                else
                    another_label = false;
            }
            another_branch = true;
        }
        else
        {
            another_branch = false;
            emit_operator(JMP, NO_CODE, 0, case_true);
        }

        if_code_get_token_else_error(COLON);

        if (another_branch)
            emit_operator(JMP, NO_CODE, 0, case_false);

        emit_code_label(case_true);

        incr_level(CASE, 0, 0);
        incr_scope();

        parse_block(rtn_idp, Data_decl_list, Stmt_start_list, Case_end_list);

        decr_level();
        decr_scope();

        emit_operator(JMP, NO_CODE, 0, case_exit);

        if (another_branch)
            emit_code_label(case_false);
    }

    if_code_get_token_else_error(END);

    if (Token.code == CASE)
    {
        emit_code_label(case_exit);                 // jump here when done
        emit_statement_marker(Token.file, Token.line);
        get_token();                                // after "case"
    }
    else
        compile_error(M_MISSING, M_CASE, M_0);
}

/*
 *   "case_label" processes a case label value
 *
 *   returns:  a pointer to the type structure of the case label
 */
TYPE_STRUCT_PTR case_label()
{
    bool saw_sign = false;                          // default
    TYPE_STRUCT_PTR label_tp = Integer_typep;       // default
    SYMTAB_NODE_PTR label_idp;
    TOKEN_CODE code;

    if ((Token.code == PLUS)||(Token.code == MINUS))
    {
        code = Token.code;
        saw_sign = true;
        get_token();                                // after sign
    }

    if (Token.code == INT_TOKEN)
    {
        label_tp = Integer_typep;
        emit_push_imm_integer(Token.integer);

        if (saw_sign && (code == MINUS))
            emit_operator(RNEG, NO_CODE, 0, 0);

        get_token();                                // after <int>
    }
    else if (Token.code == STR_TOKEN)
    {
        label_tp = String_typep;
        emit_push_imm_string(Token.lexeme);

        if (saw_sign)
            compile_error(M_INVALID, M_CASE, M_VALUE, M_0);

        get_token();                                // after <str>
    }
    else if (Token.code == CHAR_TOKEN)
    {
        label_tp = Char_typep;
        emit_push_imm_char(Token.lexeme[0]);

        if (saw_sign)
            compile_error(M_INVALID, M_CASE, M_VALUE, M_0);

        get_token();                                // after <str>
    }
    else if (Token.code == ID_TOKEN)
    {
        label_idp = search_symbol_tables(Token.lexeme);
        if (label_idp == NULL)
        {
            compile_error(M_UNDEFINED, M_IDENTIFIER, M_0);
            get_token();                            // after <id>
        }
        else if ((label_idp->typep      == Integer_typep)&&
                 (label_idp->definition == CONST_DEFN   ))
        {
            label_tp = label_idp->typep;
            emit_mem_item(PUSH, label_idp, label_tp);

            if (saw_sign && (code == MINUS))
                emit_operator(INEG, NO_CODE, 0, 0);

            get_token();                            // after <id>
        }
        else if ((label_idp->typep       == Char_typep)&&
                 (label_idp->definition  == CONST_DEFN))
        {
            if (saw_sign)
                compile_error(M_INVALID, M_CASE, M_VALUE, M_0);

            label_tp = label_idp->typep;
            emit_mem_item(PUSH, label_idp, label_tp);

            get_token();                            // after <id>
        }
        else if ((label_idp->typep->form == STRING_FORM)&&
                 (label_idp->definition  == CONST_DEFN ))
        {
            if (saw_sign)
                compile_error(M_INVALID, M_CASE, M_VALUE, M_0);

            label_tp = label_idp->typep;
            emit_mem_item(PUSH, label_idp, label_tp);

            get_token();                            // after <id>
        }
        else if ((label_idp->typep->form == ENUM_FORM)&&
                 (label_idp->definition  == TYPE_DEFN))
        {
            if (saw_sign)
                compile_error(M_INVALID, M_CASE, M_VALUE, M_0);

            get_token();                            // after <id>
    
            // push index value of enumeration field

            if (Token.code == PERIOD)
                label_tp = dot_field(label_idp->typep);
            else
                compile_error(M_INVALID, M_CASE, M_VALUE, M_0);
        }
        else
        {
            compile_error(M_INVALID, M_CASE, M_VALUE, M_0);
            while ((Token.code != COLON      )&&
                   (Token.code != COMMA      )&&
                   (Token.code != END        )&&
                   (Token.code != END_OF_FILE))
                get_token();
        }
    }
    else
    {
        compile_error(M_INVALID, M_CASE, M_VALUE, M_0);
        while ((Token.code != COLON      )&&
               (Token.code != COMMA      )&&
               (Token.code != END        )&&
               (Token.code != END_OF_FILE))
            get_token();
    }

    return label_tp;
}

/*
 *   "assert_statement" parses:
 *
 *             assert <boolean expn>
 *
 *   whichs aborts the program if the expression is false
 *
 *   returns:  nothing
 */
void assert_statement()
{
    int line = Token.line;
    get_token();                                    // after "assert"
    boolean_expression();                           // and get next token
    emit_operator(EXITZ, NO_CODE, 0, line);
}

/*
 *   "continue_statement" parses:
 *
 *             continue [when <boolean expn>]
 *
 *   returns:  nothing
 */
void continue_statement()
{
    int i;

    if (!in_for_or_loop_construct())
        compile_error(M_CONTEXT, M_ERROR, M_0);
    else
    {
        for (i = Level; i >= 0; i--)
        {
            if ((Construct_Stack[i] == FOR)||(Construct_Stack[i] == LOOP))
                break;
        }
    }

    get_token();                                    // after "continue"

    if (Token.code == WHEN)
    {
        get_token();                                // after "when"
        boolean_expression();                       // and get next token
        emit_operator(JNZ, NO_CODE, 0, Continue_label[i]);
    }
    else
        emit_operator(JMP, NO_CODE, 0, Continue_label[i]);
}

/*
 *   "exit_statement" parses:
 *
 *             exit [when <boolean expn>]
 *
 *   returns:  nothing
 */
void exit_statement()
{
    int i;

    if (!in_for_or_loop_construct())
        compile_error(M_CONTEXT, M_ERROR, M_0);
    else
    {
        for (i = Level; i >= 0; i--)
        {
            if ((Construct_Stack[i] == FOR)||(Construct_Stack[i] == LOOP))
                break;
        }
    }

    get_token();                                    // after "exit"

    if (Token.code == WHEN)
    {
        get_token();                                // after "when"
        boolean_expression();                       // and get next token
        emit_operator(JNZ, NO_CODE, 0, Exit_label[i]);
    }
    else
        emit_operator(JMP, NO_CODE, 0, Exit_label[i]);
}

/*
 *   "goto_statement" parses:
 *
 *             goto <label> 
 *
 *   returns:  nothing
 */
void goto_statement()
{
    SYMTAB_NODE_PTR idp;

    get_token();                                    // after "goto"

    if (Token.code == ID_TOKEN)
    {
        idp = search_symbol_tables(Token.lexeme);

        if (idp == NULL)
            compile_error(M_UNDEFINED, M_IDENTIFIER, M_0);
        else if (idp->definition != LABEL_DEFN)
            compile_error(M_INVALID, M_IDENTIFIER, M_USAGE, M_0);
        else
            emit_operator(JMP, NO_CODE, 0, idp->integer);

        get_token();                                // after <label>
    }
    else
        compile_error(M_MISSING, M_IDENTIFIER, M_0);
}

/*
 *   "label_statement" parses:
 *
 *             <label> :
 *
 *   returns:  nothing
 */
void label_statement(SYMTAB_NODE_PTR idp)
{
    if (idp->initialized == true)
        compile_error(M_INVALID, M_LABEL, M_USAGE, M_0);
    else
    {
        idp->initialized = true;
        emit_code_label(idp->integer);              // goto here
    }

    get_token();                                    // after <label>
   
    if_code_get_token_else_error(COLON);
}

/*
 *   "get_statement" parses:
 *
 *             get [:<streamNumber>,] <getItem> {, <getItem> }
 *
 *   in which <getItem> is one of:
 *
 *   a. <varRef>          - either string, integer, or real token
 *   b. <varRef>:*        - input a line as a string
 *   c. <varRef>:<width>  - input <width> characters as a string
 *
 *   returns:  nothing
 */
void get_statement()
{
    bool flush_stdin;                               // flush stream if true
    bool item_ok;
    TYPE_STRUCT_PTR item_tp;
    SYMTAB_NODE_PTR item_idp;

    get_token();                                    // after "get"

    if (Token.code == COLON)                        // get <streamNumber>
    {
        get_token();                                // after ":"
        get_integer_expression();

        emit_operator(SET_file_ptr, NO_CODE, 0, 0);
        flush_stdin = false;

        if_code_get_token_else_error(COMMA);
    }
    else
    {
        emit_operator(SET_stdin, NO_CODE, 0, 0);
        flush_stdin = true;
    }

    while (Token.code == ID_TOKEN)
    {
        item_idp = search_symbol_tables(Token.lexeme);
        if (item_idp == NULL)
        {
            compile_error(M_UNDEFINED, M_IDENTIFIER, M_0);
            item_ok = false;
            get_token();                            // next in file
        }
        else
        {
            item_ok = true;
            item_tp = target_variable(item_idp);    // and get token

            if ((item_tp != Integer_typep) &&
                (item_tp != Real_typep) &&
                (item_tp->form != STRING_FORM))
                compile_error(M_INVALID, M_TYPE, M_0);

            if ((item_idp->definition != VAR_DEFN) &&
                (item_idp->definition != VARPARM_DEFN) &&
                (item_idp->definition != VALPARM_DEFN))
                compile_error(M_NOT, M_VARIABLE, M_IDENTIFIER, M_0);
        }

        if (Token.code == COLON)                    // get <width>
        {
            if (item_tp->form != STRING_FORM)
                compile_error(M_INVALID, M_TYPE, M_0);

            get_token();                            // after ":"

            if (Token.code == STAR)
            {
                emit_push_imm_real(MAX_STRING_LENGTH);
                emit_operator(GET_str, NO_CODE, 0, 0);
                get_token();                        // after "*"
            }
            else
            {
                get_integer_expression();
                emit_operator(GET_str, NO_CODE, 0, 0);
            }
        }
        else
            emit_operator(GET_item, NO_CODE, 0, 0);

        if (item_ok)
        {
            if (item_tp == Integer_typep)
                emit_operator(CALL_stnd, SF_STRINT, 0, 0);
            else if (item_tp == Real_typep)
                emit_operator(CALL_stnd, SF_STRREAL, 0, 0);
      
            if (item_idp->definition == VARPARM_DEFN)
                emit_var_item(POP, item_idp, item_tp);
            else
                emit_mem_item(POP, item_idp, item_tp);

            item_idp->initialized = true;
        }

        if (Token.code == COMMA)
        {
            get_token();                            // after ","
            if (Token.code != ID_TOKEN)
            {
                compile_error(M_MISSING, M_ITEM, M_0);
                synchronize(Stmt_start_list, Data_decl_list, Stmt_end_list);
                break;
            }
        }
        else
            break;
    }
    if (flush_stdin)
        emit_operator(FLUSH_stdin, NO_CODE, 0, 0);
}

/*
 *   "put_statement" parses:
 *
 *             put [:<streamNumber>,] <putItem> {, <putItem>}[..]
 *
 *   in which a <putItem> is:
 *
 *             <expn>[:<width>[:<fractWidth>[:<expWidth>]]]
 *
 *   returns:  nothing
 */
void put_statement()
{
    TYPE_STRUCT_PTR expn_tp;
    SYMTAB_NODE_PTR idp;

    get_token();                                    // after "put"

    if (Token.code == COLON)                        // get <streamNumber>
    {
        get_token();                                // after ":"
        get_integer_expression();

        emit_operator(SET_file_ptr, NO_CODE, 0, 0);

        if_code_get_token_else_error(COMMA);
    }
    else
        emit_operator(SET_stdout, NO_CODE, 0, 0);

    while (token_in(Put_item_list)   ||
           token_in(Number_type_list)||
           token_in(String_type_list))
    {
        if (Token.code == ID_TOKEN)                 // get the output type
        {
            idp = search_symbol_tables(Token.lexeme);
            if (idp == NULL)
            {
                compile_error(M_UNDEFINED, M_IDENTIFIER, M_0);
                get_token();                        // after <id>
            }
            else
            {
                if (idp->typep->form == STRING_FORM)
                    expn_tp = string_expression();
                else
                    expn_tp = expression();
            }
        }
        else if (token_in(String_type_list))
            expn_tp = string_expression();          // and get next token
        else
            expn_tp = expression();                 // and get next token

        // check for valid output type
        if ((expn_tp       != Integer_typep)&&
            (expn_tp       != Real_typep   )&&
            (expn_tp       != Char_typep   )&&
            (expn_tp->form != STRING_FORM  ))
        {
            compile_error(M_INVALID, M_TYPE, M_0);
            expn_tp = Null_typep;
            get_token();                            // after <id>
        }

        if (Token.code == COLON)                    // get <width>
        {
            get_token();                            // after ":"
            get_integer_expression();

            if (Token.code == COLON)                // get <fractWidth>
            {
                if (expn_tp != Real_typep)
                    compile_error(M_INCOMPATIBLE, M_TYPES, M_0);

                get_token();                        // after ":"
                get_integer_expression();

                if (Token.code == COLON)            // get <expWidth>
                {
                    get_token();                    // after ":"
                    get_integer_expression();
                    emit_operator(CALL_stnd, SF_EREALSTR, 0, 0);
                }
                else
                    emit_operator(CALL_stnd, SF_FREALSTR, 0, 0);
            }
            else
            {
                if (expn_tp == Null_typep)          // source code error
                    emit_operator(NO_CODE, NO_CODE, 0, 0);
                else if (expn_tp == Real_typep)
                    emit_operator(CALL_stnd, SF_REALSTR, 0, 0);
                else if (expn_tp == Integer_typep)
                    emit_operator(CALL_stnd, SF_INTSTR, 0, 0);
                else if (expn_tp == Char_typep)
                    emit_operator(FORMAT_CHR, NO_CODE, 0, 0);
                else if (expn_tp->form == STRING_FORM)
                    emit_operator(FORMAT_STR, NO_CODE, 0, 0);
                else
                    assert(0);
            }
        }
        else
        {
            emit_push_imm_real(1.0);                // width

            if (expn_tp == Null_typep)              // source code error
                emit_operator(NO_CODE, NO_CODE, 0, 0);
            else if (expn_tp == Real_typep)
                emit_operator(CALL_stnd, SF_REALSTR, 0, 0);
            else if (expn_tp == Integer_typep)
                emit_operator(CALL_stnd, SF_INTSTR, 0, 0);
            else if (expn_tp == Char_typep)
                emit_operator(FORMAT_CHR, NO_CODE, 0, 0);
            else if (expn_tp->form == STRING_FORM)
                emit_operator(FORMAT_STR, NO_CODE, 0, 0);
            else
                assert(0);
        }

        emit_operator(PUT_str, NO_CODE, 0, 0);

        if (Token.code == COMMA)
        {
            get_token();                            // after ","

            if (!token_in(Put_item_list   ) &&
                !token_in(Number_type_list) &&
                !token_in(String_type_list))
                compile_error(M_MISSING, M_ITEM, M_0);
        }
        else
            break;
    }

    if (Token.code == ELLIPSES)
        get_token();                                // after "..."
    else
    {
        emit_push_imm_string("\n");                 // add newline
        emit_operator(PUT_str, NO_CODE, 0, 0);
    }
}

/*
 *   "return_statement" parses:
 *
 *             return [<expn>]
 *
 *   A function must return a value; the expression type must match 
 *   the function type.
 *
 *   returns:  nothing
 */
void return_statement(SYMTAB_NODE_PTR rtne_idp)     // of function
{
    TYPE_STRUCT_PTR rtne_tp, expr_tp;

    get_token();                                    // after "return"

    if (rtne_idp->definition == FUNC_DEFN)
    {
        Result_flag = true;

        rtne_tp = rtne_idp->typep;
        if (rtne_tp == Boolean_typep)
            expr_tp = boolean_expression();         // and get next token
        else if (rtne_tp->form == STRING_FORM)
            expr_tp = string_expression();          // and get next token
        else
            expr_tp = expression();                 // and get next token

        if (!assign_type_compatible(rtne_tp, expr_tp))
            compile_error(M_INCOMPATIBLE, M_ASSIGNMENT, M_0);

        emit_return_value(rtne_idp);
    }

    emit_operator(JMP, NO_CODE, 0, 0);              // zero is return label
}

/*
 *   "incr_level" enters a deeper logical nesting level.
 *
 *   returns:  nothing
 */
void incr_level(TOKEN_CODE construct, int continue_label, int exit_label)
{
    Level++;                                        // increment level

    if (Level >= NESTING_LEVELS)
        compile_error(M_NESTING, M_TOO, M_DEEP, M_0);

    Construct_Stack[Level] = construct;
    Continue_label[Level] = continue_label;
    Exit_label[Level] = exit_label;
}

/*
 *   "decr_level" exits the current logical nesting level.
 *
 *   returns:  nothing
 */
void decr_level()
{
    Level--;                                        // decrement level
}

/*
 *   "in_for_or_loop_construct" determines if the parser is within
 *   a loop or for logical construct.  This is done to determine if
 *   the token EXIT is valid.
 *
 *   returns:  true or false
 */
bool in_for_or_loop_construct()
{
    for (int i = Level; i >= 0; i--)
    {
        if ((Construct_Stack[i] == FOR)||(Construct_Stack[i] == LOOP))
            return true;
    }
    return false;
}

| home | contents | previous | next page | send comment | send link | add bookmark |

Copyright © 2004, Stephen R. Schmitt