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 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. }

À coté de ces éléments de base, qui suffisent déjà largement, il existe un mécanisme de raccourcis (macro) sur les valeurs des traits et sur les chemins

  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. }

Lors de la phase de mise au point, il est possible de désactiver une classe et ses descendants

disable verb_categorization_passive

Comme toujours, le formalisme offre son lot de petits "trucs", bons à savoir. Ainsi, les nœuds ont une propriété type, avec quelques types spéciaux:

  • alternative: pour un nœud dominant un ensemble de nœuds dont l'un seulement peut être utilisé lors d'une analyse
  • sequence: pour un nœud interne dominant une séquence de fils, mais pour lequel on n'a pas de propriétés linguistiques particulières (pas de catégorie syntaxique par exemple)
  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. }

Ces types spéciaux seront en particulier utilisés pour produire des arbres factorisés.

On peut également indiquer qu'un noeud est optionnel (sans passer par une garde)

  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. }

Il est également possible de rendre répétable un noeud, à la manière de l'opérateur "*" dans les expressions régulières (étoile de Kleene). Ceci est en essentiellement utilisé pour représenter la répétition dans les coordinations.

  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. }