Op weg naar de Programmeren-bijeenkomst in De Bilt bedacht ik dat ik een onzekerheidje over de juiste interpretatie van formele grammatica's (FG's) — en in het bijzonder Backus-Naur Forms (BNF's) — wel in de groep zou kunnen gooien. Na afloop stond ik inderdaad vaster in mijn schoenen, maar het was niet iedereen duidelijk geworden wat ik nu eigenlijk te berde had willen brengen. Bovendien kondigde iemand aan dat-ie er nog een appeltje met me over te schillen had. Daarom wil ik op deze plaats wat gedegener (maar nog steeds volgens de lijnen van mijn gedachtengang op die zaterdag) uiteenzetten waar het me in eerste instantie om te doen was en wat daar in latere instantie nog bij kwam. Mijn meest recente inzichten heb ik eveneens in deze tekst verwerkt. Na het aanstippen van wat een FG eigenlijk is en een greep uit de soms wat uiteenlopende terminologieën die rondwaren, zal ik de zaken die aan de orde kwamen een voor een bij langsgaan:
Formele oftewel generatieve grammatica's
Een FG is een metataal waarmee door middel van een stelsel van herschrijfregels syntactisch correcte uitdrukkingen in een doeltaal kunnen worden voortgebracht. De metaschrijver kan daarbij ambiëren de syntax van die doeltaal eenduidig en volledig in kaart te brengen, al lukt dat niet altijd vanwege de complexiteit van de doeltaal en/of de gebrekkige uitdrukkingsmiddelen van de metataal.
De doeltaal bevat een zeker alfabet aan symbolen, die in de metataal als terminale symbolen of kortweg terminals (ook wel literals genoemd) worden opgevoerd. Daarnaast kent de metataal nog andere metasymbolen. Onmisbaar zijn in elk geval de volgende twee categorieën. Ten eerste de niet-terminale symbolen of kortweg non-terminals, die als variabele of klasse worden gebruikt. Je komt hiervoor ook wel de Engelse term placeholder tegen, waar we in het Nederlands dan weer het fraaie woord pantoniem voor hebben. Ter aanduiding van de naam van de non-terminal kan het woord meta-identifier worden gebruikt. Voor deze post houd ik het verder maar consequent op 'pantoniem'. Ten tweede heeft de metataal meta-operatoren nodig.
Dit zijn voldoende ingrediënten om duidelijk te maken hoe een FG werkt. Elke regel schrijft voor hoe een pantoniem te vervangen is door een of meer combinaties van terminals en pantoniemen. Uiteindelijk moet er vanuit elk pantoniem al herschrijvend een wandeling door de regels naar ten minste één terminal te maken zijn. Ieder pantoniem heeft dus een of meer eindoplossingen. Eén pantoniem in de FG fungeert als startsymbool. Van hieruit zou je de complete syntax van de doeltaal moeten kunnen produceren.
Wat probeert een regel precies uit te drukken?
Voor dit betoog heb ik een zeer eenvoudige FG-notatie bedacht, volgens combinatiewijze I (mijn voorkeur) bijvoorbeeld:
x ?= "1" | "2" | "3"
Hierin staan de terminale symbolen tussen aanhalingstekens en is het pantoniem gecursiveerd. De verticale streep is een meta-operator met de betekenis "of". De wat breedsprakiger combinatiewijze II verduidelijkt dit met behulp van de in de wiskunde gebruikelijke logische OF-operator ∨:
(x ?= "1") ∨ (x ?= "2") ∨ (x ?= "3")
Nu heb ik helaas ook nog eens metahaakjes nodig om de evaluatievolgorde aan te geven. Combinatiewijze III doet het zonder expliciete OF-operator:
x ?= "1"
x ?= "2"
x ?= "3"
Hier is elke nieuwe herschrijfregel een impliciete uitbreiding van wat eraan voorafgaat. Of zou het juist een impliciete beperking van het voorafgaande kunnen zijn? Daar raken we het probleem waar ik mee zat. Het gaat om de vraag wat er precies met de meta-operator ?= wordt bedoeld. Voor de beantwoording moet ik de verzamelingenleer maar van stal halen en daarbij de volgende conventie aanhouden: X (vetgedrukte hoofdletter) is de verzameling van alle geldige waarden van het pantoniem x (corresponderende kleine letter in cursief). Laat nu A = {"1", "2", "4", "8"} zijn, en B = {"2", "3", "4"}. Dan is de vereniging A ∪ B = {"1", "2", "3", "4", "8"} en de doorsnede A ∩ B = {"2", "4"}. X ⊆ Y betekent: X is een deelverzameling van Y of gelijk aan Y. Van de herschrijfregel
x ?= a | b
zijn drie interpretaties mogelijk, die ik allemaal een eigen variant van de ?=-operator zal geven:
(1) x >= a | b ⟺ A ∪ B ⊆ X (productieregel)
(2) x <= a | b ⟺ X ⊆ A ∪ B (beperkingsregel)
(3) x == a | b ⟺ X = A ∪ B (volledige definitie)
Iedere regel van het type (1) is in staat geldige waarden voor het pantoniem x te produceren, maar niet per se alle mogelijke waarden. Misschien zijn er nog andere productieregels die mogeijkheden toevoegen. Regeltype (2) geeft aan dat niets buiten A ∪ B is toegestaan, maar laat in het midden of er binnen A ∪ B nog verboden vruchten hangen. Regeltype (3) is de combinatie van beide, en daarmee zowel een volledige productieregel als een volledige beperkingsregel.
Voorbeelden van type (1) met combinatiewijze III zijn te vinden in https://en.wikipedia.org/wiki/Production_%28computer_science%29 en https://en.wikipedia.org/wiki/Formal_grammar. Weliswaar hoeft elke regel op zich niet alle mogelijkheden te dekken, maar het totale stelsel van regels wordt geacht wel compleet te zijn. Genoemde artikelen laten zien dat alle regels met hetzelfde linkerlid via OF zijn gekoppeld, want iedere regel blijkt de mogelijkheden van dat pantoniem verder uit te breiden.
Type (2) zul je niet zou gauw in FG's tegenkomen. ISO/IEC 10206:1991 voor Extended Pascal lijkt zo'n mechanisme te bevatten, als ik afga op wat (https://www.cl.cam.ac.uk/~mgk25/iso-14977-paper.pdf) erover zegt. Omdat het om verdere beperkingen gaat, zal dat EN-koppelingen tussen de regels vergen. Ik vermoed dat zulke constructies alleen bedoeld zijn voor beperkingen die afhangen van de omstandigheden. Handig dan wellicht, maar het doet wel afbreuk aan de eenvoud die type (1) kenmerkt.
Type (3) is BNF in al zijn verschijningsvormen. Ten opzichte van type (1) geldt één extra voorschrift: voor ieder pantoniem mag maar één productieregel worden geschreven, die dan onontkoombaar de volledige (zij het mogelijk geparametriseerde) definitie van dat pantoniem dient te geven. De kwestie EN/OF oftewel inperking/uitbreiding is hiermee van de baan. BNF is feitelijk niets anders dan een wat compactere en meer voor zich sprekende schrijfwijze van type (1), dus de uitdrukkingsmogelijkheden zijn exact gelijk.
Waar wil je de FG voor gebruiken?
(α) Om mensen iets duidelijk te maken
(β) Om niet al te intelligente machines te instrueren
Mijn vraag kwam voort uit (α). Hoe geef ik zo efficiënt (m.b.t. de economie van het formuleren) en effectief (m.b.t. leesbaarheid en begrijpelijkheid) mogelijk de structuur van geldige uitdrukkingen in een zekere formele doeltaal aan? Wat moet ik doen wanneer ik niet volledig kan of wil zijn? Het lijkt me zaak expliciet te zijn over tekortkomingen in mijn specificaties. Dus als ik voor regels van type (3) kies, voor een BNF-achtige metataal dus, mag ik niet suggereren dat een regel compleet is terwijl dat niet het geval is. Er zijn wel middelen binnen de metataal te verzinnen om doelbewuste omissies en minder exacte formuleringen netjes af te handelen:
• x == a | b | more — (extra, niet-gespecificeerd pantoniem)
• x == a | b | ?? — (speciale sequentie, zou je een pseudoterminal kunnen noemen)
De welbespraaktheid van BNF's
Dit onderwerp kwam naar mijn gevoel per ongeluk ter sprake toen we het verschil tussen combinatiewijze I en III behandelden, en concludeerden dat deze twee maar beter niet moesten worden gecombineerd in één metataal, al doet Augmented BNF (ABNF) het echter lekker toch. Combinatiewijze II was alleen maar bedoeld om de betekenis van combinatiewijze I te illustreren, niet om in de praktijk te worden toegepast.
De oorspronkelijke BNF, daterend van 1960, is nogal spartaans. Met Extended BNF (EBNF), dat het in 1996 tot ISO/IEC-norm 14977 schopte, kregen we meer luxe. Ik heb EBNF volgens ISO/IEC 14977 alleen maar gebruikt zien worden in publicaties over EBNF volgens ISO/IEC 14977. Verder past iedereen onbekommerd zijn eigen maatwerk-EBNF toe, behalve Internetters die ABNF conform de evoluerende Internet-standaards gebruiken. Die houden zich wèl aan hun gemeenschappelijke norm, voor zover ze die kunnen bijbenen.
Natuurlijk kon ik in 2017 de aandrang niet weerstaan toch maar een eigen, ter onderscheid Overhauled BNF (OBNF) gedoopte variant samen te stellen, daarmee een ietwat pretentieus Improved BNF (IBNF) aan anderen overlatend en mijn belangstellend uitzien naar een Unified BNF (UBNF) evengoed handhavend. Voor wie nog zin heeft in beschouwingen over expressiviteit en ergonomie van BNF's, heb ik een white paper bijgesloten. Een white paper is een document om een idee aan de man te brengen zonder alles te vertellen wat de lezer zou willen weten wanneer deze het een goed idee vindt. Wikipedia verwoordt het een beetje anders.
- Wat probeert een FG-regel (Engels: rule) precies uit te drukken?
- Waar wil je de FG voor gebruiken?
- Hoe kan datgene wat in een BNF moet worden uitgedrukt zo goed mogelijk worden geformuleerd?
Formele oftewel generatieve grammatica's
Een FG is een metataal waarmee door middel van een stelsel van herschrijfregels syntactisch correcte uitdrukkingen in een doeltaal kunnen worden voortgebracht. De metaschrijver kan daarbij ambiëren de syntax van die doeltaal eenduidig en volledig in kaart te brengen, al lukt dat niet altijd vanwege de complexiteit van de doeltaal en/of de gebrekkige uitdrukkingsmiddelen van de metataal.
De doeltaal bevat een zeker alfabet aan symbolen, die in de metataal als terminale symbolen of kortweg terminals (ook wel literals genoemd) worden opgevoerd. Daarnaast kent de metataal nog andere metasymbolen. Onmisbaar zijn in elk geval de volgende twee categorieën. Ten eerste de niet-terminale symbolen of kortweg non-terminals, die als variabele of klasse worden gebruikt. Je komt hiervoor ook wel de Engelse term placeholder tegen, waar we in het Nederlands dan weer het fraaie woord pantoniem voor hebben. Ter aanduiding van de naam van de non-terminal kan het woord meta-identifier worden gebruikt. Voor deze post houd ik het verder maar consequent op 'pantoniem'. Ten tweede heeft de metataal meta-operatoren nodig.
Dit zijn voldoende ingrediënten om duidelijk te maken hoe een FG werkt. Elke regel schrijft voor hoe een pantoniem te vervangen is door een of meer combinaties van terminals en pantoniemen. Uiteindelijk moet er vanuit elk pantoniem al herschrijvend een wandeling door de regels naar ten minste één terminal te maken zijn. Ieder pantoniem heeft dus een of meer eindoplossingen. Eén pantoniem in de FG fungeert als startsymbool. Van hieruit zou je de complete syntax van de doeltaal moeten kunnen produceren.
Wat probeert een regel precies uit te drukken?
Voor dit betoog heb ik een zeer eenvoudige FG-notatie bedacht, volgens combinatiewijze I (mijn voorkeur) bijvoorbeeld:
x ?= "1" | "2" | "3"
Hierin staan de terminale symbolen tussen aanhalingstekens en is het pantoniem gecursiveerd. De verticale streep is een meta-operator met de betekenis "of". De wat breedsprakiger combinatiewijze II verduidelijkt dit met behulp van de in de wiskunde gebruikelijke logische OF-operator ∨:
(x ?= "1") ∨ (x ?= "2") ∨ (x ?= "3")
Nu heb ik helaas ook nog eens metahaakjes nodig om de evaluatievolgorde aan te geven. Combinatiewijze III doet het zonder expliciete OF-operator:
x ?= "1"
x ?= "2"
x ?= "3"
Hier is elke nieuwe herschrijfregel een impliciete uitbreiding van wat eraan voorafgaat. Of zou het juist een impliciete beperking van het voorafgaande kunnen zijn? Daar raken we het probleem waar ik mee zat. Het gaat om de vraag wat er precies met de meta-operator ?= wordt bedoeld. Voor de beantwoording moet ik de verzamelingenleer maar van stal halen en daarbij de volgende conventie aanhouden: X (vetgedrukte hoofdletter) is de verzameling van alle geldige waarden van het pantoniem x (corresponderende kleine letter in cursief). Laat nu A = {"1", "2", "4", "8"} zijn, en B = {"2", "3", "4"}. Dan is de vereniging A ∪ B = {"1", "2", "3", "4", "8"} en de doorsnede A ∩ B = {"2", "4"}. X ⊆ Y betekent: X is een deelverzameling van Y of gelijk aan Y. Van de herschrijfregel
x ?= a | b
zijn drie interpretaties mogelijk, die ik allemaal een eigen variant van de ?=-operator zal geven:
(1) x >= a | b ⟺ A ∪ B ⊆ X (productieregel)
(2) x <= a | b ⟺ X ⊆ A ∪ B (beperkingsregel)
(3) x == a | b ⟺ X = A ∪ B (volledige definitie)
Iedere regel van het type (1) is in staat geldige waarden voor het pantoniem x te produceren, maar niet per se alle mogelijke waarden. Misschien zijn er nog andere productieregels die mogeijkheden toevoegen. Regeltype (2) geeft aan dat niets buiten A ∪ B is toegestaan, maar laat in het midden of er binnen A ∪ B nog verboden vruchten hangen. Regeltype (3) is de combinatie van beide, en daarmee zowel een volledige productieregel als een volledige beperkingsregel.
Voorbeelden van type (1) met combinatiewijze III zijn te vinden in https://en.wikipedia.org/wiki/Production_%28computer_science%29 en https://en.wikipedia.org/wiki/Formal_grammar. Weliswaar hoeft elke regel op zich niet alle mogelijkheden te dekken, maar het totale stelsel van regels wordt geacht wel compleet te zijn. Genoemde artikelen laten zien dat alle regels met hetzelfde linkerlid via OF zijn gekoppeld, want iedere regel blijkt de mogelijkheden van dat pantoniem verder uit te breiden.
Type (2) zul je niet zou gauw in FG's tegenkomen. ISO/IEC 10206:1991 voor Extended Pascal lijkt zo'n mechanisme te bevatten, als ik afga op wat (https://www.cl.cam.ac.uk/~mgk25/iso-14977-paper.pdf) erover zegt. Omdat het om verdere beperkingen gaat, zal dat EN-koppelingen tussen de regels vergen. Ik vermoed dat zulke constructies alleen bedoeld zijn voor beperkingen die afhangen van de omstandigheden. Handig dan wellicht, maar het doet wel afbreuk aan de eenvoud die type (1) kenmerkt.
Type (3) is BNF in al zijn verschijningsvormen. Ten opzichte van type (1) geldt één extra voorschrift: voor ieder pantoniem mag maar één productieregel worden geschreven, die dan onontkoombaar de volledige (zij het mogelijk geparametriseerde) definitie van dat pantoniem dient te geven. De kwestie EN/OF oftewel inperking/uitbreiding is hiermee van de baan. BNF is feitelijk niets anders dan een wat compactere en meer voor zich sprekende schrijfwijze van type (1), dus de uitdrukkingsmogelijkheden zijn exact gelijk.
Waar wil je de FG voor gebruiken?
(α) Om mensen iets duidelijk te maken
(β) Om niet al te intelligente machines te instrueren
Mijn vraag kwam voort uit (α). Hoe geef ik zo efficiënt (m.b.t. de economie van het formuleren) en effectief (m.b.t. leesbaarheid en begrijpelijkheid) mogelijk de structuur van geldige uitdrukkingen in een zekere formele doeltaal aan? Wat moet ik doen wanneer ik niet volledig kan of wil zijn? Het lijkt me zaak expliciet te zijn over tekortkomingen in mijn specificaties. Dus als ik voor regels van type (3) kies, voor een BNF-achtige metataal dus, mag ik niet suggereren dat een regel compleet is terwijl dat niet het geval is. Er zijn wel middelen binnen de metataal te verzinnen om doelbewuste omissies en minder exacte formuleringen netjes af te handelen:
• x == a | b | more — (extra, niet-gespecificeerd pantoniem)
• x == a | b | ?? — (speciale sequentie, zou je een pseudoterminal kunnen noemen)
De welbespraaktheid van BNF's
Dit onderwerp kwam naar mijn gevoel per ongeluk ter sprake toen we het verschil tussen combinatiewijze I en III behandelden, en concludeerden dat deze twee maar beter niet moesten worden gecombineerd in één metataal, al doet Augmented BNF (ABNF) het echter lekker toch. Combinatiewijze II was alleen maar bedoeld om de betekenis van combinatiewijze I te illustreren, niet om in de praktijk te worden toegepast.
De oorspronkelijke BNF, daterend van 1960, is nogal spartaans. Met Extended BNF (EBNF), dat het in 1996 tot ISO/IEC-norm 14977 schopte, kregen we meer luxe. Ik heb EBNF volgens ISO/IEC 14977 alleen maar gebruikt zien worden in publicaties over EBNF volgens ISO/IEC 14977. Verder past iedereen onbekommerd zijn eigen maatwerk-EBNF toe, behalve Internetters die ABNF conform de evoluerende Internet-standaards gebruiken. Die houden zich wèl aan hun gemeenschappelijke norm, voor zover ze die kunnen bijbenen.
Natuurlijk kon ik in 2017 de aandrang niet weerstaan toch maar een eigen, ter onderscheid Overhauled BNF (OBNF) gedoopte variant samen te stellen, daarmee een ietwat pretentieus Improved BNF (IBNF) aan anderen overlatend en mijn belangstellend uitzien naar een Unified BNF (UBNF) evengoed handhavend. Voor wie nog zin heeft in beschouwingen over expressiviteit en ergonomie van BNF's, heb ik een white paper bijgesloten. Een white paper is een document om een idee aan de man te brengen zonder alles te vertellen wat de lezer zou willen weten wanneer deze het een goed idee vindt. Wikipedia verwoordt het een beetje anders.