J'essaie de développer quelques compétences dans les grammaires de lexing/parsing. Je regarde en arrière sur un analyseur simple que j'ai écrit pour SQL, et je ne suis pas tout à fait heureux avec lui - il semble qu'il aurait dû y avoir un moyen plus facile d'écrire l'analyseur. Le SQL m'a fait trébucher parce qu'il a beaucoup de jetons et de répétition optionnels. Par exemple:Représente la syntaxe optionnelle et la répétition avec OcamlYacc/FsYacc
SELECT *
FROM t1
INNER JOIN t2
INNER JOIN t3
WHERE t1.ID = t2.ID and t1.ID = t3.ID
équivaut à:
SELECT *
FROM t1
INNER JOIN t2 ON t1.ID = t2.ID
INNER JOIN t3 on t1.ID = t3.ID
Les ON
et WHERE
clauses sont facultatives et peuvent se produire plus d'une fois. Je me suis occupé ces derniers dans mon analyseur comme suit:
%{
open AST
%}
// ...
%token <string> ID
%token AND OR COMMA
%token EQ LT LE GT GE
%token JOIN INNER LEFT RIGHT ON
// ...
%%
op: EQ { Eq } | LT { Lt } | LE { Le } | GT { Gt } | GE { Ge }
// WHERE clause is optional
whereClause:
| { None }
| WHERE whereList { Some($2) }
whereList:
| ID op ID { Cond($1, $2, $3) }
| ID op ID AND whereList { And(Cond($1, $2, $3), $5) }
| ID op ID OR whereList { Or(Cond($1, $2, $3), $5) }
// Join clause is optional
joinList:
| { [] }
| joinClause { [$1] }
| joinClause joinList { $1 :: $2 }
joinClause:
| INNER JOIN ID joinOnClause { $3, Inner, $4 }
| LEFT JOIN ID joinOnClause { $3, Left, $4 }
| RIGHT JOIN ID joinOnClause { $3, Right, $4 }
// "On" clause is optional
joinOnClause:
| { None }
| ON whereList { Some($2) }
// ...
%%
En d'autres termes, je syntaxe facultative en traitées découpant en règles distinctes, et manutentionnés répétition utilisant récursion. Cela fonctionne, mais cela brise l'analyse d'un tas de petits sous-programmes, et il est très difficile de voir ce que la grammaire représente réellement.
Je pense qu'il serait beaucoup plus facile d'écrire si je pouvais spécifier la syntaxe optionnelle entre parenthèses et la répétition avec un * ou +. Cela réduirait mes règles whereClause et joinList à ce qui suit:
whereClause:
| { None }
// $1 $2, I envision $2 being return as an (ID * op * ID * cond) list
| WHERE [ ID op ID { (AND | OR) }]+
{ Some([for name1, op, name2, _ in $1 -> name1, op, name2]) }
joinClause:
| { None }
// $1, returned as (joinType
// * JOIN
// * ID
// * ((ID * op * ID) list) option) list
| [ (INNER | LEFT | RIGHT) JOIN ID [ON whereClause] ]*
{ let joinType, _, table, onClause = $1;
Some(table, joinType, onClause) }
Je pense que ce formulaire est beaucoup plus facile à lire et exprime la grammaire, il essaie de capturer de manière plus intuitive. Malheureusement, je ne trouve rien dans la documentation Ocaml ou F # qui supporte cette notation ou quoi que ce soit de similaire. Y at-il un moyen facile de représenter les grammaires avec des jetons optionnels ou répétitifs dans OcamlYacc ou FsYacc?
Existe-t-il un moyen facile de représenter les grammaires avec des jetons optionnels ou répétitifs dans OcamlYacc ou FsYacc?
My SQL est rouillé, mais je crois me souvenir que l'utilisation ou ON OU n'est pas strictement équivalent dans tous les cas (pour les jointures externes, si je me souviens bien). – Benjol