lunedì 12 agosto 2013

Yacc/Lex Tools v0.2

In un precedete post (Linguaggi e Traduttori con Visual Studio) ho illustrato come aggiungere un parser al proprio progetto C# utilizzando i tools GPPG e GPLEX per compilare i files YACC e LEX per generare rispettivamente il parser ed il relativo scanner.

Nel post precedente veniva illustrato come modificare a mano il file di progetto dopo aver installato il NuGet package YaccLexTools.

Il package "YaccLexTools" è stato aggiornato e dalla versione 0.2 non è più necessario modificare manualmente i tag XML del file di progetto. Con questa nuova versione è possibile gestire il tutto grazie a nuovi Cmd-Let PowerShell azionabili dal "Package Manager Console" di Visual Studio.

Ad esempio, per aggiungere un parser al tuo progetto sarà necessario eseguire il comando:

 PM> Add-Parser MyLanguage 

Con questo comando verrano creati tutti i files ed i settings necessari per la compilazione dei files YACC e LEX.


Se si vuole specificare un namespace personalizzato si potrà eseguire il seguente comando il cui secondo parametro è proprio il namespace:

 PM> Add-Parser MyLanguage Sample.NameSpace 

Per rimuovere i settings di un parser creato nel progetto si potrà utilizzare il seguente comando:

 PM> Remove-Parser MyLanguage 

Con questo comando non verranno rimossi i files, ma solo i settings per il parser specificato. I files dovranno essere rimossi manualmente.

Infine nel package è incluso un esempio funzionante per interpretare delle espressioni di calcolo. Per aggiungere al progetto questo esempio è sufficiente utilizzare il comando

 PM> Add-CalculatorExample 


Conclusioni.
In questo post sono state illustrate le semplificazioni implementate nella nuova versione del NuGet package YaccLexTools per la gestione di parsers all'interno di progetti C#.

Con la speranza che questi aggiornamenti siano utili a chi già usa il package e tutti coloro che lo utilizzeranno,

Ernesto

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 ;)


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)