Mise à jour 2021
Voici un exemple fonctionnel qui s'exécute dans le playground en ligne de Peggy.js. Peggy.js est un fork de PEG.js en développement actif. PEG.js a été abandonné par David Maida.
L'exemple montre comment les règles INDENT
, SAMEDENT
et DEDENT
sont analysées, et comment utiliser les emplacements d'analyse. Vérifiez la console.
Il utilise ces syntaxes, qui peuvent ne pas être connues d'autres générateurs d'analyseurs :
(en haut du fichier)
{{...}}
(Initialisateur global) - Exécute ...
lors de la génération de l'analyseur.
{...}
(Initialisateur par analyse) - Exécute ...
lors de l'instanciation de l'analyseur.
(dans le fichier)
X {...}
(action) - Faites ...
quand X
réussit. Les variables des initialiseurs sont disponibles. Si ...
renvoie quelque chose, cela remplacera ce que X
renvoie.
$X
- Renvoie le texte brut analysé avec X
, au lieu du résultat de X.
... @X ...
(opérateur d'extraction) - Remplace le résultat de ... X ...
par le résultat de X
.
X &{...}
(prédicat) - "et ...
doit également être vrai pour que X réussisse".
X = &(...)
- Si ...
réussit, X
réussit. ...
ne consomme pas d'entrée.
Consultez la documentation pour plus d'informations.
{{
console.clear()
console.log('Analyseur généré')
}}
{
let indentstack = []
let indent = ''
function found (what) {
let loc = location()
console.log(`[${loc.start.line}:${loc.start.column} - ${loc.end.line}:${loc.end.column}] trouvé ${what}`)
}
console.log('Analyseur instancié')
}
DOCUMENT = NEWLINES? @THINGS NEWLINES? _
THINGS = ( SAMEDENT @( OBJECT / LINE ) )*
OBJECT = key:KEY childs:(BLOCK / INLINE) {
found(`objet "${key}"`)
let o = {}
o[key] = childs
return o
}
KEY = @$( [^ \t\r\n:]+ ) _ ':' _
BLOCK = NEWLINES INDENT @THINGS DEDENT
INLINE = line:LINE { return [line] }
LINE = text:$( (!EOL .)+ ) NEWLINES? {
found(`ligne "${text}"`)
return text
}
INDENT = &(
spaces:$( [ \t]+ ) &{
return spaces.length > indent.length
} {
indentstack.push(indent)
indent = spaces
}
) {
found('indentation')
}
SAMEDENT = spaces:$( [ \t]* ) &{
return spaces === indent
} {
found('même indentation')
}
/* En raison de cette règle, le cache des résultats doit être désactivé */
DEDENT = &{
indent = indentstack.pop()
return true
} {
found('dé-indentation')
}
_ = [ \t]*
EOL = '\r\n' / '\n' / '\r'
NEWLINES = (_ EOL)+
/* Testez avec cette entrée
H:
a
b
c
G:
d
e
f
*/
Ancienne réponse
Voici un correctif pour la grammaire de @Jakub Kulhan qui fonctionne dans PEG.js v 0.10.0. La dernière ligne doit être modifiée en = &{ indent = indentStack.pop(); return true;}
car PEG.js ne permet plus les actions autonomes ({...}
) dans une grammaire. Cette ligne est maintenant un prédicat (&{...}
) qui réussit toujours (return true;
).
J'ai également supprimé le pos = offset;
car cela donne une erreur offset is not defined
. Probablement Jakub faisait référence à une variable globale disponible dans les anciennes versions de PEG.js. PEG.js fournit maintenant la fonction location()
qui renvoie un objet contenant l'offset et d'autres informations.
// Ne pas utiliser le cache des résultats, ni le suivi des lignes et des colonnes
{ var indentStack = [], indent = ""; }
start
= INDENT? l:line
{ return l; }
line
= SAMEDENT line:(!EOL c:. { return c; })+ EOL?
children:( INDENT c:line* DEDENT { return c; })?
{ var o = {}; o[line] = children; return children ? o : line.join(""); }
EOL
= "\r\n" / "\n" / "\r"
SAMEDENT
= i:[ \t]* &{ return i.join("") === indent; }
INDENT
= &(i:[ \t]+ &{ return i.length > indent.length; }
{ indentStack.push(indent); indent = i.join(""); })
DEDENT
= &{ indent = indentStack.pop(); return true;}
À partir de la v 0.11.0, PEG.js prend également en charge l'opérateur d'extraction de valeur, @
, ce qui permettrait d'écrire cette grammaire de manière encore plus simple, mais comme il n'est pas actuellement présent dans l'analyseur en ligne, je m'abstiendrai de l'ajouter à cet exemple.