Prometi na palestra sobre MDD e Oslo neste TechEd colocar neste blog o código do exemplo de como usar uma gramática Mgrammar no .Net. Pois aqui vai....
A gramática que mostrei na demo é simples. Ela lê textos da forma “X tem Y anos.”. Um exemplo seria:
“Otavio tem 50 anos.
Paula tem 29 anos.”
Para isto, preciso declarar a gramática de reconhecimento em uma variável da forma:
static private string contactGrammar = @" module lingContato { language Contatos { syntax Main = c:Contato * => Contatos { valuesof(c) }; syntax Contato = n:Nome ""tem"" i:Idade ""anos."" => { Nome = n, Idade=i }; token Nome = ('A'.. 'Z' | 'a'..'z')+; token Idade = '0'..'9'+; interleave espaco = ' ' | '\r' | '\n' | '\r\n'; } }";
O passo 2 é compilar a gramática e criar o “parser”:
CompilationResults resultsContatos = Compiler.Compile( new CompilerOptions { Sources = { new TextItem { Reader = new StringReader(contactGrammar), ContentType = TextItemType.MGrammar } } } ); DynamicParser parserContatos = resultsContatos.ParserFactories["lingContato.Contatos"].Create(); parserContatos.GraphBuilder = new NodeGraphBuilder();
CompilationResults resultsContatos = Compiler.Compile( new CompilerOptions { Sources = { new TextItem { Reader = new StringReader(contactGrammar), ContentType = TextItemType.MGrammar } } } );
DynamicParser parserContatos = resultsContatos.ParserFactories["lingContato.Contatos"].Create();
parserContatos.GraphBuilder = new NodeGraphBuilder();
O passo 3 é fazer o “parsing”do texto de entrada (na variável inText), gerando um grafo que representa a AST (Abstract Syntax Tree).
Node nContato = (Node)parserContatos.Parse(new StringTextStream(inText), null);
Por fim, estamos prontos para navegar o texto e preencher uma lista do tipo Contato:
Contato c = new Contato(); foreach (Node record in tree.ViewAllNodes() ) { foreach (var member in record.Edges ) { string s = member.Label + ": " + member.Node; if (member.Label == "Idade") { c.Idade = member.Node.ToString(); lc.Add(c); c = new Contato(); saida += s + " \r\n"; } else { c.Nome = member.Node.ToString(); saida += s + " "; } } }
Contato c = new Contato();
foreach (Node record in tree.ViewAllNodes() ) { foreach (var member in record.Edges ) { string s = member.Label + ": " + member.Node;
if (member.Label == "Idade") { c.Idade = member.Node.ToString(); lc.Add(c); c = new Contato(); saida += s + " \r\n"; } else { c.Nome = member.Node.ToString(); saida += s + " "; }
} }
onde Contato é dado pela classe:
public class Contato { public string Nome { get; set; } public string Idade { get; set; } }
public class Contato {
public string Nome { get; set; }
public string Idade { get; set; }
}
Você vai precisar destas declarações de namespaces do Oslo (no diretório bin da instalação do SDK dele):
using Microsoft.M; using Microsoft.M.Parser; using Microsoft.M.Grammar; using System.Dataflow;
Aqui abaixo vocês podem ver como fica esta exemplo no Intellipad do Oslo.
Na esquerda desta figura temos exemplos de frases de entrada. No meio, a gramática que consegue analisar sintaticamente este texto de entrada. Na esquerda vocês vêem a saida depois da análise. É um texto com chaves que representa uma AST (ou grafo).
Para quem não viu, temos um webCast similar em: https://msevents.microsoft.com/CUI/WebCastEventDetails.aspx?culture=pt-BR&EventID=1032416175&CountryCode=BR
Vale a pena também ler os artigos:
Textual Domain Specific Languages for Developers - Part 1 Textual Domain Specific Languages for Developers - Part 2 Textual Domain Specific Languages for Developers - Part 3
Textual Domain Specific Languages for Developers - Part 1
Textual Domain Specific Languages for Developers - Part 2
Textual Domain Specific Languages for Developers - Part 3
Creio que com isto você consegue criar o seu parser, não consegue?
Promessa é divida. Com isto acabei de pagar a minha!
Abraços,
Otavio