support DOT, COLON, and COMMA save lower and upper case cnames in
authorJürg Billeter <j@bitron.ch>
Sat, 22 Apr 2006 11:19:34 +0000 (11:19 +0000)
committerJürg Billeter <juergbi@src.gnome.org>
Sat, 22 Apr 2006 11:19:34 +0000 (11:19 +0000)
2006-04-22  Jürg Billeter  <j@bitron.ch>

* valac/scanner.l: support DOT, COLON, and COMMA
* valac/parser.y: save lower and upper case cnames in namespace and
  class objects, support base classes
* valac/context.h: add ValaSymbol and ValaTypeReference structs, add
  cnames to ValaNamespace and ValaClass structs
* valac/context.c: add symbol handling, type resolving, set cnames in
  root namespace
* valac/generator.c: remove cname generation, support base classes
* valac/driver.c: add symbols and resolve types
* tests/test-003.vala: test base class

svn path=/trunk/; revision=2

vala/ChangeLog
vala/tests/test-003.vala [new file with mode: 0644]
vala/valac/context.c
vala/valac/context.h
vala/valac/driver.c
vala/valac/generator.c
vala/valac/parser.y
vala/valac/scanner.l

index e69de29..9af11ba 100644 (file)
@@ -0,0 +1,12 @@
+2006-04-22  Jürg Billeter  <j@bitron.ch>
+
+       * valac/scanner.l: support DOT, COLON, and COMMA
+       * valac/parser.y: save lower and upper case cnames in namespace and
+         class objects, support base classes
+       * valac/context.h: add ValaSymbol and ValaTypeReference structs, add
+         cnames to ValaNamespace and ValaClass structs
+       * valac/context.c: add symbol handling, type resolving, set cnames in
+         root namespace
+       * valac/generator.c: remove cname generation, support base classes
+       * valac/driver.c: add symbols and resolve types
+       * tests/test-003.vala: test base class
diff --git a/vala/tests/test-003.vala b/vala/tests/test-003.vala
new file mode 100644 (file)
index 0000000..054fd3b
--- /dev/null
@@ -0,0 +1,7 @@
+namespace Maman {
+       class Bar {
+       }
+       
+       class SubBar : Bar {
+       }
+}
index 4c0f467..481e860 100644 (file)
@@ -20,7 +20,9 @@
  *     Jürg Billeter <j@bitron.ch>
  */
 
+#include <stdlib.h>
 #include <stdio.h>
+#include <string.h>
 
 #include <glib.h>
 
@@ -47,6 +49,202 @@ vala_context_parse (ValaContext *context)
        }
 }
 
+static ValaSymbol *
+vala_symbol_new (ValaSymbolType type)
+{
+       ValaSymbol *symbol;
+       symbol = g_new0 (ValaSymbol, 1);
+       symbol->type = type;
+       symbol->symbol_table = g_hash_table_new (g_str_hash, g_str_equal);
+       return symbol;
+}
+
+static void
+vala_context_add_symbols_from_namespace (ValaContext *context, ValaNamespace *namespace)
+{
+       ValaSymbol *ns_symbol, *class_symbol;
+       GList *cl;
+       
+       if (strlen (namespace->name) == 0) {
+               ns_symbol = context->root;
+       } else {
+               ns_symbol = g_hash_table_lookup (context->root->symbol_table, namespace->name);
+               if (ns_symbol == NULL) {
+                       ns_symbol = vala_symbol_new (VALA_SYMBOL_TYPE_NAMESPACE);
+                       g_hash_table_insert (context->root->symbol_table, namespace->name, ns_symbol);
+               }
+       }
+       
+       namespace->symbol = ns_symbol;
+       
+       for (cl = namespace->classes; cl != NULL; cl = cl->next) {
+               ValaClass *class = cl->data;
+               
+               class_symbol = g_hash_table_lookup (ns_symbol->symbol_table, class->name);
+               if (class_symbol != NULL) {
+                       fprintf (stderr, "Error: Class '%s.%s' defined twice.\n", namespace->name, class->name);
+                       exit (1);
+               }
+
+               class_symbol = vala_symbol_new (VALA_SYMBOL_TYPE_CLASS);
+               class_symbol->class = class;
+               g_hash_table_insert (ns_symbol->symbol_table, class->name, class_symbol);
+       }
+}
+
+void
+vala_context_add_fundamental_symbols (ValaContext *context)
+{
+       ValaSymbol *symbol;
+       ValaNamespace *namespace;
+       ValaClass *class;
+       
+       context->root = vala_symbol_new (VALA_SYMBOL_TYPE_ROOT);
+       
+       namespace = g_new0 (ValaNamespace, 1);
+       namespace->name = g_strdup ("G");
+       namespace->lower_case_cname = g_strdup ("g_");
+       namespace->upper_case_cname = g_strdup ("G_");
+       
+       class = g_new0 (ValaClass, 1);
+       class->name = g_strdup ("Object");
+       class->namespace = namespace;
+       class->lower_case_cname = g_strdup ("object");
+       class->upper_case_cname = g_strdup ("OBJECT");
+       namespace->classes = g_list_append (namespace->classes, class);
+
+       vala_context_add_symbols_from_namespace (context, namespace);
+       
+       /* Add alias object = G.Object */
+       symbol = g_hash_table_lookup (namespace->symbol->symbol_table, "Object");
+       g_hash_table_insert (context->root->symbol_table, "object", symbol);
+}
+
+void
+vala_context_add_symbols_from_source_files (ValaContext *context)
+{
+       GList *fl;
+       
+       for (fl = context->source_files; fl != NULL; fl = fl->next) {
+               ValaSourceFile *source_file = fl->data;
+               GList *nsl;
+               
+               vala_context_add_symbols_from_namespace (context, source_file->root_namespace);
+               for (nsl = source_file->namespaces; nsl != NULL; nsl = nsl->next) {
+                       vala_context_add_symbols_from_namespace (context, nsl->data);
+               }
+       }
+}
+
+static void
+vala_context_resolve_type_reference (ValaContext *context, ValaNamespace *namespace, ValaTypeReference *type_reference)
+{
+       ValaSymbol *type_symbol, *ns_symbol;
+       
+       if (type_reference->namespace_name == NULL) {
+               /* no namespace specified */
+
+               /* look in current namespace */
+               type_symbol = g_hash_table_lookup (namespace->symbol->symbol_table, type_reference->type_name);
+               if (type_symbol != NULL) {
+                       type_reference->namespace_name = namespace->name;
+               } else {
+                       /* look in root namespace */
+                       type_symbol = g_hash_table_lookup (context->root->symbol_table, type_reference->type_name);
+               }
+               
+               /* FIXME: look in namespaces specified by using directives */
+
+               if (type_symbol == NULL) {
+                       /* specified namespace not found */
+                       fprintf (stderr, "Error: Specified type '%s' not found.\n", type_reference->type_name);
+                       exit (1);
+               }
+       } else {
+               /* namespace has been explicitly specified */
+               ns_symbol = g_hash_table_lookup (context->root->symbol_table, type_reference->namespace_name);
+               if (ns_symbol == NULL) {
+                       /* specified namespace not found */
+                       fprintf (stderr, "Error: Specified namespace '%s' not found.\n", type_reference->namespace_name);
+                       exit (1);
+               }
+
+               type_symbol = g_hash_table_lookup (namespace->symbol->symbol_table, type_reference->type_name);
+               if (type_symbol == NULL) {
+                       /* specified namespace not found */
+                       fprintf (stderr, "Error: Specified type '%s' not found in namespace '%s'.\n", type_reference->type_name, type_reference->namespace_name);
+                       exit (1);
+               }
+       }
+       
+       if (type_symbol->type == VALA_SYMBOL_TYPE_CLASS) {
+               type_reference->symbol = type_symbol;
+       } else {
+               fprintf (stderr, "Error: Specified symbol '%s' is not a type.\n", type_reference->type_name);
+               exit (1);
+       }
+}
+
+static void
+vala_context_resolve_types_in_class (ValaContext *context, ValaNamespace *namespace, ValaClass *class)
+{
+       GList *l;
+       
+       for (l = class->base_types; l != NULL; l = l->next) {
+               ValaTypeReference *type_reference = l->data;
+               
+               vala_context_resolve_type_reference (context, namespace, type_reference);
+               if (type_reference->symbol->type == VALA_SYMBOL_TYPE_CLASS) {
+                       if (class->base_class != NULL) {
+                               fprintf (stderr, "Error: More than one base class specified in class '%s.%s'.\n", class->namespace->name, class->name);
+                               exit (1);
+                       }
+                       class->base_class = type_reference->symbol->class;
+                       if (class->base_class == class) {
+                               fprintf (stderr, "Error: '%s.%s' cannot be subtype of '%s.%s'.\n", class->namespace->name, class->name, class->namespace->name, class->name);
+                               exit (1);
+                       }
+                       /* FIXME: check whether base_class is not a subtype of class */
+               } else {
+                       fprintf (stderr, "Error: '%s.%s' cannot be subtype of '%s.%s'.\n", class->namespace->name, class->name, type_reference->symbol->class->namespace->name, type_reference->symbol->class->name);
+                       exit (1);
+               }
+       }
+       
+       if (class->base_class == NULL) {
+               ValaSymbol *symbol = g_hash_table_lookup (context->root->symbol_table, "object");
+               class->base_class = symbol->class;
+       }
+}
+
+static void
+vala_context_resolve_types_in_namespace (ValaContext *context, ValaNamespace *namespace)
+{
+       GList *cl;
+       
+       for (cl = namespace->classes; cl != NULL; cl = cl->next) {
+               ValaClass *class = cl->data;
+               
+               vala_context_resolve_types_in_class (context, namespace, class);
+       }
+}
+
+void
+vala_context_resolve_types (ValaContext *context)
+{
+       GList *fl;
+       
+       for (fl = context->source_files; fl != NULL; fl = fl->next) {
+               ValaSourceFile *source_file = fl->data;
+               GList *nsl;
+               
+               vala_context_resolve_types_in_namespace (context, source_file->root_namespace);
+               for (nsl = source_file->namespaces; nsl != NULL; nsl = nsl->next) {
+                       vala_context_resolve_types_in_namespace (context, nsl->data);
+               }
+       }
+}
+
 ValaSourceFile *
 vala_source_file_new (const char *filename)
 {
@@ -54,6 +252,9 @@ vala_source_file_new (const char *filename)
        
        file->filename = filename;
        file->root_namespace = g_new0 (ValaNamespace, 1);
+       file->root_namespace->name = g_strdup ("");
+       file->root_namespace->lower_case_cname = g_strdup ("");
+       file->root_namespace->upper_case_cname = g_strdup ("");
        
        return file;
 }
index 3392ef3..92daabe 100644 (file)
  *     Jürg Billeter <j@bitron.ch>
  */
 
+#include <glib.h>
+
+typedef enum _ValaSymbolType ValaSymbolType;
+
 typedef struct _ValaContext ValaContext;
+typedef struct _ValaSymbol ValaSymbol;
 typedef struct _ValaSourceFile ValaSourceFile;
 typedef struct _ValaNamespace ValaNamespace;
 typedef struct _ValaClass ValaClass;
+typedef struct _ValaTypeReference ValaTypeReference;
+
+enum _ValaSymbolType {
+       VALA_SYMBOL_TYPE_ROOT,
+       VALA_SYMBOL_TYPE_NAMESPACE,
+       VALA_SYMBOL_TYPE_CLASS,
+};
 
 struct _ValaContext {
        GList *source_files;
+       ValaSymbol *root;
+};
+
+struct _ValaSymbol {
+       ValaSymbolType type;
+       union {
+               ValaClass *class;
+       };
+       GHashTable *symbol_table;
 };
 
 struct _ValaSourceFile {
@@ -37,16 +58,33 @@ struct _ValaSourceFile {
 
 struct _ValaNamespace {
        char *name;
+       ValaSymbol *symbol;
        GList *classes;
+       char *lower_case_cname;
+       char *upper_case_cname;
 };
 
 struct _ValaClass {
-       char *name;     
+       char *name;
+       ValaNamespace *namespace;
+       ValaClass *base_class;
+       GList *base_types;
+       char *lower_case_cname;
+       char *upper_case_cname;
+};
+
+struct _ValaTypeReference {
+       char *namespace_name;
+       char *type_name;
+       ValaSymbol *symbol;
 };
 
 ValaContext *vala_context_new ();
 void vala_context_free (ValaContext *context);
 void vala_context_parse (ValaContext *context);
+void vala_context_add_fundamental_symbols (ValaContext *context);
+void vala_context_add_symbols_from_source_files (ValaContext *context);
+void vala_context_resolve_types (ValaContext *context);
 
 ValaSourceFile *vala_source_file_new (const char *filename);
 
index 04dc043..63d72e0 100644 (file)
@@ -48,6 +48,9 @@ driver_main (char **sources, char *directory)
        sources = NULL;
 
        vala_context_parse (context);
+       vala_context_add_fundamental_symbols (context);
+       vala_context_add_symbols_from_source_files (context);
+       vala_context_resolve_types (context);
        
        ValaCodeGenerator *generator = vala_code_generator_new (context);
        generator->directory = directory;
index 6f1118c..26153b6 100644 (file)
@@ -39,52 +39,6 @@ vala_code_generator_new (ValaContext *context)
 }
 
 static char *
-camel_case_to_lower_case (const char *camel_case)
-{
-       char *str = g_malloc0 (2 * strlen (camel_case));
-       const char *i = camel_case;
-       char *p = str;
-       
-       while (*i != '\0') {
-               if (isupper (*i)) {
-                       /* current character is upper case */
-                       if (i != camel_case) {
-                               /* we're not at the beginning */
-                               const char *t = i - 1;                          
-                               gboolean prev_upper = isupper (*t);
-                               t = i + 1;
-                               gboolean next_upper = isupper (*t);
-                               if (!prev_upper || (*t != '\0' && !next_upper)) {
-                                       /* previous character wasn't upper case */
-                                       *p = '_';
-                                       p++;
-                               }
-                       }
-               }
-                       
-               *p = tolower (*i);
-               i++;
-               p++;
-       }
-       
-       return str;
-}
-
-static char *
-camel_case_to_upper_case (const char *camel_case)
-{
-       char *str = camel_case_to_lower_case (camel_case);
-       
-       char *p;
-       
-       for (p = str; *p != '\0'; p++) {
-               *p = toupper (*p);
-       }
-       
-       return str;
-}
-
-static char *
 filename_to_define (const char *filename)
 {
        char *define = g_path_get_basename (filename);
@@ -101,31 +55,20 @@ filename_to_define (const char *filename)
 }
 
 static void
-vala_code_generator_process_class (ValaCodeGenerator *generator, ValaNamespace *namespace, ValaClass *class)
+vala_code_generator_process_class (ValaCodeGenerator *generator, ValaClass *class)
 {
+       ValaNamespace *namespace = class->namespace;
+
        char *camel_case;
        char *ns_lower;
        char *ns_upper;
-       
-       if (namespace->name == NULL) {
-               camel_case = g_strdup (class->name);
-
-               ns_lower = g_strdup ("");
-               ns_upper = g_strdup ("");
-       } else {
-               camel_case = g_strdup_printf ("%s%s", namespace->name, class->name);
-
-               ns_lower = camel_case_to_lower_case (namespace->name);
-               /* we know that this is safe */
-               ns_lower[strlen (ns_lower)] = '_';
 
-               ns_upper = camel_case_to_upper_case (namespace->name);
-               /* we know that this is safe */
-               ns_upper[strlen (ns_upper)] = '_';
-       }
+       camel_case = g_strdup_printf ("%s%s", namespace->name, class->name);
+       ns_lower = namespace->lower_case_cname;
+       ns_upper = namespace->upper_case_cname;
        
-       char *lower_case = camel_case_to_lower_case (class->name);
-       char *upper_case = camel_case_to_upper_case (class->name);
+       char *lower_case = class->lower_case_cname;
+       char *upper_case = class->upper_case_cname;
 
        /* type macros */
        fprintf (generator->h_file, "#define %sTYPE_%s\t(%s%s_get_type ())\n", ns_upper, upper_case, ns_lower, lower_case);
@@ -142,12 +85,12 @@ vala_code_generator_process_class (ValaCodeGenerator *generator, ValaNamespace *
        fprintf (generator->h_file, "\n");
 
        fprintf (generator->h_file, "struct _%s {\n", camel_case);
-       fprintf (generator->h_file, "\tGObject parent;\n");
+       fprintf (generator->h_file, "\t%s%s parent;\n", class->base_class->namespace->name, class->base_class->name);
        fprintf (generator->h_file, "};\n");
        fprintf (generator->h_file, "\n");
 
        fprintf (generator->h_file, "struct _%sClass {\n", camel_case);
-       fprintf (generator->h_file, "\tGObjectClass parent;\n");
+       fprintf (generator->h_file, "\t%s%sClass parent;\n", class->base_class->namespace->name, class->base_class->name);
        fprintf (generator->h_file, "};\n");
        fprintf (generator->h_file, "\n");
 
@@ -185,7 +128,9 @@ vala_code_generator_process_class (ValaCodeGenerator *generator, ValaNamespace *
        fprintf (generator->c_file, "\t\t\t0, /* n_preallocs */\n");
        fprintf (generator->c_file, "\t\t\t(GInstanceInitFunc) %s%s_init,\n", ns_lower, lower_case);
        fprintf (generator->c_file, "\t\t};\n");
-       fprintf (generator->c_file, "\t\tg_define_type_id = g_type_register_static (%sTYPE_%s, \"%s\", &g_define_type_info, 0);\n", ns_upper, upper_case, camel_case);
+       
+       fprintf (generator->c_file, "\t\tg_define_type_id = g_type_register_static (%sTYPE_%s, \"%s\", &g_define_type_info, 0);\n", class->base_class->namespace->upper_case_cname, class->base_class->upper_case_cname, camel_case);
+
        /* FIXME: add interfaces */
        fprintf (generator->c_file, "\t}\n");
        fprintf (generator->c_file, "\treturn g_define_type_id;\n");
@@ -198,7 +143,7 @@ vala_code_generator_process_namespace (ValaCodeGenerator *generator, ValaNamespa
 {
        GList *l;
        for (l = namespace->classes; l != NULL; l = l->next) {
-               vala_code_generator_process_class (generator, namespace, l->data);
+               vala_code_generator_process_class (generator, l->data);
        }
 }
 
index d6be6d8..4dd5975 100644 (file)
 
 #include "context.h"
 
+static char *
+camel_case_to_lower_case (const char *camel_case)
+{
+       char *str = g_malloc0 (2 * strlen (camel_case));
+       const char *i = camel_case;
+       char *p = str;
+       
+       /*
+        * This function tries to find word boundaries in camel case with the
+        * following constraints
+        * - words always begin with an upper case character
+        * - the remaining characters are either all upper case or all lower case
+        * - words have at least two characters
+        */
+       
+       while (*i != '\0') {
+               if (isupper (*i)) {
+                       /* current character is upper case */
+                       if (i != camel_case) {
+                               /* we're not at the beginning */
+                               const char *t = i - 1;                          
+                               gboolean prev_upper = isupper (*t);
+                               t = i + 1;
+                               gboolean next_upper = isupper (*t);
+                               if (!prev_upper || (*t != '\0' && !next_upper)) {
+                                       /* previous character wasn't upper case */
+                                       t = p - 2;
+                                       if (p - str != 1 && *t != '_') {
+                                               /* we're not creating 1 character words */
+                                               *p = '_';
+                                               p++;
+                                       }
+                               }
+                       }
+               }
+                       
+               *p = tolower (*i);
+               i++;
+               p++;
+       }
+       
+       return str;
+}
+
+static char *
+camel_case_to_upper_case (const char *camel_case)
+{
+       char *str = camel_case_to_lower_case (camel_case);
+       
+       char *p;
+       
+       for (p = str; *p != '\0'; p++) {
+               *p = toupper (*p);
+       }
+       
+       return str;
+}
+
 ValaSourceFile *current_source_file;
 ValaNamespace *current_namespace;
 %}
@@ -39,15 +97,25 @@ ValaNamespace *current_namespace;
 %glr-parser
 %union {
        char *str;
+       GList *list;
+       ValaTypeReference *type_reference;
 }
 
 %token OPEN_BRACE "{"
 %token CLOSE_BRACE "}"
+%token DOT "."
+%token COLON ":"
+%token COMMA ","
 
 %token NAMESPACE "namespace"
 %token CLASS "class"
 %token <str> IDENTIFIER "identifier"
 
+%type <list> opt_class_base
+%type <list> class_base
+%type <list> type_list
+%type <type_reference> type_name
+
 %%
 
 compilation_unit
@@ -70,6 +138,14 @@ namespace_declaration
          {
                current_namespace = g_new0 (ValaNamespace, 1);
                current_namespace->name = $2;
+               current_namespace->lower_case_cname = camel_case_to_lower_case (current_namespace->name);
+               /* we know that this is safe */
+               current_namespace->lower_case_cname[strlen (current_namespace->lower_case_cname)] = '_';
+
+               current_namespace->upper_case_cname = camel_case_to_upper_case (current_namespace->name);
+               /* we know that this is safe */
+               current_namespace->upper_case_cname[strlen (current_namespace->upper_case_cname)] = '_';
+
                current_source_file->namespaces = g_list_append (current_source_file->namespaces, current_namespace);
          }
          namespace_body
@@ -101,15 +177,64 @@ type_declaration
        ;
 
 class_declaration
-       : CLASS IDENTIFIER
+       : CLASS IDENTIFIER opt_class_base
          {
                ValaClass *class = g_new0 (ValaClass, 1);
-               class->name = $2;
+               class->name = g_strdup ($2);
+               class->base_types = $3;
+               class->namespace = current_namespace;
+               class->lower_case_cname = camel_case_to_lower_case (class->name);
+               class->upper_case_cname = camel_case_to_upper_case (class->name);
                current_namespace->classes = g_list_append (current_namespace->classes, class);
          }
          class_body
        ;
 
+opt_class_base
+       : /* empty */
+         {
+               $$ = NULL;
+         }
+       | class_base
+         {
+               $$ = $1;
+         }
+       ;
+
+class_base
+       : COLON type_list
+         {
+               $$ = $2;
+         }
+       ;
+
+type_list
+       : type_name
+         {
+               $$ = g_list_append (NULL, $1);
+         }
+       | type_list COMMA type_name
+         {
+               $$ = g_list_append ($1, $3);
+         }
+       ;
+
+type_name
+       : IDENTIFIER
+         {
+               ValaTypeReference *type_reference = g_new0 (ValaTypeReference, 1);
+               type_reference->type_name = g_strdup ($1);
+               $$ = type_reference;
+         }
+       | IDENTIFIER DOT IDENTIFIER
+         {
+               ValaTypeReference *type_reference = g_new0 (ValaTypeReference, 1);
+               type_reference->namespace_name = g_strdup ($1);
+               type_reference->type_name = g_strdup ($3);
+               $$ = type_reference;
+         }
+       ;
+
 class_body
        : OPEN_BRACE CLOSE_BRACE
        ;
index dd1df95..1d2a636 100644 (file)
@@ -21,6 +21,7 @@
  */
 
 %{
+#include "context.h"
 #include "parser.h"
 %}
  
@@ -33,6 +34,9 @@
 
 "{"            { return OPEN_BRACE; }
 "}"            { return CLOSE_BRACE; }
+"."            { return DOT; }
+":"            { return COLON; }
+","            { return COMMA; }
 
 "namespace"    { return NAMESPACE; }
 "class"                { return CLASS; }