/* * 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 #include #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; } }