I've defined a simple grammar in Irony, and generated a nice compact AST.
Now I'm trying to figure out how to evaluate it. Problem is, I can't find any tutorials on how to do this.
I've defined just 2 AST nodes:
class TagListNode : AstNode
{
public override void Init(ParsingContext context, ParseTreeNode treeNode)
{
base.Init(context, treeNode);
AsString = "TagList";
foreach (var node in treeNode.ChildNodes)
AddChild(null, node);
}
public override void EvaluateNode(Irony.Interpreter.EvaluationContext context, AstMode mode)
{
foreach (var node in ChildNodes)
node.EvaluateNode(context, AstMode.Read);
}
}
class TagBlockNode : AstNode
{
public AstNode Content;
public override void Init(ParsingContext context,ParseTreeNode treeNode)
{
base.Init(context, treeNode);
AsString = treeNode.ChildNodes[0].FindTokenAndGetText();
Content = AddChild(null, treeNode.ChildNodes[1]);
}
public override void EvaluateNode(EvaluationContext context, AstMode mode)
{
context.Write(string.Format("<{0}>", AsString));
Content.EvaluateNode(context, AstMode.Read);
context.Write(string.Format("</{0}>", AsString));
}
}
This will generate the following output:
<html><head><title></title></head><body><h1></h1><p></p><p></p></body></html>3.14159265358979
Whereas the output I want is:
<html>
<head>
<title>page title</title>
</head>
<body>
<h1>header</h1>
<p>paragraph 1</p>
<p>3.14159265358979</p>
</body>
</html>
I don't think I'm supposed to be using Context.Write(). The samples show pushing stuff onto context.Data and popping them off... but I'm not quite sure how that works.
I'm guessing pi gets tacked on at the end because it's automatically pushed onto context.Data and then one element is popped off at the end?? I'm not really sure.
Some pointers or a link to a tutorial would be nice.
Also, how am I supposed to handle the different node types? Each "Tag" can have 4 different types of content: another tag, a string literal, a variable, or a number. Should I be writing things like if(node is StringLiteral) .... in the EvaluateNode method or what?
I've found this one but they just loop over the AST and don't take advantage of EvaluateNode.
And then this one which replaces a single value in the data stack...but doesn't really explain how this gets outputted or anything.
To be clear, I specifically want to know how to override the EvaluateNode methods in Irony.Ast.AstNode to do what I want.
Okay, I've traced that tidbit at the end to this line:
if (EvaluationContext.HasLastResult)
EvaluationContext.Write(EvaluationContext.LastResult + Environment.NewLine);
Which is included in the default evaluation routine....perhaps it works well for a calculator app, but not so much in mine. Trying to figure out how to bypass the script interpreter now, but then I don't know how to set the globals.
The best way to iterate through an AST structure is to implement the visitor pattern.
Maybe this link helps you.
Related
I am learning Blazor.
https://learn-blazor.com/
I am having some difficulty creating a component in C# by overriding the BuildRenderTree(RenderTreeBuilder builder) method of the BlazorComponent class.
Here is my class:
public class TestComponent : BlazorComponent
{
protected override void BuildRenderTree(RenderTreeBuilder builder)
{
builder.OpenElement(1, "p");
builder.OpenElement(2, "strong");
builder.AddContent(3, "hello");
builder.CloseElement();
builder.CloseElement();
base.BuildRenderTree(builder); // With or without this line it doesn't work
}
}
I use it in my page like this:
<TestComponent></TestComponent>
I have tested it with and without that last line base.BuildRenderTree(builder); but either way it doesn't render. I've made the component childishly simple (just a paragraph tag, strong tag and one word of content) so I'm lost as to why it won't render. The code builds just fine.
Can anybody see what I did wrong please?
First off don't use that web site anymore. I've learned Blazor through that web site. It is an excellent web site, and the guy did a superb work. But alas, he stopped updating it, at least for the last six months. The materials have become old and useless. Right now, I'm not aware of any good substitution.
Try this:
Place base.BuildRenderTree(builder); at the start of the BuildRenderTree method , not at the end.
Use ComponentBase instead of BlazorComponent...
BlazorComponent is dead...
Always start with the value 0 for the sequence parameter.
Background:
Using Roslyn with C#, I am trying to expand auto-implemented properties, so that the accessor bodies can have code injected by later processing. I am using StackExchange.Precompilation as the compiler hook, so these syntax transformations occur in the build pipeline, not as part of an analyzer or refactoring.
I want to turn this:
[SpecialAttribute]
int AutoImplemented { get; set; }
into this:
[SpecialAttribute]
int AutoImplemented {
get { return _autoImplemented; }
set { _autoImplemented = value; }
}
private int _autoImplemented;
The problem:
I have been able to get simple transformations working, but I'm stuck on auto-properties, and a few others that are similar in some ways. The trouble I'm having is in using the SyntaxNodeExtensions.ReplaceNode and SyntaxNodeExtensions.ReplaceNodes extension methods correctly when replacing more than one node in a tree.
I am using a class extending CSharpSyntaxRewriter for the transformations. I'll just share the relevant members of that class here. This class visits each class and struct declaration, and then replaces any property declarations that are marked with SpecialAttribute.
private readonly SemanticModel model;
public override SyntaxNode VisitClassDeclaration(ClassDeclarationSyntax node) {
if (node == null) throw new ArgumentNullException(nameof(node));
node = VisitMembers(node);
return base.VisitClassDeclaration(node);
}
public override SyntaxNode VisitStructDeclaration(StructDeclarationSyntax node) {
if (node == null) throw new ArgumentNullException(nameof(node));
node = VisitMembers(node);
return base.VisitStructDeclaration(node);
}
private TNode VisitMembers<TNode>(TNode node)
where TNode : SyntaxNode {
IEnumerable<PropertyDeclarationSyntax> markedProperties =
node.DescendantNodes()
.OfType<PropertyDeclarationSyntax>()
.Where(prop => prop.HasAttribute<SpecialAttribute>(model));
foreach (var prop in markedProperties) {
SyntaxList<SyntaxNode> expanded = ExpandProperty(prop);
//If I set a breakpoint here, I can see that 'expanded' will hold the correct value.
//ReplaceNode appears to not be replacing anything
node = node.ReplaceNode(prop, expanded);
}
return node;
}
private SyntaxList<SyntaxNode> ExpandProperty(PropertyDeclarationSyntax node) {
//Generates list of new syntax elements from original.
//This method will produce correct output.
}
HasAttribute<TAttribute> is an extension method I defined for PropertyDeclarationSyntax that checks if that property has an attribute of the given type. This method works correctly.
I believe I am just not using ReplaceNode correctly. There are three related methods:
TRoot ReplaceNode<TRoot>(
TRoot root,
SyntaxNode oldNode,
SyntaxNode newNode);
TRoot ReplaceNode<TRoot>(
TRoot root,
SyntaxNode oldNode,
IEnumerable<SyntaxNode> newNodes);
TRoot ReplaceNodes<TRoot, TNode>(
TRoot root,
IEnumerable<TNode> nodes,
Func<TNode, TNode, SyntaxNode> computeReplacementNode);
I am using the second one, because I need to replace each property node with both field and property nodes. I need to do this with many nodes, but there is no overload of ReplaceNodes that allows one-to-many node replacement. The only way I found around having that overload was using a foreach loop, which seems very 'imperative' and against the functional feel of the Roslyn API.
Is there a better way to perform batch transformations like this?
Update:
I found a great blog series on Roslyn and dealing with its immutability. I haven't found the exact answer yet, but it looks like a good place to start.
https://joshvarty.wordpress.com/learn-roslyn-now/
Update:
So here is where I'm really confused. I know that the Roslyn API is all based on immutable data structures, and the problem here is in a subtlety of how the copying of structures is used to mimic mutability. I think the problem is that every time I replace a node in my tree, I then have a new tree, and so when I call ReplaceNode that tree supposedly doesn't contain my original node that I want to replace.
It is my understanding that the way trees are copied in Roslyn is that, when you replace a node in a tree you actually create a new tree that references all the same nodes of the original tree, except the node you replaced and all nodes directly above that one. The nodes below the replaced node may be removed if the replacement node no longer references them, or new references may be added, but all the old references still point to the same node instances as before. I am pretty sure this is exactly what Anders Hejlsberg describes in this interview on Roslyn (20 to 23 min in).
So shouldn't my new node instance still contain the same prop instances found in my original sequence?
Hacky solution for special cases:
I was finally able to get this particular problem of transforming property declarations to work by relying on property identifiers, which will not change in any tree transformations. However, I would still like a general solution for replacing multiple nodes with multiple nodes each. This solution is really working around the API not through it.
Here is the special case solution:
private TNode VisitMembers<TNode>(TNode node)
where TNode : SyntaxNode {
IEnumerable<PropertyDeclarationSyntax> markedPropertyNames =
node.DescendantNodes()
.OfType<PropertyDeclarationSyntax>()
.Where(prop => prop.HasAttribute<SpecialAttribute>(model))
.Select(prop => prop.Identifier.ValueText);
foreach (var prop in markedPropertyNames) {
var oldProp = node.DescendantNodes()
.OfType<PropertyDeclarationSyntax>()
.Single(p => p.Identifier.ValueText == prop.Name);
SyntaxList<SyntaxNode> newProp = ExpandProperty(oldProp);
node = node.ReplaceNode(oldProp, newProp);
}
return node;
}
Another similar problem I am working with is modifying all return statements in a method to insert postcondition checks. This case cannot obviously rely on any kind of unique identifier like a property declaration.
When you do that:
foreach (var prop in markedProperties) {
SyntaxList<SyntaxNode> expanded = ExpandProperty(prop);
//If I set a breakpoint here, I can see that 'expanded' will hold the correct value.
//ReplaceNode appears to not be replacing anything
node = node.ReplaceNode(prop, expanded);
}
After the first replacing, node (your class for example) does not contains the original property anymore .
In Roslyn, everything is immutable, so the first replace should work for you, and the you have a new tree\node.
To make it work you can consider one of the following:
Build the result in your rewriter class, without changing the original tree, and when you finishing, replace all at once. In your case, its mean replace the class note at once. I think its good option when you want to replace statement (I used it when I wrote code to convert linq query (comprehension) to fluent syntax) but for all class, maybe it's not optimal.
Use SyntaxAnnotaion \ TrackNodes to find node after the tree has changed. With these options you can change the tree as you want and you can still keep track of the old nodes in the new tree.
Use DocumentEditor its let you do multiple changes to a document and then return a new Document.
If you need example for one of them, let me know.
Consider the following HTML fragment (_ is used for whitespace):
<head>
...
<link ... ___/>
<!-- ... -->
...
</head>
I'm using Html Agility Pack (HAP) to read HTML files/fragments and to strip out links. What I want to do is find the LINK (and some other) elements and then replace them with whitespace, like so:
<head>
...
____________
<!-- ... -->
...
</head>
The parsing part seems to be working so far, I get the nodes I'm looking for. However, HAP tries to fix the HTML content while I need everything to be exactly the same, except for the changes I'm trying to make. Plus, HAP seems to have quite a few bugs when it comes to writing back content that was read in previously, so the approach I want to take is let HAP parse the input and then I go back to the original input and replace content that I don't want.
The problem is, HtmlNode doesn't seem to have an input length property. It has StreamPosition which seems to indicate where reading of the node's content started within the input but I couldn't find a length property that'd tell me how many characters were consumed to build the node.
I tried using the OuterHtml propety but, unfortunately, HAP tries to fix the LINK by removing the ___/ part (a LINK element is not supposed to be closed). Because of this, OuterHtml.Length returns the wrong length.
Is there a way in HAP to get this information?
I ended up modifying the code of HtmlAgilityPack to expose a new property that returns the private _outerlength field of HtmlNode.
public virtual int OuterLength
{
get
{
return ( _outerlength );
}
}
This seems to be working fine so far.
If you want to achieve the same result without recompiling HAP, then use reflection to access the private variable.
I usually wouldn't recommend reflection to access private variables, but I recently had the exact same situation as this and used reflection, because I was unable to use a recompiled version of the assembly. To do this, create a static variable that holds the field info object (to avoid recreating it on every use):
private static readonly FieldInfo HtmlNodeOuterLengthFieldInfo = typeof(HtmlNode).GetField("_outerlength", BindingFlags.NonPublic | BindingFlags.Instance);
Then whenever you want to access the true length of the original outer HTML:
var match = htmlDocument.DocumentNode.SelectSingleNode("xpath");
var htmlLength = (int)HtmlNodeOuterLengthFieldInfo.GetValue(match);
Transformed #Xcalibur's answer into an extension method.
Note that HtmlNode has property OuterLength, but it isn't the same as it's private field _outerlength, which is what we need. (Reading other answers here I first thought that since 2013, HtmlAgilityPack already added the OuterLength as a public property, which they did, but after some testing I noticed it simply returns length of OuterHtml). So we can either rebuild the package from source to expose the field as a public property, or use an extension method with Reflection (which is slow).
Extension method
namespace HtmlAgilityPack
{
public static class HtmlDocumentExtensions
{
private static readonly System.Reflection.FieldInfo HtmlNodeOuterLengthFieldInfo =
typeof(HtmlNode).GetField("_outerlength", System.Reflection.BindingFlags.NonPublic
| System.Reflection.BindingFlags.Instance);
public static int GetOuterLengthInStream(this HtmlNode node) =>
(int)HtmlNodeOuterLengthFieldInfo.GetValue(node ??
throw new System.ArgumentNullException(nameof(node)));
}
}
Because HtmlNode already has property OuterLength, to avoid ambiguity I called the method GetOuterLengthInStream().
Usage
node.GetOuterLengthInStream()
I'm having trouble understanding how to render a collection as a drop down list.
If I have a model like:
public class AccountViewModel {
public string[] Country { get; set; }
}
I would like the string collection to render as a drop down list.
Using the html page helper InputFor doesn't seem to work. It simply render's a text box.
I've noticed that InputFor can reflect on the property type and render html accordingly. (Like a checkbox for a boolean field).
I also notice that FubuPageExtensions has methods for CheckBoxFor and TextBoxFor, but nothing equivalent to DropDownListFor.
I'm probably missing something quite fundamental in understanding html conventions in fubu.
Do I need to build the select tag myself? If so, what is the recommended approach to do it?
You are correct that (at the time I last looked) there is no FubuMVC.Core HTML extension method for generating select tags although you could use the HtmlTags library to generate a select tag via code.
As you touch upon in your question the correct way to attack this is likely with an HTML convention together with the HtmlTags library such as that demonstrated in the FubuMVC.Recipes example 'src/UI/HtmlConventionsWithPageExtensions'.
For example an enum generation example might be:
this.Editors
.If(e => e.Accessor.PropertyType.IsEnum)
.BuildBy(er =>
{
var tag = new HtmlTag("select");
var enumValues = Enum.GetValues(er.Accessor.PropertyType);
foreach (var enumValue in enumValues)
{
tag.Children.Add(new HtmlTag("option").Text(enumValue.ToString()));
}
return tag;
});
The FubuMVC.Recipes repository is quite new and still growing so there may be some better examples around but hope this gives you some ideas.
I've been using T4MVC (FYI: v2.6.62) for quite some time, and I've been slowly moving over our code to this way of working (less reliance on magic strings).
But I've had to stop because, for some reason, T4MVC is unable to translate objects into urls, and only seems to be able to work on primitive types (int/string/etc).
Here is an example:
Route breakdown:
/MyController/MyAction/{Number}/{SomeText}
Class:
namespace MyNamespace
{
public class MyClass
{
public int Number { get; set; }
public string SomeText { get; set; }
}
}
Controller:
public class MyController
{
public virtual ActionResult MyAction(MyClass myClass)
{
return View();
}
}
View:
<%= Html.Action(
T4MVC.MyController.Actions.MyAction(
new MyClass()
{
Number = 1,
SomeText = "ABC"
}
) %>
The end result is this:
/MyController/MyAction?myClass=MyNamespace.MyClass
and not
/MyController/MyAction/1/ABC
Does anyone else have this problem? Are T4MVC urls like this available?
Question also asked at the ASP.NET Forum.
Update (10/11/2012): the recently added support for Model Unbinders (see section 3.1 in the doc) should hopefully cover a lot of these cases.
Original answer:
Copying my reply from the forum thread:
Hmmm, I don't think this has come up yet. Maybe in most cases that people have Action methods that take an object, the object's values come from posted form data, rather than being passed on the URL? In such scenario, the question doesn't arise.
I think in theory T4MVC could be changed to support this. It would just need to promote all the object's top level properties as route values rather than try to use the object itself (obviously, the current behavior is bogus, and is a result of just calling ToString() blindly).
Have others run into this and think it's worth addressing?
If I've understood the problem correctly then the following syntax should allow you to work around the problem.
<%= Html.ActionLink("test", MVC.MyController.MyAction().AddRouteValues(new MyClass() { Number = 5, SomeText = "Hello" })) %>
I think the answer to make the syntax nicer would be to wrap each non value type parameter in a RouteValueDictionary in each generated action result method
Edit: (Response to comment as not enough chars)
Ah ok I managed to recreate the simple example above using this method to give: /MyController/MyAction/5/Hello as the url.
I'm not quite sure how nested complex types would pan out in practice. You could use some recursion to dive down the into the top-level object and reflect over the values to add them but then you open up a new set of issues, such as how to cope with a child property name that is identical to the parent property name.
This seems like it could be a complex problem to solve, in a manner that would work for everyone.
Perhaps some kind of adapter pattern would be most useful to transform a complex object into route values. In the simplest case this might be to declare an extension method ToRouteDictionary that acts on your complex type and transforms it using your knowledge of how it should work. Just thinking out loud as I'm obviously not aware of your use cases