A meta-grammar is organized in a modular way through an hierarchy of classes. A class is a bag of constraints used to specify a syntactic phenomena (or just a facet of it) .

The classes of FRMG may be browsed here. Once a class is selected, the right side panel "Class Graph View" may be used to navigate through the class hierarchy.

As an example, a very basic class for adverbs will only specify that we expect an elementary tree anchored by an adverb. No more !

  1. class adverb {
  2. ...
  3. }
  1. We can define subclasses that inherits all the constraints from its parent class and that may be used to progressively refine syntactic phenomena.
  2. For instance, we may have a subclass of adverb specifying constraints about the use of adverbs as modifiers (their most common usage), essentially indicating that we expect an auxiliary tree.
  3. We can then further refine into the notion of adverb as modifier of verbs, by specifying that the root node should have category v, with maybe some other restriction on the type of the adverb.
  4. <: adverb; %% this class inherits from the adverb class
  5. ...
  6. }
  7. ....
  8. }

Besides inheritance, modularity is ensured through the use of another very powerful mechanism, namely by providing/consuming resources. A class may require some resource (or functionality) that will be provided by some other class. For instance, the previous class adverb_as_modifier may be implemented as requiring the functionality of "modifier of something" through asking for resource x_modifier. The class x_modifier will be used to provide this resource. Several classes may be in competition to provide a same resource, and several classes may require a same resource.

  1. <: adverb;
  2. - x_modifier; # require the functionality "modifier_of_something"
  3. ....
  4. }
  5. class X_modifier {
  6. + x_modifier; # provide the foncionality "modifier_of_something"
  7. ...
  8. }

This resource management mechanism is quite powerful and nicely complement inheritance. In particular, it has been extended to allow a resource to be consumed several times by a class using distinct name spaces, something that can't be easily done through inheritance.

For instance, a basic resource agreement may be defined to provide agreement on gender, number, ... between a node N and its father node. This resource is consumed twice in class superlative_as_adj_mod, once in namespace det and one in namespace adj, acting on different nodes each times.

  1. %% require agreemnt for the determiner (le|la) and the adjective
  2. - det::agreement; det=det::N;
  3. - adj::agreement; adj=adj::N;
  4. ...
  5. }
  6. class agreement {
  7. %% provide agreement constraint between a node and its father
  8. father(N).bot.gender = node(N).bot.gender;
  9. ....
  10. }

Inheritance and resources forms the backbone of a meta-grammars (its organization in terms of class). The "flesh" is provided by the content of the classes, with constraints over the nodes of the elementary trees.

First, we have topological or structural constraints:

  • equality between nodes
  • precedence: a node should precede another one in a tree
  • dominance: a node should dominate another one in a tree. We distinguish a parent dominance (a node is a father of another one) and the ancestor dominance (a node is an ancerstor of another one)

For instance, in first approximation, the Subject node should precede its verb node, and both nodes are dominated by the root S node. We can precise that S is the father of the Subject.

  1. %% declaration of nodes S, v, and Subject, with some decorations
  2. node S: [cat: S, type: std];
  3. node v: [cat: v, type: anchor, id: v];
  4. node Subject: [cat: N2, type: subst, id: subject];
  5. %% The subject precedes the verb
  6. Subject < v;
  7. %% The sentence node dominates the subject node
  8. S >> Subject;
  9. %% the sentence node also dominates the verb node, but indirectly
  10. %% (to allow other nodes in-between)
  11. S >>+ v;
  12. ....
  13. }

We have also constraints over the decorations hold by the nodes. The decoration constraints may be directly carried on nodes, or expressed as equations between feature paths and values. The source of a feature path is generally a node, but can actually be the class itself denoted by desc (equivalent to this or self in object-oriented languages) or a variable.

  1. ...
  2. %% use of variable $number to force number agreement
  3. node Subject: [cat: N2, type: subst, id: subject, top: [number: $number]];
  4. node v : [cat:v, type: anchor, id: v, top: [number: $number]];
  5. }
  1. ...
  2. %% alternative use of a path equation to force number agreement
  3. node(Subject).top.number = node(v).top.number;
  4. }
  1. ...
  2. %% a non transitive verb has only one subject argument
  3. desc.ht.arg0.function=value(subject);
  4. desc.ht.arg1.function=value(-);
  5. desc.ht.arg2.function=value(-);
  6. }
  7. %% alternative, using a full feature structure as value
  8. ...
  9. %% a non transitive verb has only one subject argument
  10. desc.ht = value([arg0: [function: subject],
  11. arg1: [function: -],
  12. arg2: [function: -]
  13. ]);
  14. }
  1. desc.ht = $ht;
  2. ...
  3. %% a non transitive verb has only one subject argument
  4. $ht.arg1 = value(subject);
  5. ....
  6. }

Going further, the equations may also used to express constraints on the presence or absence of a node. A positive or negative guard on a node is expressed as a Boolean formula over equations.

  1. ...
  2. %% a subject is present
  3. %% if and only if the verb mood is not imperative or infinitive
  4. Subject =>
  5. node(v).top.mood = value(~imperative|infinitive);
  6. ~ Subject =>
  7. node(v).top.mood = value(imperative|infinitive);
  8. }

These guards may be also be used to specify complex constraints over a node without the need to increase the number of classes.

  1. class subordonate {
  2. ...
  3. SubS +
  4. node(SubS).top.mode = value(participle|gerundive),
  5. node(SubS).top.inv = value(-),
  6. node(SubS).top.extraction = value(-),
  7. ( node(SubS).top.sat = value(-),
  8. ( node(Foot).cat = value(coo)
  9. |
  10. node(Foot).cat = value(~coo),
  11. node(Foot).top.number = node(SubS).bot.number,
  12. node(Foot).top.person = node(SubS).bot.person,
  13. node(Foot).top.gender = node(SubS).bot.gender
  14. )
  15. |
  16. node(SubS).top.sat = value(ppart)
  17. )
  18. |
  19. node(SubS).top.mode = value(~participle|gerundive),
  20. node(SubS).top.sat = value(-)
  21. ;
  22. }

To shorten descriptionsm, it is also possible to define and use macros on feature values and feature paths.

  1. %% macro on value, for default agreement
  2. template @defaultagr = [person: 3, number: sg, gender: masc]
  3. %% macro on path
  4. path @function0 = .ht.arg0.function
  5. class csu {
  6. ...
  7. node(CS).bot = value(@defaultagr);
  8. }
  9. ...
  10. desc.@function0 = value(subject);
  11. }

When debugging the meta-grammar, it is possible to disable a class and all its descendants

disable verb_categorization_passive

As often (always ?), the formalism provides its set of "hack" that may useful to known. For instance, nodes have a feature type, with a few special type values:

  • alternative: for a internal node acting as a disjunction over its children (only one of them may be used at parsing time)
  • sequence: for a internal node that has no linguistic interest (no category, no features) but having children
  1. class subject {
  2. + subject;
  3. node SubjectAlt: [type: alternative];
  4. SubjectAlt >> CliticSubj; node CliticSubj: [cat: cln, type: coanchor];
  5. SubjectAlt >> NominalSubj; node NominalSubj: [cat: N2, type: subst];
  6. SubjectAlt >> SentSubj; node SentSubj: [cat: S, type: subst];
  7. ...
  8. }

These special types are in particular used to build factorized trees.

It is also possible to state that a node is optional without a guard but by using the optional feature.

  1. class pnoun {
  2. ...
  3. %% a proper noun may be preceded by an optional title
  4. node Monsieur : [cat: title, type: coanchor, optional: yes];
  5. }

It is also possible to state that a node can be repeated zero, one, or several times in the parse trees, in a way similar to the Kleene star operator "*" used in regular expressions.

  1. class simple_coord {
  2. ...
  3. node MiddleCoordSeq: [type: sequence, star: *];
  4. node coord: [cat: coo, type: anchor];
  5. node EndCoord: [cat: $cat];
  6. MiddleCordSeq < coord;
  7. coord < EndCoord;
  8. MiddleCoordSeq >> MiddleCoord;
  9. MiddleCoordSeq >> coma;
  10. MiddleCoord < coma;
  11. node MiddleCoord: [cat: $cat];
  12. node comma: [lex: ",", type: lex];
  13. }