From: Jürg Billeter Date: Fri, 17 Oct 2008 13:41:33 +0000 (+0000) Subject: Add experimental .gir support to vapigen X-Git-Tag: VALA_0_4_0~7 X-Git-Url: http://review.tizen.org/git/?a=commitdiff_plain;h=dbcc3b2e7d1a469015692644c81f3f9095be156c;p=platform%2Fupstream%2Fvala.git Add experimental .gir support to vapigen 2008-10-17 Jürg Billeter * vapigen/Makefile.am: * vapigen/valagirparser.vala: * vapigen/valamarkupreader.vala: * vapigen/valavapigen.vala: Add experimental .gir support to vapigen svn path=/trunk/; revision=1854 --- diff --git a/ChangeLog b/ChangeLog index ff84a27..6141d24 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,5 +1,14 @@ 2008-10-17 Jürg Billeter + * vapigen/Makefile.am: + * vapigen/valagirparser.vala: + * vapigen/valamarkupreader.vala: + * vapigen/valavapigen.vala: + + Add experimental .gir support to vapigen + +2008-10-17 Jürg Billeter + * vala/valasymbolresolver.vala: Ignore non-type symbols when resolving types diff --git a/vapigen/Makefile.am b/vapigen/Makefile.am index 51c27b0..62a6d52 100644 --- a/vapigen/Makefile.am +++ b/vapigen/Makefile.am @@ -21,6 +21,8 @@ BUILT_SOURCES = vapigen.vala.stamp vapicheck.vala.stamp vapigen_VALASOURCES = \ valagidlparser.vala \ + valagirparser.vala \ + valamarkupreader.vala \ valavapigen.vala \ $(NULL) diff --git a/vapigen/valagirparser.vala b/vapigen/valagirparser.vala new file mode 100644 index 0000000..12d851d --- /dev/null +++ b/vapigen/valagirparser.vala @@ -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 + */ + +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 attributes_map = new HashMap (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 (); + var array_length_parameters = new ArrayList (); + 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 (); + var methods = new ArrayList (); + var fields = new ArrayList (); + 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 (); + 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)]; + } +} + diff --git a/vapigen/valamarkupreader.vala b/vapigen/valamarkupreader.vala new file mode 100644 index 0000000..81b2b44 --- /dev/null +++ b/vapigen/valamarkupreader.vala @@ -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 + */ + +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 attributes = new HashMap (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 & > < " ' + } 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 & > < " ' + } 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"; + } + } +} + diff --git a/vapigen/valavapigen.vala b/vapigen/valavapigen.vala index cb4882f..dded1d1 100644 --- a/vapigen/valavapigen.vala +++ b/vapigen/valavapigen.vala @@ -33,12 +33,14 @@ class Vala.VAPIGen : Object { 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 @@ class Vala.VAPIGen : Object { 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);