REPL in LINQPad leveraging Roslyn
I'd created an idea for LINQPad to offer a REPL experience already (since I can't use language features like LINQ comprehension syntax or the like from PowerShell scripts), but now that Roslyn CTP is out and csx / C# script is alive, I'm hoping that the effort required would be much lower in LINQPad to offer a REPL. If it only works when Roslyn is installed, then that's fine with me, and it helps bridge the functionality gap for those of us that love LINQPad but want to use it as a REPL so we can keep results/objects/graphs in memory and not have to re-generate them each time.
An alternative that just keeps the same 'context' around each time (same instance of UserQuery subclass, I guess?) such that the local vars (or at least instance vars) stick around between queries (in the same tab, in the same running process, not persisted anywhere) would be a useful fallback if a 'real' REPL isn't viable. :)
-
holdom commented
A very simple solution:
- Download the ScriptCs.Engine.Roslyn NuGet package and it to MyExtensions.
- Add the namespacesThen add this class to MyExtensions:
public class CSrepl {
private ScriptEngine sengine;
private Session session;
private string cmd;
private string cmd_right = "";
public CSrepl() {
// Start the ScriptEngine and create a new session
sengine = new Roslyn.Scripting.CSharp.ScriptEngine();
session = sengine.CreateSession();
// Basic operations within the session
session.Execute(@"using System;");
session.Execute(@"double ans = 0.0;");
greetingMessage();
startRepl();
}
private void startRepl()
{
while(!string.IsNullOrEmpty(cmd = Console.ReadLine()))
{
// Dump the entered expreassion
cmd.Dump();
if(cmd.Contains("="))
{
// Get the right hand part of the expresssion after the = sign
cmd_right = cmd.Split('=')[1];
// Add the semicolon at the end of the expression if it is missing
if(!cmd.EndsWith(";"))
{
cmd = cmd + ";";
}
}
try
{
if(session.Execute(cmd) != null) {
// Execute an assignment like var a = 3.5;
evalexpr(cmd);
}
else {
// Evaluate an expression like 3 -5 * Math.PI
evalexpr(cmd_right);
}
} catch (Exception e) {
e.Dump();
}
}
}
public void evalexpr(string cmd)
{
try
{
if(session.Execute(cmd) != null)
{
// Evaluate the expression
var res = session.Execute<double>(cmd);
// Dump the result
res.Dump("=");
// assign value to the ans variable
session.Execute(@"ans = " + res + @";");
// add empty line
("").Dump();
}
} catch(Exception e) {
e.Dump();
}
}
private void greetingMessage()
{
("----------------------------------------------------------------").Dump();
("LINQPad REPL - v.0.05 - Based on ScriptCs.Roslyn").Dump();
("-----------------------------------------------------------------").Dump();
(" - Last update: 30.01.2014").Dump();
("--------------------------------------------------------------------------------------").Dump();
("Enter an expression like:").Dump();
(" 4 + 5 ").Dump();
(" var a = 3.5;").Dump();
(" var d = 4 * Math.PI").Dump();
("in the black console window below.").Dump();
("--------------------------------------------------------------------------------------").Dump();
("You can omit the semicolon (;) at the end of a line. LINQPadREPL adds it automatically.").Dump();
("-------------------------------------------------------").Dump();
("").Dump();
}
}Open a new Query (C# Statements) and start the C# REPL within LINQPad:
CSrepl repl = new CSrepl();
It's a very simple solution and a lot of work remains until it is stable.
-
Shlomi commented
One advantages of REPL would be when sometimes you want to change DB connection context without loosing the memory, i.e. query a DB then use that information to query a different one.
This can be also achieved if we could use multiple contexts objects instead of 'this' (without the feature of adding additional DB's to the same context).
-
Martin Dobroucký commented
Hi, here it is.
Just paste following lines into LINQPad as C# Statements and reference Roslyn Nuget package.It's far from perfect, but it has nice 'lines of code / performance' ratio :)
var sengine = new Roslyn.Scripting.CSharp.ScriptEngine();
var session = sengine.CreateSession();
string cmd;
while(!string.IsNullOrEmpty(cmd = Console.ReadLine()))
{
try{
session.Execute(cmd).Dump();
}catch(Exception e){
e.Dump();
}
} -
Bear in mind that LINQPad now offers a Cache() method to cache results between queries (see Help | What's New for info on how to use it).
As you say, a REPL window in LINQPad is doable with Roslyn. It's just a question of how much value it adds.