sabato 25 maggio 2013

Linguaggi e Traduttori con Visual Studio

Attenzione, il NuGet package YaccLexTools è stato aggiornato. Leggi il post relativo all'aggiornamento ed alle semplificazioni introdotte: Yacc/Lex Tools v0.2
A volte è bello dare un senso a ciò che si ha studiato in passato e questo è il caso dell'esame di Linguaggi e Traduttori.

Mi è venuto in mente di potenziare la funzionalità di ricerca veloce di una applicazione web. Normalmente questa ricerca veloce prende tutte le parole inserite e fa una ricerca Full-Text su tutti i dati.
Quindi, mi sono detto, perché non dare la possibilità agli utenti di inserire delle piccole espressioni per definire in modo semplice dei filtri sulla ricerca?

Come ad esempio:

anno: 2000
anno: >= 2000
anno: 2000..2010 mese: > 6

Detto fatto! Quello di cui abbiamo bisogno in questo caso sono un analizzatore sintattico ed un parser.

Ad oggi non è molto pratico creare a mano questi strumenti, ma ci si avvale di generatori automatici. Quelli che io ho preso in considerazione sono GPPG per generare il parser e GPLEX per generare l'analizzatore sintattico, entrambi creati dal QUT, università australiana, in collaborazione con Microsoft.
GPPG genera un parser in linguaggio C# partendo da una descrizione formale della grammatica scritta in YACC, menrte GPLEX genera un analizzatore sintattico a partire dalla descrizione in LEX della sintassi del linguaggio.

Premesso che il lettore abbia già una consocenza di come scrivere una grammatica in YACC ed un analizzatore sintattico in LEX, l'obbiettivo di questo post è di guidare alla configurazione del progetto in Visual Studio per poter generare il parser e lo scanner durante il build del progetto stesso.

Prima di tutto si deve aggiungere il NuGet package "YACC/LEX tools" con il comando

 PM> Install-Package YaccLexTools 

Adesso supponiamo di voler creare il parser per le query di ricerca come descritte sopra.

1) Clicca con il tasto destro sul progetto e seleziona "Scarica Progetto"
2) Clicca con il tasto destro sul progetto e seleziona "Modifica "
3) Aggiungi il seguente frammento alla fine del file, prima della chiusura del tag </Project>
e salva.

  <Target Name="BeforeBuild" DependsOnTargets="BuildGen">
  </Target>
  <!-- Build generated file target -->
  <Target Name="BuildGen" DependsOnTargets="GenerateMyLanguageParser">
  </Target>
  <!-- Parser items -->
  <PropertyGroup>
    <ToolsDir>$(SolutionDir)packages\YaccLexTools.0.1.2\tools\</ToolsDir>
    <GplexTool>"$(ToolsDir)gplex.exe"</GplexTool>
    <GppgTool>"$(ToolsDir)gppg.exe"</GppgTool>
    <MyLanguageParser>$(ProjectDir)MyLanguage</MyLanguageParser>
  </PropertyGroup>
  <ItemGroup>
    <None Include="MyLanguage.parser" />
    <Compile Include="MyLanguage.Parser.cs">
      <DependentUpon>MyLanguage.parser</DependentUpon>
    </Compile>
    <Compile Include="MyLanguage.Scanner.cs">
      <DependentUpon>MyLanguage.parser</DependentUpon>
    </Compile>
    <None Include="MyLanguage.Language.grammar.y">
      <DependentUpon>MyLanguage.parser</DependentUpon>
    </None>
    <Compile Include="MyLanguage.Parser.Generated.cs">
      <AutoGen>True</AutoGen>
      <DesignTime>True</DesignTime>
      <DependentUpon>MyLanguage.Language.grammar.y</DependentUpon>
    </Compile>
    <None Include="MyLanguage.Language.analyzer.lex">
      <DependentUpon>MyLanguage.parser</DependentUpon>
    </None>
    <Compile Include="MyLanguage.Scanner.Generated.cs">
      <AutoGen>True</AutoGen>
      <DesignTime>True</DesignTime>
      <DependentUpon>MyLanguage.Language.analyzer.lex</DependentUpon>
    </Compile>
  </ItemGroup>
  <ItemGroup />
  <!--  Generate the parsers -->
  <Target Name="GenerateMyLanguageParser"
          Inputs="$(MyLanguageParser).Language.analyzer.lex;$(MyLanguageParser).Language.grammar.y" 
          Outputs="$(MyLanguageParser).Scanner.Generated.cs;$(MyLanguageParser).Parser.Generated.cs">
    <Message Text="Generating scanner for $(MyLanguageParser) ..." />
    <Exec Command="$(GplexTool) &quot;/out:$(MyLanguageParser).Scanner.Generated.cs&quot; &quot;$(MyLanguageParser).Language.analyzer.lex&quot;" 
          WorkingDirectory="$(ProjectDir)" 
          Outputs="$(GenDir)Scanner.cs">
      <Output TaskParameter="Outputs" ItemName="MyLanguageScanner" />
    </Exec>
    <Message Text="Generating parser for $(MyLanguageParser) ..." />
    <Exec Command="$(GppgTool) /no-lines /gplex &quot;$(MyLanguageParser).Language.grammar.y&quot; > &quot;$(MyLanguageParser).Parser.Generated.cs&quot;" 
          WorkingDirectory="$(ProjectDir)" 
          Outputs="$(MyLanguageParser).Parser.Generated.cs">
      <Output TaskParameter="Outputs" ItemName="MyLanguageParser" />
    </Exec>
  </Target>

4) Copia i files contenuti nella cartella "packages\YaccLexTools.0.1.2\src" nella cartella del progetto.
5) Clicca con il tasto destro sul progetto e seleziona "Ricarica Progetto".

Quando il progetto sarà ricaricato, questo è quello che sarà presente nel Solution Explorer di Visual Studio:



Nella figura qui sopra ci sono tutti i files che compongono il parser.
  • MyLanguage.parser è solo un segna posto e non contiene nulla di interessante. Serve solo per raggruppare in un unico nodo tutti i files del parser.
  • MyLanguage.Language.analyzer.lex è il sorgente in LEX dell'analizzatore sintattico.
  • MyLanguage.Scanner.Generated.cs è il file C# generato dal sorgente LEX "MyLanguage.Language.analyzer.lex".
  • MyLanguage.Language.grammar.y è il sorgente in YACC del parser. In esso viene descritta la grammatica.
  • MyLanguage.Parser.Generated.cs è il file C# generato dal sorgente YACC "MyLanguage.Language.grammar.y".

Infine i files MyLanguage.Parser.cs e MyLanguage.Scanner.cs sono classi parziali C# che vanno ad estendere rispettivamente il parser e lo scanner generato dai files YACC e LEX per aggiungervi i metodi chiamati nelle azioni delle regole grammaticali e sintattiche.


Note finali.
Per la realizzazione di tutto questo è stato molto utile poter vedere il codice Open Source di progetti di compilatori come per esempio IronRuby. Se non avessi potuto guardare dentro al codice di IronRuby non avrei mai fatto una cosa del genere. Quello che voglio dire è che si può imparare molto, ma anche trovare ispirazione, confrontandosi con altri ;)


Nessun commento:

Posta un commento

Etichette

ajax (1) C# (3) C++ (1) click (1) clickout (1) compilatori (2) gplex (2) gppg (2) html (1) i18n (1) internazionalization (1) javascript (2) jquery (1) lex (2) object oriented (1) plugin (1) programmazione (3) software (4) svg (1) VB (1) vector graphics (1) visual studio (2) vrml (1) web (1) Win32 (1) windows 8 (1) WinRT (1) XAML (1) yacc (2) yacclextools (1)