Making Instant C# Viable – Part 2

Completing Assignments

In part 1 we talked about what we’re trying to do and got the basics set up. Originally I mentioned I wanted to talk about loops in part 2, but since I announced things people have actually started to use the prototype and found a few bugs. Instead of loops, in this post I want to talk about post and prefix unary expressions, as well as completing assignments. Originally, I just had all types of assignments doing the same action, which ended up with some behavior that looked like this:

x *= 5; // turns into:
x *= LogObject ("x", 5);

Obviously this isn’t what we want, x will have the right value, but it’ll be logged as being “5”. We’ll need to turn these complex assignment operations into their normal version:

x = LogObject ("x", x * 5);

Something I didn’t cover well in the first post is why not instead just call LogObject on the variable after each operation, something like:

x *= 5; LogObject ("x", x);

Simply put: Side effects. When we move the expression into an argument for a method, what the method gets is as close as possible to what the variable would receive in a normal situation. However, if we were to use the above approach, what happens if the value is actually a property, or even worse a method call? Unfortunately this doesn’t guarantee that I won’t have to (or accidentally) take a route that causes such side effects, but I’m trying to avoid it where possible.

So what we’ll do is on any complex assignment, we’ll get the token for the expression minus the assignment. So, for a *= expression, we want the * token. So we’re going to add this pretty straightforward method in our LoggingRewriter class that we started before:

private SyntaxKind GetComplexAssignToken (SyntaxKind kind)
{
	switch (kind)
	{
		case SyntaxKind.AddAssignExpression:
			return SyntaxKind.PlusToken;
		case SyntaxKind.SubtractAssignExpression:
			return SyntaxKind.MinusToken;
		case SyntaxKind.MultiplyAssignExpression:
			return SyntaxKind.AsteriskToken;
		case SyntaxKind.DivideAssignExpression:
			return SyntaxKind.SlashToken;
		case SyntaxKind.AndAssignExpression:
			return SyntaxKind.AmpersandToken;
		case SyntaxKind.OrAssignExpression:
			return SyntaxKind.BarToken;
		case SyntaxKind.ExclusiveOrAssignExpression:
			return SyntaxKind.CaretToken;
		case SyntaxKind.RightShiftAssignExpression:
			return SyntaxKind.GreaterThanGreaterThanToken;
		case SyntaxKind.LeftShiftAssignExpression:
			return SyntaxKind.LessThanLessThanToken;
		case SyntaxKind.ModuloAssignExpression:
			return SyntaxKind.PercentToken;

		default:
			throw new ArgumentException();
	}
}

Then we’ll rewrite the right hand of the expression to be the variable identifier + the token + the original expression. To do that, we’ll revisit our VisitBinaryExpression method. First, we want to make sure we’re doing any deeper transforms first, so we’ll move our base method call to the top. Then, we’ll handle the rest of the types of expressions we ignored last time. So, in total we end up with:

protected override SyntaxNode VisitBinaryExpression (BinaryExpressionSyntax node)
{
	var newNode = base.VisitBinaryExpression (node);
	node = newNode as BinaryExpressionSyntax;
	if (node == null) // If something deeper changed the type, bail
		return newNode;

	var nameSyntax = FindIdentifierName (node.Left);
	if (nameSyntax == null)
		return newNode;

	switch (node.Kind)
	{
		case SyntaxKind.AddAssignExpression:
		case SyntaxKind.OrAssignExpression:
		case SyntaxKind.SubtractAssignExpression:
		case SyntaxKind.MultiplyAssignExpression:
		case SyntaxKind.DivideAssignExpression:
		case SyntaxKind.ModuloAssignExpression:
		case SyntaxKind.RightShiftAssignExpression:
		case SyntaxKind.LeftShiftAssignExpression:
		case SyntaxKind.AndAssignExpression:
		case SyntaxKind.ExclusiveOrAssignExpression:
			// Get our token using the method we defined above
			var token = Syntax.Token (GetComplexAssignOperator (node.Kind));

			// Write our new right hand expression. Every syntax-representing class I've run into in Roslyn
			// has supported .ToString() by outputing the syntax it represents, so we'll take advantage of that:
			ExpressionSyntax expr = Syntax.ParseExpression (nameSyntax.PlainName + token + node.Right);

			// Update our node with the normal assignment token, and by passing our new expression to our old
			// GetLogExpression method.
			return node.Update (node.Left, AssignToken, GetLogExpression (nameSyntax.PlainName, expr));

		case SyntaxKind.AssignExpression:
			return node.WithRight (GetLogExpression (nameSyntax.PlainName, node.Right));

		default:
			return node;
	}
}

Now we support complex assignments! On to prefix and postfix unary expressions!

Prefix and postfix unary expressions

Basically, we want to support pre and post incrementing and decrementing. Originally I thought this would be simple, just wrap i++ in LogObject and done right? Sadly this didn’t work out. It doesn’t work for postfix increments and decrements since the value recorded will be the value before the postfix operation occurs. You also can’t pass a reference to another method and execute the operator on the value, because then you’ve changed the behavior of the code and the value change will occur too early. If you try to use a series of overloads that simply adds one or subtracts one to the value you get (with two logging methods), you stop supporting custom types that have overloaded these operators. I’ve been playing with this while, went through plenty of iterations, and it’s the primary reason this post has taken too long.

I started to continue this post several times explaining that I’d given up and am using a solution that handled most things but still wasn’t completely correct. In the process of explaining things I kept coming up with new things to try until I finally arrived at the answer. The answer, of course, being stupidly simple but just out of reach because my understanding of postfix increments and decrements was incorrect. For example:

int i = 0;
int x = (i++ + i++);
// x == 1

Not that you would never necessarily do something like this, but I used to think x would be 0, that the increments did not occur until after everything in the statement. With the knowledge that the actual incrementating happens far earlier than originally thought, a simple option comes up:

i++;
// becomes:
LogPostfix (i++, "i", i);

With this we’ll return the first value passed in, and log the second value. For example if ‘i’ is 0, the first argument will be 0 and the last will be 1.

To get started, we want to expand our FindIdentifierName function to support unary expressions:

PostfixUnaryExpressionSyntax postfixUnaryExpression = expression as PostfixUnaryExpressionSyntax;
if (postfixUnaryExpression != null)
	return FindIdentifierName (postfixUnaryExpression.Operand);

PrefixUnaryExpressionSyntax prefixUnaryExpression = expression as PrefixUnaryExpressionSyntax;
if (prefixUnaryExpression != null)
	return FindIdentifierName (prefixUnaryExpression.Operand);

Then, we’ll just add a new visitor to our LoggingRewriter class to handle prefixes. We can use the existing logging mechanisms, because the value is already changed before the argument has been passed:

protected override SyntaxNode VisitPrefixUnaryExpression (PrefixUnaryExpressionSyntax node)
{
	IdentifierNameSyntax name = FindIdentifierName (node);
	if (name != null)
	{
		switch (node.Kind)
		{
			case SyntaxKind.PreIncrementExpression:
			case SyntaxKind.PreDecrementExpression:
				return GetLogExpression (name.PlainName, node);
		}
	}

	return base.VisitPrefixUnaryExpression (node);
}

Now, for postfixes we’ll need to add a new logging method to handle the extra parameter. We can reuse our existing logging method, we just need to pass the post-operation value to be logged and return the value pre-operation:

public T LogPostfix (T expression, string name, T newValue)
{
	LogObject (name, newValue);
	return expression;
}

Now for the rewriter, just like before with prefixes but with a different replacement expression:

public override SyntaxNode VisitPostfixUnaryExpression (PostfixUnaryExpressionSyntax node)
{
	IdentifierNameSyntax name = FindIdentifierName (node);
	if (name != null)
	{
		switch (node.Kind)
		{
			case SyntaxKind.PostIncrementExpression:
			case SyntaxKind.PostDecrementExpression:
				return Syntax.ParseExpression ("LogPostfix (" + node + ", \"" + name.PlainName + "\", " + name.PlainName + ")");
		}
	}

	return base.VisitPostfixUnaryExpression(node);
}

And there we have it, prefix and postfix unary expressions! Also, if you haven’t seen already, the new CTP for Roslyn is available.

This entry was posted in Instant, Projects and tagged , , , . Bookmark the permalink.

6 Responses to Making Instant C# Viable – Part 2

  1. Pingback: Interesting .NET Links – June 17 , 2012 | TechBlog

  2. Dennis says:

    You are forgetting that classes are able to specify code for operator++ and most of the other ones you have specified. Therefore your transformations are not valid.

    • ermau says:

      In fact, I’m not forgetting it at all: “If you try to use a series of overloads that simply adds one or subtracts one to the value you get (with two logging methods), you stop supporting custom types that have overloaded these operators.” Much of the time spent on it was dedicated to making sure this scenario worked, and in these transformations nothing I can see is incorrect. What is incorrect about it?

      What is invalid, however, is that the method calls are not static. Since ScriptEngine does not properly recognize calls to it’s host object within classes, the logging calls will be invalid inside of a class. This means that using a class for anything does not work correctly (since LogObject won’t be present in the class you write). Clearly this means I’ll need to move away from using the host object to something else, which I’d planned on addressing in another post.

      Edited: Clarified my point.

  3. Pingback: Making Instant C# Viable – Visualization | Code Monkey vs. The World

  4. Pingback: Making Instant C# Viable – Visualization » Code Monkey vs. The World

  5. Jason says:

    Have you seen the new Mono 3.0 evaluator? Mono 3.0 Release Notes It can evaluate an entire class now

    “Evaluation can now Compile Types

    The Evaluator.Eval () API is no longer limited to expressions and statements, you can now pass entire namespace, class, interface, struct definitions as a string and have the result get compiled.

    This extends to the csharp command:

    csharp> class X { public int a; }
    csharp> var t = new X () { a = 1 };
    csharp> print (t.a);
    1
    csharp>”

Add Comment Register



Leave a Reply

Your email address will not be published. Required fields are marked *

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>