/* * cook - file construction tool * Copyright (C) 1994, 1997, 1998, 2001, 2006, 2007 Peter Miller; * All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111, USA. * * MANIFEST: functions to parse Makefiles */ %{ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #ifdef DEBUG #define YYDEBUG 1 #ifdef YYBISON #define fprintf yytrace2 #else #define printf trace_where(__FILE__, __LINE__), yytrace #endif extern int yydebug; #endif static stmt_ty *rule_context; int no_internal_rules; void gram(char *filename) { int yyparse(void); trace(("gram(filename = %08lX)\n{\n", (long)filename)); lex_open(filename); #if YYDEBUG yydebug = trace_pretest_; #endif yyparse(); lex_close(); trace(("}\n")); } %} %token COLON %token COLON_COLON %token COLON_EQUALS %token COMMAND %token COMMAND_COMMENT %token COMMENT %token DEFINE %token ELSE %token EMPTY %token ENDDEF %token ENDIF %token EOLN %token EQUALS %token EXPORT %token IF %token INCLUDE %token INCLUDE2 %token INCLUDE3 %token OVERRIDE %token PLUS_EQUALS %token UNEXPORT %token VPATH %token VPATH2 %token WORD %union { blob_ty *lv_line; blob_list_ty *lv_list; stmt_ty *lv_stmt; int lv_int; } %type COMMAND COMMENT IF WORD define_head define_word %type COMMAND_COMMENT %type stmt conditional stmts comment assignment %type rule rule_inner rule_lhs rule_lhs_inner %type include vpath define %type command commands optional_commands conditional_commands %type assign_op rule_op %type word_list word_list_optional if %type define_list define_list_optional %right ELSE %% makefile : stmts { int j; stmt_ty *s; stmt_regroup($1); s = stmt_vpath_default(); if (s) stmt_compound_append($1, s); if (!no_internal_rules) { for (j = 0; ; ++j) { s = stmt_rule_default(j); if (!s) break; stmt_compound_append($1, s); } } for (j = 0; ; ++j) { s = stmt_assign_default($1); if (!s) break; stmt_compound_prepend($1, s); } stmt_sort($1); stmt_emit($1); stmt_free($1); } ; stmts : /* empty */ { $$ = stmt_compound_alloc(); } | stmts stmt { $$ = $1; stmt_compound_append($$, $2); } ; stmt : assignment { $$ = $1; } | comment { $$ = $1; } | rule { $$ = $1; } | conditional { $$ = $1; } | include { $$ = $1; } | vpath { $$ = $1; } | define { $$ = $1; } | EOLN { $$ = stmt_blank_alloc(); } | error EOLN { $$ = stmt_blank_alloc(); } ; assignment : WORD assign_op word_list_optional EOLN { $$ = stmt_assign_alloc(0, $1, $2, $3); } | OVERRIDE WORD assign_op word_list_optional EOLN { $$ = stmt_assign_alloc(1, $2, $3, $4); } | EXPORT WORD assign_op word_list_optional EOLN { $$ = stmt_compound_alloc(); stmt_compound_append ( $$, stmt_assign_alloc(1, blob_copy($2), $3, $4) ); stmt_compound_append($$, stmt_export_alloc($2)); } | EXPORT WORD { $$ = stmt_export_alloc($2); } | UNEXPORT WORD { $$ = stmt_unexport_alloc($2); } ; assign_op : EQUALS { $$ = stmt_assign_op_normal; } | PLUS_EQUALS { $$ = stmt_assign_op_plus; } | COLON_EQUALS { $$ = stmt_assign_op_colon; } ; word_list : WORD { $$ = blob_list_alloc(); blob_list_append($$, $1); } | word_list WORD { $$ = $1; blob_list_append($$, $2); } ; word_list_optional : /* empty */ { $$ = blob_list_alloc(); } | word_list { $$ = $1; } ; rule : rule_inner { $$ = $1; rule_context = 0; } ; rule_inner : rule_lhs { $$ = $1; } | rule_lhs commands { $$ = $1; stmt_rule_body($$, $2); } ; rule_lhs : rule_lhs_inner { $$ = $1; rule_context = $$; } ; rule_lhs_inner : word_list rule_op word_list_optional EOLN { $$ = stmt_rule_alloc($1, $2, $3, (blob_list_ty *)0, (blob_list_ty *)0, (blob_list_ty *)0); } | word_list rule_op word_list rule_op word_list_optional EOLN { $$ = stmt_rule_alloc($3, $4, $5, (blob_list_ty *)0, $1, (blob_list_ty *)0); } ; commands : command { $$ = stmt_compound_alloc(); stmt_compound_append($$, $1); } | commands command { $$ = $1; stmt_compound_append($$, $2); } ; command : COMMAND { if (rule_context) stmt_rule_context(rule_context); $$ = stmt_command_alloc($1); } | conditional_commands { $$ = $1; } | COMMAND_COMMENT { $$ = stmt_comment_alloc($1); } ; conditional_commands : if optional_commands endif { $$ = stmt_if_alloc($1, $2, (stmt_ty *)0); } | if optional_commands else optional_commands endif { $$ = stmt_if_alloc($1, $2, $4); } ; optional_commands : /* empty */ { $$ = stmt_compound_alloc(); } | commands { $$ = $1; } ; rule_op : COLON { $$ = 1; } | COLON_COLON { $$ = 2; } ; conditional : if stmts endif { $$ = stmt_if_alloc($1, $2, (stmt_ty *)0); } | if stmts else stmts endif { $$ = stmt_if_alloc($1, $2, $4); } ; if : IF word_list_optional EOLN { $$ = $2; if (rule_context) stmt_rule_context(rule_context); blob_list_prepend($$, $1); } ; eoln : word_list_optional EOLN { if ($1->length) { blob_error($1->list[0], 0, i18n("garbage on end of line")); } blob_list_free($1); } ; else : ELSE eoln ; endif : ENDIF eoln ; comment : COMMENT { $$ = stmt_comment_alloc($1); } ; include : INCLUDE word_list EOLN { $$ = stmt_include_alloc($2, 1); } | INCLUDE2 word_list EOLN { $$ = stmt_include_alloc($2, 2); } | INCLUDE3 word_list EOLN { $$ = stmt_include_alloc($2, 3); } ; vpath : VPATH WORD word_list EOLN { blob_free($2); stmt_vpath_remember1($3); $$ = stmt_blank_alloc(); } | VPATH2 assign_op word_list_optional EOLN { stmt_vpath_remember2($3); $$ = stmt_blank_alloc(); } ; define : define_head define_list_optional define_end { size_t j; /* * append newline to all but the last */ for (j = 1; j < $2->length; ++j) { string_ty *s; s = $2->list[j - 1]->text; $2->list[j - 1]->text = str_format("%s\n", s->str_text); str_free(s); } /* * Special case the last string if it is empty. * The last string will be empty if the user * wanted a trailing newline. Cook can say this * more elegantly. */ if ($2->length && $2->list[$2->length - 1]->text->str_length == 0) { $2->length--; blob_free($2->list[$2->length]); } /* * now treat it as a normal assignment */ $$ = stmt_assign_alloc(0, $1, stmt_assign_op_normal, $2); } ; define_head : DEFINE WORD EOLN { $$ = $2; } ; define_end : ENDDEF eoln ; define_list_optional : /* empty */ { $$ = blob_list_alloc(); } | define_list { $$ = $1; } ; define_list : define_word { $$ = blob_list_alloc(); blob_list_append($$, $1); } | define_list define_word { $$ = $1; blob_list_append($$, $2); } ; define_word : WORD { $$ = $1; } | EOLN { $$ = lex_blob(str_from_c("")); } ;