+2006-04-24 Jürg Billeter <j@bitron.ch>
+
+ * valac/scanner.l: set token location, support OPEN_PARENS,
+ CLOSE_PARENS, SEMICOLON, PUBLIC, STATIC
+ * valac/parser.y: save symbol location, output exact error location,
+ support method declarations
+ * valac/context.h: add ValaLocation, ValaMethod, and ValaFormalParameter
+ structs, add location to ValaClass and ValaTypeReference structs
+ * valac/context.c: output error location, support method declarations
+ * valac/generator.c: support method declarations
+ * valac/driver.c: add comment
+ * tests/test-004.vala: test method declaration
+
2006-04-22 Jürg Billeter <j@bitron.ch>
* valac/scanner.l: support DOT, COLON, and COMMA
--- /dev/null
+namespace Maman {
+ class Bar {
+ public void do_action () {
+ }
+ }
+
+ class SubBar : Bar {
+ }
+}
}
static void
+err (ValaLocation *location, const char *format, ...)
+{
+ va_list args;
+ va_start (args, format);
+ fprintf (stderr, "%s:%d:%d: ", location->source_file->filename, location->lineno, location->colno);
+ vfprintf (stderr, format, args);
+ fprintf (stderr, "\n");
+ va_end (args);
+ exit (1);
+}
+
+static void
vala_context_add_symbols_from_namespace (ValaContext *context, ValaNamespace *namespace)
{
ValaSymbol *ns_symbol, *class_symbol;
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);
+ err (class->location, "error: class ´%s.%s´ already defined", namespace->name, class->name);
}
class_symbol = vala_symbol_new (VALA_SYMBOL_TYPE_CLASS);
context->root = vala_symbol_new (VALA_SYMBOL_TYPE_ROOT);
+ /* void */
+ symbol = vala_symbol_new (VALA_SYMBOL_TYPE_VOID);
+ g_hash_table_insert (context->root->symbol_table, "void", symbol);
+
+ /* namespace G */
namespace = g_new0 (ValaNamespace, 1);
namespace->name = g_strdup ("G");
namespace->lower_case_cname = g_strdup ("g_");
/* FIXME: look in namespaces specified by using directives */
- if (type_symbol == NULL) {
+ if (type_symbol != NULL) {
+ type_reference->namespace_name = "";
+ } else {
/* specified namespace not found */
- fprintf (stderr, "Error: Specified type '%s' not found.\n", type_reference->type_name);
+ err (type_reference->location, "error: specified type ´%s´ not found", type_reference->type_name);
exit (1);
}
} else {
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);
+ err (type_reference->location, "error: specified namespace '%s' not found", type_reference->namespace_name);
}
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);
+ err (type_reference->location, "error: specified type ´%s´ not found in namespace ´%s´", type_reference->type_name, type_reference->namespace_name);
}
}
- if (type_symbol->type == VALA_SYMBOL_TYPE_CLASS) {
+ if (type_symbol->type == VALA_SYMBOL_TYPE_VOID ||
+ 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);
+ err (type_reference->location, "error: specified symbol ´%s´ is not a type", type_reference->type_name);
+ }
+}
+
+static void
+vala_context_resolve_types_in_method (ValaContext *context, ValaMethod *method)
+{
+ GList *l;
+
+ vala_context_resolve_type_reference (context, method->class->namespace, method->return_type);
+
+ for (l = method->formal_parameters; l != NULL; l = l->next) {
+ ValaFormalParameter *formal_parameter = l->data;
+
+ vala_context_resolve_type_reference (context, method->class->namespace, formal_parameter->type);
+ if (formal_parameter->type->symbol->type == VALA_SYMBOL_TYPE_VOID) {
+ err (formal_parameter->location, "error: method parameters cannot be of type `void`");
+ }
}
}
static void
-vala_context_resolve_types_in_class (ValaContext *context, ValaNamespace *namespace, ValaClass *class)
+vala_context_resolve_types_in_class (ValaContext *context, 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);
+ vala_context_resolve_type_reference (context, class->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);
+ err (type_reference->location, "error: more than one base class specified in class ´%s.%s´", class->namespace->name, class->name);
}
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);
+ err (type_reference->location, "error: ´%s.%s´ cannot be subtype of ´%s.%s´", class->namespace->name, class->name, class->namespace->name, class->name);
}
/* 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);
+ err (type_reference->location, "error: ´%s.%s´ cannot be subtype of ´%s.%s´", class->namespace->name, class->name, type_reference->namespace_name, type_reference->type_name);
}
}
ValaSymbol *symbol = g_hash_table_lookup (context->root->symbol_table, "object");
class->base_class = symbol->class;
}
+
+ for (l = class->methods; l != NULL; l = l->next) {
+ ValaMethod *method = l->data;
+ vala_context_resolve_types_in_method (context, method);
+ }
}
static void
for (cl = namespace->classes; cl != NULL; cl = cl->next) {
ValaClass *class = cl->data;
- vala_context_resolve_types_in_class (context, namespace, class);
+ vala_context_resolve_types_in_class (context, class);
}
}
#include <glib.h>
typedef enum _ValaSymbolType ValaSymbolType;
+typedef enum _ValaMethodFlags ValaMethodFlags;
typedef struct _ValaContext ValaContext;
typedef struct _ValaSymbol ValaSymbol;
typedef struct _ValaSourceFile ValaSourceFile;
+typedef struct _ValaLocation ValaLocation;
typedef struct _ValaNamespace ValaNamespace;
typedef struct _ValaClass ValaClass;
+typedef struct _ValaMethod ValaMethod;
typedef struct _ValaTypeReference ValaTypeReference;
+typedef struct _ValaFormalParameter ValaFormalParameter;
enum _ValaSymbolType {
VALA_SYMBOL_TYPE_ROOT,
VALA_SYMBOL_TYPE_NAMESPACE,
+ VALA_SYMBOL_TYPE_VOID,
VALA_SYMBOL_TYPE_CLASS,
};
+enum _ValaMethodFlags {
+ VALA_METHOD_PUBLIC = 0x01,
+ VALA_METHOD_STATIC = 0x02,
+};
+
struct _ValaContext {
GList *source_files;
ValaSymbol *root;
GList *namespaces;
};
+struct _ValaLocation {
+ ValaSourceFile *source_file;
+ int lineno;
+ int colno;
+};
+
struct _ValaNamespace {
char *name;
ValaSymbol *symbol;
struct _ValaClass {
char *name;
+ ValaLocation *location;
ValaNamespace *namespace;
ValaClass *base_class;
GList *base_types;
+ GList *methods;
char *lower_case_cname;
char *upper_case_cname;
};
+struct _ValaMethod {
+ char *name;
+ ValaLocation *location;
+ ValaClass *class;
+ ValaTypeReference *return_type;
+ GList *formal_parameters;
+ ValaMethodFlags modifiers;
+ char *cdecl1;
+ char *cdecl2;
+};
+
struct _ValaTypeReference {
char *namespace_name;
char *type_name;
+ ValaLocation *location;
ValaSymbol *symbol;
};
+struct _ValaFormalParameter {
+ char *name;
+ ValaTypeReference *type;
+ ValaLocation *location;
+};
+
ValaContext *vala_context_new ();
void vala_context_free (ValaContext *context);
void vala_context_parse (ValaContext *context);
* Copy namespace and type names from parse tree into symbol tables
* Resolve type references in parse tree
* Load metadata from parse tree into symbol tables
+ * Add memory management calls
* Generate code
*/
}
static void
-vala_code_generator_process_class (ValaCodeGenerator *generator, ValaClass *class)
+vala_code_generator_process_methods1 (ValaCodeGenerator *generator, ValaClass *class)
+{
+ GList *l;
+
+ char *camel_case;
+ char *ns_lower;
+ char *ns_upper;
+
+ ValaNamespace *namespace = class->namespace;
+
+ ns_lower = namespace->lower_case_cname;
+ ns_upper = namespace->upper_case_cname;
+
+ char *lower_case = class->lower_case_cname;
+ char *upper_case = class->upper_case_cname;
+
+ for (l = class->methods; l != NULL; l = l->next) {
+ ValaMethod *method = l->data;
+
+ char *method_return_type_cname = g_strdup_printf ("%s%s", method->return_type->namespace_name, method->return_type->type_name);
+ char *method_cname = g_strdup_printf ("%s%s_%s", ns_lower, lower_case, method->name);
+ char *parameters;
+ GList *parameter_list = NULL;
+ if ((method->modifiers & VALA_METHOD_STATIC) == 0) {
+ parameter_list = g_list_append (parameter_list, g_strdup_printf ("%s%s *self", namespace->name, class->name));
+ }
+
+ GList *pl;
+ for (pl = method->formal_parameters; pl != NULL; pl = pl->next) {
+ ValaFormalParameter *param = pl->data;
+ parameter_list = g_list_append (parameter_list, g_strdup_printf ("%s%s *%s", param->type->symbol->class->namespace->name, param->type->symbol->class->name, param->name));
+ }
+
+ if (parameter_list == NULL) {
+ parameters = g_strdup ("");
+ } else {
+ parameters = parameter_list->data;
+ GList *sl;
+ for (sl = parameter_list->next; sl != NULL; sl = sl->next) {
+ parameters = g_strdup_printf ("%s, %s", parameters, sl->data);
+ g_free (sl->data);
+ }
+ g_list_free (parameter_list);
+ }
+
+ method->cdecl2 = g_strdup_printf ("%s (%s)", method_cname, parameters);
+
+ if (method->modifiers & VALA_METHOD_PUBLIC) {
+ method->cdecl1 = g_strdup (method_return_type_cname);
+ } else {
+ method->cdecl1 = g_strdup_printf ("static %s", method_return_type_cname);
+ fprintf (generator->c_file, "%s %s;\n", method->cdecl1, method->cdecl2);
+ }
+
+ }
+ fprintf (generator->c_file, "\n");
+}
+
+static void
+vala_code_generator_process_methods2 (ValaCodeGenerator *generator, ValaClass *class)
+{
+ GList *l;
+
+ char *camel_case;
+ char *ns_lower;
+ char *ns_upper;
+
+ ValaNamespace *namespace = class->namespace;
+
+ ns_lower = namespace->lower_case_cname;
+ ns_upper = namespace->upper_case_cname;
+
+ char *lower_case = class->lower_case_cname;
+ char *upper_case = class->upper_case_cname;
+
+ for (l = class->methods; l != NULL; l = l->next) {
+ ValaMethod *method = l->data;
+
+ if (method->modifiers & VALA_METHOD_PUBLIC) {
+ fprintf (generator->h_file, "%s %s;\n", method->cdecl1, method->cdecl2);
+ }
+
+ fprintf (generator->c_file, "%s\n", method->cdecl1);
+ fprintf (generator->c_file, "%s\n", method->cdecl2);
+ fprintf (generator->c_file, "{\n");
+ fprintf (generator->c_file, "}\n");
+ fprintf (generator->c_file, "\n");
+ }
+ fprintf (generator->h_file, "\n");
+
+ /* constructors */
+ fprintf (generator->c_file, "static void\n");
+ fprintf (generator->c_file, "%s%s_init (%s%s *self)\n", ns_lower, lower_case, namespace->name, class->name);
+ fprintf (generator->c_file, "{\n");
+ fprintf (generator->c_file, "}\n");
+ fprintf (generator->c_file, "\n");
+
+ fprintf (generator->c_file, "static void\n");
+ fprintf (generator->c_file, "%s%s_class_init (%s%sClass *klass)\n", ns_lower, lower_case, namespace->name, class->name);
+ fprintf (generator->c_file, "{\n");
+ fprintf (generator->c_file, "}\n");
+ fprintf (generator->c_file, "\n");
+}
+
+static void
+vala_code_generator_process_class1 (ValaCodeGenerator *generator, ValaClass *class)
{
ValaNamespace *namespace = class->namespace;
fprintf (generator->h_file, "typedef struct _%s %s;\n", camel_case, camel_case);
fprintf (generator->h_file, "typedef struct _%sClass %sClass;\n", camel_case, camel_case);
fprintf (generator->h_file, "\n");
+
+ vala_code_generator_process_methods1 (generator, class);
+}
+
+static void
+vala_code_generator_process_class2 (ValaCodeGenerator *generator, ValaClass *class)
+{
+ ValaNamespace *namespace = class->namespace;
+
+ char *camel_case;
+ char *ns_lower;
+ char *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 = class->lower_case_cname;
+ char *upper_case = class->upper_case_cname;
+ /* structs */
fprintf (generator->h_file, "struct _%s {\n", camel_case);
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, "GType %s%s_get_type () G_GNUC_CONST;\n", ns_lower, lower_case);
fprintf (generator->h_file, "\n");
- /* constructors */
- fprintf (generator->c_file, "static void\n");
- fprintf (generator->c_file, "%s%s_init (%s *self)\n", ns_lower, lower_case, camel_case);
- fprintf (generator->c_file, "{\n");
- fprintf (generator->c_file, "}\n");
- fprintf (generator->c_file, "\n");
-
- fprintf (generator->c_file, "static void\n");
- fprintf (generator->c_file, "%s%s_class_init (%sClass *klass)\n", ns_lower, lower_case, camel_case);
- fprintf (generator->c_file, "{\n");
- fprintf (generator->c_file, "}\n");
- fprintf (generator->c_file, "\n");
-
+ vala_code_generator_process_methods2 (generator, class);
+
/* type initialization function */
fprintf (generator->c_file, "GType\n");
fprintf (generator->c_file, "%s%s_get_type ()\n", ns_lower, lower_case);
}
static void
-vala_code_generator_process_namespace (ValaCodeGenerator *generator, ValaNamespace *namespace)
+vala_code_generator_process_namespace1 (ValaCodeGenerator *generator, ValaNamespace *namespace)
{
GList *l;
for (l = namespace->classes; l != NULL; l = l->next) {
- vala_code_generator_process_class (generator, l->data);
+ vala_code_generator_process_class1 (generator, l->data);
+ }
+}
+
+static void
+vala_code_generator_process_namespace2 (ValaCodeGenerator *generator, ValaNamespace *namespace)
+{
+ GList *l;
+ for (l = namespace->classes; l != NULL; l = l->next) {
+ vala_code_generator_process_class2 (generator, l->data);
}
}
fprintf (generator->c_file, "#include \"%s\"\n", g_path_get_basename (h_filename));
fprintf (generator->c_file, "\n");
- vala_code_generator_process_namespace (generator, source_file->root_namespace);
+ vala_code_generator_process_namespace1 (generator, source_file->root_namespace);
GList *l;
+
+ for (l = source_file->namespaces; l != NULL; l = l->next) {
+ vala_code_generator_process_namespace1 (generator, l->data);
+ }
+
+ vala_code_generator_process_namespace1 (generator, source_file->root_namespace);
+
for (l = source_file->namespaces; l != NULL; l = l->next) {
- vala_code_generator_process_namespace (generator, l->data);
+ vala_code_generator_process_namespace2 (generator, l->data);
}
fprintf (generator->h_file, "G_END_DECLS\n");
%{
#include <stdlib.h>
#include <stdio.h>
+#include <string.h>
#include <glib.h>
return str;
}
-ValaSourceFile *current_source_file;
-ValaNamespace *current_namespace;
+static ValaSourceFile *current_source_file;
+static ValaNamespace *current_namespace;
+static ValaClass *current_class;
+static ValaMethod *current_method;
+
+ValaLocation *get_location (int lineno, int colno)
+{
+ ValaLocation *loc = g_new0 (ValaLocation, 1);
+ loc->source_file = current_source_file;
+ loc->lineno = lineno;
+ loc->colno = colno;
+ return loc;
+}
+
+#define current_location(token) get_location (token.first_line, token.first_column)
%}
%defines
%pure_parser
%glr-parser
%union {
+ int num;
char *str;
GList *list;
ValaTypeReference *type_reference;
+ ValaFormalParameter *formal_parameter;
}
%token OPEN_BRACE "{"
%token CLOSE_BRACE "}"
+%token OPEN_PARENS "("
+%token CLOSE_PARENS ")"
%token DOT "."
%token COLON ":"
%token COMMA ","
+%token SEMICOLON ";"
%token NAMESPACE "namespace"
%token CLASS "class"
+%token PUBLIC "public"
+%token STATIC "static"
%token <str> IDENTIFIER "identifier"
%type <list> opt_class_base
%type <list> class_base
%type <list> type_list
%type <type_reference> type_name
+%type <list> opt_formal_parameter_list
+%type <list> formal_parameter_list
+%type <list> fixed_parameters
+%type <formal_parameter> fixed_parameter
+%type <num> opt_method_modifiers
+%type <num> method_modifiers
+%type <num> method_modifier
%%
class_declaration
: CLASS IDENTIFIER opt_class_base
{
- ValaClass *class = g_new0 (ValaClass, 1);
- 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);
+ current_class = g_new0 (ValaClass, 1);
+ current_class->name = g_strdup ($2);
+ current_class->base_types = $3;
+ current_class->location = current_location (@2);
+ current_class->namespace = current_namespace;
+ current_class->lower_case_cname = camel_case_to_lower_case (current_class->name);
+ current_class->upper_case_cname = camel_case_to_upper_case (current_class->name);
+ current_namespace->classes = g_list_append (current_namespace->classes, current_class);
}
class_body
;
{
ValaTypeReference *type_reference = g_new0 (ValaTypeReference, 1);
type_reference->type_name = g_strdup ($1);
+ type_reference->location = current_location (@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->location = current_location (@1);
$$ = type_reference;
}
;
class_body
+ : OPEN_BRACE opt_class_member_declarations CLOSE_BRACE
+ ;
+
+opt_class_member_declarations
+ : /* empty */
+ | class_member_declarations
+ ;
+
+class_member_declarations
+ : class_member_declaration
+ | class_member_declarations class_member_declaration
+ ;
+
+class_member_declaration
+ : method_declaration
+ ;
+
+method_declaration
+ : method_header method_body
+ ;
+
+method_header
+ : opt_method_modifiers type_name IDENTIFIER OPEN_PARENS opt_formal_parameter_list CLOSE_PARENS
+ {
+ current_method = g_new0 (ValaMethod, 1);
+ current_method->name = g_strdup ($3);
+ current_method->class = current_class;
+ current_method->return_type = $2;
+ current_method->formal_parameters = $5;
+ current_method->modifiers = $1;
+ current_method->location = current_location (@3);
+
+ current_class->methods = g_list_append (current_class->methods, current_method);
+ }
+ ;
+
+opt_method_modifiers
+ : /* empty */
+ {
+ $$ = 0;
+ }
+ | method_modifiers
+ {
+ $$ = $1;
+ }
+ ;
+
+method_modifiers
+ : method_modifier
+ {
+ $$ = $1;
+ }
+ | method_modifiers method_modifier
+ {
+ $$ = $1 | $2;
+ }
+ ;
+
+method_modifier
+ : PUBLIC
+ {
+ $$ = VALA_METHOD_PUBLIC;
+ }
+ | STATIC
+ {
+ $$ = VALA_METHOD_STATIC;
+ }
+ ;
+
+opt_formal_parameter_list
+ : /* empty */
+ {
+ $$ = NULL;
+ }
+ | formal_parameter_list
+ {
+ $$ = $1;
+ }
+ ;
+
+formal_parameter_list
+ : fixed_parameters
+ ;
+
+fixed_parameters
+ : fixed_parameter
+ {
+ $$ = g_list_append (NULL, $1);
+ }
+ | fixed_parameters COMMA fixed_parameter
+ {
+ $$ = g_list_append ($1, $3);
+ }
+ ;
+
+fixed_parameter
+ : type_name IDENTIFIER
+ {
+ $$ = g_new0 (ValaFormalParameter, 1);
+ $$->type = $1;
+ $$->name = $2;
+ $$->location = current_location (@2);
+ }
+ ;
+
+method_body
+ : block
+ | SEMICOLON
+ ;
+
+block
: OPEN_BRACE CLOSE_BRACE
;
void yyerror (YYLTYPE *locp, const char *s)
{
- printf ("%d: %s\n", locp->first_line, s);
+ printf ("%s:%d:%d-%d: %s\n", current_source_file->filename, locp->first_line, locp->first_column, locp->last_column, s);
}
void vala_parser_parse (ValaSourceFile *source_file)
%{
#include "context.h"
#include "parser.h"
+
+#define uploc { yylloc->first_column = yylloc->last_column + 1; yylloc->last_column += strlen (yytext); }
%}
%option yylineno
%%
-"{" { return OPEN_BRACE; }
-"}" { return CLOSE_BRACE; }
-"." { return DOT; }
-":" { return COLON; }
-"," { return COMMA; }
+"{" { uploc; return OPEN_BRACE; }
+"}" { uploc; return CLOSE_BRACE; }
+"(" { uploc; return OPEN_PARENS; }
+")" { uploc; return CLOSE_PARENS; }
+"." { uploc; return DOT; }
+":" { uploc; return COLON; }
+"," { uploc; return COMMA; }
+";" { uploc; return SEMICOLON; }
-"namespace" { return NAMESPACE; }
-"class" { return CLASS; }
+"namespace" { uploc; return NAMESPACE; }
+"class" { uploc; return CLASS; }
+"public" { uploc; return PUBLIC; }
+"static" { uploc; return STATIC; }
-[[:alnum:]_]+ { yylval->str = strdup (yytext); return IDENTIFIER; }
+[[:alnum:]_]+ { uploc; yylval->str = strdup (yytext); return IDENTIFIER; }
-[ \t\n]+ /* eat up whitespace */
+[ \t]+ { uploc; /* eat up whitespace */ }
+[\n]+ { yylloc->first_line = yylloc->last_line = yylineno; yylloc->first_column = 1; yylloc->last_column = 0; }