Click here to Skip to main content
15,897,187 members
Articles / Programming Languages / C#

Generate AST for the DLR

Rate me:
Please Sign up or sign in to vote.
4.75/5 (13 votes)
1 Aug 2008CPOL7 min read 48.3K   365   47  
Learn how to generate your own code for the DLR using an Abstract Syntax Tree.
using System;
using System.Collections.Generic;
using System.Reflection;

using Microsoft.Scripting;
using Microsoft.Scripting.Ast;
using AstUtils = Microsoft.Scripting.Ast.Utils;
using Microsoft.Scripting.Runtime;


namespace ADT.DLR
{
    /// <summary>
    /// Language Context for ASTDLRTest.
    /// </summary>
    public class ADTLanguageContext : LanguageContext
    {
        /// <summary>
        /// Constructor, call parent.
        /// </summary>
        /// <param name="manager"></param>
        public ADTLanguageContext(ScriptDomainManager manager) : base(manager)
        {
            Binder = new ADTBinder(manager);
        }

        /// <summary>
        /// Dummy span
        /// </summary>
        private SourceSpan span = new SourceSpan(new SourceLocation(255, 255, 255), new SourceLocation(255, 255, 255));

        /// <summary>
        /// Parse a source code and return an AST, here only return the test AST.
        /// </summary>
        /// <param name="context"></param>
        /// <returns>an AST</returns>
        public override LambdaExpression ParseSourceCode(CompilerContext context)
        {
            // Create lambda expression
            LambdaBuilder program = AstUtils.Lambda(typeof(object), "ASTDLRTest");

            // Add statements
            program.Body = AstUtils.Block(span, GetStatements(program));

            // Return result
            return program.MakeLambda();
        }

        /// <summary>
        /// Get statements.
        /// </summary>
        /// <returns>statements</returns>
        private List<Expression> GetStatements(LambdaBuilder program)
        {
            List<Expression> statements = new List<Expression>();

            #region n = 4; r = n;
            // n = 4
            Expression n = program.CreateLocalVariable("n", typeof(int));
            statements.Add(
                Expression.Assign(
                    n,
                    Expression.Constant(4)
                )
            );

            // r = n
            Expression r = program.CreateLocalVariable("r", typeof(int));
            statements.Add(
                Expression.Assign(
                    r,
                    n
                )
            );
            #endregion

            #region r = r * (n - 1);
            // r = r * (n - 1)
            statements.Add(
                Expression.Assign(
                    r,
                    Expression.Multiply(
                        r,
                        Expression.Subtract(
                            n,
                            Expression.Constant(1)
                        )
                    )
                )
            );
            #endregion

            #region Console.WriteLine(r);
            // Console.WriteLine(r);
            MethodInfo consoleWriteLine = typeof(Console).GetMethod("WriteLine", new Type[] { typeof(int) });
            statements.Add(
                Expression.Call(
                    consoleWriteLine,
                    r
                )
            );
            #endregion

            #region while(n>1) { r = r * (n-1); n = n - 1; }
            // while(n>1) { r = r * (n-1); n = n - 1; }
            statements.Add(
                AstUtils.While(
                    Expression.GreaterThan(
                        n,
                        Expression.Constant(1)
                    ),
                    Expression.Block(
                        Expression.Assign(r, Expression.Multiply(r, Expression.Subtract(n, Expression.Constant(1)))),
                        Expression.Assign(n, Expression.Subtract(n, AstUtils.Constant(1)))
                    ),
                    AstUtils.Empty(span)
                )
            );
            #endregion

            #region Console.WriteLine(fact(5));
            // fact = function(n) { ... }
            Expression fact = program.CreateLocalVariable("fact", typeof(object));
            statements.Add(
                Expression.Assign(
                    fact,
                    factorialExpression()
                )
            );

            // fact(5)
            Expression.Action.Call(
                this.Binder,
                typeof(int),
                Expression.CodeContext(),
                fact,
                Expression.Constant(5)
            );

            // Console.WriteLine(fact(5))
            statements.Add(
                Expression.Call(
                    consoleWriteLine,
                    Expression.Action.Call(
                        this.Binder,
                        typeof(int),
                        Expression.CodeContext(),
                        fact,
                        Expression.Constant(5)
                    )
                )
            );
            #endregion

            return statements;
        }

        /// <summary>
        /// Build lambda expression for the factorial function.
        /// </summary>
        /// <returns>the lambda expression</returns>
        private Expression factorialExpression()
        {
            // Create lambda expression
            LambdaBuilder function = AstUtils.Lambda(typeof(int), "fact");

            // Declare parameter
            Expression n = function.CreateParameter("n", typeof(int));

            // Create statements
            List<Expression> statements = new List<Expression>();

            // r = n
            Expression r = function.CreateLocalVariable("r", typeof(int));
            statements.Add(
                Expression.Assign(
                    r,
                    n
                )
            );

            // while(n>1) { r = r * (n-1); n = n - 1; }
            statements.Add(
                AstUtils.While(
                    Expression.GreaterThan(
                        n,
                        Expression.Constant(1)
                    ),
                    Expression.Block(
                        Expression.Assign(r, Expression.Multiply(r, Expression.Subtract(n, Expression.Constant(1)))),
                        Expression.Assign(n, Expression.Subtract(n, AstUtils.Constant(1)))
                    ),
                    AstUtils.Empty(span)
                )
            );

            // return r
            statements.Add(
                Expression.Return(r)
            );

            // Add statements
            function.Body = AstUtils.Block(span, statements);

            // Return result
            return function.MakeLambda();
        }
    }
}

By viewing downloads associated with this article you agree to the Terms of Service and the article's licence.

If a file you wish to view isn't highlighted, and is a text file (not binary), please let us know and we'll add colourisation support for it.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
Architect C2S
France France
Lionel is a software architect at C2S, a software company based in France and subsidiary of the Bouygues group.
Lionel is also the author of Liogo, an open-source Logo compiler for .NET.
Lionel is a contributor of DotNetGuru and Dr.Dobb's Journal.
Lionel is President and co-founder of OLPC France.

Comments and Discussions