Using the Razor parser outside of ASP.Net

When Scott Guthrie originally blogged about Razor, he mentioned that it was fully hostable outside of ASP.Net.  The engine itself is not quite as detached from System.Web as we’d like, but it’s close and we’re going to get it way closer in the next release.

Having said that, you can still host Razor outside of the ASP.Net pipeline with the current beta! It’s a little trickier, and you do technically need to reference System.Web.  I’ve written a sample console app that I’m attaching to this post called “rzrc” which takes in  a .cshtml or .vbhtml Razor file and runs it through the parser and code generator to produce a .cs or .vb file.  I’ll walk through the main logic here and go over what each section does.

However, I was not the first to do this! Full credit for that goes to Gustavo Machado, who wrote an excellent post in which he used Reflector to work out how to run the Razor parser and code generator.  Well done Gustavo!  There are a few things that this version does that Gustavo’s doesn’t, such as cleaning up the Web-related stuff in the generated code and selecting the language based on the Code Language, but he basically hit it spot on!

The first thing my console app does is get the input file name, extract the extension and look up what Razor Code Language it uses.  This is done using the CodeLanguageService class, which is part of the Razor APIs:

CodeLanguageService languageService = CodeLanguageService.GetServiceByExtension(extension);
if (languageService == null) {
    Console.WriteLine("{0} is not a Razor code language", extension);
    return;
}

Then, we fire up the parser and the code generator.  A CodeLanguageService is basically a factory for constructing a Code Parser, to parse the code blocks after an “@” and a matching Code Generator to write the final C# or VB class.

InlinePageParser parser = new InlinePageParser(languageService.CreateCodeParser(), new HtmlMarkupParser());
CodeGenerator codeGenerator = 
    languageService.CreateCodeGeneratorParserListener(className,
                                                        rootNamespaceName: "Template", 
                                                        applicationTypeName: "object", 
                                                        inputFileName, 
                                                        baseClass: "System.Object");

When you run the Razor Parser, you must provide it with an object implementing IParserConsumer.  This interface has callbacks which the parser will call when it encounters various Razor constructs (more details on the Razor parse tree later).  CodeGenerator implements this interface and responds to the these callbacks by generating code.  However, it does nothing with the errors, so in the console app, I’ve written a very simple IParserConsumer called CustomParserConsumer which wraps the code generator and outputs errors to the console.  I won’t put the code here, but it’s in the sample, so take a look there if you’re interested.

Now that we’ve got all the objects we need, we can actually run the parser over the input

CustomParserConsumer consumer = new CustomParserConsumer() { CodeGenerator = codeGenerator };
using (StreamReader reader = new StreamReader(inputFileName)) {
    parser.Parse(reader, consumer);
}

Once Parse returns, the Code Generator will have built a CodeDOM tree representing the generated code during the callbacks, so we know that our code is ready to go.  Right now, the Code Generator adds in some web specific things.  For example, when we constructed the Code Gneerator above, we gave it an “applicationTypeName” which (in a web context) is the type name of the class defined in Global.asax, if there is one.  Since we are trying to generate a template that isn’t related to the web, we can get the CodeDOM tree from the Code Generator and remove these things.

codeGenerator.GeneratedCode.Namespaces[0].Types[0].Members.RemoveAt(0);
codeGenerator.GeneratedCode.Namespaces[0].Types[0].BaseTypes.Clear();
codeGenerator.GeneratedCode.Namespaces[0].Imports.Clear();

Finally, we use the CodeDOM to write the code to a C# or VB class file (provider is a CodeDomProvider from System.CodeDom.Compiler):

using (StreamWriter writer = new StreamWriter(outputFile)) {
    provider.GenerateCodeFromCompileUnit(codeGenerator.GeneratedCode, writer, new CodeDom.CodeGeneratorOptions());
}

And we’re done!  This is definitely more complicated than we’d like, but there are plans to simplify this API significantly in future releases.  For the most part, all we’ve done is left the methods our ASP.Net Build Provider uses open and accessible.  I wouldn’t bet on these APIs staying around too long, but any API changes from here on should be simplifications.  For now though, check out the sample I’ve attached and play around!  Note that you must have WebMatrix installed to use the sample. 

I’ve put some comments in which start with “EXT” which contain tips on how to extend this code to your own use.  Please feel free to take this code and use it absolutely anywhere you want!  Let me know how your using Razor by either tweeting me at @anurse or email me at andrew AT andrewnurse DOT net.

Download the console app here: rzrc.zip (3.46 KB)

Introducing Razor – A New View Engine for ASP.Net

UPDATE: Fixed broken examples (I hope :)).

Earlier this morning, Scott Guthrie blogged about a new View Engine we’re developing for ASP.Net.  As many of my readers know, I joined the ASP.Net team back in October in 2009, and I’m really excited to finally be able to share what I have been working on for the past 8 months.  When I joined Microsoft, I was shown some early prototypes for this new syntax and over the course of the next 8 months, we developed it into the Beta we’re going to be releasing very soon.

Writing the parser for Razor has essentially been my job for the last 8 months, so I’d like to describe a little about some of the design ideas that went in to it as well as some of the interesting ways we implemented things.  This is the first in a few blog posts I’ll do about the Razor syntax as well as the Parser design.

Razor syntax is designed around one primary goal: Make code and markup flow together with as little interference from control characters as possible.  For example, let’s take the following ASPX:

<ul>
    <% foreach(var p in Model.Products) { %>
    <li><%= p.Name %> ($<%= p.Price %>)</li>
    <% } %>
</ul>

Now, let's boil it down to the parts that we actually care about, removing all of the extra ASPX control characters:

<ul>
    foreach(var p in Model.Products) {
    <li>p.Name ($p.Price)</li>
    }
</ul>

Obviously there isn't enough data here to unambiguously determine what's code and what's markup. When we were designing Razor, we started from here and added as little as we could to make it absolutely clear what is code and what is markup. We wanted Razor pages to be Code+Markup, with a little extra stuff as possible. We even used that goal as inspiration for the file extension for C# and VB Razor pages: cshtml and vbhtml.

So, using the C# Razor syntax, the above example becomes:

<ul>
    @foreach(var p in Model.Products) {
    <li>@p.Name ($@p.Price)</li>
    }
</ul>

If you ask me, that's pretty darn close to the previous example. Razor takes advantage of a deep knowledge of C# (or VB) and HTML syntaxes to infer a lot about what you intended to write. Let’s take this sample and break it down chunk by chunk to see how Razor parses this document.

<ul>

When Razor starts parsing a document anything goes until we see an "@". So this line just gets classified as Markup and we move on to the next

@foreach(var p in Model.Products) {

Here's where things get interesting. Now, Razor has found an "@". The "@" character is the magic character in Razor. One character, not 5 “<%=%>”, and we let the parser figure out the rest. If you skip ahead a bit, you’ll notice that there's nothing that indicates the end of the block of code (like the "%>" sequence in ASPX). Rather than having it's own syntax for delimiting code blocks, Razor tries to add as little as possible and just uses the syntax of the underlying language to determine when the code block is finished. In this case, Razor knows that a C# foreach statement is contained within "{" and "}" characters, so when you reach the end of the foreach block, it will go back to markup

<li>@p.Name ($@p.Price)</li>

Now, things get even more interesting. Didn't I just say we were in Code until the ending of the foreach? This looks a lot like Markup, and we’re still inside the foreach! This is another case where Razor is using the syntax of the underlying language to infer your intent. We know that after the "{" C# is expecting some kind of statement. But, instead of a statement, we see an HTML tag "<li>", so Razor infers that you intended to switch back to Markup. So we've essentially got a stack of 3 contexts: When we started out we were in Markup, then we saw @foreach so we went to Code, now we've see <li> so were back in Markup. At the closing </li> tag, we know you've finished the inner Markup block, so we go back to the body of the foreach.

}

Then we see the end of the foreach block, so we go back to the top-level Markup context.

</ul>

And we continue parsing markup until the next "@", or the end of the file. You may have noticed I skipped over a bit in the middle of the <li> tag. I'll save the details of that for my next post, but the essential logic is the same: "@" starts code, and we use C# syntax to tell us when that code block is finished.

It's great to finally be able to share the result of our hard work with everyone. We've worked really hard to try to create a really clean syntax for mixing code and markup and I know I’d love to hear your feedback. Post in the comments on my blog, send me a tweet at "@anurse" or send me email at "andrew AT andrewnurse DOT net".

And I know you're all probably eagerly awaiting a chance to try this out. Don’t worry, we'll have a public beta soon that you can try out!