vala r1854 - in trunk: . vapigen



Author: juergbi
Date: Fri Oct 17 13:41:33 2008
New Revision: 1854
URL: http://svn.gnome.org/viewvc/vala?rev=1854&view=rev

Log:
2008-10-17  JÃrg Billeter  <j bitron ch>

	* vapigen/Makefile.am:
	* vapigen/valagirparser.vala:
	* vapigen/valamarkupreader.vala:
	* vapigen/valavapigen.vala:

	Add experimental .gir support to vapigen


Added:
   trunk/vapigen/valagirparser.vala
   trunk/vapigen/valamarkupreader.vala
Modified:
   trunk/ChangeLog
   trunk/vapigen/Makefile.am
   trunk/vapigen/valavapigen.vala

Modified: trunk/vapigen/Makefile.am
==============================================================================
--- trunk/vapigen/Makefile.am	(original)
+++ trunk/vapigen/Makefile.am	Fri Oct 17 13:41:33 2008
@@ -21,6 +21,8 @@
 
 vapigen_VALASOURCES = \
 	valagidlparser.vala \
+	valagirparser.vala \
+	valamarkupreader.vala \
 	valavapigen.vala \
 	$(NULL)
 

Added: trunk/vapigen/valagirparser.vala
==============================================================================
--- (empty file)
+++ trunk/vapigen/valagirparser.vala	Fri Oct 17 13:41:33 2008
@@ -0,0 +1,769 @@
+/* valagirparser.vala
+ *
+ * Copyright (C) 2008  JÃrg Billeter
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301  USA
+ *
+ * Author:
+ * 	JÃrg Billeter <j bitron ch>
+ */
+
+using GLib;
+using Gee;
+
+/**
+ * Code visitor parsing all Vala source files.
+ */
+public class Vala.GirParser : CodeVisitor {
+	MarkupReader reader;
+
+	CodeContext context;
+
+	SourceFile current_source_file;
+	SourceLocation begin;
+	SourceLocation end;
+	MarkupTokenType current_token;
+
+	HashMap<string,string> attributes_map = new HashMap<string,string> (str_hash, str_equal);
+
+	/**
+	 * Parses all .gir source files in the specified code
+	 * context and builds a code tree.
+	 *
+	 * @param context a code context
+	 */
+	public void parse (CodeContext context) {
+		this.context = context;
+		context.accept (this);
+	}
+
+	public override void visit_source_file (SourceFile source_file) {
+		if (source_file.filename.has_suffix (".gir")) {
+			parse_file (source_file);
+		}
+	}
+
+	public void parse_file (SourceFile source_file) {
+		this.current_source_file = source_file;
+		reader = new MarkupReader (source_file.filename);
+
+		// xml prolog
+		next ();
+		next ();
+
+		next ();
+		parse_repository ();
+
+		reader = null;
+		this.current_source_file = null;
+	}
+
+	void next () {
+		current_token = reader.read_token (out begin, out end);
+	}
+
+	void start_element (string name) {
+		if (current_token != MarkupTokenType.START_ELEMENT || reader.name != name) {
+			// error
+			Report.error (get_current_src (), "expected start element of `%s'".printf (name));
+		}
+	}
+
+	void end_element (string name) {
+		if (current_token != MarkupTokenType.END_ELEMENT || reader.name != name) {
+			// error
+			Report.error (get_current_src (), "expected end element of `%s'".printf (name));
+		}
+		next ();
+	}
+
+	SourceReference get_current_src () {
+		return new SourceReference (this.current_source_file, begin.line, begin.column, end.line, end.column);
+	}
+
+	void parse_repository () {
+		start_element ("repository");
+		next ();
+		while (current_token == MarkupTokenType.START_ELEMENT) {
+			if (reader.name == "namespace") {
+				context.root.add_namespace (parse_namespace ());
+			} else if (reader.name == "include") {
+				parse_include ();
+			} else {
+				// error
+				Report.error (get_current_src (), "unknown child element `%s' in `repository'".printf (reader.name));
+				break;
+			}
+		}
+		end_element ("repository");
+	}
+
+	void parse_include () {
+		start_element ("include");
+		next ();
+		end_element ("include");
+	}
+
+	Namespace parse_namespace () {
+		start_element ("namespace");
+		var ns = new Namespace (reader.get_attribute ("name"));
+		string cheader = get_attribute (ns.name, "c:header-filename");
+		if (cheader != null) {
+			ns.set_cheader_filename (cheader);
+		}
+		next ();
+		while (current_token == MarkupTokenType.START_ELEMENT) {
+			Symbol sym = null;
+			if (reader.name == "alias") {
+				sym = parse_alias ();
+			} else if (reader.name == "enumeration") {
+				sym = parse_enumeration ();
+			} else if (reader.name == "bitfield") {
+				sym = parse_bitfield ();
+			} else if (reader.name == "function") {
+				sym = parse_function ();
+			} else if (reader.name == "callback") {
+				sym = parse_callback ();
+			} else if (reader.name == "record") {
+				sym = parse_record ();
+			} else if (reader.name == "class") {
+				sym = parse_class ();
+			} else if (reader.name == "interface") {
+				sym = parse_interface ();
+			} else if (reader.name == "glib:boxed") {
+				parse_boxed ();
+			} else if (reader.name == "union") {
+				parse_union ();
+			} else if (reader.name == "constant") {
+				sym = parse_constant ();
+			} else {
+				// error
+				Report.error (get_current_src (), "unknown child element `%s' in `namespace'".printf (reader.name));
+				break;
+			}
+
+			if (sym is Class) {
+				ns.add_class ((Class) sym);
+			} else if (sym is Interface) {
+				ns.add_interface ((Interface) sym);
+			} else if (sym is Struct) {
+				ns.add_struct ((Struct) sym);
+			} else if (sym is Enum) {
+				ns.add_enum ((Enum) sym);
+			} else if (sym is Delegate) {
+				ns.add_delegate ((Delegate) sym);
+			} else if (sym is Method) {
+				ns.add_method ((Method) sym);
+			} else if (sym is Constant) {
+				ns.add_constant ((Constant) sym);
+			} else if (sym == null) {
+				continue;
+			}
+			current_source_file.add_node (sym);
+		}
+		end_element ("namespace");
+		return ns;
+	}
+
+	Struct parse_alias () {
+		start_element ("alias");
+		var st = new Struct (reader.get_attribute ("name"));
+		st.access = SymbolAccessibility.PUBLIC;
+		st.add_base_type (parse_type_from_name (reader.get_attribute ("target")));
+		next ();
+		end_element ("alias");
+		return st;
+	}
+
+	Enum parse_enumeration () {
+		start_element ("enumeration");
+		var en = new Enum (reader.get_attribute ("name"));
+		en.access = SymbolAccessibility.PUBLIC;
+		next ();
+
+		string common_prefix = null;
+		
+		while (current_token == MarkupTokenType.START_ELEMENT) {
+			if (reader.name == "member") {
+				var ev = parse_enumeration_member ();
+				en.add_value (ev);
+
+				string cname = ev.get_cname ();
+
+				if (common_prefix == null) {
+					common_prefix = cname;
+					while (common_prefix.len () > 0 && !common_prefix.has_suffix ("_")) {
+						// FIXME: could easily be made faster
+						common_prefix = common_prefix.ndup (common_prefix.size () - 1);
+					}
+				} else {
+					while (!cname.has_prefix (common_prefix)) {
+						common_prefix = common_prefix.ndup (common_prefix.size () - 1);
+					}
+				}
+				while (common_prefix.len () > 0 && (!common_prefix.has_suffix ("_") ||
+				       (cname.offset (common_prefix.size ()).get_char ().isdigit ()) && (cname.len () - common_prefix.len ()) <= 1)) {
+					// enum values may not consist solely of digits
+					common_prefix = common_prefix.ndup (common_prefix.size () - 1);
+				}
+			} else {
+				// error
+				break;
+			}
+		}
+
+		en.set_cprefix (common_prefix);
+
+		end_element ("enumeration");
+		return en;
+	}
+
+	Enum parse_bitfield () {
+		start_element ("bitfield");
+		var en = new Enum (reader.get_attribute ("name"));
+		en.access = SymbolAccessibility.PUBLIC;
+		next ();
+		while (current_token == MarkupTokenType.START_ELEMENT) {
+			if (reader.name == "member") {
+				en.add_value (parse_enumeration_member ());
+			} else {
+				// error
+				break;
+			}
+		}
+		end_element ("bitfield");
+		return en;
+	}
+
+	EnumValue parse_enumeration_member () {
+		start_element ("member");
+		var ev = new EnumValue (string.joinv ("_", reader.get_attribute ("name").up ().split ("-")));
+		ev.set_cname (reader.get_attribute ("c:identifier"));
+		next ();
+		end_element ("member");
+		return ev;
+	}
+
+	Method parse_function () {
+		start_element ("function");
+		string name = reader.get_attribute ("name");
+		next ();
+		DataType return_type;
+		if (current_token == MarkupTokenType.START_ELEMENT && reader.name == "return-value") {
+			return_type = parse_return_value ();
+		} else {
+			return_type = new VoidType ();
+		}
+		var m = new Method (name, return_type);
+		m.access = SymbolAccessibility.PUBLIC;
+		m.binding = MemberBinding.STATIC;
+		var parameters = new ArrayList<FormalParameter> ();
+		var array_length_parameters = new ArrayList<int> ();
+		if (current_token == MarkupTokenType.START_ELEMENT && reader.name == "parameters") {
+			start_element ("parameters");
+			next ();
+			while (current_token == MarkupTokenType.START_ELEMENT) {
+				int array_length_idx = -1;
+				var param = parse_parameter (out array_length_idx);
+				if (array_length_idx != -1) {
+					array_length_parameters.add (array_length_idx);
+				}
+				parameters.add (param);
+			}
+			end_element ("parameters");
+		}
+		int i = 0;
+		foreach (FormalParameter param in parameters) {
+			if (!array_length_parameters.contains (i)) {
+				m.add_parameter (param);
+			}
+			i++;
+		}
+		end_element ("function");
+		return m;
+	}
+
+	DataType parse_return_value () {
+		start_element ("return-value");
+		string transfer = reader.get_attribute ("transfer-ownership");
+		next ();
+		var type = parse_type ();
+		if (transfer == "full") {
+			type.value_owned = true;
+		}
+		end_element ("return-value");
+		return type;
+	}
+
+	FormalParameter parse_parameter (out int array_length_idx = null) {
+		FormalParameter param = null;
+
+		start_element ("parameter");
+		string name = reader.get_attribute ("name");
+		string direction = reader.get_attribute ("direction");
+		string transfer = reader.get_attribute ("transfer-ownership");
+		next ();
+		if (reader.name == "varargs") {
+			start_element ("varargs");
+			next ();
+			param = new FormalParameter.with_ellipsis ();
+			end_element ("varargs");
+		} else {
+			var type = parse_type (out array_length_idx);
+			if (transfer == "full") {
+				type.value_owned = true;
+			}
+			param = new FormalParameter (name, type);
+			if (direction == "out") {
+				param.direction = ParameterDirection.OUT;
+			} else if (direction == "inout") {
+				param.direction = ParameterDirection.REF;
+			}
+		}
+		end_element ("parameter");
+		return param;
+	}
+
+	DataType parse_type (out int array_length_index = null) {
+		if (reader.name == "array") {
+			start_element ("array");
+			if (reader.get_attribute ("length") != null
+			    && &array_length_index != null) {
+				array_length_index = reader.get_attribute ("length").to_int ();
+			}
+			next ();
+			var element_type = parse_type ();
+			end_element ("array");
+			return new ArrayType (element_type, 1, null);
+		} else {
+			start_element ("type");
+			DataType type = parse_type_from_name (reader.get_attribute ("name"));
+			next ();
+
+			// type arguments / element types
+			while (current_token == MarkupTokenType.START_ELEMENT) {
+				parse_type ();
+			}
+
+			end_element ("type");
+			return type;
+		}
+	}
+
+	DataType parse_type_from_name (string type_name) {
+		DataType type;
+		if (type_name == "none") {
+			type = new VoidType ();
+		} else if (type_name == "any") {
+			type = new PointerType (new VoidType ());
+		} else if (type_name == "GObject.Strv") {
+			type = new ArrayType (new UnresolvedType.from_symbol (new UnresolvedSymbol (null, "string")), 1, null);
+		} else {
+			if (type_name == "utf8") {
+				type_name = "string";
+			} else if (type_name == "boolean") {
+				type_name = "bool";
+			} else if (type_name == "GType") {
+				type_name = "GLib.Type";
+			} else if (type_name == "GObject.String") {
+				type_name = "GLib.StringBuilder";
+			} else if (type_name == "GObject.Class") {
+				type_name = "GLib.ObjectClass";
+			} else if (type_name == "GLib.unichar") {
+				type_name = "unichar";
+			} else if (type_name == "GLib.Data") {
+				type_name = "GLib.Datalist";
+			}
+			string[] type_components = type_name.split (".");
+			if (type_components[1] != null) {
+				// namespaced name
+				string namespace_name = type_components[0];
+				string transformed_type_name = type_components[1];
+				if (namespace_name == "GObject") {
+					namespace_name = "GLib";
+				} else if (namespace_name == "Gio") {
+					namespace_name = "GLib";
+				}
+				type = new UnresolvedType.from_symbol (new UnresolvedSymbol (new UnresolvedSymbol (null, namespace_name), transformed_type_name));
+			} else {
+				type = new UnresolvedType.from_symbol (new UnresolvedSymbol (null, type_name));
+			}
+		}
+
+		return type;
+	}
+
+	Struct parse_record () {
+		start_element ("record");
+		var st = new Struct (reader.get_attribute ("name"));
+		st.access = SymbolAccessibility.PUBLIC;
+		next ();
+		while (current_token == MarkupTokenType.START_ELEMENT) {
+			if (reader.name == "field") {
+				st.add_field (parse_field ());
+			} else if (reader.name == "callback") {
+				parse_callback ();
+			} else if (reader.name == "constructor") {
+				parse_constructor ();
+			} else if (reader.name == "method") {
+				parse_method ();
+			} else {
+				// error
+				Report.error (get_current_src (), "unknown child element `%s' in `record'".printf (reader.name));
+				break;
+			}
+		}
+		end_element ("record");
+		return st;
+	}
+
+	Class parse_class () {
+		start_element ("class");
+		var cl = new Class (reader.get_attribute ("name"));
+		cl.access = SymbolAccessibility.PUBLIC;
+
+		string parent = reader.get_attribute ("parent");
+		if (parent != null) {
+			cl.add_base_type (parse_type_from_name (parent));
+		}
+
+		next ();
+		var signals = new ArrayList<Signal> ();
+		var methods = new ArrayList<Method> ();
+		var fields = new ArrayList<Field> ();
+		while (current_token == MarkupTokenType.START_ELEMENT) {
+			if (reader.name == "implements") {
+				start_element ("implements");
+				cl.add_base_type (parse_type_from_name (reader.get_attribute ("name")));
+				next ();
+				end_element ("implements");
+			} else if (reader.name == "field") {
+				fields.add (parse_field ());
+			} else if (reader.name == "property") {
+				cl.add_property (parse_property ());
+			} else if (reader.name == "constructor") {
+				cl.add_method (parse_constructor ());
+			} else if (reader.name == "method") {
+				methods.add (parse_method ());
+			} else if (reader.name == "callback") {
+				parse_callback ();
+			} else if (reader.name == "glib:signal") {
+				signals.add (parse_signal ());
+			} else {
+				// error
+				Report.error (get_current_src (), "unknown child element `%s' in `class'".printf (reader.name));
+				break;
+			}
+		}
+
+		// signal merging
+		foreach (Signal sig in signals) {
+			var symbol = cl.scope.lookup (sig.name);
+			if (symbol == null) {
+				cl.add_signal (sig);
+			} else if (symbol is Property) {
+				// properties take precedence
+			} else {
+				Report.error (get_current_src (), "duplicate member `%s' in `%s'".printf (sig.name, cl.name));
+			}
+		}
+
+		// method merging
+		foreach (Method m in methods) {
+			var symbol = cl.scope.lookup (m.name);
+			if (symbol == null) {
+				cl.add_method (m);
+			} else if (symbol is Signal) {
+				var sig = (Signal) symbol;
+				sig.has_emitter = true;
+			} else if (symbol is Property || symbol is Field) {
+				// assume method is getter for property/field ignore method
+			} else {
+				Report.error (get_current_src (), "duplicate member `%s' in `%s'".printf (m.name, cl.name));
+			}
+		}
+
+		// fields have lowest priority
+		foreach (Field f in fields) {
+			var symbol = cl.scope.lookup (f.name);
+			if (symbol == null) {
+				cl.add_field (f);
+			}
+		}
+
+		end_element ("class");
+		return cl;
+	}
+
+	Interface parse_interface () {
+		start_element ("interface");
+		var iface = new Interface (reader.get_attribute ("name"));
+		iface.access = SymbolAccessibility.PUBLIC;
+		next ();
+		var methods = new ArrayList<Method> ();
+		while (current_token == MarkupTokenType.START_ELEMENT) {
+			if (reader.name == "field") {
+				parse_field ();
+			} else if (reader.name == "property") {
+				iface.add_property (parse_property ());
+			} else if (reader.name == "callback") {
+				parse_callback ();
+			} else if (reader.name == "method") {
+				methods.add (parse_method ());
+			} else if (reader.name == "glib:signal") {
+				iface.add_signal (parse_signal ());
+			} else {
+				// error
+				Report.error (get_current_src (), "unknown child element `%s' in `interface'".printf (reader.name));
+				break;
+			}
+		}
+
+		// method merging
+		foreach (Method m in methods) {
+			var symbol = iface.scope.lookup (m.name);
+			if (symbol == null) {
+				iface.add_method (m);
+			} else if (symbol is Signal) {
+				var sig = (Signal) symbol;
+				sig.has_emitter = true;
+			} else {
+				Report.error (get_current_src (), "duplicate member `%s' in `%s'".printf (m.name, iface.name));
+			}
+		}
+
+		end_element ("interface");
+		return iface;
+	}
+
+	Field parse_field () {
+		start_element ("field");
+		string name = reader.get_attribute ("name");
+		next ();
+		var type = parse_type ();
+		var field = new Field (name, type, null);
+		field.access = SymbolAccessibility.PUBLIC;
+		end_element ("field");
+		return field;
+	}
+
+	Property parse_property () {
+		start_element ("property");
+		string name = string.joinv ("_", reader.get_attribute ("name").split ("-"));
+		next ();
+		var type = parse_type ();
+		var prop = new Property (name, type, null, null);
+		prop.access = SymbolAccessibility.PUBLIC;
+		prop.get_accessor = new PropertyAccessor (true, false, false, null, null);
+		prop.set_accessor = new PropertyAccessor (false, true, false, null, null);
+		end_element ("property");
+		return prop;
+	}
+
+	Delegate parse_callback () {
+		start_element ("callback");
+		string name = reader.get_attribute ("name");
+		next ();
+		DataType return_type;
+		if (current_token == MarkupTokenType.START_ELEMENT && reader.name == "return-value") {
+			return_type = parse_return_value ();
+		} else {
+			return_type = new VoidType ();
+		}
+		var d = new Delegate (name, return_type);
+		d.access = SymbolAccessibility.PUBLIC;
+		if (current_token == MarkupTokenType.START_ELEMENT && reader.name == "parameters") {
+			start_element ("parameters");
+			next ();
+			while (current_token == MarkupTokenType.START_ELEMENT) {
+				d.add_parameter (parse_parameter ());
+			}
+			end_element ("parameters");
+		}
+		end_element ("callback");
+		return d;
+	}
+
+	Method parse_constructor () {
+		start_element ("constructor");
+		string name = reader.get_attribute ("name");
+		next ();
+
+		var return_type = parse_return_value ();
+
+		var m = new CreationMethod (null, name);
+		m.access = SymbolAccessibility.PUBLIC;
+		m.has_construct_function = false;
+		if (m.name.has_prefix ("new_")) {
+			m.name = m.name.offset ("new_".len ());
+		}
+		if (current_token == MarkupTokenType.START_ELEMENT && reader.name == "parameters") {
+			start_element ("parameters");
+			next ();
+			while (current_token == MarkupTokenType.START_ELEMENT) {
+				m.add_parameter (parse_parameter ());
+			}
+			end_element ("parameters");
+		}
+		end_element ("constructor");
+		return m;
+	}
+
+	Method parse_method () {
+		start_element ("method");
+		string name = reader.get_attribute ("name");
+		next ();
+		DataType return_type;
+		if (current_token == MarkupTokenType.START_ELEMENT && reader.name == "return-value") {
+			return_type = parse_return_value ();
+		} else {
+			return_type = new VoidType ();
+		}
+		var m = new Method (name, return_type);
+		m.access = SymbolAccessibility.PUBLIC;
+		if (current_token == MarkupTokenType.START_ELEMENT && reader.name == "parameters") {
+			start_element ("parameters");
+			next ();
+			while (current_token == MarkupTokenType.START_ELEMENT) {
+				m.add_parameter (parse_parameter ());
+			}
+			end_element ("parameters");
+		}
+		end_element ("method");
+		return m;
+	}
+
+	Signal parse_signal () {
+		start_element ("glib:signal");
+		string name = string.joinv ("_", reader.get_attribute ("name").split ("-"));
+		next ();
+		DataType return_type;
+		if (current_token == MarkupTokenType.START_ELEMENT && reader.name == "return-value") {
+			return_type = parse_return_value ();
+		} else {
+			return_type = new VoidType ();
+		}
+		var sig = new Signal (name, return_type);
+		sig.access = SymbolAccessibility.PUBLIC;
+		sig.is_virtual = true;
+		if (current_token == MarkupTokenType.START_ELEMENT && reader.name == "parameters") {
+			start_element ("parameters");
+			next ();
+			while (current_token == MarkupTokenType.START_ELEMENT) {
+				sig.add_parameter (parse_parameter ());
+			}
+			end_element ("parameters");
+		}
+		end_element ("glib:signal");
+		return sig;
+	}
+
+	Struct parse_boxed () {
+		start_element ("glib:boxed");
+		var st = new Struct (reader.get_attribute ("glib:name"));
+		st.access = SymbolAccessibility.PUBLIC;
+		next ();
+
+		while (current_token == MarkupTokenType.START_ELEMENT) {
+			if (reader.name == "field") {
+				st.add_field (parse_field ());
+			} else if (reader.name == "constructor") {
+				parse_constructor ();
+			} else if (reader.name == "method") {
+				st.add_method (parse_method ());
+			} else {
+				// error
+				Report.error (get_current_src (), "unknown child element `%s' in `class'".printf (reader.name));
+				break;
+			}
+		}
+
+		end_element ("glib:boxed");
+		return st;
+	}
+
+	Struct parse_union () {
+		start_element ("union");
+		var st = new Struct (reader.get_attribute ("name"));
+		st.access = SymbolAccessibility.PUBLIC;
+		next ();
+
+		while (current_token == MarkupTokenType.START_ELEMENT) {
+			if (reader.name == "field") {
+				st.add_field (parse_field ());
+			} else if (reader.name == "constructor") {
+				parse_constructor ();
+			} else if (reader.name == "method") {
+				st.add_method (parse_method ());
+			} else {
+				// error
+				Report.error (get_current_src (), "unknown child element `%s' in `union'".printf (reader.name));
+				break;
+			}
+		}
+
+		end_element ("union");
+		return st;
+	}
+
+	Constant parse_constant () {
+		start_element ("constant");
+		string name = reader.get_attribute ("name");
+		next ();
+		var type = parse_type ();
+		var c = new Constant (name, type, null, null);
+		c.access = SymbolAccessibility.PUBLIC;
+		end_element ("constant");
+		return c;
+	}
+
+	public void parse_metadata (string metadata_filename) {
+		if (FileUtils.test (metadata_filename, FileTest.EXISTS)) {
+			try {
+				string metadata;
+				ulong metadata_len;
+				FileUtils.get_contents (metadata_filename, out metadata, out metadata_len);
+				
+				foreach (string line in metadata.split ("\n")) {
+					if (line.has_prefix ("#")) {
+						// ignore comment lines
+						continue;
+					}
+
+					string[] tokens = line.split (" ", 2);
+
+					if (null == tokens[0]) {
+						continue;
+					}
+
+					foreach (string attribute in tokens[1].split (" ")) {
+						string[] pair = attribute.split ("=", 2);
+						string key = "%s/@%s".printf (tokens[0], pair[0]);
+						attributes_map.set (key, pair[1].substring (1, pair[1].length - 2));
+					}
+				}
+			} catch (FileError e) {
+				Report.error (null, "Unable to read metadata file: %s".printf (e.message));
+			}
+		} else {
+			Report.error (null, "Metadata file `%s' not found".printf (metadata_filename));
+		}
+	}
+
+	string? get_attribute (string node, string key) {
+		return attributes_map["%s/@%s".printf (node, key)];
+	}
+}
+

Added: trunk/vapigen/valamarkupreader.vala
==============================================================================
--- (empty file)
+++ trunk/vapigen/valamarkupreader.vala	Fri Oct 17 13:41:33 2008
@@ -0,0 +1,233 @@
+/* valamarkupreader.vala
+ *
+ * Copyright (C) 2008  JÃrg Billeter
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301  USA
+ *
+ * Author:
+ * 	JÃrg Billeter <j bitron ch>
+ */
+
+using GLib;
+using Gee;
+
+/**
+ * Simple reader for a subset of XML.
+ */
+public class Vala.MarkupReader : Object {
+	public string filename { get; construct; }
+
+	public string name { get; private set; }
+
+	MappedFile mapped_file;
+
+	char* begin;
+	char* current;
+	char* end;
+
+	int line;
+	int column;
+
+	Map<string,string> attributes = new HashMap<string,string> (str_hash, str_equal);
+	bool empty_element;
+
+	public MarkupReader (string filename) {
+		this.filename = filename;
+	}
+	
+	construct {
+		try {
+			mapped_file = new MappedFile (filename, false);
+			begin = mapped_file.get_contents ();
+			end = begin + mapped_file.get_length ();
+
+			current = begin;
+
+			line = 1;
+			column = 1;
+		} catch (FileError e) {
+			Report.error (null, "Unable to map file `%s': %s".printf (filename, e.message));
+		}
+	}
+
+	public string? get_attribute (string attr) {
+		return attributes[attr];
+	}
+
+	string read_name () {
+		char* begin = current;
+		while (current < end) {
+			if (current[0] == ' ' || current[0] == '>'
+			    || current[0] == '/' || current[0] == '=') {
+				break;
+			}
+			unichar u = ((string) current).get_char_validated ((long) (end - current));
+			if (u != (unichar) (-1)) {
+				current += u.to_utf8 (null);
+			} else {
+				Report.error (null, "invalid UTF-8 character");
+			}
+		}
+		if (current == begin) {
+			// syntax error: invalid name
+		}
+		return ((string) begin).ndup (current - begin);
+	}
+
+	public MarkupTokenType read_token (out SourceLocation token_begin, out SourceLocation token_end) {
+		attributes.clear ();
+
+		if (empty_element) {
+			empty_element = false;
+			return MarkupTokenType.END_ELEMENT;
+		}
+
+		space ();
+
+		MarkupTokenType type;
+		char* begin = current;
+		token_begin.pos = begin;
+		token_begin.line = line;
+		token_begin.column = column;
+
+		if (current >= end) {
+			type = MarkupTokenType.EOF;
+		} else if (current[0] == '<') {
+			current++;
+			if (current >= end) {
+				// error
+			} else if (current[0] == '?') {
+				// processing instruction
+			} else if (current[0] == '!') {
+				// comment or doctype
+			} else if (current[0] == '/') {
+				type = MarkupTokenType.END_ELEMENT;
+				current++;
+				name = read_name ();
+				if (current >= end || current[0] != '>') {
+					// error
+				}
+				current++;
+			} else {
+				type = MarkupTokenType.START_ELEMENT;
+				name = read_name ();
+				space ();
+				while (current < end && current[0] != '>' && current[0] != '/') {
+					string attr_name = read_name ();
+					if (current >= end || current[0] != '=') {
+						// error
+					}
+					current++;
+					// FIXME allow single quotes
+					if (current >= end || current[0] != '"') {
+						// error
+					}
+					current++;
+					char* attr_begin = current;
+					while (current < end && current[0] != '"') {
+						if (current[0] == '&') {
+							// process &amp; &gt; &lt; &quot; &apos;
+						} else {
+							unichar u = ((string) current).get_char_validated ((long) (end - current));
+							if (u != (unichar) (-1)) {
+								current += u.to_utf8 (null);
+							} else {
+								Report.error (null, "invalid UTF-8 character");
+							}
+						}
+					}
+					string attr_value = ((string) attr_begin).ndup (current - attr_begin);
+					if (current >= end || current[0] != '"') {
+						// error
+					}
+					current++;
+					attributes.set (attr_name, attr_value);
+					space ();
+				}
+				if (current[0] == '/') {
+					empty_element = true;
+					current++;
+					space ();
+				} else {
+					empty_element = false;
+				}
+				if (current >= end || current[0] != '>') {
+					// error
+				}
+				current++;
+			}
+		} else {
+			space ();
+			char* text_begin = current;
+			while (current < end && current[0] != '<') {
+				if (current[0] == '&') {
+					// process &amp; &gt; &lt; &quot; &apos;
+				} else {
+					unichar u = ((string) current).get_char_validated ((long) (end - current));
+					if (u != (unichar) (-1)) {
+						current += u.to_utf8 (null);
+					} else {
+						Report.error (null, "invalid UTF-8 character");
+					}
+				}
+			}
+			if (text_begin == current) {
+				// no text
+				// read next token
+				return read_token (out token_begin, out token_end);
+			}
+			type = MarkupTokenType.TEXT;
+			string text = ((string) text_begin).ndup (current - text_begin);
+		}
+
+		column += (int) (current - begin);
+
+		token_end.pos = current;
+		token_end.line = line;
+		token_end.column = column - 1;
+
+		return type;
+	}
+
+	void space () {
+		while (current < end && current[0].isspace ()) {
+			if (current[0] == '\n') {
+				line++;
+				column = 0;
+			}
+			current++;
+			column++;
+		}
+	}
+}
+
+public enum Vala.MarkupTokenType {
+	NONE,
+	START_ELEMENT,
+	END_ELEMENT,
+	TEXT,
+	EOF;
+
+	public weak string to_string () {
+		switch (this) {
+		case START_ELEMENT: return "start element";
+		case END_ELEMENT: return "end element";
+		case TEXT: return "text";
+		case EOF: return "end of file";
+		default: return "unknown token type";
+		}
+	}
+}
+

Modified: trunk/vapigen/valavapigen.vala
==============================================================================
--- trunk/vapigen/valavapigen.vala	(original)
+++ trunk/vapigen/valavapigen.vala	Fri Oct 17 13:41:33 2008
@@ -33,12 +33,14 @@
 	static string library;
 	[NoArrayLength ()]
 	static string[] packages;
+	static string metadata_filename;
 	CodeContext context;
 
 	const OptionEntry[] options = {
 		{ "vapidir", 0, 0, OptionArg.FILENAME_ARRAY, ref vapi_directories, "Look for package bindings in DIRECTORY", "DIRECTORY..." },
 		{ "pkg", 0, 0, OptionArg.STRING_ARRAY, ref packages, "Include binding for PACKAGE", "PACKAGE..." },
 		{ "library", 0, 0, OptionArg.STRING, ref library, "Library name", "NAME" },
+		{ "metadata", 0, 0, OptionArg.FILENAME, ref metadata_filename, "Metadata filename", "FILE" },
 		{ "directory", 'd', 0, OptionArg.FILENAME, ref directory, "Output directory", "DIRECTORY" },
 		{ "version", 0, 0, OptionArg.NONE, ref version, "Display version number", null },
 		{ "quiet", 'q', 0, OptionArg.NONE, ref quiet_mode, "Do not print messages to the console", null },
@@ -157,6 +159,16 @@
 			return quit ();
 		}
 		
+		var girparser = new GirParser ();
+		if (metadata_filename != null) {
+			girparser.parse_metadata (metadata_filename);
+		}
+		girparser.parse (context);
+		
+		if (Report.get_errors () > 0) {
+			return quit ();
+		}
+		
 		var gidlparser = new GIdlParser ();
 		gidlparser.parse (context);
 		



[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]