Les exemples précédents ne sont pas d'une lecture très agréable. L'utilisation exclusive des concepts de base contraint en effet à "empiler" les préfixes – par exemple : (lsp (lst (rep ...))) : on obtient une syntaxe simple mais peu lisible.
On pourrait préférer une syntaxe plus concise. Par exemple, la syntaxe d'emploi de la macro-définition lst est :
(lsp (lst (rep { atm } )))
il serait plus agréable d'écrire :
(lst { atm } )
qui contient la même information mais se lit plus aisément.
Les exemples de la figure précédente s'écriraient alors comme il est indiqué sur la figure suivante.
Figure a
(lst-init
(env ...)
(rep (param) ";"))
Figure b.1
(lst-init
(env ...)
(rep "(" (param) (lst "," (param)) ")" ))
Figure b.2
(lst-init
(env ...)
(rep "(" (lst (param) (tst
(1 (rep ")"))
(T (rep ","))))))
Figure c
"("
(lst-init
(env ...)
(rep (param) (lst "," (param))))
")"
On voit sur ces exemples qu'une petite simplification améliore grandement la lisibilité : l'ancienne et la nouvelle définitions de lst sont de même nature – typiquement une liste d'atomes atm –, la seule différence étant l'affichage des préfixes – un seul préfixe au lieu de trois.
Mais la différence est en fait plus importante qu'il n'y paraît. Toujours sur le même exemple, on voit qu'on utilise le texte param sous une forme abrégée, sans le préfixe 'use' ; au départ, il n'y a pas d'ambiguïté ; sous sa forme simplifiée, l'emploi de lst introduit lui une ambiguïté : il faut en effet savoir que, sous le préfixe 'lst', on a des objets de même nature que ceux qu'on peut trouver sous le préfixe 'rep'. En clair, on doit réécrire les fonctions de saisie et d'affichage des textes pour le cas de lst : on y indiquera que, par défaut, une liste correspond à la forme abrégée d'une utilisation.
De ceci on tire deux conséquences :
lst ressemble à s'y méprendre à un opérateur d'une syntaxe abstraite à définir. On y trouve la syntaxe abstraite sur laquelle se dérive l'opérateur ; la fonction de saisie qui va compléter l'analyseur syntaxique d'une syntaxe concrète associée ; la fonction d'affichage qui va compléter le décompilateur d'arbres de la représentation interne.De là on conclut :
Cette dernière étape n'est indispensable : elle permet juste de mieux harmoniser les concepts, en exprimant dans un même formalisme les notions qui sont prédéfinies et celles que l'utilisateur sera amené à introduire.
syntaxe abstraite
On reprend la syntaxe dans les termes d'une syntaxe abstraite (pour une définition plus générale des concepts qu'introduit une syntaxe abstraite, cf. Chapitre 5.5, « Construction de la Syntaxe Abstraite ») :
PHYLA
(P1) SEX ::= env def ref rep stg use lsp liste atome
(P2) TRM ::= env def ref lsp
(P3) ATM ::= rep stg use lsp string
(P4) ENV ::= env
(P5) REP ::= rep
(P6) LSP ::= lsp
OPERATEURS
(O1) env -> TRM*...
(O2) rep -> ATM*...
(O3) lsp -> SEX*...
(O4) def -> NOM ENV REP
(O5) ref -> NOM ENV
(O6) stg -> NOM
(O7) use -> NOM ENV
ATOMES
(A1) liste -> implemented as LISTE
(A2) atome -> implemented as ATOME
(A3) string -> implemented as STRING
SYNTAXE CONCRETE
(C1) env ::= '(' 'env' { TRM } ')'
(C2) rep ::= '(' 'env' { ATM } ')'
(C3) lsp ::= '(' 'env' { SEX } ')'
(C4) def ::= '(' 'def' NOM ENV REP ')'
(C5) ref ::= '(' 'ref' NOM ENV ')' | '(' NOM ENV ')'
(C6) stg ::= '(' 'stg' NOM ')'
(C7) use ::= '(' 'use' NOM ENV ')' | '(' NOM ENV ')'
phyla | |
| (P1) | le phylum SEX regroupe tous les opérateurs de Lisp |
| (P2) | le phylum TRM regroupe :- les opérateurs des termes def et ref,- l'opérateur des environnements env |
| (P3) | le phylum ATM regroupe :- les opérateurs des atomes stg et use,- l'opérateur des représentations rep |
| (P4) | le phylum des environnements est constitué uniquement de env |
| (P4) | le phylum des représentations est constitué uniquement de rep |
| (P4) | le phylum des expressions Lisp est constitué uniquement de lsp |
opérateurs | |
| (O1) | l'opérateur env est une liste de termes |
| (O2) | l'opérateur rep est une liste d'atomes |
| (O3) | l'opérateur lsp est une liste de S-ex |
| (O4) | l'opérateur def est défini par les champs : NOM, ENV, REP |
| (O5) | l'opérateur ref est défini par les champs : NOM, ENV |
| (O6) | l'opérateur stg est défini par le champ : NOM |
| (O7) | l'opérateur use est défini par les champs : NOM, ENV |
atomes | |
| (A1) | l'opérateur liste est un atome, du phylum prédéfini LISTE |
| (A2) | l'opérateur atome est un atome, du phylum prédéfini ATOME |
| (A3) | l'opérateur string est un atome, du phylum prédéfini STRING |
syntaxe concrète
Elle reprend la syntaxe abstraite, sous une forme Lisp. On a conservé pour def et ref la double dérivation qui dispensera de préfixer l'emploi quand il n'y a pas d'ambiguïté. On notera que tous les opérateurs, sous leur forme concrète, sont préfixés par le nom de l'opérateur : du point de vue de Lisp, les listes ainsi construites sont des appels aux fonctions dont le nom est justement le préfixe qu'on a placé. Sur un plan purement technique, on voit donc que la syntaxe concrète, visible de l'utilisateur, est aussi la syntaxe de la représentation interne des données, laquelle est inconnue de l'utilisateur.