diff options
Diffstat (limited to 'awkgram.y')
-rw-r--r-- | awkgram.y | 1177 |
1 files changed, 829 insertions, 348 deletions
@@ -53,11 +53,31 @@ static int isnoeffect(OPCODE type); static INSTRUCTION *make_assignable(INSTRUCTION *ip); static void dumpintlstr(const char *str, size_t len); static void dumpintlstr2(const char *str1, size_t len1, const char *str2, size_t len2); -static int include_source(INSTRUCTION *file); -static int load_library(INSTRUCTION *file); +static bool include_source(INSTRUCTION *file, void **srcfile_p); +static bool load_library(INSTRUCTION *file, void **srcfile_p); +static void set_namespace(INSTRUCTION *ns, INSTRUCTION *comment); static void next_sourcefile(void); static char *tokexpand(void); static NODE *set_profile_text(NODE *n, const char *str, size_t len); +static int check_qualified_special(char *token); +static INSTRUCTION *trailing_comment; +static INSTRUCTION *outer_comment; +static INSTRUCTION *interblock_comment; +static INSTRUCTION *pending_comment; +static INSTRUCTION *namespace_chain; + +#ifdef DEBUG_COMMENTS +static void +debug_print_comment_s(const char *name, INSTRUCTION *comment, int line) +{ + if (comment != NULL) + fprintf(stderr, "%d: %s: <%.*s>\n", line, name, + (int) (comment->memory->stlen - 1), + comment->memory->stptr); +} +#define debug_print_comment(comment) \ + debug_print_comment_s(# comment, comment, __LINE__) +#endif #define instruction(t) bcalloc(t, 1, 0) @@ -84,8 +104,8 @@ static void check_funcs(void); static ssize_t read_one_line(int fd, void *buffer, size_t count); static int one_line_close(int fd); -static void split_comment(void); -static void check_comment(void); +static void merge_comments(INSTRUCTION *c1, INSTRUCTION *c2); +static INSTRUCTION *make_braced_statements(INSTRUCTION *lbrace, INSTRUCTION *stmts, INSTRUCTION *rbrace); static void add_sign_to_num(NODE *n, char sign); static bool at_seen = false; @@ -142,6 +162,10 @@ extern INSTRUCTION *rule_list; extern int max_args; extern NODE **args_array; +const char awk_namespace[] = "awk"; +const char *current_namespace = awk_namespace; +bool namespace_changed = false; + static INSTRUCTION *rule_block[sizeof(ruletab)]; static INSTRUCTION *ip_rec; @@ -152,21 +176,10 @@ static INSTRUCTION *ip_endfile; static INSTRUCTION *ip_beginfile; INSTRUCTION *main_beginfile; -static INSTRUCTION *comment = NULL; -static INSTRUCTION *prior_comment = NULL; -static INSTRUCTION *comment_to_save = NULL; -static INSTRUCTION *program_comment = NULL; -static INSTRUCTION *function_comment = NULL; -static INSTRUCTION *block_comment = NULL; - -static bool func_first = true; -static bool first_rule = true; - static inline INSTRUCTION *list_create(INSTRUCTION *x); static inline INSTRUCTION *list_append(INSTRUCTION *l, INSTRUCTION *x); static inline INSTRUCTION *list_prepend(INSTRUCTION *l, INSTRUCTION *x); static inline INSTRUCTION *list_merge(INSTRUCTION *l1, INSTRUCTION *l2); -static inline INSTRUCTION *add_pending_comment(INSTRUCTION *stmt); extern double fmod(double x, double y); @@ -187,7 +200,7 @@ extern double fmod(double x, double y); %token LEX_AND LEX_OR INCREMENT DECREMENT %token LEX_BUILTIN LEX_LENGTH %token LEX_EOF -%token LEX_INCLUDE LEX_EVAL LEX_LOAD +%token LEX_INCLUDE LEX_EVAL LEX_LOAD LEX_NAMESPACE %token NEWLINE /* Lowest to highest */ @@ -214,12 +227,22 @@ extern double fmod(double x, double y); program : /* empty */ + { $$ = NULL; } | program rule { rule = 0; yyerrok; } | program nls + { + if ($2 != NULL) { + if ($1 == NULL) + outer_comment = $2; + else + interblock_comment = $2; + } + $$ = $1; + } | program LEX_EOF { next_sourcefile(); @@ -239,7 +262,10 @@ rule : pattern action { (void) append_rule($1, $2); - first_rule = false; + if (pending_comment != NULL) { + interblock_comment = pending_comment; + pending_comment = NULL; + } } | pattern statement_term { @@ -249,26 +275,51 @@ rule } else if ($1 == NULL) { msg(_("each rule must have a pattern or an action part")); errcount++; - } else /* pattern rule with non-empty pattern */ + } else { /* pattern rule with non-empty pattern */ + if ($2 != NULL) + list_append($1, $2); (void) append_rule($1, NULL); + } } | function_prologue action { in_function = NULL; (void) mk_function($1, $2); want_param_names = DONT_CHECK; + if (pending_comment != NULL) { + interblock_comment = pending_comment; + pending_comment = NULL; + } yyerrok; } | '@' LEX_INCLUDE source statement_term { want_source = false; at_seen = false; + if ($3 != NULL && $4 != NULL) { + SRCFILE *s = (SRCFILE *) $3; + s->comment = $4; + } yyerrok; } | '@' LEX_LOAD library statement_term { want_source = false; at_seen = false; + if ($3 != NULL && $4 != NULL) { + SRCFILE *s = (SRCFILE *) $3; + s->comment = $4; + } + yyerrok; + } + | '@' LEX_NAMESPACE namespace statement_term + { + want_source = false; + at_seen = false; + + // this frees $3 storage in all cases + set_namespace($3, $4); + yyerrok; } ; @@ -276,11 +327,13 @@ rule source : FILENAME { - if (include_source($1) < 0) + void *srcfile = NULL; + + if (! include_source($1, & srcfile)) YYABORT; efree($1->lextok); bcfree($1); - $$ = NULL; + $$ = (INSTRUCTION *) srcfile; } | FILENAME error { $$ = NULL; } @@ -291,11 +344,13 @@ source library : FILENAME { - if (load_library($1) < 0) + void *srcfile; + + if (! load_library($1, & srcfile)) YYABORT; efree($1->lextok); bcfree($1); - $$ = NULL; + $$ = (INSTRUCTION *) srcfile; } | FILENAME error { $$ = NULL; } @@ -303,98 +358,90 @@ library { $$ = NULL; } ; +namespace + : FILENAME + { $$ = $1; } + | FILENAME error + { $$ = NULL; } + | error + { $$ = NULL; } + ; + pattern : /* empty */ { rule = Rule; - if (comment != NULL) { - $$ = list_create(comment); - comment = NULL; - } else - $$ = NULL; + $$ = NULL; } | exp { rule = Rule; - if (comment != NULL) { - $$ = list_prepend($1, comment); - comment = NULL; - } else - $$ = $1; } - | exp ',' opt_nls exp + | exp comma exp { INSTRUCTION *tp; add_lint($1, LINT_assign_in_cond); - add_lint($4, LINT_assign_in_cond); + add_lint($3, LINT_assign_in_cond); tp = instruction(Op_no_op); list_prepend($1, bcalloc(Op_line_range, !!do_pretty_print + 1, 0)); $1->nexti->triggered = false; - $1->nexti->target_jmp = $4->nexti; + $1->nexti->target_jmp = $3->nexti; list_append($1, instruction(Op_cond_pair)); $1->lasti->line_range = $1->nexti; $1->lasti->target_jmp = tp; - list_append($4, instruction(Op_cond_pair)); - $4->lasti->line_range = $1->nexti; - $4->lasti->target_jmp = tp; + list_append($3, instruction(Op_cond_pair)); + $3->lasti->line_range = $1->nexti; + $3->lasti->target_jmp = tp; if (do_pretty_print) { ($1->nexti + 1)->condpair_left = $1->lasti; - ($1->nexti + 1)->condpair_right = $4->lasti; + ($1->nexti + 1)->condpair_right = $3->lasti; } - if (comment != NULL) { - $$ = list_append(list_merge(list_prepend($1, comment), $4), tp); - comment = NULL; - } else - $$ = list_append(list_merge($1, $4), tp); + /* Put any comments in front of the range expression */ + if ($2 != NULL) + $$ = list_append(list_merge(list_prepend($1, $2), $3), tp); + else + $$ = list_append(list_merge($1, $3), tp); rule = Rule; } | LEX_BEGIN { static int begin_seen = 0; - func_first = false; if (do_lint_old && ++begin_seen == 2) warning_ln($1->source_line, _("old awk does not support multiple `BEGIN' or `END' rules")); $1->in_rule = rule = BEGIN; $1->source_file = source; - check_comment(); $$ = $1; } | LEX_END { static int end_seen = 0; - func_first = false; if (do_lint_old && ++end_seen == 2) warning_ln($1->source_line, _("old awk does not support multiple `BEGIN' or `END' rules")); $1->in_rule = rule = END; $1->source_file = source; - check_comment(); $$ = $1; } | LEX_BEGINFILE { - func_first = false; $1->in_rule = rule = BEGINFILE; $1->source_file = source; - check_comment(); $$ = $1; } | LEX_ENDFILE { - func_first = false; $1->in_rule = rule = ENDFILE; $1->source_file = source; - check_comment(); $$ = $1; } ; @@ -402,11 +449,17 @@ pattern action : l_brace statements r_brace opt_semi opt_nls { - INSTRUCTION *ip; - if ($2 == NULL) - ip = list_create(instruction(Op_no_op)); - else - ip = $2; + INSTRUCTION *ip = make_braced_statements($1, $2, $3); + + if ($3 != NULL && $5 != NULL) { + merge_comments($3, $5); + pending_comment = $3; + } else if ($3 != NULL) { + pending_comment = $3; + } else if ($5 != NULL) { + pending_comment = $5; + } + $$ = ip; } ; @@ -437,33 +490,21 @@ lex_builtin function_prologue : LEX_FUNCTION func_name '(' { want_param_names = FUNC_HEADER; } opt_param_list r_paren opt_nls { - /* - * treat any comments between BOF and the first function - * definition (with no intervening BEGIN etc block) as - * program comments. Special kludge: iff there are more - * than one such comments, treat the last as a function - * comment. - */ - if (prior_comment != NULL) { - comment_to_save = prior_comment; - prior_comment = NULL; - } else if (comment != NULL) { - comment_to_save = comment; - comment = NULL; - } else - comment_to_save = NULL; - - if (comment_to_save != NULL && func_first - && strstr(comment_to_save->memory->stptr, "\n\n") != NULL) - split_comment(); - - /* save any other pre-function comment as function comment */ - if (comment_to_save != NULL) { - function_comment = comment_to_save; - comment_to_save = NULL; + INSTRUCTION *func_comment = NULL; + // Merge any comments found in the parameter list with those + // following the function header, associate the whole shebang + // with the function as one block comment. + if ($5 != NULL && $5->comment != NULL) { + if ($7 != NULL) { + merge_comments($5->comment, $7); + } + func_comment = $5->comment; + } else if ($7 != NULL) { + func_comment = $7; } - func_first = false; + $1->source_file = source; + $1->comment = func_comment; if (install_function($2->lextok, $1, $5) < 0) YYABORT; in_function = $2->lextok; @@ -536,61 +577,25 @@ a_slash statements : /* empty */ - { - if (prior_comment != NULL) { - $$ = list_create(prior_comment); - prior_comment = NULL; - } else if (comment != NULL) { - $$ = list_create(comment); - comment = NULL; - } else - $$ = NULL; - } + { $$ = NULL; } | statements statement { if ($2 == NULL) { - if (prior_comment != NULL) { - $$ = list_append($1, prior_comment); - prior_comment = NULL; - if (comment != NULL) { - $$ = list_append($$, comment); - comment = NULL; - } - } else if (comment != NULL) { - $$ = list_append($1, comment); - comment = NULL; - } else - $$ = $1; + $$ = $1; } else { add_lint($2, LINT_no_effect); if ($1 == NULL) { - if (prior_comment != NULL) { - $$ = list_append($2, prior_comment); - prior_comment = NULL; - if (comment != NULL) { - $$ = list_append($$, comment); - comment = NULL; - } - } else if (comment != NULL) { - $$ = list_append($2, comment); - comment = NULL; - } else - $$ = $2; + $$ = $2; } else { - if (prior_comment != NULL) { - list_append($2, prior_comment); - prior_comment = NULL; - if (comment != NULL) { - list_append($2, comment); - comment = NULL; - } - } else if (comment != NULL) { - list_append($2, comment); - comment = NULL; - } $$ = list_merge($1, $2); } } + + if (trailing_comment != NULL) { + $$ = list_append($$, trailing_comment); + trailing_comment = NULL; + } + yyerrok; } | statements error @@ -598,15 +603,27 @@ statements ; statement_term - : nls - | semi opt_nls + : nls { $$ = $1; } + | semi opt_nls { $$ = $2; } ; statement : semi opt_nls - { $$ = NULL; } + { + if ($2 != NULL) { + INSTRUCTION *ip; + + merge_comments($2, NULL); + ip = list_create(instruction(Op_no_op)); + $$ = list_append(ip, $2); + } else + $$ = NULL; + } | l_brace statements r_brace - { $$ = $2; } + { + trailing_comment = $3; // NULL or comment + $$ = make_braced_statements($1, $2, $3); + } | if_statement { if (do_pretty_print) @@ -632,8 +649,9 @@ statement if ($7 != NULL) { curr = $7->nexti; bcfree($7); /* Op_list */ - } /* else - curr = NULL; */ + } + /* else + curr = NULL; */ for (; curr != NULL; curr = nextc) { INSTRUCTION *caseexp = curr->case_exp; @@ -689,16 +707,33 @@ statement ip = $3; if (do_pretty_print) { + // first merge comments + INSTRUCTION *head_comment = NULL; + + if ($5 != NULL && $6 != NULL) { + merge_comments($5, $6); + head_comment = $5; + } else if ($5 != NULL) + head_comment = $5; + else + head_comment = $6; + + $1->comment = head_comment; + (void) list_prepend(ip, $1); (void) list_prepend(ip, instruction(Op_exec_count)); $1->target_break = tbreak; ($1 + 1)->switch_start = cexp->nexti; ($1 + 1)->switch_end = cexp->lasti; - }/* else - $1 is NULL */ + ($1 + 1)->switch_end->comment = $9; + } + /* else + $1 is NULL */ (void) list_append(cexp, dflt); (void) list_merge(ip, cexp); + if ($8 != NULL) + (void) list_append(cstmt, $8); $$ = list_merge(ip, cstmt); break_allowed--; @@ -733,8 +768,17 @@ statement $1->target_continue = tcont; ($1 + 1)->while_body = ip->lasti; (void) list_prepend(ip, $1); - }/* else - $1 is NULL */ + } + /* else + $1 is NULL */ + + if ($5 != NULL) { + if ($6 == NULL) + $6 = list_create(instruction(Op_no_op)); + + $5->memory->comment_type = BLOCK_COMMENT; + $6 = list_prepend($6, $5); + } if ($6 != NULL) (void) list_merge(ip, $6); @@ -769,8 +813,13 @@ statement ip = list_merge($3, $6); else ip = list_prepend($6, instruction(Op_no_op)); + + if ($2 != NULL) + (void) list_prepend(ip, $2); + if (do_pretty_print) (void) list_prepend(ip, instruction(Op_exec_count)); + (void) list_append(ip, instruction(Op_jmp_true)); ip->lasti->target_jmp = ip->nexti; $$ = list_append(ip, tbreak); @@ -785,7 +834,10 @@ statement ($1 + 1)->doloop_cond = tcont; $$ = list_prepend(ip, $1); bcfree($4); - } /* else + if ($8 != NULL) + $1->comment = $8; + } + /* else $1 and $4 are NULLs */ } | LEX_FOR '(' NAME LEX_IN simple_variable r_paren opt_nls statement @@ -801,7 +853,8 @@ statement && strcmp($8->nexti->memory->vname, var_name) == 0 ) { - /* Efficiency hack. Recognize the special case of + /* + * Efficiency hack. Recognize the special case of * * for (iggy in foo) * delete foo[iggy] @@ -833,6 +886,10 @@ statement bcfree($3); bcfree($4); bcfree($5); + if ($7 != NULL) { + merge_comments($7, NULL); + $8 = list_prepend($8, $7); + } $$ = $8; } else goto regular_loop; @@ -867,8 +924,9 @@ regular_loop: $1->target_continue = tcont; $1->target_break = tbreak; (void) list_append(ip, $1); - } /* else - $1 is NULL */ + } + /* else + $1 is NULL */ /* add update_FOO instruction if necessary */ if ($4->array_var->type == Node_var && $4->array_var->var_update) { @@ -889,8 +947,15 @@ regular_loop: ($1 + 1)->forloop_body = ip->lasti; } - if ($8 != NULL) + if ($7 != NULL) + merge_comments($7, NULL); + + if ($8 != NULL) { + if ($7 != NULL) + $8 = list_prepend($8, $7); (void) list_merge(ip, $8); + } else if ($7 != NULL) + (void) list_append(ip, $7); (void) list_append(ip, instruction(Op_jmp)); ip->lasti->target_jmp = $4; @@ -903,6 +968,20 @@ regular_loop: } | LEX_FOR '(' opt_simple_stmt semi opt_nls exp semi opt_nls opt_simple_stmt r_paren opt_nls statement { + if ($5 != NULL) { + merge_comments($5, NULL); + $1->comment = $5; + } + if ($8 != NULL) { + merge_comments($8, NULL); + if ($1->comment == NULL) { + $8->memory->comment_type = FOR_COMMENT; + $1->comment = $8; + } else + $1->comment->comment = $8; + } + if ($11 != NULL) + $12 = list_prepend($12, $11); $$ = mk_for_loop($1, $3, $6, $9, $12); break_allowed--; @@ -910,6 +989,20 @@ regular_loop: } | LEX_FOR '(' opt_simple_stmt semi opt_nls semi opt_nls opt_simple_stmt r_paren opt_nls statement { + if ($5 != NULL) { + merge_comments($5, NULL); + $1->comment = $5; + } + if ($7 != NULL) { + merge_comments($7, NULL); + if ($1->comment == NULL) { + $7->memory->comment_type = FOR_COMMENT; + $1->comment = $7; + } else + $1->comment->comment = $7; + } + if ($10 != NULL) + $11 = list_prepend($11, $10); $$ = mk_for_loop($1, $3, (INSTRUCTION *) NULL, $8, $11); break_allowed--; @@ -921,7 +1014,6 @@ regular_loop: $$ = list_prepend($1, instruction(Op_exec_count)); else $$ = $1; - $$ = add_pending_comment($$); } ; @@ -933,8 +1025,8 @@ non_compound_stmt _("`break' is not allowed outside a loop or switch")); $1->target_jmp = NULL; $$ = list_create($1); - $$ = add_pending_comment($$); - + if ($2 != NULL) + $$ = list_append($$, $2); } | LEX_CONTINUE statement_term { @@ -943,8 +1035,8 @@ non_compound_stmt _("`continue' is not allowed outside a loop")); $1->target_jmp = NULL; $$ = list_create($1); - $$ = add_pending_comment($$); - + if ($2 != NULL) + $$ = list_append($$, $2); } | LEX_NEXT statement_term { @@ -954,7 +1046,8 @@ non_compound_stmt _("`next' used in %s action"), ruletab[rule]); $1->target_jmp = ip_rec; $$ = list_create($1); - $$ = add_pending_comment($$); + if ($2 != NULL) + $$ = list_append($$, $2); } | LEX_NEXTFILE statement_term { @@ -966,7 +1059,8 @@ non_compound_stmt $1->target_newfile = ip_newfile; $1->target_endfile = ip_endfile; $$ = list_create($1); - $$ = add_pending_comment($$); + if ($2 != NULL) + $$ = list_append($$, $2); } | LEX_EXIT opt_exp statement_term { @@ -982,7 +1076,8 @@ non_compound_stmt $$->nexti->memory = dupnode(Nnull_string); } else $$ = list_append($2, $1); - $$ = add_pending_comment($$); + if ($3 != NULL) + $$ = list_append($$, $3); } | LEX_RETURN { @@ -995,10 +1090,16 @@ non_compound_stmt $$->nexti->memory = dupnode(Nnull_string); } else $$ = list_append($3, $1); - - $$ = add_pending_comment($$); + if ($4 != NULL) + $$ = list_append($$, $4); } | simple_stmt statement_term + { + if ($2 != NULL) + $$ = list_append($1, $2); + else + $$ = $1; + } ; /* @@ -1018,7 +1119,7 @@ simple_stmt * which is faster for these two cases. */ - if ($1->opcode == Op_K_print && + if (do_optimize && $1->opcode == Op_K_print && ($3 == NULL || ($3->lasti->opcode == Op_field_spec && $3->nexti->nexti->nexti == $3->lasti @@ -1106,7 +1207,6 @@ regular_print: } } } - $$ = add_pending_comment($$); } | LEX_DELETE NAME { sub_counter = 0; } delete_subscript_list @@ -1141,7 +1241,6 @@ regular_print: $1->expr_count = sub_counter; $$ = list_append(list_append($4, $2), $1); } - $$ = add_pending_comment($$); } | LEX_DELETE '(' NAME ')' /* @@ -1172,12 +1271,10 @@ regular_print: else if ($3->memory == func_table) fatal(_("`delete' is not allowed with FUNCTAB")); } - $$ = add_pending_comment($$); } | exp { $$ = optimize_assignment($1); - $$ = add_pending_comment($$); } ; @@ -1212,6 +1309,7 @@ case_statement (void) list_prepend(casestmt, instruction(Op_exec_count)); $1->case_exp = $2; $1->case_stmt = casestmt; + $1->comment = $4; bcfree($3); $$ = $1; } @@ -1224,6 +1322,7 @@ case_statement (void) list_prepend(casestmt, instruction(Op_exec_count)); bcfree($2); $1->case_stmt = casestmt; + $1->comment = $3; $$ = $1; } ; @@ -1305,23 +1404,51 @@ output_redir if_statement : LEX_IF '(' exp r_paren opt_nls statement { + if ($5 != NULL) + $1->comment = $5; $$ = mk_condition($3, $1, $6, NULL, NULL); } | LEX_IF '(' exp r_paren opt_nls statement LEX_ELSE opt_nls statement { + if ($5 != NULL) + $1->comment = $5; + if ($8 != NULL) + $7->comment = $8; $$ = mk_condition($3, $1, $6, $7, $9); } ; nls : NEWLINE + { + $$ = $1; + } | nls NEWLINE + { + if ($1 != NULL && $2 != NULL) { + if ($1->memory->comment_type == EOL_COMMENT) { + assert($2->memory->comment_type == BLOCK_COMMENT); + $1->comment = $2; // chain them + } else { + merge_comments($1, $2); + } + + $$ = $1; + } else if ($1 != NULL) { + $$ = $1; + } else if ($2 != NULL) { + $$ = $2; + } else + $$ = NULL; + } ; opt_nls : /* empty */ + { $$ = NULL; } | nls + { $$ = $1; } ; input_redir @@ -1350,9 +1477,17 @@ param_list | param_list comma NAME { if ($1 != NULL && $3 != NULL) { - $3->param_count = $1->lasti->param_count + 1; + $3->param_count = $1->lasti->param_count + 1; $$ = list_append($1, $3); yyerrok; + + // newlines are allowed after commas, catch any comments + if ($2 != NULL) { + if ($1->comment != NULL) + merge_comments($1->comment, $2); + else + $1->comment = $2; + } } else $$ = NULL; } @@ -1384,6 +1519,8 @@ expression_list { $$ = mk_expression_list(NULL, $1); } | expression_list comma exp { + if ($2 != NULL) + $1->lasti->comment = $2; $$ = mk_expression_list($1, $3); yyerrok; } @@ -1405,6 +1542,8 @@ expression_list | expression_list comma error { /* Ditto */ + if ($2 != NULL) + $1->lasti->comment = $2; $$ = $1; } ; @@ -1421,6 +1560,8 @@ fcall_expression_list { $$ = mk_expression_list(NULL, $1); } | fcall_expression_list comma fcall_exp { + if ($2 != NULL) + $1->lasti->comment = $2; $$ = mk_expression_list($1, $3); yyerrok; } @@ -1442,6 +1583,8 @@ fcall_expression_list | fcall_expression_list comma error { /* Ditto */ + if ($2 != NULL) + $1->comment = $2; $$ = $1; } ; @@ -1863,9 +2006,21 @@ direct_func_call : FUNC_CALL '(' opt_fcall_expression_list r_paren { NODE *n; + const char *name = $1->func_name; + + if (current_namespace != awk_namespace && strchr(name, ':') == NULL) { + size_t len = strlen(current_namespace) + 2 + strlen(name) + 1; + char *buf; + + emalloc(buf, char *, len, "direct_func_call"); + sprintf(buf, "%s::%s", current_namespace, name); + + efree((void *) $1->func_name); + $1->func_name = buf; + } if (! at_seen) { - n = lookup($1->func_name); + n = lookup($1->func_name, true); if (n != NULL && n->type != Node_func && n->type != Node_ext_func) { error_ln($1->source_line, @@ -1873,6 +2028,7 @@ direct_func_call $1->func_name); } } + param_sanity($3); $1->opcode = Op_func_call; $1->func_body = NULL; @@ -2006,15 +2162,16 @@ opt_incdec { $1->opcode = Op_postdecrement; } - | /* empty */ { $$ = NULL; } + | /* empty */ + { $$ = NULL; } ; l_brace - : '{' opt_nls + : '{' opt_nls { $$ = $2; } ; r_brace - : '}' opt_nls { yyerrok; } + : '}' opt_nls { $$ = $2; yyerrok; } ; r_paren @@ -2023,6 +2180,7 @@ r_paren opt_semi : /* empty */ + { $$ = NULL; } | semi ; @@ -2035,7 +2193,7 @@ colon ; comma - : ',' opt_nls { yyerrok; } + : ',' opt_nls { $$ = $2; yyerrok; } ; %% @@ -2089,7 +2247,7 @@ static const struct token tokentab[] = { {"BEGIN", Op_rule, LEX_BEGIN, 0, 0, 0}, {"BEGINFILE", Op_rule, LEX_BEGINFILE, GAWKX, 0, 0}, {"END", Op_rule, LEX_END, 0, 0, 0}, -{"ENDFILE", Op_rule, LEX_ENDFILE, GAWKX, 0, 0}, +{"ENDFILE", Op_rule, LEX_ENDFILE, GAWKX, 0, 0}, #ifdef ARRAYDEBUG {"adump", Op_builtin, LEX_BUILTIN, GAWKX|A(1)|A(2)|DEBUG_USE, do_adump, 0}, #endif @@ -2115,8 +2273,8 @@ static const struct token tokentab[] = { {"exp", Op_builtin, LEX_BUILTIN, A(1), do_exp, MPF(exp)}, {"fflush", Op_builtin, LEX_BUILTIN, A(0)|A(1), do_fflush, 0}, {"for", Op_K_for, LEX_FOR, BREAK|CONTINUE, 0, 0}, -{"func", Op_func, LEX_FUNCTION, NOT_POSIX|NOT_OLD, 0, 0}, -{"function",Op_func, LEX_FUNCTION, NOT_OLD, 0, 0}, +{"func", Op_func, LEX_FUNCTION, NOT_POSIX|NOT_OLD, 0, 0}, +{"function", Op_func, LEX_FUNCTION, NOT_OLD, 0, 0}, {"gensub", Op_sub_builtin, LEX_BUILTIN, GAWKX|A(3)|A(4), 0, 0}, {"getline", Op_K_getline_redir, LEX_GETLINE, NOT_OLD, 0, 0}, {"gsub", Op_sub_builtin, LEX_BUILTIN, NOT_OLD|A(2)|A(3), 0, 0}, @@ -2135,6 +2293,7 @@ static const struct token tokentab[] = { {"lshift", Op_builtin, LEX_BUILTIN, GAWKX|A(2), do_lshift, MPF(lshift)}, {"match", Op_builtin, LEX_BUILTIN, NOT_OLD|A(2)|A(3), do_match, 0}, {"mktime", Op_builtin, LEX_BUILTIN, GAWKX|A(1)|A(2), do_mktime, 0}, +{"namespace", Op_symbol, LEX_NAMESPACE, GAWKX, 0, 0}, {"next", Op_K_next, LEX_NEXT, 0, 0, 0}, {"nextfile", Op_K_nextfile, LEX_NEXTFILE, 0, 0, 0}, {"or", Op_builtin, LEX_BUILTIN, GAWKX, do_or, MPF(or)}, @@ -2161,7 +2320,7 @@ static const struct token tokentab[] = { {"systime", Op_builtin, LEX_BUILTIN, GAWKX|A(0), do_systime, 0}, {"tolower", Op_builtin, LEX_BUILTIN, NOT_OLD|A(1), do_tolower, 0}, {"toupper", Op_builtin, LEX_BUILTIN, NOT_OLD|A(1), do_toupper, 0}, -{"typeof", Op_builtin, LEX_BUILTIN, GAWKX|A(1), do_typeof, 0}, +{"typeof", Op_builtin, LEX_BUILTIN, GAWKX|A(1)|A(2), do_typeof, 0}, {"while", Op_K_while, LEX_WHILE, BREAK|CONTINUE, 0, 0}, {"xor", Op_builtin, LEX_BUILTIN, GAWKX, do_xor, MPF(xor)}, }; @@ -2181,15 +2340,22 @@ static int cur_ring_idx; /* getfname --- return name of a builtin function (for pretty printing) */ const char * -getfname(NODE *(*fptr)(int)) +getfname(NODE *(*fptr)(int), bool prepend_awk) { int i, j; + static char buf[100]; j = sizeof(tokentab) / sizeof(tokentab[0]); /* linear search, no other way to do it */ - for (i = 0; i < j; i++) - if (tokentab[i].ptr == fptr || tokentab[i].ptr2 == fptr) + for (i = 0; i < j; i++) { + if (tokentab[i].ptr == fptr || tokentab[i].ptr2 == fptr) { + if (prepend_awk && (tokentab[i].flags & GAWKX) != 0) { + sprintf(buf, "awk::%s", tokentab[i].operator); + return buf; + } return tokentab[i].operator; + } + } return NULL; } @@ -2354,6 +2520,9 @@ yyerror(const char *m, ...) char *buf; int count; static char end_of_file_line[] = "(END OF FILE)"; + static char syntax_error[] = "syntax error"; + static size_t syn_err_len = sizeof(syntax_error) - 1; + bool generic_error = (strncmp(m, syntax_error, syn_err_len) == 0); print_included_from(); @@ -2384,7 +2553,11 @@ yyerror(const char *m, ...) bp = thisline + strlen(thisline); } - msg("%.*s", (int) (bp - thisline), thisline); + if (lexeof && mesg == NULL && generic_error) { + msg("%s", end_of_file_line); + mesg = _("source files / command-line arguments must contain complete functions or rules"); + } else + msg("%.*s", (int) (bp - thisline), thisline); va_start(args, m); if (mesg == NULL) @@ -2475,11 +2648,12 @@ mk_program() cp = end_block; else cp = list_merge(begin_block, end_block); - if (program_comment != NULL) { - (void) list_prepend(cp, program_comment); + + if (interblock_comment != NULL) { + (void) list_append(cp, interblock_comment); + interblock_comment = NULL; } - if (comment != NULL) - (void) list_append(cp, comment); + (void) list_append(cp, ip_atexit); (void) list_append(cp, instruction(Op_stop)); @@ -2490,6 +2664,16 @@ mk_program() (void) list_merge(cp, beginfile_block); (void) list_merge(cp, endfile_block); + if (outer_comment != NULL) { + cp = list_merge(list_create(outer_comment), cp); + outer_comment = NULL; + } + + if (interblock_comment != NULL) { + (void) list_append(cp, interblock_comment); + interblock_comment = NULL; + } + goto out; } else { @@ -2512,12 +2696,16 @@ mk_program() if (begin_block != NULL) cp = list_merge(begin_block, cp); - if (program_comment != NULL) { - (void) list_prepend(cp, program_comment); + if (outer_comment != NULL) { + cp = list_merge(list_create(outer_comment), cp); + outer_comment = NULL; } - if (comment != NULL) { - (void) list_append(cp, comment); + + if (interblock_comment != NULL) { + (void) list_append(cp, interblock_comment); + interblock_comment = NULL; } + (void) list_append(cp, ip_atexit); (void) list_append(cp, instruction(Op_stop)); @@ -2525,10 +2713,6 @@ out: /* delete the Op_list, not needed */ tmp = cp->nexti; bcfree(cp); - /* these variables are not used again but zap them anyway. */ - comment = NULL; - function_comment = NULL; - program_comment = NULL; return tmp; #undef begin_block @@ -2703,33 +2887,35 @@ add_srcfile(enum srctype stype, char *src, SRCFILE *thisfile, bool *already_incl /* include_source --- read program from source included using `@include' */ -static int -include_source(INSTRUCTION *file) +static bool +include_source(INSTRUCTION *file, void **srcfile_p) { SRCFILE *s; char *src = file->lextok; int errcode; bool already_included; + *srcfile_p = NULL; + if (do_traditional || do_posix) { error_ln(file->source_line, _("@include is a gawk extension")); - return -1; + return false; } if (strlen(src) == 0) { if (do_lint) lintwarn_ln(file->source_line, _("empty filename after @include")); - return 0; + return true; } s = add_srcfile(SRC_INC, src, sourcefile, &already_included, &errcode); if (s == NULL) { if (already_included) - return 0; + return true; error_ln(file->source_line, _("can't open source file `%s' for reading (%s)"), src, errcode ? strerror(errcode) : _("reason unknown")); - return -1; + return false; } /* save scanner state for the current sourcefile */ @@ -2739,6 +2925,7 @@ include_source(INSTRUCTION *file) sourcefile->lexptr_begin = lexptr_begin; sourcefile->lexeme = lexeme; sourcefile->lasttok = lasttok; + sourcefile->namespace = current_namespace; /* included file becomes the current source */ sourcefile = s; @@ -2748,42 +2935,54 @@ include_source(INSTRUCTION *file) lasttok = 0; lexeof = false; eof_warned = false; - return 0; + current_namespace = awk_namespace; + *srcfile_p = (void *) s; + return true; } /* load_library --- load a shared library */ -static int -load_library(INSTRUCTION *file) +static bool +load_library(INSTRUCTION *file, void **srcfile_p) { SRCFILE *s; char *src = file->lextok; int errcode; bool already_included; + *srcfile_p = NULL; + if (do_traditional || do_posix) { error_ln(file->source_line, _("@load is a gawk extension")); - return -1; + return false; } + if (strlen(src) == 0) { if (do_lint) lintwarn_ln(file->source_line, _("empty filename after @load")); - return 0; + return true; } - s = add_srcfile(SRC_EXTLIB, src, sourcefile, &already_included, &errcode); - if (s == NULL) { - if (already_included) - return 0; - error_ln(file->source_line, - _("can't open shared library `%s' for reading (%s)"), - src, errcode ? strerror(errcode) : _("reason unknown")); - return -1; + if (do_pretty_print && ! do_profile) { + // create a fake one, don't try to open the file + s = do_add_srcfile(SRC_EXTLIB, src, src, sourcefile); + } else { + s = add_srcfile(SRC_EXTLIB, src, sourcefile, &already_included, &errcode); + if (s == NULL) { + if (already_included) + return true; + error_ln(file->source_line, + _("can't open shared library `%s' for reading (%s)"), + src, errcode ? strerror(errcode) : _("reason unknown")); + return false; + } + + load_ext(s->fullpath); } - load_ext(s->fullpath); - return 0; + *srcfile_p = (void *) s; + return true; } /* next_sourcefile --- read program from the next source in srcfiles */ @@ -2842,11 +3041,15 @@ next_sourcefile() lexeme = sourcefile->lexeme; sourceline = sourcefile->srclines; source = sourcefile->src; + if (current_namespace != awk_namespace) + efree((char *) current_namespace); + current_namespace = sourcefile->namespace; } else { lexptr = NULL; sourceline = 0; source = NULL; lasttok = 0; + current_namespace = awk_namespace; } } @@ -3092,6 +3295,9 @@ check_bad_char(int c) /* nextc --- get the next input character */ +// For namespaces, -e chunks must be syntactic units. +#define NO_CONTINUE_SOURCE_STRINGS 1 + static int nextc(bool check_for_bad) { @@ -3179,6 +3385,7 @@ again: return END_SRC; } } +#undef NO_CONTINUE_SOURCE_STRINGS /* pushback --- push a character back on the input */ @@ -3191,37 +3398,23 @@ pushback(void) (! lexeof && lexptr && lexptr > lexptr_begin ? lexptr-- : lexptr); } -/* check_comment --- check for block comment */ - -void -check_comment(void) -{ - if (comment != NULL) { - if (first_rule) { - program_comment = comment; - } else - block_comment = comment; - comment = NULL; - } - first_rule = false; -} - /* * get_comment --- collect comment text. * Flag = EOL_COMMENT for end-of-line comments. - * Flag = FULL_COMMENT for self-contained comments. + * Flag = BLOCK_COMMENT for self-contained comments. */ -int -get_comment(int flag) +static int +get_comment(enum commenttype flag, INSTRUCTION **comment_instruction) { int c; int sl; + char *p1; + char *p2; + tok = tokstart; tokadd('#'); sl = sourceline; - char *p1; - char *p2; while (true) { while ((c = nextc(false)) != '\n' && c != END_FILE) { @@ -3257,9 +3450,6 @@ get_comment(int flag) break; } - if (comment != NULL) - prior_comment = comment; - /* remove any trailing blank lines (consecutive \n) from comment */ p1 = tok - 1; p2 = tok - 2; @@ -3269,49 +3459,18 @@ get_comment(int flag) tok--; } - comment = bcalloc(Op_comment, 1, sl); - comment->source_file = source; - comment->memory = make_str_node(tokstart, tok - tokstart, 0); - comment->memory->comment_type = flag; + (*comment_instruction) = bcalloc(Op_comment, 1, sl); + (*comment_instruction)->source_file = source; + (*comment_instruction)->memory = make_str_node(tokstart, tok - tokstart, 0); + (*comment_instruction)->memory->comment_type = flag; return c; } -/* split_comment --- split initial comment text into program and function parts */ - -static void -split_comment(void) -{ - char *p; - int l; - NODE *n; - - p = comment_to_save->memory->stptr; - l = comment_to_save->memory->stlen - 3; - /* have at least two comments so split at last blank line (\n\n) */ - while (l >= 0) { - if (p[l] == '\n' && p[l+1] == '\n') { - function_comment = comment_to_save; - n = function_comment->memory; - function_comment->memory = make_string(p + l + 2, n->stlen - l - 2); - /* create program comment */ - program_comment = bcalloc(Op_comment, 1, sourceline); - program_comment->source_file = comment_to_save->source_file; - p[l + 2] = 0; - program_comment->memory = make_str_node(p, l + 2, 0); - comment_to_save = NULL; - freenode(n); - break; - } - else - l--; - } -} - /* allow_newline --- allow newline after &&, ||, ? and : */ static void -allow_newline(void) +allow_newline(INSTRUCTION **new_comment) { int c; @@ -3323,8 +3482,8 @@ allow_newline(void) } if (c == '#') { if (do_pretty_print && ! do_profile) { - /* collect comment byte code iff doing pretty print but not profiling. */ - c = get_comment(EOL_COMMENT); + /* collect comment byte code iff doing pretty print but not profiling. */ + c = get_comment(EOL_COMMENT, new_comment); } else { while ((c = nextc(false)) != '\n' && c != END_FILE) continue; @@ -3391,6 +3550,7 @@ yylex(void) bool intlstr = false; AWKNUM d; bool collecting_typed_regexp = false; + static int qm_col_count = 0; #define GET_INSTRUCTION(op) bcalloc(op, 1, sourceline) @@ -3555,18 +3715,25 @@ retry: return lasttok = NEWLINE; case '#': /* it's a comment */ + yylval = NULL; if (do_pretty_print && ! do_profile) { /* * Collect comment byte code iff doing pretty print * but not profiling. */ + INSTRUCTION *new_comment; + if (lasttok == NEWLINE || lasttok == 0) - c = get_comment(FULL_COMMENT); + c = get_comment(BLOCK_COMMENT, & new_comment); else - c = get_comment(EOL_COMMENT); + c = get_comment(EOL_COMMENT, & new_comment); - if (c == END_FILE) - return lasttok = NEWLINE_EOF; + yylval = new_comment; + + if (c == END_FILE) { + pushback(); + return lasttok = NEWLINE; + } } else { while ((c = nextc(false)) != '\n') { if (c == END_FILE) @@ -3595,7 +3762,10 @@ retry: * Use it at your own risk. We think it's a bad idea, which * is why it's not on by default. */ + yylval = NULL; if (! do_traditional) { + INSTRUCTION *new_comment; + /* strip trailing white-space and/or comment */ while ((c = nextc(true)) == ' ' || c == '\t' || c == '\r') continue; @@ -3607,9 +3777,11 @@ retry: lintwarn( _("use of `\\ #...' line continuation is not portable")); } - if (do_pretty_print && ! do_profile) - c = get_comment(EOL_COMMENT); - else { + if (do_pretty_print && ! do_profile) { + c = get_comment(EOL_COMMENT, & new_comment); + yylval = new_comment; + return lasttok = c; + } else { while ((c = nextc(false)) != '\n') if (c == END_FILE) break; @@ -3630,11 +3802,20 @@ retry: } break; - case ':': case '?': + qm_col_count++; + // fall through + case ':': yylval = GET_INSTRUCTION(Op_cond_exp); - if (! do_posix) - allow_newline(); + if (qm_col_count > 0) { + if (! do_posix) { + INSTRUCTION *new_comment = NULL; + allow_newline(& new_comment); + yylval->comment = new_comment; + } + if (c == ':') + qm_col_count--; + } return lasttok = c; /* @@ -4056,7 +4237,10 @@ retry: case '&': if ((c = nextc(true)) == '&') { yylval = GET_INSTRUCTION(Op_and); - allow_newline(); + INSTRUCTION *new_comment = NULL; + allow_newline(& new_comment); + yylval->comment = new_comment; + return lasttok = LEX_AND; } pushback(); @@ -4066,11 +4250,15 @@ retry: case '|': if ((c = nextc(true)) == '|') { yylval = GET_INSTRUCTION(Op_or); - allow_newline(); + INSTRUCTION *new_comment = NULL; + allow_newline(& new_comment); + yylval->comment = new_comment; + return lasttok = LEX_OR; } else if (! do_traditional && c == '&') { yylval = GET_INSTRUCTION(Op_symbol); yylval->redir_type = redirect_twoway; + return lasttok = (in_print && in_parens == 0 ? IO_OUT : IO_IN); } pushback(); @@ -4118,18 +4306,39 @@ retry: while (c != END_FILE && is_identchar(c)) { tokadd(c); c = nextc(true); + + if (! do_traditional && c == ':') { + int peek = nextc(true); + + if (peek == ':') { // saw identifier:: + tokadd(c); + tokadd(c); + c = nextc(true); + } else + pushback(); + // then continue around the loop, c == ':' + } } tokadd('\0'); pushback(); + (void) validate_qualified_name(tokstart); + /* See if it is a special token. */ - if ((mid = check_special(tokstart)) >= 0) { + if ((mid = check_qualified_special(tokstart)) >= 0) { static int warntab[sizeof(tokentab) / sizeof(tokentab[0])]; int class = tokentab[mid].class; - if ((class == LEX_INCLUDE || class == LEX_LOAD || class == LEX_EVAL) - && lasttok != '@') - goto out; + switch (class) { + case LEX_EVAL: + case LEX_INCLUDE: + case LEX_LOAD: + case LEX_NAMESPACE: + if (lasttok != '@') + goto out; + default: + break; + } /* allow parameter names to shadow the names of gawk extension built-ins */ if ((tokentab[mid].flags & GAWKX) != 0) { @@ -4141,7 +4350,7 @@ retry: goto out; case FUNC_BODY: /* in body, name must be in symbol table for it to be a parameter */ - if ((f = lookup(tokstart)) != NULL) { + if ((f = lookup(tokstart, false)) != NULL) { if (f->type == Node_builtin_func) break; else @@ -4184,6 +4393,7 @@ retry: continue_allowed++; switch (class) { + case LEX_NAMESPACE: case LEX_INCLUDE: case LEX_LOAD: want_source = true; @@ -4203,7 +4413,7 @@ retry: case LEX_END: case LEX_BEGINFILE: case LEX_ENDFILE: - yylval = bcalloc(tokentab[mid].value, 3, sourceline); + yylval = bcalloc(tokentab[mid].value, 4, sourceline); break; case LEX_FOR: @@ -4260,8 +4470,11 @@ out: yylval->lextok = tokkey; #define SMART_ALECK 1 - if (SMART_ALECK && do_lint - && ! goto_warned && strcasecmp(tokkey, "goto") == 0) { + if (SMART_ALECK + && do_lint + && ! goto_warned + && tolower(tokkey[0]) == 'g' + && strcasecmp(tokkey, "goto") == 0) { goto_warned = true; lintwarn(_("`goto' considered harmful!")); } @@ -4442,10 +4655,20 @@ snode(INSTRUCTION *subn, INSTRUCTION *r) if (arg->nexti == arg->lasti && arg->nexti->opcode == Op_push) arg->nexti->opcode = Op_push_arg; /* argument may be array */ } - } else if (r->builtin == do_isarray || r->builtin == do_typeof) { + } else if (r->builtin == do_isarray) { + arg = subn->nexti; + if (arg->nexti == arg->lasti && arg->nexti->opcode == Op_push) + arg->nexti->opcode = Op_push_arg_untyped; /* argument may be untyped */ + } else if (r->builtin == do_typeof) { arg = subn->nexti; if (arg->nexti == arg->lasti && arg->nexti->opcode == Op_push) arg->nexti->opcode = Op_push_arg_untyped; /* argument may be untyped */ + if (nexp == 2) { /* 2nd argument there */ + arg = subn->nexti->lasti->nexti; /* 2nd arg list */ + ip = arg->lasti; + if (ip->opcode == Op_push) + ip->opcode = Op_push_array; + } #ifdef SUPPLY_INTDIV } else if (r->builtin == do_intdiv #ifdef HAVE_MPFR @@ -4623,7 +4846,7 @@ parms_shadow(INSTRUCTION *pc, bool *shadow) * about all shadowed parameters. */ for (i = 0; i < pcount; i++) { - if (lookup(fp[i].param) != NULL) { + if (lookup(fp[i].param, false) != NULL) { warning( _("function `%s': parameter `%s' shadows global variable"), fname, fp[i].param); @@ -4734,13 +4957,15 @@ mk_function(INSTRUCTION *fi, INSTRUCTION *def) /* add any pre-function comment to start of action for profile.c */ - if (function_comment != NULL) { - function_comment->source_line = 0; - (void) list_prepend(def, function_comment); - function_comment = NULL; + if (interblock_comment != NULL) { + interblock_comment->source_line = 0; + merge_comments(interblock_comment, fi->comment); + fi->comment = interblock_comment; + interblock_comment = NULL; } - /* add an implicit return at end; + /* + * Add an implicit return at end; * also used by 'return' command in debugger */ @@ -4748,8 +4973,16 @@ mk_function(INSTRUCTION *fi, INSTRUCTION *def) def->lasti->memory = dupnode(Nnull_string); (void) list_append(def, instruction(Op_K_return)); - if (do_pretty_print) + if (trailing_comment != NULL) { + (void) list_append(def, trailing_comment); + trailing_comment = NULL; + } + + if (do_pretty_print) { + fi[3].nexti = namespace_chain; + namespace_chain = NULL; (void) list_prepend(def, instruction(Op_exec_count)); + } /* fi->opcode = Op_func */ (fi + 1)->firsti = def->nexti; @@ -4781,7 +5014,7 @@ install_function(char *fname, INSTRUCTION *fi, INSTRUCTION *plist) NODE *r, *f; int pcount = 0; - r = lookup(fname); + r = lookup(fname, true); if (r != NULL) { error_ln(fi->source_line, _("function name `%s' previously defined"), fname); return -1; @@ -4834,7 +5067,10 @@ check_params(char *fname, int pcount, INSTRUCTION *list) error_ln(p->source_line, _("function `%s': can't use special variable `%s' as a function parameter"), fname, name); - } + } else if (strchr(name, ':') != NULL) + error_ln(p->source_line, + _("function `%s': parameter `%s' cannot contain a namespace"), + fname, name); /* check for duplicate parameters */ for (j = 0; j < i; j++) { @@ -4922,22 +5158,19 @@ check_funcs() for (i = 0; i < HASHSIZE; i++) { for (fp = ftable[i]; fp != NULL; fp = fp->next) { -#ifdef REALLYMEAN - /* making this the default breaks old code. sigh. */ - if (fp->defined == 0 && ! fp->extension) { - error( - _("function `%s' called but never defined"), fp->name); - errcount++; - } -#else - if (do_lint && fp->defined == 0 && ! fp->extension) - lintwarn( - _("function `%s' called but never defined"), fp->name); -#endif + if (do_lint && ! fp->extension) { + /* + * Making this not a lint check and + * incrementing * errcount breaks old code. + * Sigh. + */ + if (fp->defined == 0) + lintwarn(_("function `%s' called but never defined"), + fp->name); - if (do_lint && fp->used == 0 && ! fp->extension) { - lintwarn(_("function `%s' defined but never called directly"), - fp->name); + if (fp->used == 0) + lintwarn(_("function `%s' defined but never called directly"), + fp->name); } } } @@ -4981,7 +5214,7 @@ variable(int location, char *name, NODETYPE type) { NODE *r; - if ((r = lookup(name)) != NULL) { + if ((r = lookup(name, true)) != NULL) { if (r->type == Node_func || r->type == Node_ext_func ) error_ln(location, _("function `%s' called with space between name and `(',\nor used as a variable or an array"), r->vname); @@ -5448,24 +5681,31 @@ append_rule(INSTRUCTION *pattern, INSTRUCTION *action) if (rule != Rule) { rp = pattern; - if (do_pretty_print) + if (do_pretty_print) { + rp[3].nexti = namespace_chain; + namespace_chain = NULL; (void) list_append(action, instruction(Op_no_op)); + } (rp + 1)->firsti = action->nexti; (rp + 1)->lasti = action->lasti; (rp + 2)->first_line = pattern->source_line; (rp + 2)->last_line = lastline; - if (block_comment != NULL) { - ip = list_prepend(list_prepend(action, block_comment), rp); - block_comment = NULL; - } else - ip = list_prepend(action, rp); - + ip = list_prepend(action, rp); + if (interblock_comment != NULL) { + ip = list_prepend(ip, interblock_comment); + interblock_comment = NULL; + } } else { - rp = bcalloc(Op_rule, 3, 0); + rp = bcalloc(Op_rule, 4, 0); rp->in_rule = Rule; rp->source_file = source; tp = instruction(Op_no_op); + if (do_pretty_print) { + rp[3].nexti = namespace_chain; + namespace_chain = NULL; + } + if (pattern == NULL) { /* assert(action != NULL); */ if (do_pretty_print) @@ -5485,14 +5725,20 @@ append_rule(INSTRUCTION *pattern, INSTRUCTION *action) (rp + 2)->last_line = find_line(pattern, LAST_LINE); action = list_create(instruction(Op_K_print_rec)); if (do_pretty_print) - (void) list_prepend(action, instruction(Op_exec_count)); + action = list_prepend(action, instruction(Op_exec_count)); } else (rp + 2)->last_line = lastline; + if (interblock_comment != NULL) { // was after previous action + pattern = list_prepend(pattern, interblock_comment); + interblock_comment = NULL; + } + if (do_pretty_print) { - (void) list_prepend(pattern, instruction(Op_exec_count)); - (void) list_prepend(action, instruction(Op_exec_count)); + pattern = list_prepend(pattern, instruction(Op_exec_count)); + action = list_prepend(action, instruction(Op_exec_count)); } + (rp + 1)->firsti = action->nexti; (rp + 1)->lasti = tp; ip = list_append( @@ -5864,8 +6110,9 @@ mk_for_loop(INSTRUCTION *forp, INSTRUCTION *init, INSTRUCTION *cond, forp->target_break = tbreak; forp->target_continue = tcont; ret = list_prepend(ret, forp); - } /* else - forp is NULL */ + } + /* else + forp is NULL */ return ret; } @@ -6079,26 +6326,6 @@ list_merge(INSTRUCTION *l1, INSTRUCTION *l2) return l1; } -/* add_pending_comment --- add a pending comment to a statement */ - -static inline INSTRUCTION * -add_pending_comment(INSTRUCTION *stmt) -{ - INSTRUCTION *ret = stmt; - - if (prior_comment != NULL) { - if (function_comment != prior_comment) - ret = list_append(stmt, prior_comment); - prior_comment = NULL; - } else if (comment != NULL && comment->memory->comment_type == EOL_COMMENT) { - if (function_comment != comment) - ret = list_append(stmt, comment); - comment = NULL; - } - - return ret; -} - /* See if name is a special token. */ int @@ -6196,6 +6423,9 @@ one_line_close(int fd) builtin_func_t lookup_builtin(const char *name) { + if (strncmp(name, "awk::", 5) == 0) + name += 5; + int mid = check_special(name); if (mid == -1) @@ -6267,9 +6497,6 @@ install_builtins(void) bool is_alpha(int c) { -#ifdef I_DONT_KNOW_WHAT_IM_DOING - return isalpha(c); -#else /* ! I_DONT_KNOW_WHAT_IM_DOING */ switch (c) { case 'a': case 'b': case 'c': case 'd': case 'e': case 'f': case 'g': case 'h': case 'i': case 'j': case 'k': case 'l': @@ -6284,7 +6511,6 @@ is_alpha(int c) return true; } return false; -#endif /* ! I_DONT_KNOW_WHAT_IM_DOING */ } /* is_alnum --- return true for alphanumeric, English only letters */ @@ -6342,3 +6568,258 @@ set_profile_text(NODE *n, const char *str, size_t len) return n; } + +/* + * merge_comments --- merge c2 into c1 and free c2 if successful. + * Allow c2 to be NULL, in which case just merged chained + * comments in c1. + */ + +static void +merge_comments(INSTRUCTION *c1, INSTRUCTION *c2) +{ + assert(c1->opcode == Op_comment); + + if (c1->comment == NULL && c2 == NULL) // nothing to do + return; + + size_t total = c1->memory->stlen; + if (c1->comment != NULL) + total += 1 /* \n */ + c1->comment->memory->stlen; + + if (c2 != NULL) { + assert(c2->opcode == Op_comment); + total += 1 /* \n */ + c2->memory->stlen; + if (c2->comment != NULL) + total += c2->comment->memory->stlen + 1; + } + + char *buffer; + emalloc(buffer, char *, total + 1, "merge_comments"); + + strcpy(buffer, c1->memory->stptr); + if (c1->comment != NULL) { + strcat(buffer, "\n"); + strcat(buffer, c1->comment->memory->stptr); + } + + if (c2 != NULL) { + strcat(buffer, "\n"); + strcat(buffer, c2->memory->stptr); + if (c2->comment != NULL) { + strcat(buffer, "\n"); + strcat(buffer, c2->comment->memory->stptr); + } + + unref(c2->memory); + if (c2->comment != NULL) { + unref(c2->comment->memory); + bcfree(c2->comment); + c2->comment = NULL; + } + bcfree(c2); + } + + c1->memory->comment_type = BLOCK_COMMENT; + free(c1->memory->stptr); + c1->memory->stptr = buffer; + c1->memory->stlen = strlen(buffer); + + // now free everything else + if (c1->comment != NULL) { + unref(c1->comment->memory); + bcfree(c1->comment); + c1->comment = NULL; + } +} + +/* make_braced_statements --- handle `l_brace statements r_brace' with comments */ + +static INSTRUCTION * +make_braced_statements(INSTRUCTION *lbrace, INSTRUCTION *stmts, INSTRUCTION *rbrace) +{ + INSTRUCTION *ip; + + if (stmts == NULL) + ip = list_create(instruction(Op_no_op)); + else + ip = stmts; + + if (lbrace != NULL) { + INSTRUCTION *comment2 = lbrace->comment; + if (comment2 != NULL) { + ip = list_prepend(ip, comment2); + lbrace->comment = NULL; + } + ip = list_prepend(ip, lbrace); + } + + return ip; +} + +/* validate_qualified_name --- make sure that a qualified name is built correctly */ + +/* + * This routine returns upon first error, no need to produce multiple, possibly + * conflicting / confusing error messages. + */ + +bool +validate_qualified_name(char *token) +{ + char *cp, *cp2; + + // no colon, by definition it's well formed + if ((cp = strchr(token, ':')) == NULL) + return true; + + if (do_traditional || do_posix) { + error_ln(sourceline, _("identifier %s: qualified names not allowed in traditional / POSIX mode"), token); + return false; + } + + if (cp[1] != ':') { // could happen from command line + error_ln(sourceline, _("identifier %s: namespace separator is two colons, not one"), token); + return false; + } + + if (! is_letter(cp[2])) { + error_ln(sourceline, + _("qualified identifier `%s' is badly formed"), + token); + return false; + } + + if ((cp2 = strchr(cp+2, ':')) != NULL) { + error_ln(sourceline, + _("identifier `%s': namespace separator can only appear once in a qualified name"), + token); + return false; + } + + return true; +} + +/* check_qualified_special --- decide if a name is special or not */ + +static int +check_qualified_special(char *token) +{ + char *cp; + + if ((cp = strchr(token, ':')) == NULL && current_namespace == awk_namespace) + return check_special(token); + + /* + * Now it's more complicated. Here are the rules. + * + * 1. Namespace name cannot be a standard awk reserved word or function. + * 2. Subordinate part of the name cannot be standard awk reserved word or function. + * 3. If namespace part is explicitly "awk", return result of check_special(). + * 4. Else return -1 (gawk extensions allowed, we check standard awk in step 2). + */ + + const struct token *tok; + int i; + if (cp == NULL) { // namespace not awk, but a simple identifier + i = check_special(token); + if (i < 0) + return i; + + tok = & tokentab[i]; + if ((tok->flags & GAWKX) != 0 && tok->class == LEX_BUILTIN) + return -1; + else + return i; + } + + char *ns, *end, *subname; + ns = token; + *(end = cp) = '\0'; // temporarily turn it into standalone string + subname = end + 2; + + // First check the namespace part + i = check_special(ns); + if (i >= 0 && (tokentab[i].flags & GAWKX) == 0) { + error_ln(sourceline, _("using reserved identifier `%s' as a namespace is not allowed"), ns); + goto done; + } + + // Now check the subordinate part + i = check_special(subname); + if (i >= 0 && (tokentab[i].flags & GAWKX) == 0 && strcmp(ns, "awk") != 0) { + error_ln(sourceline, _("using reserved identifier `%s' as second component of a qualified name is not allowed"), subname); + goto done; + } + + if (strcmp(ns, "awk") == 0) { + i = check_special(subname); + if (i >= 0) { + if ((tokentab[i].flags & GAWKX) != 0 && tokentab[i].class == LEX_BUILTIN) + ; // gawk additional builtin function, is ok + else + error_ln(sourceline, _("using reserved identifier `%s' as second component of a qualified name is not allowed"), subname); + } + } else + i = -1; +done: + *end = ':'; + return i; +} + +/* set_namespace --- change the current namespace */ + +static void +set_namespace(INSTRUCTION *ns, INSTRUCTION *comment) +{ + if (ns == NULL) + return; + + if (do_traditional || do_posix) { + error_ln(ns->source_line, _("@namespace is a gawk extension")); + efree(ns->lextok); + bcfree(ns); + return; + } + + if (! is_valid_identifier(ns->lextok)) { + error_ln(ns->source_line, _("namespace name `%s' must meet identifier naming rules"), ns->lextok); + efree(ns->lextok); + bcfree(ns); + return; + } + + int mid = check_special(ns->lextok); + + if (mid >= 0) { + error_ln(ns->source_line, _("using reserved identifier `%s' as a namespace is not allowed"), ns->lextok); + efree(ns->lextok); + bcfree(ns); + return; + } + + if (strcmp(ns->lextok, current_namespace) == 0) + efree(ns->lextok); + else if (strcmp(ns->lextok, awk_namespace) == 0) { + efree(ns->lextok); + current_namespace = awk_namespace; + } else { + if (current_namespace != awk_namespace) + efree((char *) current_namespace); + current_namespace = ns->lextok; + } + + // save info and push on front of list of namespaces seen + INSTRUCTION *new_ns = instruction(Op_K_namespace); + new_ns->comment = comment; + new_ns->ns_name = estrdup(current_namespace, strlen(current_namespace)); + new_ns->nexti = namespace_chain; + namespace_chain = new_ns; + + ns->lextok = NULL; + bcfree(ns); + + namespace_changed = true; + + return; +} |