HelenOS sources

root/uspace/app/sbi/src/parse.c

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. parse_init
  2. parse_module
  3. parse_csi
  4. parse_csimbr
  5. parse_ctor
  6. parse_enum
  7. parse_embr
  8. parse_deleg
  9. parse_fun
  10. parse_var
  11. parse_prop
  12. parse_symbol_attrs
  13. parse_symbol_attr
  14. parse_proc_arg
  15. parse_arg_attr
  16. parse_fun_sig
  17. parse_prop_get
  18. parse_prop_set
  19. parse_block
  20. parse_stat
  21. parse_vdecl
  22. parse_if
  23. parse_switch
  24. parse_while
  25. parse_for
  26. parse_raise
  27. parse_break
  28. parse_return
  29. parse_wef
  30. parse_exps
  31. parse_except
  32. parse_ident
  33. parse_raise_error
  34. parse_note_error
  35. parse_is_error
  36. parse_recover_error
  37. lcur
  38. lcur_lc
  39. lcur_span
  40. lprev_span
  41. lskip
  42. lcheck
  43. lmatch
  44. lunexpected_error
  45. terminates_block

/*
 * Copyright (c) 2011 Jiri Svoboda
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 *
 * - Redistributions of source code must retain the above copyright
 *   notice, this list of conditions and the following disclaimer.
 * - Redistributions in binary form must reproduce the above copyright
 *   notice, this list of conditions and the following disclaimer in the
 *   documentation and/or other materials provided with the distribution.
 * - The name of the author may not be used to endorse or promote products
 *   derived from this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */

/** @file Parser
 *
 * Consumes a sequence of lexical elements and produces a syntax tree (stree).
 * Parsing primitives, module members, statements.
 */

#include <assert.h>
#include <stdlib.h>
#include "cspan.h"
#include "debug.h"
#include "lex.h"
#include "list.h"
#include "mytypes.h"
#include "p_expr.h"
#include "p_type.h"
#include "stree.h"
#include "strtab.h"
#include "symbol.h"

#include "parse.h"

/*
 * Module and CSI members
 */
static stree_csi_t *parse_csi(parse_t *parse, lclass_t dclass,
    stree_csi_t *outer_csi);
static stree_csimbr_t *parse_csimbr(parse_t *parse, stree_csi_t *outer_csi);

static stree_ctor_t *parse_ctor(parse_t *parse, stree_csi_t *outer_csi);

static stree_enum_t *parse_enum(parse_t *parse, stree_csi_t *outer_csi);
static stree_embr_t *parse_embr(parse_t *parse, stree_enum_t *outer_enum);

static stree_deleg_t *parse_deleg(parse_t *parse, stree_csi_t *outer_csi);
static stree_fun_t *parse_fun(parse_t *parse, stree_csi_t *outer_csi);
static stree_var_t *parse_var(parse_t *parse, stree_csi_t *outer_csi);
static stree_prop_t *parse_prop(parse_t *parse, stree_csi_t *outer_csi);

static void parse_symbol_attrs(parse_t *parse, stree_symbol_t *symbol);
static stree_symbol_attr_t *parse_symbol_attr(parse_t *parse);

static stree_proc_arg_t *parse_proc_arg(parse_t *parse);
static stree_arg_attr_t *parse_arg_attr(parse_t *parse);
static stree_fun_sig_t *parse_fun_sig(parse_t *parse);

static void parse_prop_get(parse_t *parse, stree_prop_t *prop);
static void parse_prop_set(parse_t *parse, stree_prop_t *prop);

/*
 * Statements
 */
static stree_block_t *parse_block(parse_t *parse);

static stree_vdecl_t *parse_vdecl(parse_t *parse);
static stree_if_t *parse_if(parse_t *parse);
static stree_switch_t *parse_switch(parse_t *parse);
static stree_while_t *parse_while(parse_t *parse);
static stree_for_t *parse_for(parse_t *parse);
static stree_raise_t *parse_raise(parse_t *parse);
static stree_break_t *parse_break(parse_t *parse);
static stree_return_t *parse_return(parse_t *parse);
static stree_wef_t *parse_wef(parse_t *parse);
static stree_exps_t *parse_exps(parse_t *parse);

static stree_except_t *parse_except(parse_t *parse);

/** Initialize parser object.
 *
 * Set up parser @a parse to use lexer @a lex for input and to store
 * output (i.e. new declarations) to program @a prog. @a prog is not
 * necessarily empty, the declarations being parsed are simply added
 * to it.
 *
 * @param parse         Parser object.
 * @param prog          Destination program stree.
 * @param lex           Input lexer.
 */
void parse_init(parse_t *parse, stree_program_t *prog, struct lex *lex)
{
        parse->program = prog;
        parse->cur_mod = parse->program->module;
        parse->lex = lex;

        parse->error = b_false;
        parse->error_bailout = b_false;

        lex_next(parse->lex);
}

/** Parse module.
 *
 * Parse a program module.
 *
 * The input is read using the lexer associated with @a parse. The resulting
 * declarations are added to existing declarations in the program associated
 * with @a parse.
 *
 * If any parse error occurs, parse->error will @c b_true when this function
 * returns. parse->error_bailout will be @c b_true if the error has not
 * been recovered yet. Similar holds for other parsing functions in this
 * module.
 *
 * @param parse         Parser object.
 */
void parse_module(parse_t *parse)
{
        stree_csi_t *csi;
        stree_enum_t *enum_d;
        stree_modm_t *modm;

        while (lcur_lc(parse) != lc_eof && !parse_is_error(parse)) {
                switch (lcur_lc(parse)) {
                case lc_class:
                case lc_struct:
                case lc_interface:
                        csi = parse_csi(parse, lcur_lc(parse), NULL);
                        modm = stree_modm_new(mc_csi);
                        modm->u.csi = csi;

                        list_append(&parse->cur_mod->members, modm);
                        break;
                case lc_enum:
                        enum_d = parse_enum(parse, NULL);
                        modm = stree_modm_new(mc_enum);
                        modm->u.enum_d = enum_d;

                        list_append(&parse->cur_mod->members, modm);
                        break;
                default:
                        lunexpected_error(parse);
                        lex_next(parse->lex);
                        break;
                }

        }
}

/** Parse class, struct or interface declaration.
 *
 * @param parse         Parser object.
 * @param dclass        What to parse: @c lc_class, @c lc_struct or @c lc_csi.
 * @param outer_csi     CSI containing this declaration or @c NULL if global.
 * @return              New syntax tree node.
 */
static stree_csi_t *parse_csi(parse_t *parse, lclass_t dclass,
    stree_csi_t *outer_csi)
{
        stree_csi_t *csi;
        csi_class_t cc;
        stree_csimbr_t *csimbr;
        stree_symbol_t *symbol;
        stree_ident_t *targ_name;
        stree_targ_t *targ;
        stree_texpr_t *pref;

        switch (dclass) {
        case lc_class:
                cc = csi_class;
                break;
        case lc_struct:
                cc = csi_struct;
                break;
        case lc_interface:
                cc = csi_interface;
                break;
        default:
                assert(b_false);
        }

        lskip(parse);

        csi = stree_csi_new(cc);
        csi->name = parse_ident(parse);

        list_init(&csi->targ);

        while (lcur_lc(parse) == lc_slash) {
                lskip(parse);
                targ_name = parse_ident(parse);

                targ = stree_targ_new();
                targ->name = targ_name;

                list_append(&csi->targ, targ);
        }

        symbol = stree_symbol_new(sc_csi);
        symbol->u.csi = csi;
        symbol->outer_csi = outer_csi;
        csi->symbol = symbol;

#ifdef DEBUG_PARSE_TRACE
        printf("parse_csi: csi=%p, csi->name = %p (%s)\n", csi, csi->name,
            strtab_get_str(csi->name->sid));
#endif
        if (lcur_lc(parse) == lc_colon) {
                /* Inheritance list */
                lskip(parse);

                while (b_true) {
                        pref = parse_texpr(parse);
                        if (parse_is_error(parse))
                                break;

                        list_append(&csi->inherit, pref);
                        if (lcur_lc(parse) != lc_plus)
                                break;

                        lskip(parse);
                }
        }

        lmatch(parse, lc_is);
        list_init(&csi->members);

        /* Parse class, struct or interface members. */
        while (lcur_lc(parse) != lc_end && !parse_is_error(parse)) {
                csimbr = parse_csimbr(parse, csi);
                if (csimbr == NULL)
                        continue;

                list_append(&csi->members, csimbr);
        }

        lmatch(parse, lc_end);

        if (outer_csi != NULL) {
                switch (outer_csi->cc) {
                case csi_class:
                case csi_struct:
                        break;
                case csi_interface:
                        cspan_print(csi->name->cspan);
                        printf(" Error: CSI declared inside interface.\n");
                        parse_note_error(parse);
                        /* XXX Free csi */
                        return NULL;
                }
        }

        return csi;
}

/** Parse class, struct or interface member.
 *
 * @param parse         Parser object.
 * @param outer_csi     CSI containing this declaration.
 * @return              New syntax tree node. In case of parse error,
 *                      @c NULL may (but need not) be returned.
 */
static stree_csimbr_t *parse_csimbr(parse_t *parse, stree_csi_t *outer_csi)
{
        stree_csimbr_t *csimbr;

        stree_csi_t *csi;
        stree_ctor_t *ctor;
        stree_deleg_t *deleg;
        stree_enum_t *enum_d;
        stree_fun_t *fun;
        stree_var_t *var;
        stree_prop_t *prop;

        csimbr = NULL;

        switch (lcur_lc(parse)) {
        case lc_class:
        case lc_struct:
        case lc_interface:
                csi = parse_csi(parse, lcur_lc(parse), outer_csi);
                if (csi != NULL) {
                        csimbr = stree_csimbr_new(csimbr_csi);
                        csimbr->u.csi = csi;
                }
                break;
        case lc_new:
                ctor = parse_ctor(parse, outer_csi);
                if (ctor != NULL) {
                        csimbr = stree_csimbr_new(csimbr_ctor);
                        csimbr->u.ctor = ctor;
                }
                break;
        case lc_deleg:
                deleg = parse_deleg(parse, outer_csi);
                if (deleg != NULL) {
                        csimbr = stree_csimbr_new(csimbr_deleg);
                        csimbr->u.deleg = deleg;
                }
                break;
        case lc_enum:
                enum_d = parse_enum(parse, outer_csi);
                if (enum_d != NULL) {
                        csimbr = stree_csimbr_new(csimbr_enum);
                        csimbr->u.enum_d = enum_d;
                }
                break;
        case lc_fun:
                fun = parse_fun(parse, outer_csi);
                csimbr = stree_csimbr_new(csimbr_fun);
                csimbr->u.fun = fun;
                break;
        case lc_var:
                var = parse_var(parse, outer_csi);
                if (var != NULL) {
                        csimbr = stree_csimbr_new(csimbr_var);
                        csimbr->u.var = var;
                }
                break;
        case lc_prop:
                prop = parse_prop(parse, outer_csi);
                csimbr = stree_csimbr_new(csimbr_prop);
                csimbr->u.prop = prop;
                break;
        default:
                lunexpected_error(parse);
                lex_next(parse->lex);
                break;
        }

        return csimbr;
}

/** Parse constructor.
 *
 * @param parse         Parser object.
 * @param outer_csi     CSI containing this declaration or @c NULL if global.
 * @return              New syntax tree node.
 */
static stree_ctor_t *parse_ctor(parse_t *parse, stree_csi_t *outer_csi)
{
        stree_ctor_t *ctor;
        stree_symbol_t *symbol;
        cspan_t *cspan;

        ctor = stree_ctor_new();
        symbol = stree_symbol_new(sc_ctor);

        symbol->u.ctor = ctor;
        symbol->outer_csi = outer_csi;
        ctor->symbol = symbol;

        lmatch(parse, lc_new);
        cspan = lprev_span(parse);

        /* Fake identifier. */
        ctor->name = stree_ident_new();
        ctor->name->sid = strtab_get_sid(CTOR_IDENT);
        ctor->name->cspan = lprev_span(parse);

#ifdef DEBUG_PARSE_TRACE
        printf("Parsing constructor of CSI '");
        symbol_print_fqn(csi_to_symbol(outer_csi));
        printf("'.\n");
#endif
        ctor->sig = parse_fun_sig(parse);
        if (ctor->sig->rtype != NULL) {
                cspan_print(cspan);
                printf(" Error: Constructor of CSI '");
                symbol_print_fqn(csi_to_symbol(outer_csi));
                printf("' has a return type.\n");
                parse_note_error(parse);
        }

        /* Parse attributes. */
        parse_symbol_attrs(parse, symbol);

        ctor->proc = stree_proc_new();
        ctor->proc->outer_symbol = symbol;

        if (lcur_lc(parse) == lc_scolon) {
                lskip(parse);

                /* This constructor has no body. */
                cspan_print(cspan);
                printf(" Error: Constructor of CSI '");
                symbol_print_fqn(csi_to_symbol(outer_csi));
                printf("' has no body.\n");
                parse_note_error(parse);

                ctor->proc->body = NULL;
        } else {
                lmatch(parse, lc_is);
                ctor->proc->body = parse_block(parse);
                lmatch(parse, lc_end);
        }

        switch (outer_csi->cc) {
        case csi_class:
        case csi_struct:
                break;
        case csi_interface:
                cspan_print(ctor->name->cspan);
                printf(" Error: Constructor declared inside interface.\n");
                parse_note_error(parse);
                /* XXX Free ctor */
                return NULL;
        }

        return ctor;
}

/** Parse @c enum declaration.
 *
 * @param parse         Parser object.
 * @param outer_csi     CSI containing this declaration or @c NULL if global.
 * @return              New syntax tree node.
 */
static stree_enum_t *parse_enum(parse_t *parse, stree_csi_t *outer_csi)
{
        stree_enum_t *enum_d;
        stree_symbol_t *symbol;
        stree_embr_t *embr;

        enum_d = stree_enum_new();
        symbol = stree_symbol_new(sc_enum);

        symbol->u.enum_d = enum_d;
        symbol->outer_csi = outer_csi;
        enum_d->symbol = symbol;

        lmatch(parse, lc_enum);
        enum_d->name = parse_ident(parse);
        list_init(&enum_d->members);

#ifdef DEBUG_PARSE_TRACE
        printf("Parse enum '%s'.\n", strtab_get_str(enum_d->name->sid));
#endif
        lmatch(parse, lc_is);

        /* Parse enum members. */
        while (lcur_lc(parse) != lc_end && !parse_is_error(parse)) {
                embr = parse_embr(parse, enum_d);
                if (embr == NULL)
                        break;

                list_append(&enum_d->members, embr);
        }

        if (list_is_empty(&enum_d->members)) {
                cspan_print(enum_d->name->cspan);
                printf("Error: Enum type '%s' has no members.\n",
                    strtab_get_str(enum_d->name->sid));
                parse_note_error(parse);
        }

        lmatch(parse, lc_end);

        if (outer_csi != NULL) {
                switch (outer_csi->cc) {
                case csi_class:
                case csi_struct:
                        break;
                case csi_interface:
                        cspan_print(enum_d->name->cspan);
                        printf(" Error: Enum declared inside interface.\n");
                        parse_note_error(parse);
                        /* XXX Free enum */
                        return NULL;
                }
        }

        return enum_d;
}

/** Parse enum member.
 *
 * @param parse         Parser object.
 * @param outer_enum    Enum containing this declaration.
 * @return              New syntax tree node. In case of parse error,
 *                      @c NULL may (but need not) be returned.
 */
static stree_embr_t *parse_embr(parse_t *parse, stree_enum_t *outer_enum)
{
        stree_embr_t *embr;

        embr = stree_embr_new();
        embr->outer_enum = outer_enum;
        embr->name = parse_ident(parse);

        lmatch(parse, lc_scolon);

        return embr;
}

/** Parse delegate.
 *
 * @param parse         Parser object.
 * @param outer_csi     CSI containing this declaration or @c NULL if global.
 * @return              New syntax tree node.
 */
static stree_deleg_t *parse_deleg(parse_t *parse, stree_csi_t *outer_csi)
{
        stree_deleg_t *deleg;
        stree_symbol_t *symbol;

        deleg = stree_deleg_new();
        symbol = stree_symbol_new(sc_deleg);

        symbol->u.deleg = deleg;
        symbol->outer_csi = outer_csi;
        deleg->symbol = symbol;

        lmatch(parse, lc_deleg);
        deleg->name = parse_ident(parse);

#ifdef DEBUG_PARSE_TRACE
        printf("Parsing delegate '%s'.\n", strtab_get_str(deleg->name->sid));
#endif

        deleg->sig = parse_fun_sig(parse);

        /* Parse attributes. */
        parse_symbol_attrs(parse, symbol);

        lmatch(parse, lc_scolon);

        switch (outer_csi->cc) {
        case csi_class:
        case csi_struct:
                break;
        case csi_interface:
                cspan_print(deleg->name->cspan);
                printf(" Error: Delegate declared inside interface.\n");
                parse_note_error(parse);
                /* XXX Free deleg */
                return NULL;
        }

        return deleg;
}

/** Parse member function.
 *
 * @param parse         Parser object.
 * @param outer_csi     CSI containing this declaration or @c NULL if global.
 * @return              New syntax tree node.
 */
static stree_fun_t *parse_fun(parse_t *parse, stree_csi_t *outer_csi)
{
        stree_fun_t *fun;
        stree_symbol_t *symbol;
        bool_t body_expected;

        fun = stree_fun_new();
        symbol = stree_symbol_new(sc_fun);

        symbol->u.fun = fun;
        symbol->outer_csi = outer_csi;
        fun->symbol = symbol;

        lmatch(parse, lc_fun);
        fun->name = parse_ident(parse);

#ifdef DEBUG_PARSE_TRACE
        printf("Parsing function '%s'.\n", strtab_get_str(fun->name->sid));
#endif
        fun->sig = parse_fun_sig(parse);

        /* Parse attributes. */
        parse_symbol_attrs(parse, symbol);

        body_expected = !stree_symbol_has_attr(symbol, sac_builtin) &&
            (outer_csi->cc != csi_interface);

        fun->proc = stree_proc_new();
        fun->proc->outer_symbol = symbol;

        if (lcur_lc(parse) == lc_scolon) {
                lskip(parse);

                /* Body not present */
                if (body_expected) {
                        cspan_print(fun->name->cspan);
                        printf(" Error: Function '");
                        symbol_print_fqn(symbol);
                        printf("' should have a body.\n");
                        parse_note_error(parse);
                }

                fun->proc->body = NULL;
        } else {
                lmatch(parse, lc_is);
                fun->proc->body = parse_block(parse);
                lmatch(parse, lc_end);

                /* Body present */
                if (!body_expected) {
                        cspan_print(fun->name->cspan);
                        printf(" Error: Function declaration '");
                        symbol_print_fqn(symbol);
                        printf("' should not have a body.\n");
                        parse_note_error(parse);
                }
        }

        return fun;
}

/** Parse member variable.
 *
 * @param parse         Parser object.
 * @param outer_csi     CSI containing this declaration or @c NULL if global.
 * @return              New syntax tree node.
 */
static stree_var_t *parse_var(parse_t *parse, stree_csi_t *outer_csi)
{
        stree_var_t *var;
        stree_symbol_t *symbol;

        var = stree_var_new();
        symbol = stree_symbol_new(sc_var);
        symbol->u.var = var;
        symbol->outer_csi = outer_csi;
        var->symbol = symbol;

        lmatch(parse, lc_var);
        var->name = parse_ident(parse);
        lmatch(parse, lc_colon);
        var->type =  parse_texpr(parse);

        parse_symbol_attrs(parse, symbol);

        lmatch(parse, lc_scolon);

        switch (outer_csi->cc) {
        case csi_class:
        case csi_struct:
                break;
        case csi_interface:
                cspan_print(var->name->cspan);
                printf(" Error: Variable declared inside interface.\n");
                parse_note_error(parse);
                /* XXX Free var */
                return NULL;
        }

        return var;
}

/** Parse member property.
 *
 * @param parse         Parser object.
 * @param outer_csi     CSI containing this declaration or @c NULL if global.
 * @return              New syntax tree node.
 */
static stree_prop_t *parse_prop(parse_t *parse, stree_csi_t *outer_csi)
{
        stree_prop_t *prop;
        stree_symbol_t *symbol;

        stree_ident_t *ident;
        stree_proc_arg_t *arg;

        prop = stree_prop_new();
        list_init(&prop->args);

        symbol = stree_symbol_new(sc_prop);
        symbol->u.prop = prop;
        symbol->outer_csi = outer_csi;
        prop->symbol = symbol;

        lmatch(parse, lc_prop);

        if (lcur_lc(parse) == lc_self) {
                /* Indexed property set */

                /* Use some name that is impossible as identifier. */
                ident = stree_ident_new();
                ident->sid = strtab_get_sid(INDEXER_IDENT);
                prop->name = ident;

                lskip(parse);
                lmatch(parse, lc_lsbr);

                /* Parse formal parameters. */
                while (!parse_is_error(parse)) {
                        arg = parse_proc_arg(parse);
                        if (stree_arg_has_attr(arg, aac_packed)) {
                                prop->varg = arg;
                                break;
                        } else {
                                list_append(&prop->args, arg);
                        }

                        if (lcur_lc(parse) == lc_rsbr)
                                break;

                        lmatch(parse, lc_scolon);
                }

                lmatch(parse, lc_rsbr);
        } else {
                /* Named property */
                prop->name = parse_ident(parse);
        }

        lmatch(parse, lc_colon);
        prop->type = parse_texpr(parse);

        /* Parse attributes. */
        parse_symbol_attrs(parse, symbol);

        lmatch(parse, lc_is);

        while (lcur_lc(parse) != lc_end && !parse_is_error(parse)) {
                switch (lcur_lc(parse)) {
                case lc_get:
                        parse_prop_get(parse, prop);
                        break;
                case lc_set:
                        parse_prop_set(parse, prop);
                        break;
                default:
                        lunexpected_error(parse);
                }
        }

        lmatch(parse, lc_end);

        return prop;
}

/** Parse symbol attributes.
 *
 * Parse list of attributes and add them to @a symbol.
 *
 * @param parse         Parser object
 * @param symbol        Symbol to add these attributes to
 */
static void parse_symbol_attrs(parse_t *parse, stree_symbol_t *symbol)
{
        stree_symbol_attr_t *attr;

        /* Parse attributes. */
        while (lcur_lc(parse) == lc_comma && !parse_is_error(parse)) {
                lskip(parse);
                attr = parse_symbol_attr(parse);
                list_append(&symbol->attr, attr);
        }
}

/** Parse symbol attribute.
 *
 * @param parse         Parser object
 * @return              New syntax tree node
 */
static stree_symbol_attr_t *parse_symbol_attr(parse_t *parse)
{
        stree_symbol_attr_t *attr;
        symbol_attr_class_t sac;

        /* Make compiler happy. */
        sac = 0;

        switch (lcur_lc(parse)) {
        case lc_builtin:
                sac = sac_builtin;
                break;
        case lc_static:
                sac = sac_static;
                break;
        default:
                cspan_print(lcur_span(parse));
                printf(" Error: Unexpected attribute '");
                lem_print(lcur(parse));
                printf("'.\n");
                parse_note_error(parse);
                break;
        }

        lskip(parse);

        attr = stree_symbol_attr_new(sac);
        return attr;
}

/** Parse formal function argument.
 *
 * @param parse         Parser object.
 * @return              New syntax tree node.
 */
static stree_proc_arg_t *parse_proc_arg(parse_t *parse)
{
        stree_proc_arg_t *arg;
        stree_arg_attr_t *attr;

        arg = stree_proc_arg_new();
        arg->name = parse_ident(parse);
        lmatch(parse, lc_colon);
        arg->type = parse_texpr(parse);

#ifdef DEBUG_PARSE_TRACE
        printf("Parse procedure argument.\n");
#endif
        list_init(&arg->attr);

        /* Parse attributes. */
        while (lcur_lc(parse) == lc_comma && !parse_is_error(parse)) {
                lskip(parse);
                attr = parse_arg_attr(parse);
                list_append(&arg->attr, attr);
        }

        return arg;
}

/** Parse argument attribute.
 *
 * @param parse         Parser object.
 * @return              New syntax tree node.
 */
static stree_arg_attr_t *parse_arg_attr(parse_t *parse)
{
        stree_arg_attr_t *attr;

        if (lcur_lc(parse) != lc_packed) {
                cspan_print(lcur_span(parse));
                printf(" Error: Unexpected attribute '");
                lem_print(lcur(parse));
                printf("'.\n");
                parse_note_error(parse);
        }

        lskip(parse);

        attr = stree_arg_attr_new(aac_packed);
        return attr;
}

/** Parse function signature.
 *
 * @param parse         Parser object.
 * @return              New syntax tree node.
 */
static stree_fun_sig_t *parse_fun_sig(parse_t *parse)
{
        stree_fun_sig_t *sig;
        stree_proc_arg_t *arg;

        sig = stree_fun_sig_new();

        lmatch(parse, lc_lparen);

#ifdef DEBUG_PARSE_TRACE
        printf("Parsing function signature.\n");
#endif

        list_init(&sig->args);

        if (lcur_lc(parse) != lc_rparen) {

                /* Parse formal parameters. */
                while (!parse_is_error(parse)) {
                        arg = parse_proc_arg(parse);

                        if (stree_arg_has_attr(arg, aac_packed)) {
                                sig->varg = arg;
                                break;
                        } else {
                                list_append(&sig->args, arg);
                        }

                        if (lcur_lc(parse) == lc_rparen)
                                break;

                        lmatch(parse, lc_scolon);
                }
        }

        lmatch(parse, lc_rparen);

        if (lcur_lc(parse) == lc_colon) {
                lskip(parse);
                sig->rtype = parse_texpr(parse);
        } else {
                sig->rtype = NULL;
        }

        return sig;
}

/** Parse member property getter.
 *
 * @param parse         Parser object.
 * @param prop          Property containing this declaration.
 */
static void parse_prop_get(parse_t *parse, stree_prop_t *prop)
{
        cspan_t *cspan;
        stree_block_t *block;
        stree_proc_t *getter;
        bool_t body_expected;

        body_expected = (prop->symbol->outer_csi->cc != csi_interface);

        lskip(parse);
        cspan = lprev_span(parse);

        if (prop->getter != NULL) {
                cspan_print(cspan);
                printf(" Error: Duplicate getter.\n");
                parse_note_error(parse);
                return;
        }

        if (lcur_lc(parse) == lc_scolon) {
                /* Body not present */
                lskip(parse);
                block = NULL;

                if (body_expected) {
                        cspan_print(prop->name->cspan);
                        printf(" Error: Property '");
                        symbol_print_fqn(prop->symbol);
                        printf("' getter should have "
                            "a body.\n");
                        parse_note_error(parse);
                }
        } else {
                /* Body present */
                lmatch(parse, lc_is);
                block = parse_block(parse);
                lmatch(parse, lc_end);

                if (!body_expected) {
                        cspan_print(prop->name->cspan);
                        printf(" Error: Property '");
                        symbol_print_fqn(prop->symbol);
                        printf("' getter declaration should "
                            "not have a body.\n");
                        parse_note_error(parse);

                        /* XXX Free block */
                        block = NULL;
                }
        }

        /* Create getter procedure */
        getter = stree_proc_new();
        getter->body = block;
        getter->outer_symbol = prop->symbol;

        /* Store getter in property. */
        prop->getter = getter;
}

/** Parse member property setter.
 *
 * @param parse         Parser object.
 * @param prop          Property containing this declaration.
 */
static void parse_prop_set(parse_t *parse, stree_prop_t *prop)
{
        cspan_t *cspan;
        stree_block_t *block;
        stree_proc_t *setter;
        bool_t body_expected;

        body_expected = (prop->symbol->outer_csi->cc != csi_interface);

        lskip(parse);
        cspan = lprev_span(parse);

        if (prop->setter != NULL) {
                cspan_print(cspan);
                printf(" Error: Duplicate setter.\n");
                parse_note_error(parse);
                return;
        }

        prop->setter_arg = stree_proc_arg_new();
        prop->setter_arg->name = parse_ident(parse);
        prop->setter_arg->type = prop->type;

        if (lcur_lc(parse) == lc_scolon) {
                /* Body not present */
                lskip(parse);

                block = NULL;

                if (body_expected) {
                        cspan_print(prop->name->cspan);
                        printf(" Error: Property '");
                        symbol_print_fqn(prop->symbol);
                        printf("' setter should have "
                            "a body.\n");
                        parse_note_error(parse);
                }
        } else {
                /* Body present */
                lmatch(parse, lc_is);
                block = parse_block(parse);
                lmatch(parse, lc_end);

                if (!body_expected) {
                        cspan_print(prop->name->cspan);
                        printf(" Error: Property '");
                        symbol_print_fqn(prop->symbol);
                        printf("' setter declaration should "
                            "not have a body.\n");
                        parse_note_error(parse);
                }
        }

        /* Create setter procedure */
        setter = stree_proc_new();
        setter->body = block;
        setter->outer_symbol = prop->symbol;

        /* Store setter in property. */
        prop->setter = setter;
}

/** Parse statement block.
 *
 * @param parse         Parser object.
 * @return              New syntax tree node.
 */
static stree_block_t *parse_block(parse_t *parse)
{
        stree_block_t *block;
        stree_stat_t *stat;

        block = stree_block_new();
        list_init(&block->stats);

        /* Avoid peeking if there is an error condition. */
        if (parse_is_error(parse))
                return block;

        while (terminates_block(lcur_lc(parse)) != b_true &&
            !parse_is_error(parse)) {

                stat = parse_stat(parse);
                list_append(&block->stats, stat);
        }

        return block;
}

/** Parse statement.
 *
 * @param parse         Parser object.
 * @return              New syntax tree node.
 */
stree_stat_t *parse_stat(parse_t *parse)
{
        stree_stat_t *stat;

        stree_vdecl_t *vdecl_s;
        stree_if_t *if_s;
        stree_switch_t *switch_s;
        stree_while_t *while_s;
        stree_for_t *for_s;
        stree_raise_t *raise_s;
        stree_break_t *break_s;
        stree_return_t *return_s;
        stree_wef_t *wef_s;
        stree_exps_t *exp_s;

#ifdef DEBUG_PARSE_TRACE
        printf("Parse statement.\n");
#endif
        switch (lcur_lc(parse)) {
        case lc_var:
                vdecl_s = parse_vdecl(parse);
                stat = stree_stat_new(st_vdecl);
                stat->u.vdecl_s = vdecl_s;
                break;
        case lc_if:
                if_s = parse_if(parse);
                stat = stree_stat_new(st_if);
                stat->u.if_s = if_s;
                break;
        case lc_switch:
                switch_s = parse_switch(parse);
                stat = stree_stat_new(st_switch);
                stat->u.switch_s = switch_s;
                break;
        case lc_while:
                while_s = parse_while(parse);
                stat = stree_stat_new(st_while);
                stat->u.while_s = while_s;
                break;
        case lc_for:
                for_s = parse_for(parse);
                stat = stree_stat_new(st_for);
                stat->u.for_s = for_s;
                break;
        case lc_raise:
                raise_s = parse_raise(parse);
                stat = stree_stat_new(st_raise);
                stat->u.raise_s = raise_s;
                break;
        case lc_break:
                break_s = parse_break(parse);
                stat = stree_stat_new(st_break);
                stat->u.break_s = break_s;
                break;
        case lc_return:
                return_s = parse_return(parse);
                stat = stree_stat_new(st_return);
                stat->u.return_s = return_s;
                break;
        case lc_do:
        case lc_with:
                wef_s = parse_wef(parse);
                stat = stree_stat_new(st_wef);
                stat->u.wef_s = wef_s;
                break;
        default:
                exp_s = parse_exps(parse);
                stat = stree_stat_new(st_exps);
                stat->u.exp_s = exp_s;
                break;
        }

#ifdef DEBUG_PARSE_TRACE
        printf("Parsed statement %p\n", stat);
#endif
        return stat;
}

/** Parse variable declaration statement.
 *
 * @param parse         Parser object.
 * @return              New syntax tree node.
 */
static stree_vdecl_t *parse_vdecl(parse_t *parse)
{
        stree_vdecl_t *vdecl;

        vdecl = stree_vdecl_new();

        lmatch(parse, lc_var);
        vdecl->name = parse_ident(parse);
        lmatch(parse, lc_colon);
        vdecl->type = parse_texpr(parse);

        if (lcur_lc(parse) == lc_assign) {
                lskip(parse);
                (void) parse_expr(parse);
        }

        lmatch(parse, lc_scolon);

#ifdef DEBUG_PARSE_TRACE
        printf("Parsed vdecl for '%s'\n", strtab_get_str(vdecl->name->sid));
        printf("vdecl = %p, vdecl->name = %p, sid=%d\n",
            vdecl, vdecl->name, vdecl->name->sid);
#endif
        return vdecl;
}

/** Parse @c if statement.
 *
 * @param parse         Parser object.
 * @return              New syntax tree node.
 */
static stree_if_t *parse_if(parse_t *parse)
{
        stree_if_t *if_s;
        stree_if_clause_t *if_c;

#ifdef DEBUG_PARSE_TRACE
        printf("Parse 'if' statement.\n");
#endif
        if_s = stree_if_new();
        list_init(&if_s->if_clauses);

        /* Parse @c if clause. */
        lmatch(parse, lc_if);

        if_c = stree_if_clause_new();
        if_c->cond = parse_expr(parse);
        lmatch(parse, lc_then);
        if_c->block = parse_block(parse);

        list_append(&if_s->if_clauses, if_c);

        /* Parse @c elif clauses. */
        while (lcur_lc(parse) == lc_elif) {
                lskip(parse);
                if_c = stree_if_clause_new();
                if_c->cond = parse_expr(parse);
                lmatch(parse, lc_then);
                if_c->block = parse_block(parse);

                list_append(&if_s->if_clauses, if_c);
        }

        /* Parse @c else clause. */
        if (lcur_lc(parse) == lc_else) {
                lskip(parse);
                if_s->else_block = parse_block(parse);
        } else {
                if_s->else_block = NULL;
        }

        lmatch(parse, lc_end);
        return if_s;
}

/** Parse @c switch statement.
 *
 * @param parse         Parser object.
 * @return              New syntax tree node.
 */
static stree_switch_t *parse_switch(parse_t *parse)
{
        stree_switch_t *switch_s;
        stree_when_t *when_c;
        stree_expr_t *expr;

#ifdef DEBUG_PARSE_TRACE
        printf("Parse 'switch' statement.\n");
#endif
        lmatch(parse, lc_switch);

        switch_s = stree_switch_new();
        list_init(&switch_s->when_clauses);

        switch_s->expr = parse_expr(parse);
        lmatch(parse, lc_is);

        /* Parse @c when clauses. */
        while (lcur_lc(parse) == lc_when) {
                lskip(parse);
                when_c = stree_when_new();
                list_init(&when_c->exprs);
                while (b_true) {
                        expr = parse_expr(parse);
                        list_append(&when_c->exprs, expr);
                        if (lcur_lc(parse) != lc_comma)
                                break;
                        lskip(parse);
                }

                lmatch(parse, lc_do);
                when_c->block = parse_block(parse);

                list_append(&switch_s->when_clauses, when_c);
        }

        /* Parse @c else clause. */
        if (lcur_lc(parse) == lc_else) {
                lskip(parse);
                lmatch(parse, lc_do);
                switch_s->else_block = parse_block(parse);
        } else {
                switch_s->else_block = NULL;
        }

        lmatch(parse, lc_end);
        return switch_s;
}

/** Parse @c while statement.
 *
 * @param parse         Parser object.
 */
static stree_while_t *parse_while(parse_t *parse)
{
        stree_while_t *while_s;

#ifdef DEBUG_PARSE_TRACE
        printf("Parse 'while' statement.\n");
#endif
        while_s = stree_while_new();

        lmatch(parse, lc_while);
        while_s->cond = parse_expr(parse);
        lmatch(parse, lc_do);
        while_s->body = parse_block(parse);
        lmatch(parse, lc_end);

        return while_s;
}

/** Parse @c for statement.
 *
 * @param parse         Parser object.
 * @return              New syntax tree node.
 */
static stree_for_t *parse_for(parse_t *parse)
{
        stree_for_t *for_s;

#ifdef DEBUG_PARSE_TRACE
        printf("Parse 'for' statement.\n");
#endif
        for_s = stree_for_new();

        lmatch(parse, lc_for);
        lmatch(parse, lc_ident);
        lmatch(parse, lc_colon);
        (void) parse_texpr(parse);
        lmatch(parse, lc_in);
        (void) parse_expr(parse);
        lmatch(parse, lc_do);
        for_s->body = parse_block(parse);
        lmatch(parse, lc_end);

        return for_s;
}

/** Parse @c raise statement.
 *
 * @param parse         Parser object.
 */
static stree_raise_t *parse_raise(parse_t *parse)
{
        stree_raise_t *raise_s;

#ifdef DEBUG_PARSE_TRACE
        printf("Parse 'raise' statement.\n");
#endif
        raise_s = stree_raise_new();
        lmatch(parse, lc_raise);
        raise_s->expr = parse_expr(parse);
        lmatch(parse, lc_scolon);

        return raise_s;
}

/** Parse @c break statement.
 *
 * @param parse         Parser object.
 * @return              New syntax tree node.
 */
static stree_break_t *parse_break(parse_t *parse)
{
        stree_break_t *break_s;

#ifdef DEBUG_PARSE_TRACE
        printf("Parse 'break' statement.\n");
#endif
        break_s = stree_break_new();

        lmatch(parse, lc_break);
        lmatch(parse, lc_scolon);

        return break_s;
}

/** Parse @c return statement.
 *
 * @param parse         Parser object.
 * @return              New syntax tree node.
 */
static stree_return_t *parse_return(parse_t *parse)
{
        stree_return_t *return_s;

#ifdef DEBUG_PARSE_TRACE
        printf("Parse 'return' statement.\n");
#endif
        return_s = stree_return_new();

        lmatch(parse, lc_return);

        if (lcur_lc(parse) != lc_scolon)
                return_s->expr = parse_expr(parse);

        lmatch(parse, lc_scolon);

        return return_s;
}

/** Parse @c with-except-finally statement.
 *
 * @param parse         Parser object.
 * @return              New syntax tree node.
 */
static stree_wef_t *parse_wef(parse_t *parse)
{
        stree_wef_t *wef_s;
        stree_except_t *except_c;

#ifdef DEBUG_PARSE_TRACE
        printf("Parse WEF statement.\n");
#endif
        wef_s = stree_wef_new();
        list_init(&wef_s->except_clauses);

        if (lcur_lc(parse) == lc_with) {
                lmatch(parse, lc_with);
                lmatch(parse, lc_ident);
                lmatch(parse, lc_colon);
                (void) parse_texpr(parse);
                lmatch(parse, lc_assign);
                (void) parse_expr(parse);
        }

        lmatch(parse, lc_do);
        wef_s->with_block = parse_block(parse);

        while (lcur_lc(parse) == lc_except && !parse_is_error(parse)) {
                except_c = parse_except(parse);
                list_append(&wef_s->except_clauses, except_c);
        }

        if (lcur_lc(parse) == lc_finally) {
                lmatch(parse, lc_finally);
                lmatch(parse, lc_do);
                wef_s->finally_block = parse_block(parse);
        } else {
                wef_s->finally_block = NULL;
        }

        lmatch(parse, lc_end);

        return wef_s;
}

/** Parse expression statement.
 *
 * @param parse         Parser object.
 * @return              New syntax tree node.
 */
static stree_exps_t *parse_exps(parse_t *parse)
{
        stree_expr_t *expr;
        stree_exps_t *exps;

#ifdef DEBUG_PARSE_TRACE
        printf("Parse expression statement.\n");
#endif
        expr = parse_expr(parse);
        lmatch(parse, lc_scolon);

        exps = stree_exps_new();
        exps->expr = expr;

        return exps;
}

/** Parse @c except clause.
 *
 * @param parse         Parser object.
 * @return              New syntax tree node.
 */
static stree_except_t *parse_except(parse_t *parse)
{
        stree_except_t *except_c;

#ifdef DEBUG_PARSE_TRACE
        printf("Parse 'except' statement.\n");
#endif
        except_c = stree_except_new();

        lmatch(parse, lc_except);
        except_c->evar = parse_ident(parse);
        lmatch(parse, lc_colon);
        except_c->etype = parse_texpr(parse);
        lmatch(parse, lc_do);

        except_c->block = parse_block(parse);

        return except_c;
}

/** Parse identifier.
 *
 * @param parse         Parser object.
 * @return              New syntax tree node.
 */
stree_ident_t *parse_ident(parse_t *parse)
{
        stree_ident_t *ident;

#ifdef DEBUG_PARSE_TRACE
        printf("Parse identifier.\n");
#endif
        lcheck(parse, lc_ident);
        ident = stree_ident_new();
        ident->sid = lcur(parse)->u.ident.sid;
        ident->cspan = lcur_span(parse);
        lskip(parse);

        return ident;
}

/** Signal a parse error, start bailing out from parser.
 *
 * @param parse         Parser object.
 */
void parse_raise_error(parse_t *parse)
{
        parse->error = b_true;
        parse->error_bailout = b_true;
}

/** Note a parse error that has been immediately recovered.
 *
 * @param parse         Parser object.
 */
void parse_note_error(parse_t *parse)
{
        parse->error = b_true;
}

/** Check if we are currently bailing out of parser due to a parse error.
 *
 * @param parse         Parser object.
 */
bool_t parse_is_error(parse_t *parse)
{
        return parse->error_bailout;
}

/** Recover from parse error bailout.
 *
 * Still remember that there was an error, but stop bailing out.
 *
 * @param parse         Parser object.
 */
void parse_recover_error(parse_t *parse)
{
        assert(parse->error == b_true);
        assert(parse->error_bailout == b_true);

        parse->error_bailout = b_false;
}

/** Return current lem.
 *
 * @param parse         Parser object.
 * @return              Pointer to current lem. Only valid until the lexing
 *                      position is advanced.
 */
lem_t *lcur(parse_t *parse)
{
#ifdef DEBUG_LPARSE_TRACE
        printf("lcur()\n");
#endif
        return lex_get_current(parse->lex);
}

/** Return current lem lclass.
 *
 * @param parse         Parser object
 * @return              Lclass of the current lem
 */
lclass_t lcur_lc(parse_t *parse)
{
        lem_t *lem;

        /*
         * This allows us to skip error checking in many places. If there is an
         * active error, lcur_lc() returns lc_invalid without reading input.
         *
         * Without this measure we would have to check for error all the time
         * or risk requiring extra input from the user (in interactive mode)
         * before actually bailing out from the parser.
         */
        if (parse_is_error(parse))
                return lc_invalid;

        lem = lcur(parse);
        return lem->lclass;
}

/** Return coordinate span of current lem.
 *
 * @param parse         Parser object
 * @return              Coordinate span of current lem or @c NULL if a
 *                      parse error is active
 */
cspan_t *lcur_span(parse_t *parse)
{
        lem_t *lem;

        if (parse_is_error(parse))
                return NULL;

        lem = lcur(parse);
        return lem->cspan;
}

/** Return coordinate span of previous lem.
 *
 * @param parse         Parser object
 * @return              Coordinate span of previous lem or @c NULL if
 *                      parse error is active or previous lem is not
 *                      available.
 */
cspan_t *lprev_span(parse_t *parse)
{
        lem_t *lem;

        if (parse_is_error(parse))
                return NULL;

        lem = lex_peek_prev(parse->lex);
        if (lem == NULL)
                return NULL;

        return lem->cspan;
}

/** Skip to next lem.
 *
 * @param parse         Parser object.
 */
void lskip(parse_t *parse)
{
#ifdef DEBUG_LPARSE_TRACE
        printf("lskip()\n");
#endif
        lex_next(parse->lex);
}

/** Verify that lclass of current lem is @a lc.
 *
 * If a lem of different lclass is found, a parse error is raised and
 * a message is printed.
 *
 * @param parse         Parser object.
 * @param lc            Expected lclass.
 */
void lcheck(parse_t *parse, lclass_t lc)
{
#ifdef DEBUG_LPARSE_TRACE
        printf("lcheck(");
        lclass_print(lc);
        printf(")\n");
#endif
        if (lcur(parse)->lclass != lc) {
                lem_print_coords(lcur(parse));
                printf(" Error: expected '");
                lclass_print(lc);
                printf("', got '");
                lem_print(lcur(parse));
                printf("'.\n");
                parse_raise_error(parse);
        }
}

/** Verify that lclass of current lem is @a lc and go to next lem.
 *
 * If a lem of different lclass is found, a parse error is raised and
 * a message is printed.
 *
 * @param parse         Parser object.
 * @param lc            Expected lclass.
 */
void lmatch(parse_t *parse, lclass_t lc)
{
#ifdef DEBUG_LPARSE_TRACE
        printf("lmatch(");
        lclass_print(lc);
        printf(")\n");
#endif
        /*
         * This allows us to skip error checking in many places. If there is an
         * active error, lmatch() does nothing (similar to parse_block(), etc.
         *
         * Without this measure we would have to check for error all the time
         * or risk requiring extra input from the user (in interactive mode)
         * before actually bailing out from the parser.
         */
        if (parse_is_error(parse))
                return;

        lcheck(parse, lc);
        lskip(parse);
}

/** Raise and display generic parsing error.
 *
 * @param parse         Parser object.
 */
void lunexpected_error(parse_t *parse)
{
        lem_print_coords(lcur(parse));
        printf(" Error: unexpected token '");
        lem_print(lcur(parse));
        printf("'.\n");
        parse_raise_error(parse);
}

/** Determine whether @a lclass is in follow(block).
 *
 * Tests whether @a lclass belongs to the follow(block) set, i.e. if it is
 * lclass of a lem that can follow a block in the program.
 *
 * @param lclass        Lclass.
 */
bool_t terminates_block(lclass_t lclass)
{
        switch (lclass) {
        case lc_elif:
        case lc_else:
        case lc_end:
        case lc_except:
        case lc_finally:
        case lc_when:
                return b_true;
        default:
                return b_false;
        }
}

/* [<][>][^][v][top][bottom][index][help] */
HelenOS homepage, sources at GitHub