Repository navigation
How Mate Works
Mate is a grammar-driven framework for generating, parsing, and validating object graphs. It provides a declarative language to define the structure (types, fields, constraints, and relationships) of interconnected object instances. Much like a traditional grammar defines valid sentences in a language, a Mate grammar defines valid object graphs in a software system.
A Mate grammar (MateOGGrammar) is a collection of type declarations that describe how objects relate to one another. Each type declaration maps a class to its expected structure: what instance variables it has, what types those variables hold, and what constraints apply.
Player -> {
name: ByteString,
characterClass: Warrior | Rogue | Mage,
level: { type: SmallInteger, between: 0 and: 99 },
missingExperienceForNextLevel: { type: SmallInteger, greaterThan: 0 }
}This declaration states that a Player has four fields: name is a ByteString, characterClass is one of three possible classes (a union type), level is a SmallInteger constrained between 0 and 99, and missingExperienceForNextLevel is a SmallInteger greater than 0.
Mate performs three core operations over a grammar:
-
Fuzzing — Generating random, grammar-conforming object graphs (
MateGenerator) -
Parsing — Taking an existing object and producing its derivation tree (
MateParser) - Validation — Checking that an object conforms to the grammar's type and constraint rules (implicit in parsing)
Each of these operations is implemented as a visitor over the grammar's declaration tree. Mate uses the Visitor pattern: each declaration node (MateInstDecl, MateInstVarDecl, MateInstArrayVarDecl, etc.) dispatches to the appropriate visit* method on the active visitor.
Grammars can be defined in two ways:
Programmatically, by constructing declaration objects directly:
MatePersonGrammar >> initialize
super initialize.
self addTypeDeclaration: (MateInstDecl new
type: MatePerson;
instanceVariableDeclarations: {
MateInstVarDecl new name: 'age'; type: MateAge
}).Declaratively, using the Mate language (a textual DSL parsed by MateLanguageParser):
MatePersonGrammarByLang >> defineObjectGraph
^ '
MatePerson -> { age: MateAge }
MateAge -> { value: SmallInteger }
'Both approaches create the same internal representation. When a grammar subclass implements defineObjectGraph, the base class automatically parses the returned string and populates the type declarations.
Every grammar operation follows the same pattern:
Grammar → TypeDeclaration → visit:from:constraint: → Visitor method
The MateGrammarVisitor defines the abstract visitor interface with these methods:
| Visitor Method | Declaration Node | Purpose |
|---|---|---|
visitInstanceDeclaration:from: |
MateInstDecl |
Composite object with named fields |
visitInstVarDecl:from: |
MateInstVarDecl |
A single typed field |
visitArrayDecl:from: |
MateInstArrayVarDecl |
A collection field with element type and size constraints |
visitOneOfDecl:from: |
MateInstOneOfVarDecl |
A field with a fixed set of literal options |
visitInstRefDecl:from: |
MateInstRefVarDecl |
A back-reference to a parent object |
visitBuiltinDeclaration:from:constraint: |
MateBuiltInDecl |
Built-in types (SmallInteger, ByteString, etc.) |
visitClassDeclaration:from: |
MateClassDecl |
Class-level declarations |
Concrete visitors (MateGenerator, MateParser, MateWriter) implement each of these methods to perform their specific operation.
Mate recognizes the following built-in types, each backed by a specialized generator:
| Type | Generator |
|---|---|
SmallInteger |
MateSmallIntegerGenerator |
Float / SmallFloat64
|
MateFloatGenerator |
Boolean |
MateBooleanGenerator |
ByteString |
MateByteStringGenerator |
Color |
MateColorGenerator |
Point |
MatePointGenerator |
Built-in types don't need explicit declarations in the grammar — they are automatically registered.
-
Union types: A field can accept multiple types (
Warrior | Rogue | Mage), represented internally byMateOptionalType -
Constraints: Numeric fields can be bounded (
between: 0 and: 99,greaterThan: 0), represented byMateConstraint -
Arrays: Collection fields with element type and size bounds (
{ type: [ BlElement ], sizeBetween: 0 and: 10 }) -
Self-references: Cyclic relationships where a child references its parent (
selfReferences: { children.array[ .parent ] }) -
Reference types: Fields that point back to an existing object rather than creating a new one (
{ refType: BlElement }) - Reward back-propagation: The derivation tree supports feedback propagation for grammar-guided fuzzing (see Derivation Model and Object Graph Fuzzing)