update for 0.4.0 release
[vala-lang.git] / vapigen / valagirparser.vala
blob12d851dc8ebe87b5b04847cfa2f1334c16a4d3fb
1 /* valagirparser.vala
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
19 * Author:
20 * Jürg Billeter <j@bitron.ch>
23 using GLib;
24 using Gee;
26 /**
27 * Code visitor parsing all Vala source files.
29 public class Vala.GirParser : CodeVisitor {
30 MarkupReader reader;
32 CodeContext context;
34 SourceFile current_source_file;
35 SourceLocation begin;
36 SourceLocation end;
37 MarkupTokenType current_token;
39 HashMap<string,string> attributes_map = new HashMap<string,string> (str_hash, str_equal);
41 /**
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);
62 // xml prolog
63 next ();
64 next ();
66 next ();
67 parse_repository ();
69 reader = null;
70 this.current_source_file = null;
73 void next () {
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) {
79 // error
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) {
86 // error
87 Report.error (get_current_src (), "expected end element of `%s'".printf (name));
89 next ();
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");
98 next ();
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") {
103 parse_include ();
104 } else {
105 // error
106 Report.error (get_current_src (), "unknown child element `%s' in `repository'".printf (reader.name));
107 break;
110 end_element ("repository");
113 void parse_include () {
114 start_element ("include");
115 next ();
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);
126 next ();
127 while (current_token == MarkupTokenType.START_ELEMENT) {
128 Symbol sym = null;
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") {
146 parse_boxed ();
147 } else if (reader.name == "union") {
148 parse_union ();
149 } else if (reader.name == "constant") {
150 sym = parse_constant ();
151 } else {
152 // error
153 Report.error (get_current_src (), "unknown child element `%s' in `namespace'".printf (reader.name));
154 break;
157 if (sym is Class) {
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) {
172 continue;
174 current_source_file.add_node (sym);
176 end_element ("namespace");
177 return ns;
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")));
185 next ();
186 end_element ("alias");
187 return st;
190 Enum parse_enumeration () {
191 start_element ("enumeration");
192 var en = new Enum (reader.get_attribute ("name"));
193 en.access = SymbolAccessibility.PUBLIC;
194 next ();
196 string common_prefix = null;
198 while (current_token == MarkupTokenType.START_ELEMENT) {
199 if (reader.name == "member") {
200 var ev = parse_enumeration_member ();
201 en.add_value (ev);
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);
211 } else {
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);
221 } else {
222 // error
223 break;
227 en.set_cprefix (common_prefix);
229 end_element ("enumeration");
230 return en;
233 Enum parse_bitfield () {
234 start_element ("bitfield");
235 var en = new Enum (reader.get_attribute ("name"));
236 en.access = SymbolAccessibility.PUBLIC;
237 next ();
238 while (current_token == MarkupTokenType.START_ELEMENT) {
239 if (reader.name == "member") {
240 en.add_value (parse_enumeration_member ());
241 } else {
242 // error
243 break;
246 end_element ("bitfield");
247 return en;
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"));
254 next ();
255 end_element ("member");
256 return ev;
259 Method parse_function () {
260 start_element ("function");
261 string name = reader.get_attribute ("name");
262 next ();
263 DataType return_type;
264 if (current_token == MarkupTokenType.START_ELEMENT && reader.name == "return-value") {
265 return_type = parse_return_value ();
266 } else {
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");
276 next ();
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");
287 int i = 0;
288 foreach (FormalParameter param in parameters) {
289 if (!array_length_parameters.contains (i)) {
290 m.add_parameter (param);
292 i++;
294 end_element ("function");
295 return m;
298 DataType parse_return_value () {
299 start_element ("return-value");
300 string transfer = reader.get_attribute ("transfer-ownership");
301 next ();
302 var type = parse_type ();
303 if (transfer == "full") {
304 type.value_owned = true;
306 end_element ("return-value");
307 return type;
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");
317 next ();
318 if (reader.name == "varargs") {
319 start_element ("varargs");
320 next ();
321 param = new FormalParameter.with_ellipsis ();
322 end_element ("varargs");
323 } else {
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");
336 return param;
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 ();
346 next ();
347 var element_type = parse_type ();
348 end_element ("array");
349 return new ArrayType (element_type, 1, null);
350 } else {
351 start_element ("type");
352 DataType type = parse_type_from_name (reader.get_attribute ("name"));
353 next ();
355 // type arguments / element types
356 while (current_token == MarkupTokenType.START_ELEMENT) {
357 parse_type ();
360 end_element ("type");
361 return type;
365 DataType parse_type_from_name (string type_name) {
366 DataType type;
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);
373 } else {
374 if (type_name == "utf8") {
375 type_name = "string";
376 } else if (type_name == "boolean") {
377 type_name = "bool";
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) {
391 // namespaced name
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));
400 } else {
401 type = new UnresolvedType.from_symbol (new UnresolvedSymbol (null, type_name));
405 return type;
408 Struct parse_record () {
409 start_element ("record");
410 var st = new Struct (reader.get_attribute ("name"));
411 st.access = SymbolAccessibility.PUBLIC;
412 next ();
413 while (current_token == MarkupTokenType.START_ELEMENT) {
414 if (reader.name == "field") {
415 st.add_field (parse_field ());
416 } else if (reader.name == "callback") {
417 parse_callback ();
418 } else if (reader.name == "constructor") {
419 parse_constructor ();
420 } else if (reader.name == "method") {
421 parse_method ();
422 } else {
423 // error
424 Report.error (get_current_src (), "unknown child element `%s' in `record'".printf (reader.name));
425 break;
428 end_element ("record");
429 return st;
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));
442 next ();
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")));
450 next ();
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") {
461 parse_callback ();
462 } else if (reader.name == "glib:signal") {
463 signals.add (parse_signal ());
464 } else {
465 // error
466 Report.error (get_current_src (), "unknown child element `%s' in `class'".printf (reader.name));
467 break;
471 // signal merging
472 foreach (Signal sig in signals) {
473 var symbol = cl.scope.lookup (sig.name);
474 if (symbol == null) {
475 cl.add_signal (sig);
476 } else if (symbol is Property) {
477 // properties take precedence
478 } else {
479 Report.error (get_current_src (), "duplicate member `%s' in `%s'".printf (sig.name, cl.name));
483 // method merging
484 foreach (Method m in methods) {
485 var symbol = cl.scope.lookup (m.name);
486 if (symbol == null) {
487 cl.add_method (m);
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
493 } else {
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) {
502 cl.add_field (f);
506 end_element ("class");
507 return cl;
510 Interface parse_interface () {
511 start_element ("interface");
512 var iface = new Interface (reader.get_attribute ("name"));
513 iface.access = SymbolAccessibility.PUBLIC;
514 next ();
515 var methods = new ArrayList<Method> ();
516 while (current_token == MarkupTokenType.START_ELEMENT) {
517 if (reader.name == "field") {
518 parse_field ();
519 } else if (reader.name == "property") {
520 iface.add_property (parse_property ());
521 } else if (reader.name == "callback") {
522 parse_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 ());
527 } else {
528 // error
529 Report.error (get_current_src (), "unknown child element `%s' in `interface'".printf (reader.name));
530 break;
534 // method merging
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;
542 } else {
543 Report.error (get_current_src (), "duplicate member `%s' in `%s'".printf (m.name, iface.name));
547 end_element ("interface");
548 return iface;
551 Field parse_field () {
552 start_element ("field");
553 string name = reader.get_attribute ("name");
554 next ();
555 var type = parse_type ();
556 var field = new Field (name, type, null);
557 field.access = SymbolAccessibility.PUBLIC;
558 end_element ("field");
559 return field;
562 Property parse_property () {
563 start_element ("property");
564 string name = string.joinv ("_", reader.get_attribute ("name").split ("-"));
565 next ();
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");
572 return prop;
575 Delegate parse_callback () {
576 start_element ("callback");
577 string name = reader.get_attribute ("name");
578 next ();
579 DataType return_type;
580 if (current_token == MarkupTokenType.START_ELEMENT && reader.name == "return-value") {
581 return_type = parse_return_value ();
582 } else {
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");
589 next ();
590 while (current_token == MarkupTokenType.START_ELEMENT) {
591 d.add_parameter (parse_parameter ());
593 end_element ("parameters");
595 end_element ("callback");
596 return d;
599 Method parse_constructor () {
600 start_element ("constructor");
601 string name = reader.get_attribute ("name");
602 next ();
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");
614 next ();
615 while (current_token == MarkupTokenType.START_ELEMENT) {
616 m.add_parameter (parse_parameter ());
618 end_element ("parameters");
620 end_element ("constructor");
621 return m;
624 Method parse_method () {
625 start_element ("method");
626 string name = reader.get_attribute ("name");
627 next ();
628 DataType return_type;
629 if (current_token == MarkupTokenType.START_ELEMENT && reader.name == "return-value") {
630 return_type = parse_return_value ();
631 } else {
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");
638 next ();
639 while (current_token == MarkupTokenType.START_ELEMENT) {
640 m.add_parameter (parse_parameter ());
642 end_element ("parameters");
644 end_element ("method");
645 return m;
648 Signal parse_signal () {
649 start_element ("glib:signal");
650 string name = string.joinv ("_", reader.get_attribute ("name").split ("-"));
651 next ();
652 DataType return_type;
653 if (current_token == MarkupTokenType.START_ELEMENT && reader.name == "return-value") {
654 return_type = parse_return_value ();
655 } else {
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");
663 next ();
664 while (current_token == MarkupTokenType.START_ELEMENT) {
665 sig.add_parameter (parse_parameter ());
667 end_element ("parameters");
669 end_element ("glib:signal");
670 return sig;
673 Struct parse_boxed () {
674 start_element ("glib:boxed");
675 var st = new Struct (reader.get_attribute ("glib:name"));
676 st.access = SymbolAccessibility.PUBLIC;
677 next ();
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 ());
686 } else {
687 // error
688 Report.error (get_current_src (), "unknown child element `%s' in `class'".printf (reader.name));
689 break;
693 end_element ("glib:boxed");
694 return st;
697 Struct parse_union () {
698 start_element ("union");
699 var st = new Struct (reader.get_attribute ("name"));
700 st.access = SymbolAccessibility.PUBLIC;
701 next ();
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 ());
710 } else {
711 // error
712 Report.error (get_current_src (), "unknown child element `%s' in `union'".printf (reader.name));
713 break;
717 end_element ("union");
718 return st;
721 Constant parse_constant () {
722 start_element ("constant");
723 string name = reader.get_attribute ("name");
724 next ();
725 var type = parse_type ();
726 var c = new Constant (name, type, null, null);
727 c.access = SymbolAccessibility.PUBLIC;
728 end_element ("constant");
729 return c;
732 public void parse_metadata (string metadata_filename) {
733 if (FileUtils.test (metadata_filename, FileTest.EXISTS)) {
734 try {
735 string metadata;
736 ulong metadata_len;
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
742 continue;
745 string[] tokens = line.split (" ", 2);
747 if (null == tokens[0]) {
748 continue;
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));
760 } else {
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)];