Skip to content

Commit

Permalink
allow evaluation of non-expressions
Browse files Browse the repository at this point in the history
  • Loading branch information
brettfo committed Aug 22, 2019
1 parent 60e7df8 commit 6746a3b
Show file tree
Hide file tree
Showing 4 changed files with 23 additions and 11 deletions.
5 changes: 2 additions & 3 deletions src/fsharp/FSharp.Compiler.Scripting/FSharpScript.fs
Expand Up @@ -39,10 +39,9 @@ type FSharpScript(?captureInput: bool, ?captureOutput: bool) as this =
member __.ErrorProduced = errorProduced.Publish

member __.Eval(code: string) =
let ch, errors = fsi.EvalExpressionNonThrowing code
let ch, errors = fsi.EvalInteractionNonThrowing code
match ch with
| Choice1Of2(Some(value)) -> Ok(value), errors
| Choice1Of2 None -> Error(Exception("No value produced")), errors
| Choice1Of2 v -> Ok(v), errors
| Choice2Of2 ex -> Error(ex), errors

interface IDisposable with
Expand Down
3 changes: 1 addition & 2 deletions src/fsharp/fsi/fsi.fs
Expand Up @@ -2709,9 +2709,8 @@ type FsiEvaluationSession (fsi: FsiEvaluationSessionHostConfig, argv:string[], i

let errorOptions = TcConfig.Create(tcConfigB,validate = false).errorSeverityOptions
let errorLogger = CompilationErrorLogger("EvalInteraction", errorOptions)
fsiInteractionProcessor.EvalInteraction(ctok, sourceText, dummyScriptFileName, errorLogger)
fsiInteractionProcessor.EvalInteraction(ctok, sourceText, dummyScriptFileName, errorLogger)
|> commitResultNonThrowing errorOptions "input.fsx" errorLogger
|> function Choice1Of2 (_), errs -> Choice1Of2 (), errs | Choice2Of2 exn, errs -> Choice2Of2 exn, errs

member x.EvalScript(scriptPath) : unit =
// Explanation: When the user of the FsiInteractiveSession object calls this method, the
Expand Down
2 changes: 1 addition & 1 deletion src/fsharp/fsi/fsi.fsi
Expand Up @@ -149,7 +149,7 @@ type FsiEvaluationSession =
///
/// Due to a current limitation, it is not fully thread-safe to run this operation concurrently with evaluation triggered
/// by input from 'stdin'.
member EvalInteractionNonThrowing : code: string -> Choice<unit, exn> * FSharpErrorInfo[]
member EvalInteractionNonThrowing : code: string -> Choice<FsiValue option, exn> * FSharpErrorInfo[]

/// Execute the given script. Stop on first error, discarding the rest
/// of the script. Errors are sent to the output writer, a 'true' return value indicates there
Expand Down
24 changes: 19 additions & 5 deletions tests/FSharp.Compiler.Scripting.UnitTests/FSharpScriptTests.fs
Expand Up @@ -12,25 +12,29 @@ open NUnit.Framework
[<TestFixture>]
type InteractiveTests() =

let getValue ((value: Result<FsiValue, exn>), (errors: FSharpErrorInfo[])) =
let getValue ((value: Result<FsiValue option, exn>), (errors: FSharpErrorInfo[])) =
if errors.Length > 0 then
failwith <| sprintf "Evaluation returned %d errors:\r\n\t%s" errors.Length (String.Join("\r\n\t", errors))
match value with
| Ok(value) -> value
| Error ex -> raise ex

let ignoreValue = getValue >> ignore

[<Test>]
member __.``Eval object value``() =
use fsi = new FSharpScript()
let value = fsi.Eval("1+1") |> getValue
use script = new FSharpScript()
let opt = script.Eval("1+1") |> getValue
let value = opt.Value
Assert.AreEqual(typeof<int>, value.ReflectionType)
Assert.AreEqual(2, value.ReflectionValue :?> int)

[<Test>]
member __.``Capture console input``() =
use script = new FSharpScript(captureInput=true)
script.ProvideInput "stdin:1234\r\n"
let value = script.Eval("System.Console.ReadLine()") |> getValue
let opt = script.Eval("System.Console.ReadLine()") |> getValue
let value = opt.Value
Assert.AreEqual(typeof<string>, value.ReflectionType)
Assert.AreEqual("stdin:1234", value.ReflectionValue)

Expand All @@ -41,8 +45,18 @@ type InteractiveTests() =
use sawErrorSentinel = new ManualResetEvent(false)
script.OutputProduced.Add (fun line -> if line = "stdout:1234" then sawOutputSentinel.Set() |> ignore)
script.ErrorProduced.Add (fun line -> if line = "stderr:5678" then sawErrorSentinel.Set() |> ignore)
let value = script.Eval("printfn \"stdout:1234\"; eprintfn \"stderr:5678\"") |> getValue
let opt = script.Eval("printfn \"stdout:1234\"; eprintfn \"stderr:5678\"") |> getValue
let value = opt.Value
Assert.AreEqual(typeof<unit>, value.ReflectionType)
Assert.Null(value.ReflectionValue)
Assert.True(sawOutputSentinel.WaitOne(TimeSpan.FromSeconds(5.0)), "Expected to see output sentinel value written")
Assert.True(sawErrorSentinel.WaitOne(TimeSpan.FromSeconds(5.0)), "Expected to see error sentinel value written")

[<Test>]
member __.``Maintain state between submissions``() =
use script = new FSharpScript()
script.Eval("let add x y = x + y") |> ignoreValue
let opt = script.Eval("add 2 3") |> getValue
let value = opt.Value
Assert.AreEqual(typeof<int>, value.ReflectionType)
Assert.AreEqual(5, value.ReflectionValue :?> int)

0 comments on commit 6746a3b

Please sign in to comment.