User:BoundedBeans/Deadfish in one statement of C Sharp
I wrote a Deadfish interpreter in C#, and while that might not be super special on its own, this Deadfish interpreter uses only one statement, and it doesn't cheat by using lambda expressions for blocks. (It does use lambda expressions, but they don't have multiple statements, just one expression.)
Strategies used
Persistent data
To interpret Deadfish, you need at least the accumulator to be persistent across commands. Unfortunately, in C#, you can assign variables in an expression, but you can't declare new ones. The workaround used here relies on the fact that parameters to lambda expressions are named and persistent through the execution of the lambda. This is significant even if the lambda has only one expression. In this interpreter, the persistent data actually isn't the accumulator, code, or position directly, but dictionaries containing ParameterExpression
s and LabelTarget
s for use by the program. This is because all of the logic is wrapped inside of the code generation features of System.Linq.Expressions
, and when using that, an identical name is not enough for parameters and labels to be the same. They must be the same instance.
Various other problems
The issues of running multiple actions in sequence, looping, and executing methods with the return type void (such as printing to the console) are all handled by generating, compiling, and calling a lambda with System.Linq.Expressions
. Running multiple actions in sequence can also be done by putting things inside of an array, but since the approach used to solve the other problems can also solve this one, it's easiest to reuse the same solution in this case. Looping and void methods are otherwise pretty difficult; looping can be handled by System.Reflection.Emit, but that requires a ton of void method calls, and there aren't any other solutions I know of to fix that problem.
Edit: You could actually use reflection to call void methods, since it would always return null at the very least (which we can handle, and is separate from the void return type). I think looping could also be handled by passing a lambda to itself (by nesting it inside of another lambda), and then having the lambda call itself with itself. This is recursive, and isn't an infinite looping solution, but you could possibly make it infinite by starting the call inside a new thread repeatedly (getting rid of the original thread each time.)
Code
To run this online:
- prefix with:
using System; using System.Collections.Generic; public class Program { public static void Main() {
- suffix with:
} }
- use this online compiler rather than tio.run.
- Note that sometimes this site will occasionally forget a newline in the output, so numbers may occasionally concatenate to give the wrong result.
((Func< Dictionary<string, System.Linq.Expressions.ParameterExpression>, Dictionary<string, System.Linq.Expressions.LabelTarget>, int>)((vars, labels) => ( System.Linq.Expressions.Expression.Lambda<Func<int>>( System.Linq.Expressions.Expression.Block( new System.Linq.Expressions.ParameterExpression[] { vars["code"], vars["acc"], vars["pos"] }, System.Linq.Expressions.Expression.Loop( System.Linq.Expressions.Expression.Block( System.Linq.Expressions.Expression.Call( typeof(Console).GetMethod("Write", new Type[] { typeof(string) }), System.Linq.Expressions.Expression.Constant(">>") ), System.Linq.Expressions.Expression.Assign( vars["code"], System.Linq.Expressions.Expression.Call( System.Linq.Expressions.Expression.Call( typeof(Console).GetMethod("ReadLine", Type.EmptyTypes) ), typeof(string).GetMethod("ToCharArray", Type.EmptyTypes) ) ), System.Linq.Expressions.Expression.Assign( vars["pos"], System.Linq.Expressions.Expression.Constant(0) ), System.Linq.Expressions.Expression.Assign( vars["acc"], System.Linq.Expressions.Expression.Constant(0) ), System.Linq.Expressions.Expression.IfThen( System.Linq.Expressions.Expression.Equal( System.Linq.Expressions.Expression.ArrayLength(vars["code"]), System.Linq.Expressions.Expression.Constant(0) ), System.Linq.Expressions.Expression.Break( labels["endouter"] ) ), System.Linq.Expressions.Expression.Loop( System.Linq.Expressions.Expression.Block( System.Linq.Expressions.Expression.IfThen( System.Linq.Expressions.Expression.Equal( System.Linq.Expressions.Expression.ArrayAccess( vars["code"], vars["pos"] ), System.Linq.Expressions.Expression.Constant('i') ), System.Linq.Expressions.Expression.AddAssign( vars["acc"], System.Linq.Expressions.Expression.Constant(1) ) ), System.Linq.Expressions.Expression.IfThen( System.Linq.Expressions.Expression.Equal( System.Linq.Expressions.Expression.ArrayAccess( vars["code"], vars["pos"] ), System.Linq.Expressions.Expression.Constant('d') ), System.Linq.Expressions.Expression.SubtractAssign( vars["acc"], System.Linq.Expressions.Expression.Constant(1) ) ), System.Linq.Expressions.Expression.IfThen( System.Linq.Expressions.Expression.Equal( System.Linq.Expressions.Expression.ArrayAccess( vars["code"], vars["pos"] ), System.Linq.Expressions.Expression.Constant('s') ), System.Linq.Expressions.Expression.MultiplyAssign( vars["acc"], vars["acc"] ) ), System.Linq.Expressions.Expression.IfThen( System.Linq.Expressions.Expression.Equal( System.Linq.Expressions.Expression.ArrayAccess( vars["code"], vars["pos"] ), System.Linq.Expressions.Expression.Constant('o') ), System.Linq.Expressions.Expression.Call( typeof(Console).GetMethod("WriteLine",new Type[] { typeof(int) }), vars["acc"] ) ), System.Linq.Expressions.Expression.IfThen( System.Linq.Expressions.Expression.OrElse( System.Linq.Expressions.Expression.Equal( vars["acc"], System.Linq.Expressions.Expression.Constant(256) ), System.Linq.Expressions.Expression.Equal( vars["acc"], System.Linq.Expressions.Expression.Constant(-1) ) ), System.Linq.Expressions.Expression.Assign( vars["acc"], System.Linq.Expressions.Expression.Constant(0) ) ), System.Linq.Expressions.Expression.AddAssign( vars["pos"], System.Linq.Expressions.Expression.Constant(1) ), System.Linq.Expressions.Expression.IfThen( System.Linq.Expressions.Expression.GreaterThanOrEqual( vars["pos"], System.Linq.Expressions.Expression.ArrayLength(vars["code"]) ), System.Linq.Expressions.Expression.Break(labels["endinner"]) ) ) ), System.Linq.Expressions.Expression.Label(labels["endinner"]) ) ), System.Linq.Expressions.Expression.Label(labels["endouter"]), System.Linq.Expressions.Expression.Constant(0) ), false, null ) .Compile()() ))) ( new Dictionary<string, System.Linq.Expressions.ParameterExpression> { { "code", System.Linq.Expressions.Expression.Parameter(typeof(char[]), "code") }, { "acc", System.Linq.Expressions.Expression.Parameter(typeof(int), "acc") }, { "pos", System.Linq.Expressions.Expression.Parameter(typeof(int), "pos") } }, new Dictionary<string, System.Linq.Expressions.LabelTarget> { { "endouter", System.Linq.Expressions.Expression.Label() }, { "endinner", System.Linq.Expressions.Expression.Label() } } );