Alsalam alikom wa ra7mat Allah wa barakatoh (Peace Upon You)
Part II: Consume the Abstract Syntax Tree … Do some action! …
If you have not read Part I, we have created our first grammar to recognize this language:
Send "D:\Reports\Templates\Regular.tmpl" to sm1@hotmail.comSend "D:\Reports\Templates\Special.tmpl" to sm2@hotmail.com,sm3@hotmail.com
Visit here [Part I link] to find the reporter.mg file listing at the end.
In this post, we will explore how to compile/parse the generated tree programmatically. And we will dig a little bit one cool way to do .NET code generation works.
private static void Compile(){ MGrammarCompiler compiler = new MGrammarCompiler(); compiler.FileNames = new string[] { @"C:\Users\HaythamAlaa\Documents\reporter.mg" }; compiler.OutFile = "reporter.mgx"; compiler.Execute(ErrorReporter.Standard);}
private static void Parse(){ FileStream mgxFile = File.OpenRead("reporter.mgx"); DynamicParser parser = DynamicParser.LoadFromMgx(mgxFile, "Basic.Languages.Reporter"); object root = parser.Parse<object>( @"C:\Users\HaythamAlaa\Documents\reportsData.bundle", null, ErrorReporter.Standard); IGraphBuilder graphBuilder = parser.GraphBuilder; Traverse(root, graphBuilder);
private static void Traverse(object root, IGraphBuilder graphBuilder){ if (root is string) { Console.WriteLine(root.ToString()); return; } Console.WriteLine("->" + graphBuilder.GetLabel(root)); foreach (object childNode in graphBuilder.GetSuccessors(root)) { Traverse(childNode, graphBuilder); }}
->Commands->SendD:\Reports\Templates\Regular.tmpl->Emailssm1@hotmail.com->SendD:\Reports\Templates\Special.tmpl->Emailssm2@hotmail.comsm3@hotmail.com
=> Send{. . .};
Send{...}
=> Send[. . .];
Send[.....]
public static void Send(string reportPath, params string[] contacts){ Console.WriteLine(reportPath); foreach (string str in contacts) Console.WriteLine("->" + str);}
MethodCallExpression expression = Expression.Call(typeof(Program).GetMethod("Send"), new Expression[] { Expression.Constant(path, typeof(string)), Expression.Constant(emails, typeof(string[])) });LambdaExpression lambda = Expression.Lambda(expression);lambda.DynamicInvoke();
1: private static LambdaExpression[] TraverseAndBuildTree(object root,
2: IGraphBuilder graphBuilder)
3: {
4: if (graphBuilder.GetLabel(root).ToString().ToLower() == "send")
5: {
6: IEnumerable<object> childs = graphBuilder.GetSuccessors(root);
7:
8: string path =
9: graphBuilder.GetSuccessors(childs.First()).First().ToString(
10: List<string> emails = new List<string>();
11: foreach (object objContact in
12: graphBuilder.GetSequenceElements(childs.Last()))
13: {
14: emails.Add(objContact.ToString());
15: }
16:
17: MethodCallExpression expression =
18: Expression.Call(typeof(Program).GetMethod("Send"),
19: new Expression[] { Expression.Constant(path, typeof(string)),
20: Expression.Constant(emails.ToArray(), typeof(string[])) })
21: LambdaExpression lambda = Expression.Lambda(expression);
22: return new LambdaExpression[] { lambda };
23: }
24:
25: List<LambdaExpression> expressions = new List<LambdaExpression>();
26: if (graphBuilder.IsSequence(root))
27: foreach (object childNode in
28: graphBuilder.GetSequenceElements(root))
29: {
30: expressions.AddRange(
31: TraverseAndBuildTree(childNode, graphBuilder));
32: }
33: else if (graphBuilder.IsNode(root))
34: foreach (object childNode in
35: graphBuilder.GetSuccessors(root))
36: {
37: expressions.AddRange(
38: TraverseAndBuildTree(childNode, graphBuilder));
39: }
40: return expressions.ToArray();
41: }
LambdaExpression[] lambdas = TraverseAndBuildTree(root, graphBuilder);foreach (LambdaExpression lambda in lambdas) lambda.Compile().DynamicInvoke();
Congrats, you’ve reached your 2nd Checkpoint! The technique described here works well with simple languages, but once your language gets more complicated, you will need a more structured design (and hopefully an automated process) which we will explore in the next part.