Skip to content

Instantly share code, notes, and snippets.

@elfalem
Created September 23, 2019 20:36
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save elfalem/b7befb7a91ddb5ba0f12d8a156b54179 to your computer and use it in GitHub Desktop.
Save elfalem/b7befb7a91ddb5ba0f12d8a156b54179 to your computer and use it in GitHub Desktop.
Alternative view engine for ASP.NET Core: Parsing template syntax
using System.Collections.Generic;
using System.Linq;
using Superpower;
using Superpower.Parsers;
using Superpower.Tokenizers;
namespace foo.Stache
{
public enum Tokens
{
String,
OpenBraces,
CloseBraces
}
public abstract class ParseNode
{
}
public class TextNode : ParseNode
{
public string Value { get; set; }
}
public class ExpressionNode : ParseNode
{
public string Value { get; set; }
}
public class DocumentNode : ParseNode
{
public List<ParseNode> Nodes { get; set; }
}
public class StacheParser
{
public static Tokenizer<Tokens> Tokenizer = new TokenizerBuilder<Tokens>()
.Match(Span.EqualTo("{{"), Tokens.OpenBraces)
.Match(Span.EqualTo("}}"), Tokens.CloseBraces)
// Note: the following ignores single braces, e.g. it would fail on {{foo{bar}}
.Match(Span.MatchedBy(Character.ExceptIn('{','}')).Many(), Tokens.String)
.Build();
private readonly static TokenListParser<Tokens, ParseNode> ExpressionParser =
from ob in Token.EqualTo(Tokens.OpenBraces)
from str in Token.EqualTo(Tokens.String)
from cb in Token.EqualTo(Tokens.CloseBraces)
select (ParseNode)new ExpressionNode {
Value = str.ToStringValue()
};
private readonly static TokenListParser<Tokens, ParseNode> LiteralParser =
from str in Token.EqualTo(Tokens.String)
select (ParseNode)new TextNode {
Value = str.ToStringValue()
};
public readonly static TokenListParser<Tokens, ParseNode> MainParser =
from nodes in ExpressionParser.Or(LiteralParser).Many()
select (ParseNode)new DocumentNode {
Nodes = nodes.ToList()
};
}
}
using System;
using System.IO;
using System.Text;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Mvc.Rendering;
using Microsoft.AspNetCore.Mvc.ViewEngines;
using Superpower;
namespace foo.Stache{
public class StacheView : IView
{
public StacheView(string path){
Path = path;
}
public string Path {get; private set;}
public Task RenderAsync(ViewContext context)
{
var template = File.ReadAllText(Path);
var tokens = StacheParser.Tokenizer.Tokenize(template);
var parsedResult = StacheParser.MainParser.TryParse(tokens);
var processedOutput = new StringBuilder();
if(parsedResult.HasValue){
var document = (DocumentNode)parsedResult.Value;
foreach(var node in document.Nodes){
switch(node){
case TextNode textNode:
processedOutput.Append(textNode.Value);
break;
case ExpressionNode expNode:
processedOutput.Append(context.ViewData[expNode.Value]?.ToString());
break;
}
}
}else{
throw new Exception(parsedResult.ErrorMessage);
}
return context.Writer.WriteAsync(processedOutput.ToString());
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment