3 * Copyright (C) 2008 Jürg Billeter
5 * This library is free software; you can redistribute it and/or
6 * modify it under the terms of the GNU Lesser General Public
7 * License as published by the Free Software Foundation; either
8 * version 2.1 of the License, or (at your option) any later version.
10 * This library is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 * Lesser General Public License for more details.
15 * You should have received a copy of the GNU Lesser General Public
16 * License along with this library; if not, write to the Free Software
17 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
20 * Jürg Billeter <j@bitron.ch>
27 * Code visitor parsing all Vala source files.
29 public class Vala
.GirParser
: CodeVisitor
{
34 SourceFile current_source_file
;
37 MarkupTokenType current_token
;
39 HashMap
<string,string> attributes_map
= new HashMap
<string,string> (str_hash
, str_equal
);
42 * Parses all .gir source files in the specified code
43 * context and builds a code tree.
45 * @param context a code context
47 public void parse (CodeContext context
) {
48 this
.context
= context
;
49 context
.accept (this
);
52 public override void visit_source_file (SourceFile source_file
) {
53 if (source_file
.filename
.has_suffix (".gir")) {
54 parse_file (source_file
);
58 public void parse_file (SourceFile source_file
) {
59 this
.current_source_file
= source_file
;
60 reader
= new
MarkupReader (source_file
.filename
);
70 this
.current_source_file
= null;
74 current_token
= reader
.read_token (out begin
, out end
);
77 void start_element (string name
) {
78 if (current_token
!= MarkupTokenType
.START_ELEMENT
|| reader
.name
!= name
) {
80 Report
.error (get_current_src (), "expected start element of `%s'".printf (name
));
84 void end_element (string name
) {
85 if (current_token
!= MarkupTokenType
.END_ELEMENT
|| reader
.name
!= name
) {
87 Report
.error (get_current_src (), "expected end element of `%s'".printf (name
));
92 SourceReference
get_current_src () {
93 return new
SourceReference (this
.current_source_file
, begin
.line
, begin
.column
, end
.line
, end
.column
);
96 void parse_repository () {
97 start_element ("repository");
99 while (current_token
== MarkupTokenType
.START_ELEMENT
) {
100 if (reader
.name
== "namespace") {
101 context
.root
.add_namespace (parse_namespace ());
102 } else if (reader
.name
== "include") {
106 Report
.error (get_current_src (), "unknown child element `%s' in `repository'".printf (reader
.name
));
110 end_element ("repository");
113 void parse_include () {
114 start_element ("include");
116 end_element ("include");
119 Namespace
parse_namespace () {
120 start_element ("namespace");
121 var ns
= new
Namespace (reader
.get_attribute ("name"));
122 string cheader
= get_attribute (ns
.name
, "c:header-filename");
123 if (cheader
!= null) {
124 ns
.set_cheader_filename (cheader
);
127 while (current_token
== MarkupTokenType
.START_ELEMENT
) {
129 if (reader
.name
== "alias") {
130 sym
= parse_alias ();
131 } else if (reader
.name
== "enumeration") {
132 sym
= parse_enumeration ();
133 } else if (reader
.name
== "bitfield") {
134 sym
= parse_bitfield ();
135 } else if (reader
.name
== "function") {
136 sym
= parse_function ();
137 } else if (reader
.name
== "callback") {
138 sym
= parse_callback ();
139 } else if (reader
.name
== "record") {
140 sym
= parse_record ();
141 } else if (reader
.name
== "class") {
142 sym
= parse_class ();
143 } else if (reader
.name
== "interface") {
144 sym
= parse_interface ();
145 } else if (reader
.name
== "glib:boxed") {
147 } else if (reader
.name
== "union") {
149 } else if (reader
.name
== "constant") {
150 sym
= parse_constant ();
153 Report
.error (get_current_src (), "unknown child element `%s' in `namespace'".printf (reader
.name
));
158 ns
.add_class ((Class
) sym
);
159 } else if (sym is Interface
) {
160 ns
.add_interface ((Interface
) sym
);
161 } else if (sym is Struct
) {
162 ns
.add_struct ((Struct
) sym
);
163 } else if (sym is Enum
) {
164 ns
.add_enum ((Enum
) sym
);
165 } else if (sym is Delegate
) {
166 ns
.add_delegate ((Delegate
) sym
);
167 } else if (sym is Method
) {
168 ns
.add_method ((Method
) sym
);
169 } else if (sym is Constant
) {
170 ns
.add_constant ((Constant
) sym
);
171 } else if (sym
== null) {
174 current_source_file
.add_node (sym
);
176 end_element ("namespace");
180 Struct
parse_alias () {
181 start_element ("alias");
182 var st
= new
Struct (reader
.get_attribute ("name"));
183 st
.access
= SymbolAccessibility
.PUBLIC
;
184 st
.add_base_type (parse_type_from_name (reader
.get_attribute ("target")));
186 end_element ("alias");
190 Enum
parse_enumeration () {
191 start_element ("enumeration");
192 var en
= new
Enum (reader
.get_attribute ("name"));
193 en
.access
= SymbolAccessibility
.PUBLIC
;
196 string common_prefix
= null;
198 while (current_token
== MarkupTokenType
.START_ELEMENT
) {
199 if (reader
.name
== "member") {
200 var ev
= parse_enumeration_member ();
203 string cname
= ev
.get_cname ();
205 if (common_prefix
== null) {
206 common_prefix
= cname
;
207 while (common_prefix
.len () > 0 && !common_prefix
.has_suffix ("_")) {
208 // FIXME: could easily be made faster
209 common_prefix
= common_prefix
.ndup (common_prefix
.size () - 1);
212 while (!cname
.has_prefix (common_prefix
)) {
213 common_prefix
= common_prefix
.ndup (common_prefix
.size () - 1);
216 while (common_prefix
.len () > 0 && (!common_prefix
.has_suffix ("_") ||
217 (cname
.offset (common_prefix
.size ()).get_char ().isdigit ()) && (cname
.len () - common_prefix
.len ()) <= 1)) {
218 // enum values may not consist solely of digits
219 common_prefix
= common_prefix
.ndup (common_prefix
.size () - 1);
227 en
.set_cprefix (common_prefix
);
229 end_element ("enumeration");
233 Enum
parse_bitfield () {
234 start_element ("bitfield");
235 var en
= new
Enum (reader
.get_attribute ("name"));
236 en
.access
= SymbolAccessibility
.PUBLIC
;
238 while (current_token
== MarkupTokenType
.START_ELEMENT
) {
239 if (reader
.name
== "member") {
240 en
.add_value (parse_enumeration_member ());
246 end_element ("bitfield");
250 EnumValue
parse_enumeration_member () {
251 start_element ("member");
252 var ev
= new
EnumValue (string.joinv ("_", reader
.get_attribute ("name").up ().split ("-")));
253 ev
.set_cname (reader
.get_attribute ("c:identifier"));
255 end_element ("member");
259 Method
parse_function () {
260 start_element ("function");
261 string name
= reader
.get_attribute ("name");
263 DataType return_type
;
264 if (current_token
== MarkupTokenType
.START_ELEMENT
&& reader
.name
== "return-value") {
265 return_type
= parse_return_value ();
267 return_type
= new
VoidType ();
269 var m
= new
Method (name
, return_type
);
270 m
.access
= SymbolAccessibility
.PUBLIC
;
271 m
.binding
= MemberBinding
.STATIC
;
272 var parameters
= new ArrayList
<FormalParameter
> ();
273 var array_length_parameters
= new ArrayList
<int> ();
274 if (current_token
== MarkupTokenType
.START_ELEMENT
&& reader
.name
== "parameters") {
275 start_element ("parameters");
277 while (current_token
== MarkupTokenType
.START_ELEMENT
) {
278 int array_length_idx
= -1;
279 var param
= parse_parameter (out array_length_idx
);
280 if (array_length_idx
!= -1) {
281 array_length_parameters
.add (array_length_idx
);
283 parameters
.add (param
);
285 end_element ("parameters");
288 foreach (FormalParameter param
in parameters
) {
289 if (!array_length_parameters
.contains (i
)) {
290 m
.add_parameter (param
);
294 end_element ("function");
298 DataType
parse_return_value () {
299 start_element ("return-value");
300 string transfer
= reader
.get_attribute ("transfer-ownership");
302 var type
= parse_type ();
303 if (transfer
== "full") {
304 type
.value_owned
= true;
306 end_element ("return-value");
310 FormalParameter
parse_parameter (out int array_length_idx
= null) {
311 FormalParameter param
= null;
313 start_element ("parameter");
314 string name
= reader
.get_attribute ("name");
315 string direction
= reader
.get_attribute ("direction");
316 string transfer
= reader
.get_attribute ("transfer-ownership");
318 if (reader
.name
== "varargs") {
319 start_element ("varargs");
321 param
= new FormalParameter
.with_ellipsis ();
322 end_element ("varargs");
324 var type
= parse_type (out array_length_idx
);
325 if (transfer
== "full") {
326 type
.value_owned
= true;
328 param
= new
FormalParameter (name
, type
);
329 if (direction
== "out") {
330 param
.direction
= ParameterDirection
.OUT
;
331 } else if (direction
== "inout") {
332 param
.direction
= ParameterDirection
.REF
;
335 end_element ("parameter");
339 DataType
parse_type (out int array_length_index
= null) {
340 if (reader
.name
== "array") {
341 start_element ("array");
342 if (reader
.get_attribute ("length") != null
343 && &array_length_index
!= null) {
344 array_length_index
= reader
.get_attribute ("length").to_int ();
347 var element_type
= parse_type ();
348 end_element ("array");
349 return new
ArrayType (element_type
, 1, null);
351 start_element ("type");
352 DataType type
= parse_type_from_name (reader
.get_attribute ("name"));
355 // type arguments / element types
356 while (current_token
== MarkupTokenType
.START_ELEMENT
) {
360 end_element ("type");
365 DataType
parse_type_from_name (string type_name
) {
367 if (type_name
== "none") {
368 type
= new
VoidType ();
369 } else if (type_name
== "any") {
370 type
= new
PointerType (new
VoidType ());
371 } else if (type_name
== "GObject.Strv") {
372 type
= new
ArrayType (new UnresolvedType
.from_symbol (new
UnresolvedSymbol (null, "string")), 1, null);
374 if (type_name
== "utf8") {
375 type_name
= "string";
376 } else if (type_name
== "boolean") {
378 } else if (type_name
== "GType") {
379 type_name
= "GLib.Type";
380 } else if (type_name
== "GObject.String") {
381 type_name
= "GLib.StringBuilder";
382 } else if (type_name
== "GObject.Class") {
383 type_name
= "GLib.ObjectClass";
384 } else if (type_name
== "GLib.unichar") {
385 type_name
= "unichar";
386 } else if (type_name
== "GLib.Data") {
387 type_name
= "GLib.Datalist";
389 string[] type_components
= type_name
.split (".");
390 if (type_components
[1] != null) {
392 string namespace_name
= type_components
[0];
393 string transformed_type_name
= type_components
[1];
394 if (namespace_name
== "GObject") {
395 namespace_name
= "GLib";
396 } else if (namespace_name
== "Gio") {
397 namespace_name
= "GLib";
399 type
= new UnresolvedType
.from_symbol (new
UnresolvedSymbol (new
UnresolvedSymbol (null, namespace_name
), transformed_type_name
));
401 type
= new UnresolvedType
.from_symbol (new
UnresolvedSymbol (null, type_name
));
408 Struct
parse_record () {
409 start_element ("record");
410 var st
= new
Struct (reader
.get_attribute ("name"));
411 st
.access
= SymbolAccessibility
.PUBLIC
;
413 while (current_token
== MarkupTokenType
.START_ELEMENT
) {
414 if (reader
.name
== "field") {
415 st
.add_field (parse_field ());
416 } else if (reader
.name
== "callback") {
418 } else if (reader
.name
== "constructor") {
419 parse_constructor ();
420 } else if (reader
.name
== "method") {
424 Report
.error (get_current_src (), "unknown child element `%s' in `record'".printf (reader
.name
));
428 end_element ("record");
432 Class
parse_class () {
433 start_element ("class");
434 var cl
= new
Class (reader
.get_attribute ("name"));
435 cl
.access
= SymbolAccessibility
.PUBLIC
;
437 string parent
= reader
.get_attribute ("parent");
438 if (parent
!= null) {
439 cl
.add_base_type (parse_type_from_name (parent
));
443 var signals
= new ArrayList
<Signal
> ();
444 var methods
= new ArrayList
<Method
> ();
445 var fields
= new ArrayList
<Field
> ();
446 while (current_token
== MarkupTokenType
.START_ELEMENT
) {
447 if (reader
.name
== "implements") {
448 start_element ("implements");
449 cl
.add_base_type (parse_type_from_name (reader
.get_attribute ("name")));
451 end_element ("implements");
452 } else if (reader
.name
== "field") {
453 fields
.add (parse_field ());
454 } else if (reader
.name
== "property") {
455 cl
.add_property (parse_property ());
456 } else if (reader
.name
== "constructor") {
457 cl
.add_method (parse_constructor ());
458 } else if (reader
.name
== "method") {
459 methods
.add (parse_method ());
460 } else if (reader
.name
== "callback") {
462 } else if (reader
.name
== "glib:signal") {
463 signals
.add (parse_signal ());
466 Report
.error (get_current_src (), "unknown child element `%s' in `class'".printf (reader
.name
));
472 foreach (Signal sig
in signals
) {
473 var symbol
= cl
.scope
.lookup (sig
.name
);
474 if (symbol
== null) {
476 } else if (symbol is Property
) {
477 // properties take precedence
479 Report
.error (get_current_src (), "duplicate member `%s' in `%s'".printf (sig
.name
, cl
.name
));
484 foreach (Method m
in methods
) {
485 var symbol
= cl
.scope
.lookup (m
.name
);
486 if (symbol
== null) {
488 } else if (symbol is Signal
) {
489 var sig
= (Signal
) symbol
;
490 sig
.has_emitter
= true;
491 } else if (symbol is Property
|| symbol is Field
) {
492 // assume method is getter for property/field ignore method
494 Report
.error (get_current_src (), "duplicate member `%s' in `%s'".printf (m
.name
, cl
.name
));
498 // fields have lowest priority
499 foreach (Field f
in fields
) {
500 var symbol
= cl
.scope
.lookup (f
.name
);
501 if (symbol
== null) {
506 end_element ("class");
510 Interface
parse_interface () {
511 start_element ("interface");
512 var iface
= new
Interface (reader
.get_attribute ("name"));
513 iface
.access
= SymbolAccessibility
.PUBLIC
;
515 var methods
= new ArrayList
<Method
> ();
516 while (current_token
== MarkupTokenType
.START_ELEMENT
) {
517 if (reader
.name
== "field") {
519 } else if (reader
.name
== "property") {
520 iface
.add_property (parse_property ());
521 } else if (reader
.name
== "callback") {
523 } else if (reader
.name
== "method") {
524 methods
.add (parse_method ());
525 } else if (reader
.name
== "glib:signal") {
526 iface
.add_signal (parse_signal ());
529 Report
.error (get_current_src (), "unknown child element `%s' in `interface'".printf (reader
.name
));
535 foreach (Method m
in methods
) {
536 var symbol
= iface
.scope
.lookup (m
.name
);
537 if (symbol
== null) {
538 iface
.add_method (m
);
539 } else if (symbol is Signal
) {
540 var sig
= (Signal
) symbol
;
541 sig
.has_emitter
= true;
543 Report
.error (get_current_src (), "duplicate member `%s' in `%s'".printf (m
.name
, iface
.name
));
547 end_element ("interface");
551 Field
parse_field () {
552 start_element ("field");
553 string name
= reader
.get_attribute ("name");
555 var type
= parse_type ();
556 var field
= new
Field (name
, type
, null);
557 field
.access
= SymbolAccessibility
.PUBLIC
;
558 end_element ("field");
562 Property
parse_property () {
563 start_element ("property");
564 string name
= string.joinv ("_", reader
.get_attribute ("name").split ("-"));
566 var type
= parse_type ();
567 var prop
= new
Property (name
, type
, null, null);
568 prop
.access
= SymbolAccessibility
.PUBLIC
;
569 prop
.get_accessor
= new
PropertyAccessor (true, false, false, null, null);
570 prop
.set_accessor
= new
PropertyAccessor (false, true, false, null, null);
571 end_element ("property");
575 Delegate
parse_callback () {
576 start_element ("callback");
577 string name
= reader
.get_attribute ("name");
579 DataType return_type
;
580 if (current_token
== MarkupTokenType
.START_ELEMENT
&& reader
.name
== "return-value") {
581 return_type
= parse_return_value ();
583 return_type
= new
VoidType ();
585 var d
= new
Delegate (name
, return_type
);
586 d
.access
= SymbolAccessibility
.PUBLIC
;
587 if (current_token
== MarkupTokenType
.START_ELEMENT
&& reader
.name
== "parameters") {
588 start_element ("parameters");
590 while (current_token
== MarkupTokenType
.START_ELEMENT
) {
591 d
.add_parameter (parse_parameter ());
593 end_element ("parameters");
595 end_element ("callback");
599 Method
parse_constructor () {
600 start_element ("constructor");
601 string name
= reader
.get_attribute ("name");
604 var return_type
= parse_return_value ();
606 var m
= new
CreationMethod (null, name
);
607 m
.access
= SymbolAccessibility
.PUBLIC
;
608 m
.has_construct_function
= false;
609 if (m
.name
.has_prefix ("new_")) {
610 m
.name
= m
.name
.offset ("new_".len ());
612 if (current_token
== MarkupTokenType
.START_ELEMENT
&& reader
.name
== "parameters") {
613 start_element ("parameters");
615 while (current_token
== MarkupTokenType
.START_ELEMENT
) {
616 m
.add_parameter (parse_parameter ());
618 end_element ("parameters");
620 end_element ("constructor");
624 Method
parse_method () {
625 start_element ("method");
626 string name
= reader
.get_attribute ("name");
628 DataType return_type
;
629 if (current_token
== MarkupTokenType
.START_ELEMENT
&& reader
.name
== "return-value") {
630 return_type
= parse_return_value ();
632 return_type
= new
VoidType ();
634 var m
= new
Method (name
, return_type
);
635 m
.access
= SymbolAccessibility
.PUBLIC
;
636 if (current_token
== MarkupTokenType
.START_ELEMENT
&& reader
.name
== "parameters") {
637 start_element ("parameters");
639 while (current_token
== MarkupTokenType
.START_ELEMENT
) {
640 m
.add_parameter (parse_parameter ());
642 end_element ("parameters");
644 end_element ("method");
648 Signal
parse_signal () {
649 start_element ("glib:signal");
650 string name
= string.joinv ("_", reader
.get_attribute ("name").split ("-"));
652 DataType return_type
;
653 if (current_token
== MarkupTokenType
.START_ELEMENT
&& reader
.name
== "return-value") {
654 return_type
= parse_return_value ();
656 return_type
= new
VoidType ();
658 var sig
= new
Signal (name
, return_type
);
659 sig
.access
= SymbolAccessibility
.PUBLIC
;
660 sig
.is_virtual
= true;
661 if (current_token
== MarkupTokenType
.START_ELEMENT
&& reader
.name
== "parameters") {
662 start_element ("parameters");
664 while (current_token
== MarkupTokenType
.START_ELEMENT
) {
665 sig
.add_parameter (parse_parameter ());
667 end_element ("parameters");
669 end_element ("glib:signal");
673 Struct
parse_boxed () {
674 start_element ("glib:boxed");
675 var st
= new
Struct (reader
.get_attribute ("glib:name"));
676 st
.access
= SymbolAccessibility
.PUBLIC
;
679 while (current_token
== MarkupTokenType
.START_ELEMENT
) {
680 if (reader
.name
== "field") {
681 st
.add_field (parse_field ());
682 } else if (reader
.name
== "constructor") {
683 parse_constructor ();
684 } else if (reader
.name
== "method") {
685 st
.add_method (parse_method ());
688 Report
.error (get_current_src (), "unknown child element `%s' in `class'".printf (reader
.name
));
693 end_element ("glib:boxed");
697 Struct
parse_union () {
698 start_element ("union");
699 var st
= new
Struct (reader
.get_attribute ("name"));
700 st
.access
= SymbolAccessibility
.PUBLIC
;
703 while (current_token
== MarkupTokenType
.START_ELEMENT
) {
704 if (reader
.name
== "field") {
705 st
.add_field (parse_field ());
706 } else if (reader
.name
== "constructor") {
707 parse_constructor ();
708 } else if (reader
.name
== "method") {
709 st
.add_method (parse_method ());
712 Report
.error (get_current_src (), "unknown child element `%s' in `union'".printf (reader
.name
));
717 end_element ("union");
721 Constant
parse_constant () {
722 start_element ("constant");
723 string name
= reader
.get_attribute ("name");
725 var type
= parse_type ();
726 var c
= new
Constant (name
, type
, null, null);
727 c
.access
= SymbolAccessibility
.PUBLIC
;
728 end_element ("constant");
732 public void parse_metadata (string metadata_filename
) {
733 if (FileUtils
.test (metadata_filename
, FileTest
.EXISTS
)) {
737 FileUtils
.get_contents (metadata_filename
, out metadata
, out metadata_len
);
739 foreach (string line
in metadata
.split ("\n")) {
740 if (line
.has_prefix ("#")) {
741 // ignore comment lines
745 string[] tokens
= line
.split (" ", 2);
747 if (null == tokens
[0]) {
751 foreach (string attribute
in tokens
[1].split (" ")) {
752 string[] pair
= attribute
.split ("=", 2);
753 string key
= "%s/@%s".printf (tokens
[0], pair
[0]);
754 attributes_map
.set (key
, pair
[1].substring (1, pair
[1].length
- 2));
757 } catch (FileError e
) {
758 Report
.error (null, "Unable to read metadata file: %s".printf (e
.message
));
761 Report
.error (null, "Metadata file `%s' not found".printf (metadata_filename
));
765 string?
get_attribute (string node
, string key
) {
766 return attributes_map
["%s/@%s".printf (node
, key
)];