am-project: Fix loading Makefile.am containing keywords in make rules
[anjuta.git] / plugins / am-project / am-scanner.l
blobf1f717d7e96f9d2cf0ff5ba24587b551700a1526
1 /* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 4; tab-width: 4 -*- */
2 /*
3  * am-scanner.l
4  * Copyright (C) Sébastien Granjoux 2009 <seb.sfo@free.fr>
5  *
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.
10  *
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.
15  *
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/>.
18  */
22 #include "am-scanner.h"
23 #include "am-parser.h"
24 #include "amp-node.h"
25 #include "amp-target.h"
27 #include "libanjuta/anjuta-debug.h"
28 #include "libanjuta/anjuta-token-stream.h"
30 #include <stdlib.h>
31 #include <string.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); \
45                     return tok
47 struct _AmpAmScanner
49         yyscan_t scanner;
51         AnjutaTokenStream *stream;
53         AmpProject *project;
54         AmpGroupNode *group;
55         GHashTable *orphan_properties;
56         GList *am_variables;
57         GList *variables;
58         gboolean eof;           /* TRUE to emit EOF at the end */
59         gboolean expansion;             /* Expand variables */
62 struct _AmpVariableDepend
64         GList *token;
65         GList *depend;
66         const gchar *name;
67         gboolean evaluated;
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
86 %option batch
88 %option debug
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); }
222 /* Private functions
223  *---------------------------------------------------------------------------*/
225 static void
226 amp_am_scanner_reparse_token (AmpAmScanner *scanner, AnjutaToken *token, GFile *filename)
228         AnjutaToken *root;
229         AnjutaToken *list;
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);
255         return dep;
258 static void
259 amp_variable_depend_free (AmpVariableDepend *depend)
261         g_list_free (depend->token);
262         g_list_free (depend->depend);
265 static void
266 list_depend (AnjutaToken *token, gpointer user_data)
268         GList **depend = (GList **)user_data;
270         if (anjuta_token_get_type (token) == ANJUTA_TOKEN_VARIABLE)
271         {
272                 gchar *string;
273                 gchar *name;
274                 guint length;
276                 string = anjuta_token_evaluate_name(token);
277                 length = strlen (string);
278                 if (length > 1)
279                 {
280                         if (string[1] == '(')
281                         {
282                                 string[length - 1] = '\0';
283                                 name = string + 2;
284                         }
285                         else
286                         {
287                                 string[2] = '\0';
288                                 name = string + 1;
289                         }
290                         name = g_strdup (name);
291                         *depend = g_list_prepend (*depend, name);
292                 }
293         }
296 static void
297 convert_dependencies (gchar *variable_name, AmpVariableDepend *variable, gpointer user_data)
299         GList *list;
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;)
307         {
308                 gchar *name = (gchar *)list->data;
309                 AmpVariableDepend *depend;
310                 GList *next;
312                 depend =g_hash_table_lookup (dependencies, name);
313                 g_free (name);
314                 next = g_list_next (list);
316                 if (depend == NULL)
317                 {
318                         /* Unexisting variable, remove it from dependencies */
319                         variable->depend = g_list_delete_link (variable->depend, list);
320                 }
321                 else
322                 {
323                         GList *dup;
325                         /* Look for duplicate */
326                         for (dup = g_list_first (variable->depend); dup != list; dup = g_list_next (dup))
327                         {
328                                 if (dup->data == depend) break;
329                         }
331                         if (dup == list)
332                         {
333                                 dup->data = depend;
334                         }
335                         else
336                         {
337                                 /* Find a duplicate */
338                                 variable->depend = g_list_delete_link (variable->depend, list);
339                         }
340                 }
341                 list = next;
342         }
346 static GHashTable *
347 amp_am_scanner_compute_dependencies (AmpAmScanner *scanner)
349         GHashTable *variable_dependencies;
350         GList *variable;
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))
355         {
356                 AnjutaToken *arg;
357                 AnjutaToken *value;
358                 gchar *name;
359                 GList *list = NULL;
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);
367                 if (depend == NULL)
368                 {
369                         depend = amp_variable_depend_new ();
370                         depend->name = name;
371                         g_hash_table_insert (variable_dependencies, name, depend);
372                 }
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);
380         }
382         /* Replace name in dependencies by pointer */
383         g_hash_table_foreach (variable_dependencies, (GHFunc)convert_dependencies, variable_dependencies);
385         return variable_dependencies;
388 static gint
389 amp_am_scanner_parse_end (AmpAmScanner *scanner)
392         if (scanner->stream == NULL)
393         {
394                 yyterminate();
395         }
396         else
397         {
398                 if (scanner->eof)
399                 {
400                         scanner->eof = FALSE;
401                         return END_OF_FILE;
402                 }
404                 yypop_buffer_state(scanner->scanner);
405         scanner->stream = anjuta_token_stream_pop (scanner->stream);
407                 if (scanner->stream == NULL)
408                 {
409                         yyterminate();
410                 }
411                 else
412                 {
413                         scanner->eof = anjuta_token_stream_get_current_file (scanner->stream) != NULL;
415                         /* Continue parsing the parent file */
416                         return 1;
417                 }
418         }
422 /* Parser functions
423  *---------------------------------------------------------------------------*/
425 void
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))
431     {
432         g_message ("%s:%d.%d %s\n", location.filename, location.line, location.column, s);
433         g_free (location.filename);
434     }
435     else
436     {
437         g_message ("%s \n", s);
438     }
441 void
442 amp_am_scanner_set_am_variable (AmpAmScanner *scanner, AnjutaToken *variable)
444         if (!scanner->expansion)
445         {
446                 /* Keep variable token to expand them at the end */
447                 scanner->am_variables = g_list_prepend (scanner->am_variables, variable);
448         }
449         else
450         {
451             amp_project_set_am_variable (scanner->project, scanner->group, variable, scanner->orphan_properties);
452         }
455 void
456 amp_am_scanner_include (AmpAmScanner *scanner, AnjutaToken *list)
458         GFile *file;
459         AnjutaTokenFile *include;
460         AnjutaToken *token;
461         AnjutaToken *name;
462         gchar *filename;
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);
469         g_free (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);
476 void
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);
484 void
485 amp_am_scanner_parse_variable (AmpAmScanner *scanner, AnjutaToken *variable)
487         AnjutaToken *value;
489         anjuta_token_set_type (variable, ANJUTA_TOKEN_VARIABLE);
491         value = amp_group_node_get_variable_token (scanner->group, variable);
492         if (value != NULL)
493         {
494                 amp_am_scanner_parse_token (scanner, variable, value, NULL, NULL);
495         }
499 /* Public functions
500  *---------------------------------------------------------------------------*/
502 AnjutaToken *
503 amp_am_scanner_parse_token (AmpAmScanner *scanner, AnjutaToken *root, AnjutaToken *content, GFile *filename, GError **error)
505     AnjutaToken *first;
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)
514     {
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);
519     }
520     else
521     {
522         amp_am_yypstate *ps;
523         gint status;
525         scanner->stream = stream;
526         ps = amp_am_yypstate_new ();
527         do
528         {
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)
541                 {
542                         GHashTable *variable_dependencies;
543                         GList *variables;
544                         GList *var;
545                         guint evaluated;
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;
552                         do
553                         {
554                                 evaluated = 0;
555                                 for (var =variables; var != NULL;)
556                                 {
557                                         AmpVariableDepend *depend = (AmpVariableDepend *)var->data;
558                                         GList *next;
560                                         next = g_list_next (var);
561                                         if (depend->depend == NULL)
562                                         {
563                                                 /* No need to reevaluate */
564                                                 variables = g_list_delete_link (variables, var);
565                                         }
566                                         else
567                                         {
568                                                 /* Check that all dependencies are evaluated */
569                                                 GList*list;
570                                                 gboolean missing = FALSE;
572                                                 for (list = g_list_first (depend->depend); list != NULL; list = g_list_next (list))
573                                                 {
574                                                         if (((AmpVariableDepend *)list->data)->evaluated == FALSE)
575                                                         {
576                                                                 missing = TRUE;
577                                                                 break;
578                                                         }
579                                                 }
580                                                 if (missing == FALSE)
581                                                 {
582                                                         for (list = g_list_first (depend->token); list != NULL; list = g_list_next (list))
583                                                         {
584                                                                 amp_am_scanner_reparse_token (scanner, (AnjutaToken *)list->data, filename);
585                                                         }
586                                                         variables = g_list_delete_link (variables, var);
587                                                         depend->evaluated = TRUE;
588                                                         evaluated++;
589                                                 }
590                                         }
591                                         var = next;
592                                 }
593                         }
594                         while (evaluated);
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))
599                         {
600                                 AmpVariableDepend *depend = (AmpVariableDepend *)var->data;
601                                 GList *list;
603                                 for (list = g_list_first (depend->token); list != NULL; list = g_list_next (list))
604                                 {
605                                         amp_am_scanner_reparse_token (scanner, (AnjutaToken *)list->data, filename);
606                                 }
607                         }
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))
614                         {
615                                 AnjutaToken *token = (AnjutaToken *)var->data;
617                                 amp_am_scanner_reparse_token (scanner, token, filename);
618                         }
619                 }
620     }
622     return first;
625 /* Constructor & Destructor
626  *---------------------------------------------------------------------------*/
628 AmpAmScanner *
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);
652         return scanner;
655 void
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);
669         g_free (scanner);