Part II · Grammar as a Computational Object
Chapter 7
Operational Grammars
mixed The central definitions of the program (provisional); their components are standard, and the chapter says precisely which.
- Define an operational grammar and its three layers precisely.
- Define what it means for a representation to be adequate for a grammar, and why the SDK tests adequacy.
- Distinguish operational grammars (which generate operations over a structure) from structural grammars (which generate the structure).
- Write the grammar of each structure of Part I in one common format.
- State the five mechanisms of advantage and the mechanism thesis, and say why the thesis is a conjecture.
- Locate the definitions with respect to labelled transition systems, Kleene algebra with tests, attribute grammars and automatic structures.
7.1 The object
Part I described structures one at a time. Each had a set of operations (“moves”), a meaning for each move, a notion of which sequences of moves are allowed, and a representation with costs. The following definition collects these components.
An operational grammar is a tuple
where is a computational structure with set of states ; is a finite move signature, each move with an arity; is the relational semantics of move (for moves with arguments, one relation per argument tuple); and , for a subset , is the admissibility language, given by a finite description (a regular expression, an automaton, a context-free grammar). The class of is the class of (regular, context-free, …).
A word (with arguments) is an operation expression. It is
- syntactically admissible if its moves are in with correct arities and its projection onto lies in ;
- semantically defined at if , where is the composition of Definition Definition 6.1;
- executed by a representation at a cost given by a cost profile (Definition 7.4).
Three remarks fix the reading.
- Admissibility is a projection. The language constrains only the moves it mentions; observer moves such as
hereorreadare free unless named. This is a definitional choice (CGT-D-015); without it, every constraint would have to enumerate every observer. - Admissibility and definedness are different.
popon an empty stack is admissible under the universal language and undefined;(a b)* amay rejecta balthough is non-empty. Syntax is decided by without looking at the state; semantics depends on the state. - The finite description of is a conventional formal grammar or an automaton. What the definition adds is the structure-dependent semantics and, in Definition 7.4, a cost model.
7.2 Adequate representations
A representation implements the moves on concrete memory. It is correct when it computes what the semantics says.
A representation of a structure with grammar is adequate if for every state and every expression : executes to completion iff , the values it returns at each call are those of the semantics, and when is undefined, reports undefinedness at the first call at which the prefix becomes undefined.
Adequacy is the standard notion of a correct implementation of an abstract data type (an abstraction function relating concrete and abstract states). The SDK makes it executable: every structure has a pure reference semantics denote on its abstract value and an instrumented apply on its representation, and execute compares them call by call. The test suite checks adequacy for every structure on thousands of random expressions, including undefined ones (CGT-D-014).
Here is the whole interface, used to define a new structure from scratch: a counter that can be incremented and decremented within bounds, with an admissibility language forbidding two decrements in a row.
from cgtsdk import Cost, Inadmissible, Move, Structure
class BoundedCounter(Structure):
"""A counter in [0, K]. Abstract value: the integer."""
name = "counter"
SIGNATURE = (Move("inc", (), "add one"), Move("dec", (), "subtract one"), Move("value", (), "read"))
def __init__(self, K, start=0):
self.K, self.v = K, start
def abstract(self):
return (self.K, self.v)
@classmethod
def denote(cls, state, call):
K, v = state
if call.name == "inc":
if v == K: raise Inadmissible("counter at its maximum")
return (K, v + 1), None
if call.name == "dec":
if v == 0: raise Inadmissible("counter at zero")
return (K, v - 1), None
return state, v
def apply(self, call, cost):
cost.add("compare", 1)
if call.name == "value":
cost.add("read", 1); return self.v
step = 1 if call.name == "inc" else -1
if not 0 <= self.v + step <= self.K:
raise Inadmissible("counter at its maximum" if step > 0 else "counter at zero")
cost.add("write", 1); self.v += step
for expr in ["inc inc dec value", "inc dec dec", "dec", "inc inc inc inc", "inc value inc dec value"]:
g = BoundedCounter(K=3).grammar(admissible="(inc | dec inc)* (dec | ε)") # fresh state each time
r = g.execute(expr)
status = (f"syntax: {r.syntax.error}" if not r.syntax.ok else
f"undefined at call {r.undefined_at}: {r.reason}" if not r.defined else f"values {r.values}")
print(f"{expr:24s} -> {status} [adequate={r.adequate}]")
inc inc dec value -> values [None, None, None, 1] [adequate=True]
inc dec dec -> syntax: move word inc dec dec is not in the admissibility language [adequate=None]
dec -> undefined at call 0: counter at zero [adequate=True]
inc inc inc inc -> undefined at call 3: counter at its maximum [adequate=True]
inc value inc dec value -> values [None, 1, None, None, 1] [adequate=True]
Execution mutates the representation, so each expression starts from a fresh counter at zero. The second expression is rejected before execution because dec dec is not admissible; the third is admissible and undefined (the counter starts at zero); the fourth runs off the top. The last shows projection at work: value is not mentioned by the language, so it does not interfere with it.
7.3 The grammar of each structure
With the definition in hand, the structures of Part I can be written in one format. The table is a summary of Chapters 2–6; the SDK implements each row.
| Structure | States | Moves | Defined when | Admissibility class (from empty) | Typical representation and move cost |
|---|---|---|---|---|---|
| array | sequences | get(i), set(i,x), insert, delete, append, pop, len |
index in range | universal | contiguous: access, shifts |
| linked list | sequences + cursor | get(i)… or next, prev |
index in range / neighbour exists | universal | records: access |
| stack | sequences | push(x), pop, top, empty |
non-empty for pop, top |
context-free, not regular (Dyck prefixes) | array: amortized |
| queue / deque | sequences | enqueue, dequeue, … |
non-empty | same language as the stack | ring buffer on : amortized |
| map / set | finite functions | put, get, contains, delete |
key present for get, delete |
universal | hashing: expected; trie: |
| heap | multisets | push, pop_min, peek |
non-empty | Dyck-type, as for stacks | array with heap numeration: |
| binary tree | trees + cursor | L, R, U, goto(w), read |
target node exists | universal; addresses form a prefix-closed language | pointers: ; heap numerals: |
| search tree | sorted sets | insert, delete, contains, min |
key present for delete |
universal | ; AVL: |
| labelled graph | vertex sets | one move per label, at(v), here |
image non-empty | any regular / context-free by choice | adjacency lists: |
| automaton | states | one move per letter | transition defined | — | table: per letter |
The table makes visible what is common — every structure is a set of states with partial moves — and what is not: the admissibility class (universal, regular, context-free), the dependence of definedness on state, and above all the cost column.
7.4 Structural grammars
An operational grammar generates operations over a structure. A second kind of grammar generates the structure itself.
A structural grammar for a structure is a grammar whose unique derivation (or unique generated object) is or its representation: a minimal DAG of a tree, a straight-line program for a string, a tree straight-line program, a hyperedge-replacement grammar for a graph. Its size is the total length of its productions.
The two kinds combine: navigating a grammar-compressed tree is an operational grammar whose states are given by a structural grammar (Chapter 5, CGT-THM-005). Grammar-based compression is an established field [Charikar et al. 2005; Lohrey 2015; Bille et al. 2011]; random access into a grammar-compressed string of length takes time [Bille et al. 2011], and lower bounds show that for polynomial-space structures this cannot in general be improved to constant time [Verbin & Yu 2013]. This book uses these objects and does not claim new compression results.
7.5 Cost profiles
For a class of structures and a family of queries, a resolution scheme is a triple of algorithms (Build, Query, Update) on a fixed machine model. Its cost profile is the tuple
each a function of the structure size , the grammar size , the query size , update parameters and output size . A grammatical index is a resolution scheme whose Build takes a grammar as input; a grammar-based algorithm is one whose control flow follows derivations (product constructions, fixpoint evaluation of productions, descent through a structural grammar).
The notation is a reporting discipline, not yet a theory: whether grammar parameters yield distinctions not already captured by parameterized complexity or by preprocessing–query trade-offs is an open problem (CGT-OPEN-005).
7.6 Five mechanisms
Every advantage observed so far in this research — in the chapters of Part I and in the ten experiments of the ledger — traces to one of five mechanisms.
| Code | Mechanism | Where it appears |
|---|---|---|
| M-ARITH | an arithmetic numeration of a dense address space replaces stored structure by computation | arrays; ring buffers; heaps (CGT-THM-003); compiled words (CGT-THM-006) |
| M-PRE | precomputation: answers are stored and later retrieved | closures, LCA tables, perfect hashing — bounded below by information content (CGT-THM-002) |
| M-SHARE | repeated substructure is represented once | minimal DAGs, minimal automata — fragile under updates (CGT-THM-005) |
| M-RESTRICT | a restriction of the instance class makes structure exploitable | interval labels on forests; 2-SAT |
| M-PRUNE | the admissibility language excludes moves before they are executed | product construction — and it can also multiply work (CGT-THM-001) |
Every asymptotic advantage of a grammatical resolution scheme over the best conventional scheme for the same queries, machine model and cost component is attributable to a combination of M-ARITH, M-PRE, M-SHARE, M-RESTRICT and M-PRUNE.
The thesis is not yet a mathematical statement: “conventional scheme” and “attributable” are undefined, and making them precise is an open problem (CGT-OPEN-001). It is nevertheless falsifiable in practice — by a grammar-based scheme with an advantage none of the five explains — and it organizes the rest of the book. Chapters 8–14 examine each mechanism with its cost profile and its failure cases.
7.7 Relationship to established theory
The components of an operational grammar are not new, and it is important to say which existing theories each comes from.
- Labelled transition systems and relational semantics. is a labelled transition system; the extension to words is the relational model of Kleene algebra. \emph{Kleene algebra with tests} [Kozen 1997] adds Boolean guards and is a natural home for admissibility conditions that depend on state.
- Formal-language-constrained paths. With regular or context-free over a labelled graph, the grammar is exactly the setting of regular path queries and CFL-reachability [Mendelzon & Wood 1995; Barrett et al. 2000; Yannakakis 1990; Reps 1998].
- Attribute grammars. Knuth’s attribute grammars [Knuth 1968] attach semantic functions to the productions of a context-free grammar and evaluate them over derivation trees. They are the classical way of making a grammar compute. CGT differs in where the grammar lives: attribute grammars describe strings and their parse trees; operational grammars describe operations over an arbitrary structure. A systematic comparison is a literature debt of this program (CGT-OPEN-010).
- Automatic structures and groups. Structures whose elements are words of a regular language and whose relations are recognized by synchronous automata [Blumensath & Grädel 2000; Khoussainov & Nerode 1995], and groups whose multiplication by generators is so recognized [Epstein et al. 1992], are the closest existing theory to the address-as-sentence idea. Chapter 13’s compiled words are a cost-oriented special case.
- Abstract data types. Adequacy is the standard notion of a correct implementation of an abstract type.
What the program adds is not a new object but a discipline: every claim filed under one layer, every cost stated as a profile in an explicit model, every advantage attributed to a mechanism and paired with the inputs that defeat it.
Extend BoundedCounter with a move reset and an admissibility language in which reset may only occur after at least two inc. Write the language as a regular expression and check your answer with the SDK.
Prove that adequacy is preserved under sequential composition: if a representation is adequate for every single-call expression from every reachable state, it is adequate for all expressions.
Solution to Exercise 7.2
Induction on the length of . By hypothesis the representation agrees with the semantics on , including the point of first undefinedness; if is defined, the representation reaches a state whose abstraction is the semantic state reached by , and the single-call hypothesis at gives agreement on . The point of first undefinedness of is that of if is undefined, and otherwise is the last call iff .
Give an operational grammar (states, moves, semantics, admissibility) for a traffic light with moves next and emergency, in which emergency is admissible only from green. Which component of the definition carries the “only from green” condition — syntax or semantics? Argue for both choices.
For each mechanism in Definition 7.5, give one example from outside this book (a database index, a compiler optimization, a cache) and say which cost component of the profile it improves and which it worsens.
- An operational grammar consists of moves, relational semantics on a structure, and an admissibility language constraining the projection of move words.
- Syntax (admissibility), semantics (definedness and values) and execution (cost) are separate layers; a representation is adequate when its execution agrees with the semantics, and the SDK tests this.
- Structural grammars generate the structure; operational grammars generate operations over it.
- Costs are reported as profiles .
- Five mechanisms — arithmetic addressing, precomputation, sharing, restriction, pruning — account for every advantage observed so far; that they account for all advantages is a conjecture.
- The components are standard; the program’s contribution is the discipline that keeps them apart.
References cited in this chapter
- Barrett et al. 2000 C. Barrett, R. Jacob, M. Marathe (2000). Formal-language-constrained path problems. SIAM Journal on Computing 30(3):809–837. doi:10.1137/S0097539798337716.
- Bille et al. 2011 P. Bille, G. M. Landau, R. Raman, K. Sadakane, S. R. Satti, O. Weimann (2011). Random access to grammar-compressed strings. Proc. 22nd ACM-SIAM SODA, pp. 373–389. link.
- Blumensath & Grädel 2000 A. Blumensath, E. Grädel (2000). Automatic structures. Proc. 15th IEEE LICS, pp. 51–62.
- Charikar et al. 2005 M. Charikar, E. Lehman, D. Liu, R. Panigrahy, M. Prabhakaran, A. Sahai, A. Shelat (2005). The smallest grammar problem. IEEE Transactions on Information Theory 51(7):2554–2576. doi:10.1109/TIT.2005.850116.
- Epstein et al. 1992 D. B. A. Epstein, J. W. Cannon, D. F. Holt, S. V. F. Levy, M. S. Paterson, W. P. Thurston (1992). Word Processing in Groups. Jones and Bartlett (later A K Peters).
- Khoussainov & Nerode 1995 B. Khoussainov, A. Nerode (1995). Automatic presentations of structures. Logic and Computational Complexity, LNCS 960.
- Knuth 1968 D. E. Knuth (1968). Semantics of context-free languages. Mathematical Systems Theory 2(2):127–145. doi:10.1007/BF01692511.
- Kozen 1997 D. Kozen (1997). Kleene algebra with tests. ACM Transactions on Programming Languages and Systems 19(3):427–443. doi:10.1145/256167.256195.
- Lohrey 2015 M. Lohrey (2015). Grammar-based tree compression. Proc. DLT 2015 (LNCS). doi:10.1007/978-3-319-21500-6_3.
- Mendelzon & Wood 1995 A. O. Mendelzon, P. T. Wood (1995). Finding regular simple paths in graph databases. SIAM Journal on Computing 24(6):1235–1258.
- Reps 1998 T. Reps (1998). Program analysis via graph reachability. Information and Software Technology 40(11–12):701–726. doi:10.1016/S0950-5849(98)00093-7.
- Verbin & Yu 2013 E. Verbin, W. Yu (2013). Data structure lower bounds on random access to grammar-compressed strings. Proc. CPM 2013 (LNCS). link.
- Yannakakis 1990 M. Yannakakis (1990). Graph-theoretic methods in database theory. Proc. 9th ACM PODS, pp. 230–242. doi:10.1145/298514.298576.