/*
 * $Id: semant.c 23501 2007-12-05 15:13:21Z paultcochrane $
 *
 * semant.c
 *
 * Cola compiler for Parrot
 *
 * Copyright (C) 2002 Melvin Smith
 *
 */

/*

=head1 NAME

languages/cola/semant.c

=head1 DESCRIPTION

Semantic and type checking phase.

=head2 Functions

=over 4

=cut

*/

#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include "cola.h"
#include "parser.h"

/*

=item C<void build_ast(AST * tree)>

Walk the AST tree and resolve symbol types.

=over 4

=item 1) Resolve identifiers to symbol table references
=item 2) Resolve typenames to type references
=item 3) Check for invalid assignments and operations

=back

=cut

*/

void build_ast(AST * tree)
{
    if (!tree) return;
    switch (tree->asttype) {
        case ASTT_CLASS_DECL:
            build_class_decl(tree);
            break;
        default:
            fprintf(stderr, "build_ast: Invalid asttype [%d]\n", tree->asttype);
            abort();
    }

    build_ast(tree->next);
}

/*

=item C<void build_class_decl(AST * c)>

RT#48200: Not yet documented!!!

=cut

*/

void build_class_decl(AST * c)
{
    if (!c) return;
#if DEBUG
    fprintf(stderr, "Pass 2: class [%s]\n", c->sym->name);
#endif
    push_namespace(c->sym);
    if (c->Attr.Class.body)
            build_class_body(c->Attr.Class.body);
    pop_namespace();
}

/*

=item C<void build_class_body(AST * b)>

RT#48200: Not yet documented!!!

=cut

*/

void build_class_body(AST * b)
{
    if (!b) return;
    fprintf(stderr, "Pass 2: class body\n");
    while (b) {
        switch (b->asttype) {
            case ASTT_CONSTANT_DECL:
                build_field_decl(b);
                    break;
            case ASTT_FIELD_DECL:
                build_field_decl(b);
                    break;
            case ASTT_METHOD_DECL:
#if DEBUG
                fprintf(stderr, "Pass 2: Method [%s]\n", b->sym->name);
#endif
                build_method_decl(b);
#if DEBUG
                fprintf(stderr, "Pass 2: End Method [%s]\n", b->sym->name);
#endif
                break;
            default:
                fprintf(stderr, "build_class_body: Invalid asttype [%d]\n", b->asttype);
                abort();
        }
            b = b->next;
    }
}

/*

=item C<void build_field_decl(AST * d)>

A class field declaration

=cut

*/

void build_field_decl(AST * d)
{
#if DEBUG
    if (d->asttype == ASTT_CONSTANT_DECL)
        fprintf(stderr, "Pass 2: class const [%s]\n", d->arg1->sym->name);
    else
        fprintf(stderr, "Pass 2: class field [%s]\n", d->arg1->sym->name);
#endif
    declare_field(d->arg1->sym);
    d->arg1->type = d->arg1->sym->type;
    build_expr(d->arg2);
}

/*

=item C<void build_method_decl(AST * m)>

A class method declaration

=cut

*/

void build_method_decl(AST * m)
{
    /* Uses enclosing class's namespace, nested scope */
    /*push_namespace(m->sym);*/
#if DEBUG
    fprintf(stderr, "build_method_decl\n");
#endif
    push_scope();
    if (m->Attr.Method.params) {
        Symbol * s = m->Attr.Method.params;
        while (s) {
            declare_local(s);
            s = s->tnext;
        }
    }
    if (m->Attr.Method.body) {
        push_scope();
        build_statement_list(m->Attr.Method.body);
        m->Attr.Method.body->vars = pop_scope();
        {
            Symbol * s = m->vars;
            fprintf(stderr, "\tPopped locals:\n");
            while (s) {
                fprintf(stderr, "\t[%s]\n", s->name);
                s = s->tnext;
            }
        }
    }
    /*
     * Don't use pop_scope here because this scope's symbols
     * are also referenced by the ordered list in Method.params
     * pop_scope would reorder the symbol list we already have.
     */
    discard_scope();
}

/*

=item C<void build_var_decl(AST * d)>

A block or method local variable declaration

=cut

*/

void build_var_decl(AST * d)
{
#if DEBUG
    if (d->asttype == ASTT_CONSTANT_DECL)
        fprintf(stderr, "Pass 2: Local const [%s]\n", d->arg1->sym->name);
    else
        fprintf(stderr, "Pass 2: Local var [%s]\n", d->arg1->sym->name);
#endif
    declare_local(d->arg1->sym);
    d->arg1->type = d->arg1->sym->type;
    build_expr(d->arg2);
}

/*

=item C<void build_if(AST * i)>

RT#48200: Not yet documented!!!

=cut

*/

void build_if(AST * i)
{
    /* IF */
#if DEBUG
    fprintf(stderr, "build_if\n");
#endif
    build_expr(i->Attr.Conditional.condition);
    /* THEN */
    build_statement_list(i->arg1);
    /* ELSE */
    build_statement_list(i->arg2);
}

/*

=item C<void build_conditional(AST * c)>

RT#48200: Not yet documented!!!

=cut

*/

void build_conditional(AST * c)
{
    /* IF */
#if DEBUG
    fprintf(stderr, "build_conditional\n");
#endif
    build_expr(c->Attr.Conditional.condition);
    /* THEN */
    build_expr(c->arg1);
    /* ELSE */
    build_expr(c->arg2);
    if (c->arg1->type != c->arg2->type) {
        fprintf(stderr, "Error: expression types not equivalent in ternary expression\n");
        exit(EXIT_SUCCESS);
    }
    c->typename = c->arg1->typename;
    c->type = c->arg1->type;
}

/*

=item C<void build_method_call(AST * c)>

RT#48200: Not yet documented!!!

=cut

*/

void build_method_call(AST * c)
{
    fprintf(stderr, "build_method_call\n");
    build_expr(c->arg1);
    c->type = c->arg1->type;
    c->typename = c->arg1->typename;
    /* The argument list */
    build_expr_list(c->arg2);
}

/*

=item C<void build_new_expr(AST * n)>

RT#48200: Not yet documented!!!

=cut

*/

void build_new_expr(AST * n)
{
    n->type = lookup_type_symbol(n->typename);
    if (!n->type) {
        fprintf(stderr, "Internal error: new called for unknown type [%s]\n",
            n->typename->name);
        abort();
    }
}

/*

=item C<void build_loop(AST * l)>

RT#48200: Not yet documented!!!

=cut

*/

void build_loop(AST * l)
{
#if DEBUG
    fprintf(stderr, "build_loop\n");
#endif
    build_statement_list(l->Attr.Loop.init);
    build_statement_list(l->Attr.Loop.iteration);
    build_expr(l->Attr.Loop.condition);
    build_statement_list(l->Attr.Loop.body);
}

/*

=item C<void build_return(AST * r)>

RT#48200: Not yet documented!!!

=cut

*/

void build_return(AST * r)
{
    if (r->arg1)
        build_expr(r->arg1);
}

/*

=item C<void build_expr_list(AST * e)>

RT#48200: Not yet documented!!!

=cut

*/

void build_expr_list(AST * e)
{
    AST * p = e;
    while (p) {
        build_expr(p);
        p = p->next;
    }
}

/*

=item C<void build_expr(AST * e)>

RT#48200: Not yet documented!!!

=cut

*/

void build_expr(AST * e)
{
#if DEBUG
    fprintf(stderr, "build_expr\n");
#endif
    if (!e) return;
    switch (e->asttype) {
        case ASTT_LITERAL:
            e->type = e->sym->type;
            e->typename = e->sym->typename;
            return;

        case ASTT_IDENTIFIER:
            resolve_identifier(&e->sym);
            e->type = e->sym->type;
            e->typename = e->sym->typename;
            return;

        case ASTT_METHOD_CALL:
            build_method_call(e);
            break;

        case ASTT_NEW_OBJECT:
            build_new_expr(e);
            break;

        case ASTT_ASSIGN:
            build_expr(e->arg1);
            build_expr(e->arg2);
            break;

        case ASTT_CONDITIONAL_EXPR:
            build_conditional(e);
            break;

        case ASTT_OP:
            build_expr(e->arg1);
            build_expr(e->arg2);
            if (e->arg1->type == t_string) {
                /* Implicitly convert + operator on string types to
                 * . concat operator until the compiler can handle class
                 * operators.
                 */
                e->op = '.';
            }
            break;
        case ASTT_LOGICAL:
        case ASTT_INDEX:
        case ASTT_COMPARISON:
        case ASTT_PREINC:
        case ASTT_POSTINC:
            build_expr(e->arg1);
            build_expr(e->arg2);
            break;

        case ASTT_BOOLEAN:
            build_expr(e->arg1);
            break;
        default:
            fprintf(stderr, "build_expr: Invalid expr asttype [%d]\n", e->asttype);
            abort();
    }

    if (e->arg1) {
        e->type = e->arg1->type;
        e->typename = e->arg1->typename;
    }
}

/*

=item C<void build_statement_list(AST * s)>

RT#48200: Not yet documented!!!

=cut

*/

void build_statement_list(AST * s)
{
    static int statements;
    if (!s) return;
#if DEBUG
    fprintf(stderr, "statements parsed [%d]\n", statements++);
#endif
    if (s->kind == KIND_EXPR) {
        build_expr(s);
        goto END;
    }

    switch (s->asttype) {
        case ASTT_IF:
            build_if(s);
            break;
        case ASTT_CONSTANT_DECL:
            build_var_decl(s);
            break;
        case ASTT_FIELD_DECL:
            build_var_decl(s);
            break;
        case ASTT_WHILE:
        case ASTT_FOR:
            build_loop(s);
            break;
        case ASTT_BREAK:
        case ASTT_CONTINUE:
            break;
        case ASTT_RETURN:
            build_return(s);
            break;
        default:
            fprintf(stderr, "build_statement_list: Invalid asttype [%d]\n", s->asttype);
            abort();
    }

END:
    build_statement_list(s->next);
}

/*

=back

=cut

*/


/*
 * Local variables:
 *   c-file-style: "parrot"
 * End:
 * vim: expandtab shiftwidth=4:
 */


syntax highlighted by Code2HTML, v. 0.9.1