+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
--- /dev/null
+namespace Maman {
+ class Bar {
+ }
+
+ class SubBar : Bar {
+ }
+}
* Jürg Billeter <j@bitron.ch>
*/
+#include <stdlib.h>
#include <stdio.h>
+#include <string.h>
#include <glib.h>
}
}
+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)
{
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;
}
* 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 {
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);
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;
}
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);
}
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);
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");
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");
{
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);
}
}
#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;
%}
%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
{
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
;
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
;
*/
%{
+#include "context.h"
#include "parser.h"
%}
"{" { return OPEN_BRACE; }
"}" { return CLOSE_BRACE; }
+"." { return DOT; }
+":" { return COLON; }
+"," { return COMMA; }
"namespace" { return NAMESPACE; }
"class" { return CLASS; }