1 /* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 4; tab-width: 4 -*- */
4 * Copyright (C) Sébastien Granjoux 2009 <seb.sfo@free.fr>
6 * main.c is free software: you can redistribute it and/or modify it
7 * under the terms of the GNU General Public License as published by the
8 * Free Software Foundation, either version 3 of the License, or
9 * (at your option) any later version.
11 * main.c is distributed in the hope that it will be useful, but
12 * WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
14 * See the GNU General Public License for more details.
16 * You should have received a copy of the GNU General Public License along
17 * with this program. If not, see <http://www.gnu.org/licenses/>.
22 #include "am-scanner.h"
23 #include "am-parser.h"
25 #include "amp-target.h"
27 #include "libanjuta/anjuta-debug.h"
28 #include "libanjuta/anjuta-token-stream.h"
34 #define YY_INPUT(buffer, result, max_size) result = anjuta_token_stream_read (yyextra->stream, buffer, max_size)
36 #define YY_EXTRA_TYPE AmpAmScanner*
38 #define YY_DECL static int am_yylex (YYSTYPE * yylval_param,YYLTYPE * yylloc_param ,yyscan_t yyscanner)
40 //#define YY_USER_INIT {yy_flex_debug = 1;}
42 static int amp_am_scanner_parse_end (AmpAmScanner *scanner);
44 #define RETURN(tok) *yylval = anjuta_token_stream_tokenize (yyextra->stream, tok, yyleng); \
51 AnjutaTokenStream *stream;
55 GHashTable *orphan_properties;
58 gboolean eof; /* TRUE to emit EOF at the end */
59 gboolean expansion; /* Expand variables */
62 struct _AmpVariableDepend
70 typedef struct _AmpVariableDepend AmpVariableDepend;
75 %option reentrant noyywrap yylineno
77 /* Remove some warnings */
78 %option nounput noinput noyy_pop_state noyy_top_state
80 %option prefix="amp_am_yy"
82 %option bison-bridge bison-locations
84 %option never-interactive
90 NAME [^ \t\n\r:#=$"'`&@\\]*
94 <INITIAL><<EOF>> { gint ret = amp_am_scanner_parse_end (yyextra); if (ret !=1) return ret; }
96 <INITIAL>\n { RETURN (END_OF_LINE); }
98 <INITIAL>([ ]|\\\n)([ \t]|\\\n)* { RETURN (SPACE); }
100 <INITIAL>([ \t])*#.*\n { RETURN (COMMENT); }
102 <INITIAL>\t { RETURN (TAB); }
104 <INITIAL>@{NAME}@ { RETURN (MACRO); }
106 <INITIAL>\$\([^ \t\n\r:#=$)]+\) { RETURN (VARIABLE); }
108 <INITIAL>\$\{[^ \t\n\r:#=$}]+\} { RETURN (VARIABLE); }
110 <INITIAL>\$[^ \t\n\r\(\{] { RETURN (VARIABLE); }
112 <INITIAL>: { RETURN (COLON); }
114 <INITIAL>:: { RETURN (DOUBLE_COLON); }
116 <INITIAL>; { RETURN (SEMI_COLON); }
118 <INITIAL>\| { RETURN (ORDER); }
120 <INITIAL>\= { RETURN (EQUAL); }
122 <INITIAL>:= { RETURN (IMMEDIATE_EQUAL); }
124 <INITIAL>\?= { RETURN (CONDITIONAL_EQUAL); }
126 <INITIAL>\+= { RETURN (APPEND); }
128 <INITIAL>\\[ ] { RETURN (CHARACTER); }
130 <INITIAL>\\: { RETURN (CHARACTER); }
132 <INITIAL>\\= { RETURN (CHARACTER); }
134 <INITIAL>\\# { RETURN (CHARACTER); }
136 <INITIAL>include { RETURN (INCLUDE); }
138 <INITIAL>\-include { RETURN (INCLUDE); }
140 <INITIAL>SUBDIRS { RETURN (SUBDIRS); }
142 <INITIAL>DIST_SUBDIRS { RETURN (DIST_SUBDIRS); }
144 <INITIAL>{NAME}_DATA { RETURN (_DATA); }
146 <INITIAL>{NAME}_HEADERS { RETURN (_HEADERS); }
148 <INITIAL>{NAME}_LIBRARIES { RETURN (_LIBRARIES); }
150 <INITIAL>{NAME}_LISP { RETURN (_LISP); }
152 <INITIAL>{NAME}_LTLIBRARIES { RETURN (_LTLIBRARIES); }
154 <INITIAL>{NAME}_MANS { RETURN (_MANS); }
156 <INITIAL>{NAME}_PROGRAMS { RETURN (_PROGRAMS); }
158 <INITIAL>{NAME}_PYTHON { RETURN (_PYTHON); }
160 <INITIAL>{NAME}_JAVA { RETURN (_JAVA); }
162 <INITIAL>{NAME}_SCRIPTS { RETURN (_SCRIPTS); }
164 <INITIAL>{NAME}_SOURCES { RETURN (_SOURCES); }
166 <INITIAL>{NAME}_TEXINFOS { RETURN (_TEXINFOS); }
168 <INITIAL>{NAME}dir { RETURN (_DIR); }
170 <INITIAL>AM_LDFLAGS { RETURN (_LDFLAGS);}
172 <INITIAL>AM_CPPFLAGS { RETURN (_CPPFLAGS);}
174 <INITIAL>AM_CFLAGS { RETURN (_CFLAGS);}
176 <INITIAL>AM_CXXFLAGS { RETURN (_CXXFLAGS);}
178 <INITIAL>AM_JAVACFLAGS { RETURN (_JAVACFLAGS);}
180 <INITIAL>AM_VALAFLAGS { RETURN (_VALAFLAGS);}
182 <INITIAL>AM_FCFLAGS { RETURN (_FCFLAGS);}
184 <INITIAL>AM_OBJCFLAGS { RETURN (_OBJCFLAGS);}
186 <INITIAL>AM_LFLAGS { RETURN (_LFLAGS);}
188 <INITIAL>AM_YFLAGS { RETURN (_YFLAGS);}
190 <INITIAL>{NAME}_LDFLAGS { RETURN (TARGET_LDFLAGS);}
192 <INITIAL>{NAME}_CPPFLAGS { RETURN (TARGET_CPPFLAGS);}
194 <INITIAL>{NAME}_CFLAGS { RETURN (TARGET_CFLAGS);}
196 <INITIAL>{NAME}_CXXFLAGS { RETURN (TARGET_CXXFLAGS);}
198 <INITIAL>{NAME}_JAVACFLAGS { RETURN (TARGET_JAVACFLAGS);}
200 <INITIAL>{NAME}_VALAFLAGS { RETURN (TARGET_VALAFLAGS);}
202 <INITIAL>{NAME}_FCFLAGS { RETURN (TARGET_FCFLAGS);}
204 <INITIAL>{NAME}_OBJCFLAGS { RETURN (TARGET_OBJCFLAGS);}
206 <INITIAL>{NAME}_LFLAGS { RETURN (TARGET_LFLAGS);}
208 <INITIAL>{NAME}_YFLAGS { RETURN (TARGET_YFLAGS);}
210 <INITIAL>{NAME}_DEPENDENCIES { RETURN (TARGET_DEPENDENCIES);}
212 <INITIAL>{NAME}_LDADD { RETURN (TARGET_LDADD);}
214 <INITIAL>{NAME}_LIBADD { RETURN (TARGET_LIBADD);}
216 <INITIAL>{NAME} { RETURN (NAME); }
218 <INITIAL>. { RETURN (CHARACTER); }
223 *---------------------------------------------------------------------------*/
226 amp_am_scanner_reparse_token (AmpAmScanner *scanner, AnjutaToken *token, GFile *filename)
231 if (token == NULL) return;
233 token = anjuta_token_concat (token);
235 yylex_init(&scanner->scanner);
236 yyset_extra (scanner, scanner->scanner);
238 root = anjuta_token_new_static (ANJUTA_TOKEN_FILE, NULL);
239 list = amp_am_scanner_parse_token (scanner, root, token, filename, NULL);
240 list = anjuta_token_delete_parent (list);
241 if (list != NULL) anjuta_token_insert_before (token, list);
242 anjuta_token_free (token);
245 /* Private functions, variable dependencies
246 *---------------------------------------------------------------------------*/
248 static AmpVariableDepend *
249 amp_variable_depend_new (void)
251 AmpVariableDepend *dep;
253 dep = g_new0 (AmpVariableDepend, 1);
259 amp_variable_depend_free (AmpVariableDepend *depend)
261 g_list_free (depend->token);
262 g_list_free (depend->depend);
266 list_depend (AnjutaToken *token, gpointer user_data)
268 GList **depend = (GList **)user_data;
270 if (anjuta_token_get_type (token) == ANJUTA_TOKEN_VARIABLE)
276 string = anjuta_token_evaluate_name(token);
277 length = strlen (string);
280 if (string[1] == '(')
282 string[length - 1] = '\0';
290 name = g_strdup (name);
291 *depend = g_list_prepend (*depend, name);
297 convert_dependencies (gchar *variable_name, AmpVariableDepend *variable, gpointer user_data)
300 GHashTable *dependencies = (GHashTable *)user_data;
302 variable->token = g_list_reverse (variable->token);
304 list = g_list_first (variable->depend);
305 if (list == NULL) variable->evaluated = TRUE;
306 for (; list != NULL;)
308 gchar *name = (gchar *)list->data;
309 AmpVariableDepend *depend;
312 depend =g_hash_table_lookup (dependencies, name);
314 next = g_list_next (list);
318 /* Unexisting variable, remove it from dependencies */
319 variable->depend = g_list_delete_link (variable->depend, list);
325 /* Look for duplicate */
326 for (dup = g_list_first (variable->depend); dup != list; dup = g_list_next (dup))
328 if (dup->data == depend) break;
337 /* Find a duplicate */
338 variable->depend = g_list_delete_link (variable->depend, list);
347 amp_am_scanner_compute_dependencies (AmpAmScanner *scanner)
349 GHashTable *variable_dependencies;
352 /* Compute variables dependencies */
353 variable_dependencies = g_hash_table_new_full (g_str_hash, g_str_equal, (GDestroyNotify)g_free, (GDestroyNotify)amp_variable_depend_free);
354 for (variable = g_list_first (scanner->variables); variable != NULL; variable = g_list_next (variable))
360 AmpVariableDepend *depend;
362 arg = anjuta_token_first_item ((AnjutaToken *)variable->data);
363 name = g_strstrip (anjuta_token_evaluate (arg));
365 /* Find variable dependencies data */
366 depend =(AmpVariableDepend *)g_hash_table_lookup (variable_dependencies, name);
369 depend = amp_variable_depend_new ();
371 g_hash_table_insert (variable_dependencies, name, depend);
374 depend->token = g_list_prepend (depend->token, variable->data);
376 /* Find dependencies */
377 value = anjuta_token_last_item ((AnjutaToken *)variable->data);
378 anjuta_token_foreach_token (value, list_depend, &list);
379 depend->depend = g_list_concat (list, depend->depend);
382 /* Replace name in dependencies by pointer */
383 g_hash_table_foreach (variable_dependencies, (GHFunc)convert_dependencies, variable_dependencies);
385 return variable_dependencies;
389 amp_am_scanner_parse_end (AmpAmScanner *scanner)
392 if (scanner->stream == NULL)
400 scanner->eof = FALSE;
404 yypop_buffer_state(scanner->scanner);
405 scanner->stream = anjuta_token_stream_pop (scanner->stream);
407 if (scanner->stream == NULL)
413 scanner->eof = anjuta_token_stream_get_current_file (scanner->stream) != NULL;
415 /* Continue parsing the parent file */
423 *---------------------------------------------------------------------------*/
426 amp_am_yyerror (YYLTYPE *loc, AmpAmScanner *scanner, char const *s)
428 AnjutaTokenFileLocation location;
430 if (amp_project_get_token_location (scanner->project, &location, *loc))
432 g_message ("%s:%d.%d %s\n", location.filename, location.line, location.column, s);
433 g_free (location.filename);
437 g_message ("%s \n", s);
442 amp_am_scanner_set_am_variable (AmpAmScanner *scanner, AnjutaToken *variable)
444 if (!scanner->expansion)
446 /* Keep variable token to expand them at the end */
447 scanner->am_variables = g_list_prepend (scanner->am_variables, variable);
451 amp_project_set_am_variable (scanner->project, scanner->group, variable, scanner->orphan_properties);
456 amp_am_scanner_include (AmpAmScanner *scanner, AnjutaToken *list)
459 AnjutaTokenFile *include;
464 name = anjuta_token_first_item (list);
465 name = anjuta_token_next_item (name);
466 filename = g_strstrip (anjuta_token_evaluate (name));
467 //g_message ("read include =%s=", filename);
468 file = g_file_resolve_relative_path (anjuta_token_stream_get_current_directory (scanner->stream), filename);
470 include = anjuta_token_file_new (file);
471 token = anjuta_token_file_load (include, NULL);
472 amp_am_scanner_parse_token (scanner, list, token, file, NULL);
473 g_object_unref (file);
477 amp_am_scanner_update_variable (AmpAmScanner *scanner, AnjutaToken *variable)
479 if (scanner->expansion == FALSE) scanner->variables = g_list_prepend (scanner->variables, variable);
480 amp_group_node_update_variable (scanner->group, variable);
485 amp_am_scanner_parse_variable (AmpAmScanner *scanner, AnjutaToken *variable)
489 anjuta_token_set_type (variable, ANJUTA_TOKEN_VARIABLE);
491 value = amp_group_node_get_variable_token (scanner->group, variable);
494 amp_am_scanner_parse_token (scanner, variable, value, NULL, NULL);
500 *---------------------------------------------------------------------------*/
503 amp_am_scanner_parse_token (AmpAmScanner *scanner, AnjutaToken *root, AnjutaToken *content, GFile *filename, GError **error)
506 AnjutaTokenStream *stream;
508 stream = anjuta_token_stream_push (scanner->stream, root, content, filename);
509 first = anjuta_token_stream_get_root (stream);
511 scanner->eof = filename != NULL;
513 if (scanner->stream != NULL)
515 /* Parse an included file or a expanded variable */
517 scanner->stream = stream;
518 yypush_buffer_state(yy_create_buffer(NULL, YY_BUF_SIZE, scanner->scanner), scanner->scanner);
525 scanner->stream = stream;
526 ps = amp_am_yypstate_new ();
529 YYSTYPE yylval_param;
530 YYLTYPE yylloc_param;
531 gint yychar = am_yylex (&yylval_param, &yylloc_param, scanner->scanner);
533 yylloc_param = yylval_param;
534 status = amp_am_yypush_parse (ps, yychar, &yylval_param, &yylloc_param, scanner);
536 } while (status == YYPUSH_MORE);
537 amp_am_yypstate_delete (ps);
539 /* Evaluate autotools variable and needed variables at the end */
540 if (scanner->expansion == FALSE)
542 GHashTable *variable_dependencies;
547 variable_dependencies =amp_am_scanner_compute_dependencies (scanner);
548 variables = g_hash_table_get_values (variable_dependencies);
550 /* Reevaluate variable having dependencies */
551 scanner->expansion = TRUE;
555 for (var =variables; var != NULL;)
557 AmpVariableDepend *depend = (AmpVariableDepend *)var->data;
560 next = g_list_next (var);
561 if (depend->depend == NULL)
563 /* No need to reevaluate */
564 variables = g_list_delete_link (variables, var);
568 /* Check that all dependencies are evaluated */
570 gboolean missing = FALSE;
572 for (list = g_list_first (depend->depend); list != NULL; list = g_list_next (list))
574 if (((AmpVariableDepend *)list->data)->evaluated == FALSE)
580 if (missing == FALSE)
582 for (list = g_list_first (depend->token); list != NULL; list = g_list_next (list))
584 amp_am_scanner_reparse_token (scanner, (AnjutaToken *)list->data, filename);
586 variables = g_list_delete_link (variables, var);
587 depend->evaluated = TRUE;
596 /* The remaining variables have dependencies loop, evaluate them in order */
597 if (variables != NULL) g_warning ("Dependencies loop in variables");
598 for (var =variables; var != NULL; var = g_list_next (var))
600 AmpVariableDepend *depend = (AmpVariableDepend *)var->data;
603 for (list = g_list_first (depend->token); list != NULL; list = g_list_next (list))
605 amp_am_scanner_reparse_token (scanner, (AnjutaToken *)list->data, filename);
608 g_list_free (variables);
609 g_hash_table_destroy (variable_dependencies);
611 /* Evaluate autotools variables */
612 scanner->am_variables = g_list_reverse (scanner->am_variables);
613 for (var = g_list_first (scanner->am_variables); var != NULL; var = g_list_next (var))
615 AnjutaToken *token = (AnjutaToken *)var->data;
617 amp_am_scanner_reparse_token (scanner, token, filename);
625 /* Constructor & Destructor
626 *---------------------------------------------------------------------------*/
629 amp_am_scanner_new (AmpProject *project, AmpGroupNode *group)
631 AmpAmScanner *scanner;
633 scanner = g_new0 (AmpAmScanner, 1);
635 scanner->project = project;
636 scanner->group = group;
637 scanner->eof = FALSE;
639 /* Create hash table for sources list */
640 scanner->orphan_properties = g_hash_table_new_full (g_str_hash, g_str_equal, (GDestroyNotify)g_free, (GDestroyNotify)amp_target_node_free);
642 /* Create hash table for variable */
643 scanner->am_variables = NULL;
644 scanner->expansion = FALSE;
646 /* Create list of variable */
647 scanner->variables = NULL;
649 yylex_init(&scanner->scanner);
650 yyset_extra (scanner, scanner->scanner);
656 amp_am_scanner_free (AmpAmScanner *scanner)
658 g_return_if_fail (scanner != NULL);
660 yylex_destroy(scanner->scanner);
662 /* Free unused sources files */
663 g_hash_table_destroy (scanner->orphan_properties);
665 g_list_free (scanner->am_variables);
667 g_list_free (scanner->variables);