Move the scanner to a separate library.
authorJohan Dahlin <johan@gnome.org>
Tue, 25 Mar 2008 20:53:32 +0000 (20:53 +0000)
committerJohan Dahlin <johan@src.gnome.org>
Tue, 25 Mar 2008 20:53:32 +0000 (20:53 +0000)
2008-03-25  Johan Dahlin  <johan@gnome.org>

* Makefile.am:
* configure.ac:
* giscanner/Makefile.am:
* giscanner/sourcescanner.c:
* giscanner/sourcescanner.h:
* tools/Makefile.am:
* tools/grealpath.h:
* tools/sourcescanner.c:
* tools/sourcescanner.h:

Move the scanner to a separate library.

svn path=/trunk/; revision=164

ChangeLog
Makefile.am
configure.ac
giscanner/Makefile.am [new file with mode: 0644]
giscanner/grealpath.h [moved from tools/grealpath.h with 100% similarity]
giscanner/scannerlexer.l [new file with mode: 0644]
giscanner/scannerparser.y [new file with mode: 0644]
giscanner/sourcescanner.c [moved from tools/sourcescanner.c with 98% similarity]
giscanner/sourcescanner.h [moved from tools/sourcescanner.h with 95% similarity]
tools/Makefile.am

index ef0f824..20be8f6 100644 (file)
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,17 @@
+2008-03-25  Johan Dahlin  <johan@gnome.org>
+
+       * Makefile.am:
+       * configure.ac:
+       * giscanner/Makefile.am:
+       * giscanner/sourcescanner.c:
+       * giscanner/sourcescanner.h:
+       * tools/Makefile.am:
+       * tools/grealpath.h:
+       * tools/sourcescanner.c:
+       * tools/sourcescanner.h:
+
+       Move the scanner to a separate library.
+       
 2008-03-23  Johan Dahlin  <johan@gnome.org>
 
        * tools/Makefile.am:
index 189f1d7..32d1628 100644 (file)
@@ -1,7 +1,7 @@
 ## Process this file with automake to produce Makefile.in
 ACLOCAL_AMFLAGS = -I m4
 
-SUBDIRS = gidl girepository tools tests
+SUBDIRS = gidl girepository giscanner tools tests
 DIST_SUBDIRS = m4 $(SUBDIRS)
 
 pkgconfigdir = $(libdir)/pkgconfig
index 00cbc3c..5ca163d 100644 (file)
@@ -94,6 +94,7 @@ AC_CHECK_FUNCS([memchr strchr strspn strstr strtol strtoull])
 AC_CONFIG_FILES([Makefile
                  gidl/Makefile
                  girepository/Makefile
+                 giscanner/Makefile
                  m4/Makefile
                  tools/Makefile
                  tests/Makefile
diff --git a/giscanner/Makefile.am b/giscanner/Makefile.am
new file mode 100644 (file)
index 0000000..30ed335
--- /dev/null
@@ -0,0 +1,25 @@
+## Process this file with automake to produce Makefile.in
+
+INCLUDES = -I$(top_srcdir)/girepository
+BUILT_SOURCES = scannerparser.c scannerparser.h scannerlexer.c scannerlexer.h
+
+CLEANFILES = scannerparser.c scannerparser.h scannerlexer.c scannerlexer.h
+AM_YFLAGS = -d -t
+
+# Why do I have to do this automake?
+scannerlexer.h: scannerlexer.c
+
+noinst_LTLIBRARIES = libgiscanner.la
+
+libgiscanner_la_SOURCES = \
+       sourcescanner.c                         \
+       sourcescanner.h                         \
+       scannerlexer.l                          \
+       scannerparser.y                         \
+       grealpath.h
+libgiscanner_la_LIBADD = $(GOBJECT_LIBS)
+libgiscanner_la_CFLAGS = $(GOBJECT_CFLAGS)
+
+GCOVSOURCES = $(libgiscanner_la_SOURCES)
+
+include $(top_srcdir)/gcov.mak
similarity index 100%
rename from tools/grealpath.h
rename to giscanner/grealpath.h
diff --git a/giscanner/scannerlexer.l b/giscanner/scannerlexer.l
new file mode 100644 (file)
index 0000000..03455d7
--- /dev/null
@@ -0,0 +1,394 @@
+/* -*- Mode: C -*-
+/* GObject introspection: C lexer
+ *
+ * Copyright (c) 1997 Sandro Sigala  <ssigala@globalnet.it>
+ * Copyright (c) 2007-2008 Jürg Billeter  <j@bitron.ch>
+ *
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+%{
+#include <ctype.h>
+#include <stdio.h>
+
+#include <glib.h>
+#include "sourcescanner.h"
+#include "scannerparser.h"
+#include "grealpath.h"
+
+int lineno;
+
+extern int yylex (GISourceScanner *scanner);
+#define YY_DECL int yylex (GISourceScanner *scanner)
+static int yywrap (void);
+static void parse_comment (GISourceScanner *scanner);
+static void process_directive (GISourceScanner *scanner);
+static int check_identifier (GISourceScanner *scanner, const char *);
+%}
+
+intsuffix                              ([uU][lL]?)|([lL][uU]?)
+fracconst                              ([0-9]*\.[0-9]+)|([0-9]+\.)
+exppart                                        [eE][-+]?[0-9]+
+floatsuffix                            [fFlL]
+chartext                               ([^\'])|(\\.) 
+stringtext                             ([^\"])|(\\.)
+
+%%
+
+"\n"                                   { ++lineno; } /* " */
+[\t\f\v\r ]+                           { /* Ignore whitespace. */ }
+
+"/*"                                   { parse_comment(scanner); }
+"//".*                                 { }
+
+"#define "[a-zA-Z_][a-zA-Z_0-9]*"("    { yyless (yyleng - 1); return FUNCTION_MACRO; }
+"#define "[a-zA-Z_][a-zA-Z_0-9]*       { return OBJECT_MACRO; }
+
+"#"                                    { process_directive(scanner); }
+
+"{"                                    { return '{'; }
+"<%"                                   { return '{'; }
+"}"                                    { return '}'; }
+"%>"                                   { return '}'; }
+"["                                    { return '['; }
+"<:"                                   { return '['; }
+"]"                                    { return ']'; }
+":>"                                   { return ']'; }
+"("                                    { return '('; }
+")"                                    { return ')'; }
+";"                                    { return ';'; }
+":"                                    { return ':'; }
+"..."                                  { return ELLIPSIS; }
+"?"                                    { return '?'; }
+"."                                    { return '.'; }
+"+"                                    { return '+'; }
+"-"                                    { return '-'; }
+"*"                                    { return '*'; }
+"/"                                    { return '/'; }
+"%"                                    { return '%'; }
+"^"                                    { return '^'; }
+"&"                                    { return '&'; }
+"|"                                    { return '|'; }
+"~"                                    { return '~'; }
+"!"                                    { return '!'; }
+"="                                    { return '='; }
+"<"                                    { return '<'; }
+">"                                    { return '>'; }
+"+="                                   { return ADDEQ; }
+"-="                                   { return SUBEQ; }
+"*="                                   { return MULEQ; }
+"/="                                   { return DIVEQ; }
+"%="                                   { return MODEQ; }
+"^="                                   { return XOREQ; }
+"&="                                   { return ANDEQ; }
+"|="                                   { return OREQ; }
+"<<"                                   { return SL; }
+">>"                                   { return SR; }
+"<<="                                  { return SLEQ; }
+">>="                                  { return SREQ; }
+"=="                                   { return EQ; }
+"!="                                   { return NOTEQ; }
+"<="                                   { return LTEQ; }
+">="                                   { return GTEQ; }
+"&&"                                   { return ANDAND; }
+"||"                                   { return OROR; }
+"++"                                   { return PLUSPLUS; }
+"--"                                   { return MINUSMINUS; }
+","                                    { return ','; }
+"->"                                   { return ARROW; }
+
+[a-zA-Z_][a-zA-Z_0-9]*                 { if (scanner->macro_scan) return IDENTIFIER; else REJECT; }
+
+"auto"                                 { return AUTO; }
+"_Bool"                                        { return BOOL; }
+"break"                                        { return BREAK; }
+"case"                                 { return CASE; }
+"char"                                 { return CHAR; }
+"const"                                        { return CONST; }
+"continue"                             { return CONTINUE; }
+"default"                              { return DEFAULT; }
+"do"                                   { return DO; }
+"double"                               { return DOUBLE; }
+"else"                                 { return ELSE; }
+"enum"                                 { return ENUM; }
+"extern"                               { return EXTERN; }
+"float"                                        { return FLOAT; }
+"for"                                  { return FOR; }
+"goto"                                 { return GOTO; }
+"if"                                   { return IF; }
+"inline"                               { return INLINE; }
+"int"                                  { return INT; }
+"long"                                 { return LONG; }
+"register"                             { return REGISTER; }
+"restrict"                             { return RESTRICT; }
+"return"                               { return RETURN; }
+"short"                                        { return SHORT; }
+"signed"                               { return SIGNED; }
+"sizeof"                               { return SIZEOF; }
+"static"                               { return STATIC; }
+"struct"                               { return STRUCT; }
+"switch"                               { return SWITCH; }
+"typedef"                              { return TYPEDEF; }
+"union"                                        { return UNION; }
+"unsigned"                             { return UNSIGNED; }
+"void"                                 { return VOID; }
+"volatile"                             { return VOLATILE; }
+"while"                                        { return WHILE; }
+
+[a-zA-Z_][a-zA-Z_0-9]*                 { return check_identifier(scanner, yytext); }
+
+"0"[xX][0-9a-fA-F]+{intsuffix}?                { return INTEGER; }
+"0"[0-7]+{intsuffix}?                  { return INTEGER; }
+[0-9]+{intsuffix}?                     { return INTEGER; }
+
+{fracconst}{exppart}?{floatsuffix}?    { return FLOATING; }
+[0-9]+{exppart}{floatsuffix}?          { return FLOATING; }
+
+"'"{chartext}*"'"                      { return CHARACTER; }
+"L'"{chartext}*"'"                     { return CHARACTER; }
+
+"\""{stringtext}*"\""                  { return STRING; }
+"L\""{stringtext}*"\""                 { return STRING; }
+
+.                                      { fprintf(stderr, "%s:%d: unexpected character `%c'\n", scanner->current_filename, lineno, yytext[0]); }
+
+%%
+
+static int
+yywrap (void)
+{
+  return 1;
+}
+
+
+static void
+parse_gtkdoc (GISourceScanner *scanner,
+             gchar           *symbol,
+             int             *c1,
+             int             *c2)
+{
+  gboolean isline = FALSE;
+  gchar line[256];
+  int i;
+  gchar **parts;
+  GISourceDirective *directive;
+  char *name,*value;
+  GSList *directives;
+  GSList *options = NULL;
+  char *rname;
+
+  i = 0;
+  do 
+    {
+      *c1 = *c2;
+      if (*c1 == '\n')
+        {
+          isline = TRUE;
+          break;
+        }
+      if (i >= 256)
+        break;
+      line[i++] = *c1;
+      *c2 = input();
+    } while (*c2 != EOF && !(*c1 == '*' && *c2 == '/'));
+  
+  if (!isline)
+    return;
+
+  line[i] = '\0';
+
+  parts = g_strsplit (line, ": ", 3);
+
+  if (g_strv_length (parts) >= 2)
+    {
+      name = parts[0];
+
+      if (g_strv_length (parts) == 3) 
+        {
+          char *ptr = parts[1];
+          GString *current = NULL;
+          gboolean open = (*ptr == '(');
+
+          current = g_string_new ("");
+          value = parts[2];
+
+          while (*ptr++) 
+            {
+              if (*ptr == '(')
+                 open = TRUE;
+              else if (*ptr != ')' && open)
+                g_string_append_c (current, *ptr);
+              else if (*ptr == ')') {
+                options = g_slist_prepend (options, g_strdup (current->str));
+                open = FALSE;
+              }
+            }
+          g_string_free (current, TRUE);
+        } 
+      else
+        value = parts[1];
+    }
+  else /* parts == 1 */
+    {
+      name = parts[0];
+      value = NULL;
+    }
+
+  /*
+   * This is a special case for return values, name will only be
+   * 'eturn' or a valid name, check the call site.
+   * Context-sensitive parsing would probably be the right way to go
+   */
+  if (g_ascii_strncasecmp ("eturn", name, 5) == 0)
+    rname = "return";
+  else
+    rname = name;
+
+  directive = gi_source_directive_new (rname, value, options);
+  directives = g_hash_table_lookup (scanner->directives_map, symbol);
+  directives = g_slist_prepend (directives, directive);
+  g_hash_table_replace (scanner->directives_map, 
+                       g_strdup (symbol), directives);
+
+  g_strfreev (parts);
+  
+}
+
+
+static void
+parse_comment (GISourceScanner *scanner)
+{
+  GString *symbol = NULL;
+  gboolean startofline = FALSE, have_symbol = FALSE, start1 = FALSE, start_symbol = FALSE;
+  int c1, c2;
+
+  c1 = input();
+  c2 = input();
+
+  while (c2 != EOF && !(c1 == '*' && c2 == '/'))
+    {
+      if (c1 == ':')
+        have_symbol = TRUE;
+      else if (c1 == '\n')
+         start1 = TRUE;
+      else if (c1 == '*' && start1)
+         start_symbol = TRUE;
+      else if (!have_symbol && start_symbol) 
+        {
+          if (!symbol)
+            symbol = g_string_new ("");
+          if (c1 != ' ')
+            g_string_append_c (symbol, c1);
+        }
+
+      if (c1 == '\n') 
+        {
+          ++lineno;
+          startofline = TRUE;
+        }
+
+      c1 = c2;
+      c2 = input();
+
+      if ((c1 != '*' && c1 != ' '))
+          startofline = FALSE;
+
+      if (startofline && (c1 == ' ') && (c2 == '@' || (c2 == 'r') || (c2 == 'R')))
+        {
+           c1 = c2;
+           c2 = input();
+           if (symbol)
+             parse_gtkdoc (scanner, symbol->str, &c1, &c2);
+        }
+    }
+
+  if (symbol)
+    g_string_free (symbol, TRUE);
+  
+}
+
+static int
+check_identifier (GISourceScanner *scanner,
+                 const char  *s)
+{
+       /*
+        * This function checks if `s' is a type name or an
+        * identifier.
+        */
+
+       if (gi_source_scanner_is_typedef (scanner, s)) {
+               return TYPEDEF_NAME;
+       } else if (strcmp (s, "__builtin_va_list") == 0) {
+               return TYPEDEF_NAME;
+       }
+
+       return IDENTIFIER;
+}
+
+static void
+process_directive (GISourceScanner *scanner)
+{
+       /* extract current filename from #line directives */
+       GString *filename_builder;
+       gboolean in_string, found_filename;
+
+       lineno = 0;
+       found_filename = FALSE;
+       in_string = FALSE;
+       filename_builder = g_string_new ("");
+
+       int c = input ();
+       while (c != EOF && c != '\n') {
+               if (!in_string) {
+                       if (c == '\"') {
+                               in_string = TRUE;
+                               found_filename = TRUE;
+                       } else if (c >= '0' && c <= '9') {
+                               if (!found_filename) {
+                                       lineno = lineno * 10 + (c - '0');
+                               }
+                       }
+               } else {
+                       if (c == '\"') {
+                               in_string = FALSE;
+                       } else if (c == '\\') {
+                               g_string_append_c (filename_builder, c);
+                               c = input ();
+                               g_string_append_c (filename_builder, c);
+                       } else {
+                               g_string_append_c (filename_builder, c);
+                       }
+               }
+               c = input ();
+       }
+
+       if (filename_builder->len > 0) {
+               char *filename = g_strcompress (filename_builder->str);
+               g_free (scanner->current_filename);
+               scanner->current_filename = g_realpath(filename);
+               g_free(filename);
+       }
+
+       g_string_free (filename_builder, TRUE);
+}
+
diff --git a/giscanner/scannerparser.y b/giscanner/scannerparser.y
new file mode 100644 (file)
index 0000000..45d6789
--- /dev/null
@@ -0,0 +1,1268 @@
+/* GObject introspection: C parser
+ *
+ * Copyright (c) 1997 Sandro Sigala  <ssigala@globalnet.it>
+ * Copyright (c) 2007-2008 Jürg Billeter  <j@bitron.ch>
+ *
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+%{
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+#include <glib.h>
+#include "sourcescanner.h"
+#include "scannerparser.h"
+
+extern FILE *yyin;
+extern int lineno;
+extern char *yytext;
+
+extern int yylex (GISourceScanner *scanner);
+static void yyerror (GISourceScanner *scanner, const char *str);
+static int last_enum_value = -1;
+static GHashTable *const_table = NULL;
+%}
+
+%error-verbose
+%union {
+  char *str;
+  GList *list;
+  GISourceSymbol *symbol;
+  GISourceType *ctype;
+  StorageClassSpecifier storage_class_specifier;
+  TypeQualifier type_qualifier;
+  FunctionSpecifier function_specifier;
+  UnaryOperator unary_operator;
+}
+
+%parse-param { GISourceScanner* scanner }
+%lex-param { GISourceScanner* scanner }
+
+%token <str> IDENTIFIER "identifier"
+%token <str> TYPEDEF_NAME "typedef-name"
+
+%token INTEGER FLOATING CHARACTER STRING
+
+%token ELLIPSIS ADDEQ SUBEQ MULEQ DIVEQ MODEQ XOREQ ANDEQ OREQ SL SR
+%token SLEQ SREQ EQ NOTEQ LTEQ GTEQ ANDAND OROR PLUSPLUS MINUSMINUS ARROW
+
+%token AUTO BOOL BREAK CASE CHAR CONST CONTINUE DEFAULT DO DOUBLE ELSE ENUM
+%token EXTERN FLOAT FOR GOTO IF INLINE INT LONG REGISTER RESTRICT RETURN SHORT
+%token SIGNED SIZEOF STATIC STRUCT SWITCH TYPEDEF UNION UNSIGNED VOID VOLATILE
+%token WHILE
+
+%token FUNCTION_MACRO OBJECT_MACRO
+
+%start translation_unit
+
+%type <ctype> declaration_specifiers
+%type <ctype> enum_specifier
+%type <ctype> pointer
+%type <ctype> specifier_qualifier_list
+%type <ctype> type_name
+%type <ctype> struct_or_union
+%type <ctype> struct_or_union_specifier
+%type <ctype> type_specifier
+%type <str> identifier
+%type <str> typedef_name
+%type <str> identifier_or_typedef_name
+%type <symbol> abstract_declarator
+%type <symbol> init_declarator
+%type <symbol> declarator
+%type <symbol> enumerator
+%type <symbol> direct_abstract_declarator
+%type <symbol> direct_declarator
+%type <symbol> parameter_declaration
+%type <symbol> struct_declarator
+%type <list> enumerator_list
+%type <list> identifier_list
+%type <list> init_declarator_list
+%type <list> parameter_type_list
+%type <list> parameter_list
+%type <list> struct_declaration
+%type <list> struct_declaration_list
+%type <list> struct_declarator_list
+%type <storage_class_specifier> storage_class_specifier
+%type <type_qualifier> type_qualifier
+%type <type_qualifier> type_qualifier_list
+%type <function_specifier> function_specifier
+%type <symbol> expression
+%type <symbol> constant_expression
+%type <symbol> conditional_expression
+%type <symbol> logical_and_expression
+%type <symbol> logical_or_expression
+%type <symbol> inclusive_or_expression
+%type <symbol> exclusive_or_expression
+%type <symbol> multiplicative_expression
+%type <symbol> additive_expression
+%type <symbol> shift_expression
+%type <symbol> relational_expression
+%type <symbol> equality_expression
+%type <symbol> and_expression
+%type <symbol> cast_expression
+%type <symbol> assignment_expression
+%type <symbol> unary_expression
+%type <symbol> postfix_expression
+%type <symbol> primary_expression
+%type <unary_operator> unary_operator
+%type <str> function_macro
+%type <str> object_macro
+%type <symbol> strings
+
+%%
+
+/* A.2.1 Expressions. */
+
+primary_expression
+       : identifier
+         {
+               $$ = g_hash_table_lookup (const_table, $1);
+               if ($$ == NULL) {
+                       $$ = gi_source_symbol_new (CSYMBOL_TYPE_INVALID);
+               } else {
+                       $$ = gi_source_symbol_ref ($$);
+               }
+         }
+       | INTEGER
+         {
+               $$ = gi_source_symbol_new (CSYMBOL_TYPE_CONST);
+               $$->const_int_set = TRUE;
+               if (g_str_has_prefix (yytext, "0x") && strlen (yytext) > 2) {
+                       $$->const_int = strtol (yytext + 2, NULL, 16);
+               } else if (g_str_has_prefix (yytext, "0") && strlen (yytext) > 1) {
+                       $$->const_int = strtol (yytext + 1, NULL, 8);
+               } else {
+                       $$->const_int = atoi (yytext);
+               }
+         }
+       | CHARACTER
+         {
+               $$ = gi_source_symbol_new (CSYMBOL_TYPE_INVALID);
+         }
+       | FLOATING
+         {
+               $$ = gi_source_symbol_new (CSYMBOL_TYPE_INVALID);
+         }
+       | strings
+       | '(' expression ')'
+         {
+               $$ = $2;
+         }
+       ;
+
+/* concatenate adjacent string literal tokens */
+strings
+       : STRING
+         {
+               $$ = gi_source_symbol_new (CSYMBOL_TYPE_CONST);
+               yytext[strlen (yytext) - 1] = '\0';
+               $$->const_string = g_strcompress (yytext + 1);
+         }
+       | strings STRING
+         {
+               char *strings, *string2;
+               $$ = $1;
+               yytext[strlen (yytext) - 1] = '\0';
+               string2 = g_strcompress (yytext + 1);
+               strings = g_strconcat ($$->const_string, string2, NULL);
+               g_free ($$->const_string);
+               g_free (string2);
+               $$->const_string = strings;
+         }
+       ;
+
+identifier
+       : IDENTIFIER
+         {
+               $$ = g_strdup (yytext);
+         }
+       ;
+
+identifier_or_typedef_name
+       : identifier
+       | typedef_name
+       ;
+
+postfix_expression
+       : primary_expression
+       | postfix_expression '[' expression ']'
+         {
+               $$ = gi_source_symbol_new (CSYMBOL_TYPE_INVALID);
+         }
+       | postfix_expression '(' argument_expression_list ')'
+         {
+               $$ = gi_source_symbol_new (CSYMBOL_TYPE_INVALID);
+         }
+       | postfix_expression '(' ')'
+         {
+               $$ = gi_source_symbol_new (CSYMBOL_TYPE_INVALID);
+         }
+       | postfix_expression '.' identifier_or_typedef_name
+         {
+               $$ = gi_source_symbol_new (CSYMBOL_TYPE_INVALID);
+         }
+       | postfix_expression ARROW identifier_or_typedef_name
+         {
+               $$ = gi_source_symbol_new (CSYMBOL_TYPE_INVALID);
+         }
+       | postfix_expression PLUSPLUS
+         {
+               $$ = gi_source_symbol_new (CSYMBOL_TYPE_INVALID);
+         }
+       | postfix_expression MINUSMINUS
+         {
+               $$ = gi_source_symbol_new (CSYMBOL_TYPE_INVALID);
+         }
+       ;
+
+argument_expression_list
+       : assignment_expression
+       | argument_expression_list ',' assignment_expression
+       ;
+
+unary_expression
+       : postfix_expression
+       | PLUSPLUS unary_expression
+         {
+               $$ = gi_source_symbol_new (CSYMBOL_TYPE_INVALID);
+         }
+       | MINUSMINUS unary_expression
+         {
+               $$ = gi_source_symbol_new (CSYMBOL_TYPE_INVALID);
+         }
+       | unary_operator cast_expression
+         {
+               switch ($1) {
+               case UNARY_PLUS:
+                       $$ = $2;
+                       break;
+               case UNARY_MINUS:
+                       $$ = $2;
+                       $$->const_int = -$2->const_int;
+                       break;
+               case UNARY_BITWISE_COMPLEMENT:
+                       $$ = $2;
+                       $$->const_int = ~$2->const_int;
+                       break;
+               case UNARY_LOGICAL_NEGATION:
+                       $$ = $2;
+                       $$->const_int = !gi_source_symbol_get_const_boolean ($2);
+                       break;
+               default:
+                       $$ = gi_source_symbol_new (CSYMBOL_TYPE_INVALID);
+                       break;
+               }
+         }
+       | SIZEOF unary_expression
+         {
+               $$ = gi_source_symbol_new (CSYMBOL_TYPE_INVALID);
+         }
+       | SIZEOF '(' type_name ')'
+         {
+               ctype_free ($3);
+               $$ = gi_source_symbol_new (CSYMBOL_TYPE_INVALID);
+         }
+       ;
+
+unary_operator
+       : '&'
+         {
+               $$ = UNARY_ADDRESS_OF;
+         }
+       | '*'
+         {
+               $$ = UNARY_POINTER_INDIRECTION;
+         }
+       | '+'
+         {
+               $$ = UNARY_PLUS;
+         }
+       | '-'
+         {
+               $$ = UNARY_MINUS;
+         }
+       | '~'
+         {
+               $$ = UNARY_BITWISE_COMPLEMENT;
+         }
+       | '!'
+         {
+               $$ = UNARY_LOGICAL_NEGATION;
+         }
+       ;
+
+cast_expression
+       : unary_expression
+       | '(' type_name ')' cast_expression
+         {
+               ctype_free ($2);
+               $$ = $4;
+         }
+       ;
+
+multiplicative_expression
+       : cast_expression
+       | multiplicative_expression '*' cast_expression
+         {
+               $$ = gi_source_symbol_new (CSYMBOL_TYPE_CONST);
+               $$->const_int_set = TRUE;
+               $$->const_int = $1->const_int * $3->const_int;
+         }
+       | multiplicative_expression '/' cast_expression
+         {
+               $$ = gi_source_symbol_new (CSYMBOL_TYPE_CONST);
+               $$->const_int_set = TRUE;
+               if ($3->const_int != 0) {
+                       $$->const_int = $1->const_int / $3->const_int;
+               }
+         }
+       | multiplicative_expression '%' cast_expression
+         {
+               $$ = gi_source_symbol_new (CSYMBOL_TYPE_CONST);
+               $$->const_int_set = TRUE;
+               $$->const_int = $1->const_int % $3->const_int;
+         }
+       ;
+
+additive_expression
+       : multiplicative_expression
+       | additive_expression '+' multiplicative_expression
+         {
+               $$ = gi_source_symbol_new (CSYMBOL_TYPE_CONST);
+               $$->const_int_set = TRUE;
+               $$->const_int = $1->const_int + $3->const_int;
+         }
+       | additive_expression '-' multiplicative_expression
+         {
+               $$ = gi_source_symbol_new (CSYMBOL_TYPE_CONST);
+               $$->const_int_set = TRUE;
+               $$->const_int = $1->const_int - $3->const_int;
+         }
+       ;
+
+shift_expression
+       : additive_expression
+       | shift_expression SL additive_expression
+         {
+               $$ = gi_source_symbol_new (CSYMBOL_TYPE_CONST);
+               $$->const_int_set = TRUE;
+               $$->const_int = $1->const_int << $3->const_int;
+         }
+       | shift_expression SR additive_expression
+         {
+               $$ = gi_source_symbol_new (CSYMBOL_TYPE_CONST);
+               $$->const_int_set = TRUE;
+               $$->const_int = $1->const_int >> $3->const_int;
+         }
+       ;
+
+relational_expression
+       : shift_expression
+       | relational_expression '<' shift_expression
+         {
+               $$ = gi_source_symbol_new (CSYMBOL_TYPE_CONST);
+               $$->const_int_set = TRUE;
+               $$->const_int = $1->const_int < $3->const_int;
+         }
+       | relational_expression '>' shift_expression
+         {
+               $$ = gi_source_symbol_new (CSYMBOL_TYPE_CONST);
+               $$->const_int_set = TRUE;
+               $$->const_int = $1->const_int > $3->const_int;
+         }
+       | relational_expression LTEQ shift_expression
+         {
+               $$ = gi_source_symbol_new (CSYMBOL_TYPE_CONST);
+               $$->const_int_set = TRUE;
+               $$->const_int = $1->const_int <= $3->const_int;
+         }
+       | relational_expression GTEQ shift_expression
+         {
+               $$ = gi_source_symbol_new (CSYMBOL_TYPE_CONST);
+               $$->const_int_set = TRUE;
+               $$->const_int = $1->const_int >= $3->const_int;
+         }
+       ;
+
+equality_expression
+       : relational_expression
+       | equality_expression EQ relational_expression
+         {
+               $$ = gi_source_symbol_new (CSYMBOL_TYPE_CONST);
+               $$->const_int_set = TRUE;
+               $$->const_int = $1->const_int == $3->const_int;
+         }
+       | equality_expression NOTEQ relational_expression
+         {
+               $$ = gi_source_symbol_new (CSYMBOL_TYPE_CONST);
+               $$->const_int_set = TRUE;
+               $$->const_int = $1->const_int != $3->const_int;
+         }
+       ;
+
+and_expression
+       : equality_expression
+       | and_expression '&' equality_expression
+         {
+               $$ = gi_source_symbol_new (CSYMBOL_TYPE_CONST);
+               $$->const_int_set = TRUE;
+               $$->const_int = $1->const_int & $3->const_int;
+         }
+       ;
+
+exclusive_or_expression
+       : and_expression
+       | exclusive_or_expression '^' and_expression
+         {
+               $$ = gi_source_symbol_new (CSYMBOL_TYPE_CONST);
+               $$->const_int_set = TRUE;
+               $$->const_int = $1->const_int ^ $3->const_int;
+         }
+       ;
+
+inclusive_or_expression
+       : exclusive_or_expression
+       | inclusive_or_expression '|' exclusive_or_expression
+         {
+               $$ = gi_source_symbol_new (CSYMBOL_TYPE_CONST);
+               $$->const_int_set = TRUE;
+               $$->const_int = $1->const_int | $3->const_int;
+         }
+       ;
+
+logical_and_expression
+       : inclusive_or_expression
+       | logical_and_expression ANDAND inclusive_or_expression
+         {
+               $$ = gi_source_symbol_new (CSYMBOL_TYPE_CONST);
+               $$->const_int_set = TRUE;
+               $$->const_int =
+                 gi_source_symbol_get_const_boolean ($1) &&
+                 gi_source_symbol_get_const_boolean ($3);
+         }
+       ;
+
+logical_or_expression
+       : logical_and_expression
+       | logical_or_expression OROR logical_and_expression
+         {
+               $$ = gi_source_symbol_new (CSYMBOL_TYPE_CONST);
+               $$->const_int_set = TRUE;
+               $$->const_int =
+                 gi_source_symbol_get_const_boolean ($1) ||
+                 gi_source_symbol_get_const_boolean ($3);
+         }
+       ;
+
+conditional_expression
+       : logical_or_expression
+       | logical_or_expression '?' expression ':' conditional_expression
+         {
+               $$ = gi_source_symbol_get_const_boolean ($1) ? $3 : $5;
+         }
+       ;
+
+assignment_expression
+       : conditional_expression
+       | unary_expression assignment_operator assignment_expression
+         {
+               $$ = gi_source_symbol_new (CSYMBOL_TYPE_INVALID);
+         }
+       ;
+
+assignment_operator
+       : '='
+       | MULEQ
+       | DIVEQ
+       | MODEQ
+       | ADDEQ
+       | SUBEQ
+       | SLEQ
+       | SREQ
+       | ANDEQ
+       | XOREQ
+       | OREQ
+       ;
+
+expression
+       : assignment_expression
+       | expression ',' assignment_expression
+         {
+               $$ = gi_source_symbol_new (CSYMBOL_TYPE_INVALID);
+         }
+       ;
+
+constant_expression
+       : conditional_expression
+       ;
+
+/* A.2.2 Declarations. */
+
+declaration
+       : declaration_specifiers init_declarator_list ';'
+         {
+               GList *l;
+               for (l = $2; l != NULL; l = l->next) {
+                       GISourceSymbol *sym = l->data;
+                       gi_source_symbol_merge_type (sym, gi_source_type_copy ($1));
+                       if ($1->storage_class_specifier & STORAGE_CLASS_TYPEDEF) {
+                               sym->type = CSYMBOL_TYPE_TYPEDEF;
+                       } else if (sym->base_type->type == CTYPE_FUNCTION) {
+                               sym->type = CSYMBOL_TYPE_FUNCTION;
+                       } else {
+                               sym->type = CSYMBOL_TYPE_OBJECT;
+                       }
+                       gi_source_scanner_add_symbol (scanner, sym);
+                       gi_source_symbol_unref (sym);
+               }
+               ctype_free ($1);
+         }
+       | declaration_specifiers ';'
+         {
+               ctype_free ($1);
+         }
+       ;
+
+declaration_specifiers
+       : storage_class_specifier declaration_specifiers
+         {
+               $$ = $2;
+               $$->storage_class_specifier |= $1;
+         }
+       | storage_class_specifier
+         {
+               $$ = gi_source_type_new (CTYPE_INVALID);
+               $$->storage_class_specifier |= $1;
+         }
+       | type_specifier declaration_specifiers
+         {
+               $$ = $1;
+               $$->base_type = $2;
+         }
+       | type_specifier
+       | type_qualifier declaration_specifiers
+         {
+               $$ = $2;
+               $$->type_qualifier |= $1;
+         }
+       | type_qualifier
+         {
+               $$ = gi_source_type_new (CTYPE_INVALID);
+               $$->type_qualifier |= $1;
+         }
+       | function_specifier declaration_specifiers
+         {
+               $$ = $2;
+               $$->function_specifier |= $1;
+         }
+       | function_specifier
+         {
+               $$ = gi_source_type_new (CTYPE_INVALID);
+               $$->function_specifier |= $1;
+         }
+       ;
+
+init_declarator_list
+       : init_declarator
+         {
+               $$ = g_list_append (NULL, $1);
+         }
+       | init_declarator_list ',' init_declarator
+         {
+               $$ = g_list_append ($1, $3);
+         }
+       ;
+
+init_declarator
+       : declarator
+       | declarator '=' initializer
+       ;
+
+storage_class_specifier
+       : TYPEDEF
+         {
+               $$ = STORAGE_CLASS_TYPEDEF;
+         }
+       | EXTERN
+         {
+               $$ = STORAGE_CLASS_EXTERN;
+         }
+       | STATIC
+         {
+               $$ = STORAGE_CLASS_STATIC;
+         }
+       | AUTO
+         {
+               $$ = STORAGE_CLASS_AUTO;
+         }
+       | REGISTER
+         {
+               $$ = STORAGE_CLASS_REGISTER;
+         }
+       ;
+
+type_specifier
+       : VOID
+         {
+               $$ = gi_source_type_new (CTYPE_VOID);
+         }
+       | CHAR
+         {
+               $$ = gi_source_basic_type_new ("char");
+         }
+       | SHORT
+         {
+               $$ = gi_source_basic_type_new ("short");
+         }
+       | INT
+         {
+               $$ = gi_source_basic_type_new ("int");
+         }
+       | LONG
+         {
+               $$ = gi_source_basic_type_new ("long");
+         }
+       | FLOAT
+         {
+               $$ = gi_source_basic_type_new ("float");
+         }
+       | DOUBLE
+         {
+               $$ = gi_source_basic_type_new ("double");
+         }
+       | SIGNED
+         {
+               $$ = gi_source_basic_type_new ("signed");
+         }
+       | UNSIGNED
+         {
+               $$ = gi_source_basic_type_new ("unsigned");
+         }
+       | BOOL
+         {
+               $$ = gi_source_basic_type_new ("bool");
+         }
+       | struct_or_union_specifier
+       | enum_specifier
+       | typedef_name
+         {
+               $$ = gi_source_typedef_new ($1);
+               g_free ($1);
+         }
+       ;
+
+struct_or_union_specifier
+       : struct_or_union identifier_or_typedef_name '{' struct_declaration_list '}'
+         {
+               $$ = $1;
+               $$->name = $2;
+               $$->child_list = $4;
+
+               GISourceSymbol *sym = gi_source_symbol_new (CSYMBOL_TYPE_INVALID);
+               if ($$->type == CTYPE_STRUCT) {
+                       sym->type = CSYMBOL_TYPE_STRUCT;
+               } else if ($$->type == CTYPE_UNION) {
+                       sym->type = CSYMBOL_TYPE_UNION;
+               } else {
+                       g_assert_not_reached ();
+               }
+               sym->ident = g_strdup ($$->name);
+               sym->base_type = gi_source_type_copy ($$);
+               gi_source_scanner_add_symbol (scanner, sym);
+               gi_source_symbol_unref (sym);
+         }
+       | struct_or_union '{' struct_declaration_list '}'
+         {
+               $$ = $1;
+               $$->child_list = $3;
+         }
+       | struct_or_union identifier_or_typedef_name
+         {
+               $$ = $1;
+               $$->name = $2;
+         }
+       ;
+
+struct_or_union
+       : STRUCT
+         {
+               $$ = gi_source_struct_new (NULL);
+         }
+       | UNION
+         {
+               $$ = gi_source_union_new (NULL);
+         }
+       ;
+
+struct_declaration_list
+       : struct_declaration
+       | struct_declaration_list struct_declaration
+         {
+               $$ = g_list_concat ($1, $2);
+         }
+       ;
+
+struct_declaration
+       : specifier_qualifier_list struct_declarator_list ';'
+         {
+               GList *l;
+               $$ = NULL;
+               for (l = $2; l != NULL; l = l->next) {
+                       GISourceSymbol *sym = l->data;
+                       if ($1->storage_class_specifier & STORAGE_CLASS_TYPEDEF) {
+                               sym->type = CSYMBOL_TYPE_TYPEDEF;
+                       }
+                       gi_source_symbol_merge_type (sym, gi_source_type_copy ($1));
+                       $$ = g_list_append ($$, sym);
+               }
+               ctype_free ($1);
+         }
+       ;
+
+specifier_qualifier_list
+       : type_specifier specifier_qualifier_list
+         {
+               $$ = $1;
+               $$->base_type = $2;
+         }
+       | type_specifier
+       | type_qualifier specifier_qualifier_list
+         {
+               $$ = $2;
+               $$->type_qualifier |= $1;
+         }
+       | type_qualifier
+         {
+               $$ = gi_source_type_new (CTYPE_INVALID);
+               $$->type_qualifier |= $1;
+         }
+       ;
+
+struct_declarator_list
+       : struct_declarator
+         {
+               $$ = g_list_append (NULL, $1);
+         }
+       | struct_declarator_list ',' struct_declarator
+         {
+               $$ = g_list_append ($1, $3);
+         }
+       ;
+
+struct_declarator
+       : /* empty, support for anonymous structs and unions */
+         {
+               $$ = gi_source_symbol_new (CSYMBOL_TYPE_INVALID);
+         }
+       | declarator
+       | ':' constant_expression
+         {
+               $$ = gi_source_symbol_new (CSYMBOL_TYPE_INVALID);
+         }
+       | declarator ':' constant_expression
+       ;
+
+enum_specifier
+       : ENUM identifier_or_typedef_name '{' enumerator_list '}'
+         {
+               $$ = gi_source_enum_new ($2);
+               $$->child_list = $4;
+               last_enum_value = -1;
+         }
+       | ENUM '{' enumerator_list '}'
+         {
+               $$ = gi_source_enum_new (NULL);
+               $$->child_list = $3;
+               last_enum_value = -1;
+         }
+       | ENUM identifier_or_typedef_name '{' enumerator_list ',' '}'
+         {
+               $$ = gi_source_enum_new ($2);
+               $$->child_list = $4;
+               last_enum_value = -1;
+         }
+       | ENUM '{' enumerator_list ',' '}'
+         {
+               $$ = gi_source_enum_new (NULL);
+               $$->child_list = $3;
+               last_enum_value = -1;
+         }
+       | ENUM identifier_or_typedef_name
+         {
+               $$ = gi_source_enum_new ($2);
+         }
+       ;
+
+enumerator_list
+       : enumerator
+         {
+               $$ = g_list_append (NULL, $1);
+         }
+       | enumerator_list ',' enumerator
+         {
+               $$ = g_list_append ($1, $3);
+         }
+       ;
+
+enumerator
+       : identifier
+         {
+               $$ = gi_source_symbol_new (CSYMBOL_TYPE_OBJECT);
+               $$->ident = $1;
+               $$->const_int_set = TRUE;
+               $$->const_int = ++last_enum_value;
+               g_hash_table_insert (const_table, g_strdup ($$->ident), gi_source_symbol_ref ($$));
+         }
+       | identifier '=' constant_expression
+         {
+               $$ = gi_source_symbol_new (CSYMBOL_TYPE_OBJECT);
+               $$->ident = $1;
+               $$->const_int_set = TRUE;
+               $$->const_int = $3->const_int;
+               last_enum_value = $$->const_int;
+               g_hash_table_insert (const_table, g_strdup ($$->ident), gi_source_symbol_ref ($$));
+         }
+       ;
+
+type_qualifier
+       : CONST
+         {
+               $$ = TYPE_QUALIFIER_CONST;
+         }
+       | RESTRICT
+         {
+               $$ = TYPE_QUALIFIER_RESTRICT;
+         }
+       | VOLATILE
+         {
+               $$ = TYPE_QUALIFIER_VOLATILE;
+         }
+       ;
+
+function_specifier
+       : INLINE
+         {
+               $$ = FUNCTION_INLINE;
+         }
+       ;
+
+declarator
+       : pointer direct_declarator
+         {
+               $$ = $2;
+               gi_source_symbol_merge_type ($$, $1);
+         }
+       | direct_declarator
+       ;
+
+direct_declarator
+       : identifier
+         {
+               $$ = gi_source_symbol_new (CSYMBOL_TYPE_INVALID);
+               $$->ident = $1;
+         }
+       | '(' declarator ')'
+         {
+               $$ = $2;
+         }
+       | direct_declarator '[' assignment_expression ']'
+         {
+               $$ = $1;
+               gi_source_symbol_merge_type ($$, gi_source_array_new ());
+         }
+       | direct_declarator '[' ']'
+         {
+               $$ = $1;
+               gi_source_symbol_merge_type ($$, gi_source_array_new ());
+         }
+       | direct_declarator '(' parameter_type_list ')'
+         {
+               GISourceType *func = gi_source_function_new ();
+               // ignore (void) parameter list
+               if ($3 != NULL && ($3->next != NULL || ((GISourceSymbol *) $3->data)->base_type->type != CTYPE_VOID)) {
+                       func->child_list = $3;
+               }
+               $$ = $1;
+               gi_source_symbol_merge_type ($$, func);
+         }
+       | direct_declarator '(' identifier_list ')'
+         {
+               GISourceType *func = gi_source_function_new ();
+               func->child_list = $3;
+               $$ = $1;
+               gi_source_symbol_merge_type ($$, func);
+         }
+       | direct_declarator '(' ')'
+         {
+               GISourceType *func = gi_source_function_new ();
+               $$ = $1;
+               gi_source_symbol_merge_type ($$, func);
+         }
+       ;
+
+pointer
+       : '*' type_qualifier_list
+         {
+               $$ = gi_source_pointer_new (NULL);
+               $$->type_qualifier = $2;
+         }
+       | '*'
+         {
+               $$ = gi_source_pointer_new (NULL);
+         }
+       | '*' type_qualifier_list pointer
+         {
+               $$ = gi_source_pointer_new ($3);
+               $$->type_qualifier = $2;
+         }
+       | '*' pointer
+         {
+               $$ = gi_source_pointer_new ($2);
+         }
+       ;
+
+type_qualifier_list
+       : type_qualifier
+       | type_qualifier_list type_qualifier
+         {
+               $$ = $1 | $2;
+         }
+       ;
+
+parameter_type_list
+       : parameter_list
+       | parameter_list ',' ELLIPSIS
+       ;
+
+parameter_list
+       : parameter_declaration
+         {
+               $$ = g_list_append (NULL, $1);
+         }
+       | parameter_list ',' parameter_declaration
+         {
+               $$ = g_list_append ($1, $3);
+         }
+       ;
+
+parameter_declaration
+       : declaration_specifiers declarator
+         {
+               $$ = $2;
+               gi_source_symbol_merge_type ($$, $1);
+         }
+       | declaration_specifiers abstract_declarator
+         {
+               $$ = $2;
+               gi_source_symbol_merge_type ($$, $1);
+         }
+       | declaration_specifiers
+         {
+               $$ = gi_source_symbol_new (CSYMBOL_TYPE_INVALID);
+               $$->base_type = $1;
+         }
+       ;
+
+identifier_list
+       : identifier
+         {
+               GISourceSymbol *sym = gi_source_symbol_new (CSYMBOL_TYPE_INVALID);
+               sym->ident = $1;
+               $$ = g_list_append (NULL, sym);
+         }
+       | identifier_list ',' identifier
+         {
+               GISourceSymbol *sym = gi_source_symbol_new (CSYMBOL_TYPE_INVALID);
+               sym->ident = $3;
+               $$ = g_list_append ($1, sym);
+         }
+       ;
+
+type_name
+       : specifier_qualifier_list
+       | specifier_qualifier_list abstract_declarator
+       ;
+
+abstract_declarator
+       : pointer
+         {
+               $$ = gi_source_symbol_new (CSYMBOL_TYPE_INVALID);
+               gi_source_symbol_merge_type ($$, $1);
+         }
+       | direct_abstract_declarator
+       | pointer direct_abstract_declarator
+         {
+               $$ = $2;
+               gi_source_symbol_merge_type ($$, $1);
+         }
+       ;
+
+direct_abstract_declarator
+       : '(' abstract_declarator ')'
+         {
+               $$ = $2;
+         }
+       | '[' ']'
+         {
+               $$ = gi_source_symbol_new (CSYMBOL_TYPE_INVALID);
+               gi_source_symbol_merge_type ($$, gi_source_array_new ());
+         }
+       | '[' assignment_expression ']'
+         {
+               $$ = gi_source_symbol_new (CSYMBOL_TYPE_INVALID);
+               gi_source_symbol_merge_type ($$, gi_source_array_new ());
+         }
+       | direct_abstract_declarator '[' ']'
+         {
+               $$ = $1;
+               gi_source_symbol_merge_type ($$, gi_source_array_new ());
+         }
+       | direct_abstract_declarator '[' assignment_expression ']'
+         {
+               $$ = $1;
+               gi_source_symbol_merge_type ($$, gi_source_array_new ());
+         }
+       | '(' ')'
+         {
+               GISourceType *func = gi_source_function_new ();
+               $$ = gi_source_symbol_new (CSYMBOL_TYPE_INVALID);
+               gi_source_symbol_merge_type ($$, func);
+         }
+       | '(' parameter_type_list ')'
+         {
+               GISourceType *func = gi_source_function_new ();
+               // ignore (void) parameter list
+               if ($2 != NULL && ($2->next != NULL || ((GISourceSymbol *) $2->data)->base_type->type != CTYPE_VOID)) {
+                       func->child_list = $2;
+               }
+               $$ = gi_source_symbol_new (CSYMBOL_TYPE_INVALID);
+               gi_source_symbol_merge_type ($$, func);
+         }
+       | direct_abstract_declarator '(' ')'
+         {
+               GISourceType *func = gi_source_function_new ();
+               $$ = $1;
+               gi_source_symbol_merge_type ($$, func);
+         }
+       | direct_abstract_declarator '(' parameter_type_list ')'
+         {
+               GISourceType *func = gi_source_function_new ();
+               // ignore (void) parameter list
+               if ($3 != NULL && ($3->next != NULL || ((GISourceSymbol *) $3->data)->base_type->type != CTYPE_VOID)) {
+                       func->child_list = $3;
+               }
+               $$ = $1;
+               gi_source_symbol_merge_type ($$, func);
+         }
+       ;
+
+typedef_name
+       : TYPEDEF_NAME
+         {
+               $$ = g_strdup (yytext);
+         }
+       ;
+
+initializer
+       : assignment_expression
+       | '{' initializer_list '}'
+       | '{' initializer_list ',' '}'
+       ;
+
+initializer_list
+       : initializer
+       | initializer_list ',' initializer
+       ;
+
+/* A.2.3 Statements. */
+
+statement
+       : labeled_statement
+       | compound_statement
+       | expression_statement
+       | selection_statement
+       | iteration_statement
+       | jump_statement
+       ;
+
+labeled_statement
+       : identifier_or_typedef_name ':' statement
+       | CASE constant_expression ':' statement
+       | DEFAULT ':' statement
+       ;
+
+compound_statement
+       : '{' '}'
+       | '{' block_item_list '}'
+       ;
+
+block_item_list
+       : block_item
+       | block_item_list block_item
+       ;
+
+block_item
+       : declaration
+       | statement
+       ;
+
+expression_statement
+       : ';'
+       | expression ';'
+       ;
+
+selection_statement
+       : IF '(' expression ')' statement
+       | IF '(' expression ')' statement ELSE statement
+       | SWITCH '(' expression ')' statement
+       ;
+
+iteration_statement
+       : WHILE '(' expression ')' statement
+       | DO statement WHILE '(' expression ')' ';'
+       | FOR '(' ';' ';' ')' statement
+       | FOR '(' expression ';' ';' ')' statement
+       | FOR '(' ';' expression ';' ')' statement
+       | FOR '(' expression ';' expression ';' ')' statement
+       | FOR '(' ';' ';' expression ')' statement
+       | FOR '(' expression ';' ';' expression ')' statement
+       | FOR '(' ';' expression ';' expression ')' statement
+       | FOR '(' expression ';' expression ';' expression ')' statement
+       ;
+
+jump_statement
+       : GOTO identifier_or_typedef_name ';'
+       | CONTINUE ';'
+       | BREAK ';'
+       | RETURN ';'
+       | RETURN expression ';'
+       ;
+
+/* A.2.4 External definitions. */
+
+translation_unit
+       : external_declaration
+       | translation_unit external_declaration
+       ;
+
+external_declaration
+       : function_definition
+       | declaration
+       | macro
+       ;
+
+function_definition
+       : declaration_specifiers declarator declaration_list compound_statement
+       | declaration_specifiers declarator compound_statement
+       ;
+
+declaration_list
+       : declaration
+       | declaration_list declaration
+       ;
+
+/* Macros */
+
+function_macro
+       : FUNCTION_MACRO
+         {
+               $$ = g_strdup (yytext + strlen ("#define "));
+         }
+       ;
+
+object_macro
+       : OBJECT_MACRO
+         {
+               $$ = g_strdup (yytext + strlen ("#define "));
+         }
+       ;
+
+function_macro_define
+       : function_macro '(' identifier_list ')'
+       ;
+
+object_macro_define
+       : object_macro constant_expression
+         {
+               if ($2->const_int_set || $2->const_string != NULL) {
+                       $2->ident = $1;
+                       gi_source_scanner_add_symbol (scanner, $2);
+                       gi_source_symbol_unref ($2);
+               }
+         }
+       ;
+
+macro
+       : function_macro_define
+       | object_macro_define
+       | error
+       ;
+
+%%
+static void
+yyerror (GISourceScanner *scanner, const char *s)
+{
+  /* ignore errors while doing a macro scan as not all object macros
+   * have valid expressions */
+  if (!scanner->macro_scan)
+    {
+      fprintf(stderr, "%s:%d: %s\n",
+             scanner->current_filename, lineno, s);
+    }
+}
+
+gboolean
+gi_source_scanner_parse_file (GISourceScanner *scanner, FILE *file)
+{
+  g_return_val_if_fail (file != NULL, FALSE);
+  
+  const_table = g_hash_table_new_full (g_str_hash, g_str_equal,
+                                      g_free, (GDestroyNotify)gi_source_symbol_unref);
+  
+  lineno = 1;
+  yyin = file;
+  yyparse (scanner);
+  
+  g_hash_table_destroy (const_table);
+  const_table = NULL;
+  
+  yyin = NULL;
+
+  return TRUE;
+}
+
+gboolean
+gi_source_scanner_lex_filename (GISourceScanner *scanner, const gchar *filename)
+{
+  yyin = fopen (filename, "r");
+
+  while (yylex (scanner) != YYEOF)
+    ;
+
+  fclose (yyin);
+  
+  return TRUE;
+}
similarity index 98%
rename from tools/sourcescanner.c
rename to giscanner/sourcescanner.c
index 8354b1b..bb916e8 100644 (file)
@@ -236,6 +236,13 @@ gi_source_scanner_is_typedef (GISourceScanner *scanner,
 }
 
 void
+gi_source_scanner_set_macro_scan (GISourceScanner  *scanner,
+                                 gboolean          macro_scan)
+{
+  scanner->macro_scan = macro_scan;
+}
+
+void
 gi_source_scanner_add_symbol (GISourceScanner  *scanner,
                              GISourceSymbol   *symbol)
 {
similarity index 95%
rename from tools/sourcescanner.h
rename to giscanner/sourcescanner.h
index 71236ce..c1365e0 100644 (file)
@@ -125,14 +125,14 @@ struct _GISourceType
   FunctionSpecifier function_specifier;
   char *name;
   GISourceType *base_type;
-  GList *child_list;
+  GList *child_list; /* list of GISourceSymbol */
 };
 
 struct _GISourceDirective
 {
   char *name;
   char *value;
-  GSList *options;
+  GSList *options; /* list of options (key=value) */
 };
 
 GISourceScanner *   gi_source_scanner_new              (void);
@@ -140,6 +140,8 @@ gboolean            gi_source_scanner_lex_filename     (GISourceScanner  *igener
                                                        const gchar      *filename);
 gboolean            gi_source_scanner_parse_file       (GISourceScanner  *igenerator,
                                                        FILE             *file);
+void                gi_source_scanner_set_macro_scan   (GISourceScanner  *scanner,
+                                                       gboolean          macro_scan);
 GSList *            gi_source_scanner_get_symbols      (GISourceScanner  *scanner);
 void                gi_source_scanner_free             (GISourceScanner  *scanner);
 
index 46fff6b..545050a 100644 (file)
@@ -1,16 +1,14 @@
 ## Process this file with automake to produce Makefile.in
 
-INCLUDES = -DGIREPO_DEFAULT_SEARCH_PATH="\"$(libdir)\""
-BUILT_SOURCES = scannerparser.c scannerparser.h scannerlexer.c scannerlexer.h \
-               gmetadata-header.c
+INCLUDES = \
+       -DGIREPO_DEFAULT_SEARCH_PATH="\"$(libdir)\"" \
+       -I$(top_srcdir)/girepository \
+       -I$(top_srcdir)/giscanner
+BUILT_SOURCES = gmetadata-header.c
 
-CLEANFILES = scannerparser.c scannerparser.h scannerlexer.c scannerlexer.h gmetadata-header.c
-AM_YFLAGS = -d -t
+CLEANFILES = gmetadata-header.c
 EXTRA_DIST = quote-file.sh
 
-# Why do I have to do this automake?
-scannerlexer.h: scannerlexer.c
-
 noinst_LTLIBRARIES = libgirepository-parser.la
 bin_PROGRAMS = g-idl-compiler g-idl-generate g-idl-scanner
 
@@ -32,28 +30,28 @@ libgirepository_parser_la_SOURCES =         \
        gidlcompilertypenode.h                  \
        gmetadata-header.c
 
-libgirepository_parser_la_CFLAGS = $(GIREPO_CFLAGS) -I$(top_srcdir)/girepository
+libgirepository_parser_la_CFLAGS = $(GIREPO_CFLAGS)
 
 g_idl_compiler_SOURCES = compiler.c    
-g_idl_compiler_CFLAGS = $(GIREPO_CFLAGS) -I$(top_srcdir)/girepository
+g_idl_compiler_CFLAGS = $(GIREPO_CFLAGS)
 g_idl_compiler_LDADD = $(GIREPO_LIBS) $(top_builddir)/girepository/libgirepository.la libgirepository-parser.la
 
 g_idl_generate_SOURCES = generate.c
-g_idl_generate_CFLAGS = $(GIREPO_CFLAGS) -I$(top_srcdir)/girepository
+g_idl_generate_CFLAGS = $(GIREPO_CFLAGS)
 g_idl_generate_LDADD = $(GIREPO_LIBS) $(top_builddir)/girepository/libgirepository.la
 
 g_idl_scanner_SOURCES =                                \
-       sourcescanner.c                         \
-       sourcescanner.h                         \
        scanner.c                               \
        scanner.h                               \
-       scannerlexer.l                          \
-       scannerparser.y                         \
        gidlwriter.c                            \
-       gidlwriter.h                            \
-       grealpath.h
-g_idl_scanner_CFLAGS = $(GIREPO_CFLAGS) $(SCANNER_CFLAGS) -I$(top_srcdir)/girepository
-g_idl_scanner_LDADD = $(GIREPO_LIBS) $(SCANNER_LIBS) $(top_builddir)/girepository/libgirepository.la libgirepository-parser.la
+       gidlwriter.h
+g_idl_scanner_CFLAGS = $(GIREPO_CFLAGS) $(SCANNER_CFLAGS)
+g_idl_scanner_LDADD = \
+       $(GIREPO_LIBS) \
+       $(SCANNER_LIBS) \
+       $(top_builddir)/girepository/libgirepository.la \
+       $(top_builddir)/giscanner/libgiscanner.la \
+       libgirepository-parser.la
 
 GCOVSOURCES =                                  \
        $(libgirepository_la_SOURCES)           \