Skip to content

jwharm/vala-gir-parser

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

vala-gir-parser

Reimplementation of the Vala GIR parser and VAPI generator: a new backend for vapigen. The tool is designed with a focus on easy maintainability.

Build and usage

Requires libvala-0.56 to be installed where pkg-config can find it.

Build with meson:

meson setup _build
meson compile -C _build

Use it like you would use vapigen:

_build/vapigen --library libfoo-1.0 Foo-1.0.gir

The tool can be used as a drop-in replacement for the existing vapigen tool. Technically, it acts as a new back-end that vapigen will call.

Please be aware that this project is still in development and is not yet finished.

Design

The tool is split in three separate parts:

  1. A parser that generates a GIR node tree from a GIR file
  2. A GIR metadata processor, that applies metadata rules to the GIR node tree
  3. A series of Vala AST (abstract syntax tree) builders from the GIR node tree, that generate the Vala code that is later written into a VAPI file.

These three parts are found in three directories below src/. The top-level file vapigen.vala was copied from Vala 0.56 and updated to launch the new gir parser, and is otherwise identical to the original.

GIR parser

The GIR parser is invoked from src/gir-parser.vala, a CodeVisitor that accepts source files with a ".gir" extension. The XML parser itself is found in src/gir/parser.vala. It uses the MarkupReader XML parser from libvala to build a tree of Gir.Node objects (see src/gir/node.vala) with the following properties:

  • A parent Node
  • The XML tag name (namespace, method, return-type, etc)
  • A map of attributes (name, c:type, etc)
  • A list of child nodes
  • The text contents of the XML element (for example in a <doc> element)
  • A Vala SourceReference with the location in the GIR XML file (for error reporting)

The Gir.Node class contains helper methods to get and set attributes, and to iterate child nodes: Node.any_of (tag) will return a single child node, while Node.all_of (tag) will return a list (actually a "filtered view") of all child nodes with the requested tag.

The GIR node tree can be displayed in an easy-to-read text format with to_string (), or in XML format with to_xml (). The generated XML is identical to the original GIR XML file, except the XML element attributes have a different ordering (the attributes are kept in a Vala.Map, an unordered collection).

Built-in GIR transformations

After the GIR file has been parsed, a few transformations will always be applied to the GIR node tree. For example, methods with one out-parameter that return void, are updated to return the out-parameter value. These transformations can be found in src/gir/transformations.vala.

GIR Metadata

Next, all instructions from the GIR Metadata file (when available for this GIR file) are applied to the GIR node tree. The Metadata parser and processor are located in src/gir-metadata/.

This is a major departure from the current GIR parser: The metadata transforms the GIR data, and the resulting GIR data is used to generate the VAPI.

Most metadata rules can applied 1-on-1 in the GIR. In some cases (like compact classes), a "custom" GIR attribute (e.g. <class ... vala:compact="1">) is added.

VAPI generator

The final step is to generate a VAPI file from the GIR data on which the metadata was applied.

A Vala AST is generated by a series of Builder classes that convert a GIR node into Vala symbols. For example, ClassBuilder generates a Vala Class with all its fields, methods etc. from a GIR class. In other words: each Builder converts a GIR node (e.g. <record>) into a Vala Symbol (e.g. Vala.Struct).

  • To avoid confusion, most variables that refer to a GIR node are prefixed with g_ (e.g. g_class), while Vala symbols are prefixed with v_ (v_class).

The NamespaceBuilder class ties the output of all the builders together into a new namespace. The namespace is added to the current CodeContext, and then written to a VAPI file by vapigen.vala (this part is completely unchanged).

Output differences compared to the current GIR parser

There are a few differences in the vapigen output that are not planned to be fixed:

Different numbering of hidden (decimal) parameter positions

The current gir parser often uses 0.5 or 0.9 to indicate the position of a "hidden" parameter index (for example the array_length_pos) between two "visible" parameters. However, I found it much simpler to always start counting from 0.1. The actual parameter ordering will still be the same. (If not, please log an issue.)

Example: instance_pos of Adw.AnimationTargetFunc

Different parameter names for signals

When a signal declaration has different parameter names than its emitter method, the current gir parser uses the names of the emitter method parameters for the Vala signal declaration, but I prefer to use the names of the signal parameters.

Example: Soup.Server.request_finished

Trailing GLib.Cancellable parameters will always be default null

For unknown reasons, the current gir parser often omits the default null value for trailing GLib.Cancellable parameters. This is fixed in the new gir parser.

Example: Adw.AlertDialog.choose

Multiple first_... parameters in varargs functions will be hidden

When variadic functions declare the "first variadic parameter" like first_param, ..., the gir parser hides the first_param in Vala. However, when multiple "first params" exist, like first_key, first_value, ..., it only hides one. This is fixed in the new gir parser.

Example: Adw.Breakpoint.add_setters

Better cprefix for enums

The current gir parser sometimes generates a suboptimal cprefix that, when combined with the names of the enum members, is different from the enum member names in C, requiring cname annotations on the enum members or a metadata override. This is fixed in the new gir parser.

Example: Soup.HTTPVersion

Deprecated function will not be merged into its replacement

When a function is deprecated and has a documented replacement (with the "moved-to" gir attribute), the current gir parser sometimes merges the original function with its replacement, and flags the replacement as deprecated. This is fixed in the new gir parser.

Example: Graphene.Rect.alloc

Bugs / TODO's

The following items are still on the backlog:

Returned structs are not nullable

When a function returns a struct, valac will generate incorrect C code because it assumes the struct is actually an out argument. To prevent this, the return value must be marked as nullable. I haven't yet implemented this.

Example: Graphene.Box.init, and many other Graphene functions

return_void metadata not implemented yet

When a function returns void and has a trailing out argument, it's automatically changed into a return value. This is an automatic transformation in the GIR data. The return_void metadata rule should revert this, but I haven't yet implemented this.

Complex type signatures in metadata not implemented yet

The current GIR parser allows to override the type of a parameter, field or constant declaration with a metadata type rule that uses Vala syntax (e.g. unowned List<string?>). This hasn't been implemented yet.

Correctly handle special C types

The current GIR parser contains special treatment for off_t, time_t, dev_t, pid_t, uid_t and socklen_t that I have not reimplemented yet.

Contributing

Contributions are welcome. The code is LGPL-licensed. Please post issues and changes on GitHub.

About

Proof-of-concept gir parser developed in Vala

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published