diff --git a/FSharp.sln b/FSharp.sln
index e1c47795e8d..75fa9032a91 100644
--- a/FSharp.sln
+++ b/FSharp.sln
@@ -39,6 +39,10 @@ Project("{6EC3EE1D-3C4E-46DD-8F32-0CC8E7565705}") = "FSharp.Compiler.LanguageSer
EndProject
Project("{6EC3EE1D-3C4E-46DD-8F32-0CC8E7565705}") = "FSharp.Compiler.LanguageServer.UnitTests", "tests\FSharp.Compiler.LanguageServer.UnitTests\FSharp.Compiler.LanguageServer.UnitTests.fsproj", "{C97819B0-B428-4B96-9CD7-497D2D1C738C}"
EndProject
+Project("{6EC3EE1D-3C4E-46DD-8F32-0CC8E7565705}") = "FSharp.Compiler.Scripting", "src\fsharp\FSharp.Compiler.Scripting\FSharp.Compiler.Scripting.fsproj", "{6771860A-614D-4FDD-A655-4C70EBCC91B0}"
+EndProject
+Project("{6EC3EE1D-3C4E-46DD-8F32-0CC8E7565705}") = "FSharp.Compiler.Scripting.UnitTests", "tests\FSharp.Compiler.Scripting.UnitTests\FSharp.Compiler.Scripting.UnitTests.fsproj", "{4FEDF286-0252-4EBC-9E75-879CCA3B85DC}"
+EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
@@ -205,6 +209,30 @@ Global
{C97819B0-B428-4B96-9CD7-497D2D1C738C}.Release|Any CPU.Build.0 = Release|Any CPU
{C97819B0-B428-4B96-9CD7-497D2D1C738C}.Release|x86.ActiveCfg = Release|Any CPU
{C97819B0-B428-4B96-9CD7-497D2D1C738C}.Release|x86.Build.0 = Release|Any CPU
+ {6771860A-614D-4FDD-A655-4C70EBCC91B0}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {6771860A-614D-4FDD-A655-4C70EBCC91B0}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {6771860A-614D-4FDD-A655-4C70EBCC91B0}.Debug|x86.ActiveCfg = Debug|Any CPU
+ {6771860A-614D-4FDD-A655-4C70EBCC91B0}.Debug|x86.Build.0 = Debug|Any CPU
+ {6771860A-614D-4FDD-A655-4C70EBCC91B0}.Proto|Any CPU.ActiveCfg = Debug|Any CPU
+ {6771860A-614D-4FDD-A655-4C70EBCC91B0}.Proto|Any CPU.Build.0 = Debug|Any CPU
+ {6771860A-614D-4FDD-A655-4C70EBCC91B0}.Proto|x86.ActiveCfg = Debug|Any CPU
+ {6771860A-614D-4FDD-A655-4C70EBCC91B0}.Proto|x86.Build.0 = Debug|Any CPU
+ {6771860A-614D-4FDD-A655-4C70EBCC91B0}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {6771860A-614D-4FDD-A655-4C70EBCC91B0}.Release|Any CPU.Build.0 = Release|Any CPU
+ {6771860A-614D-4FDD-A655-4C70EBCC91B0}.Release|x86.ActiveCfg = Release|Any CPU
+ {6771860A-614D-4FDD-A655-4C70EBCC91B0}.Release|x86.Build.0 = Release|Any CPU
+ {4FEDF286-0252-4EBC-9E75-879CCA3B85DC}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {4FEDF286-0252-4EBC-9E75-879CCA3B85DC}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {4FEDF286-0252-4EBC-9E75-879CCA3B85DC}.Debug|x86.ActiveCfg = Debug|Any CPU
+ {4FEDF286-0252-4EBC-9E75-879CCA3B85DC}.Debug|x86.Build.0 = Debug|Any CPU
+ {4FEDF286-0252-4EBC-9E75-879CCA3B85DC}.Proto|Any CPU.ActiveCfg = Debug|Any CPU
+ {4FEDF286-0252-4EBC-9E75-879CCA3B85DC}.Proto|Any CPU.Build.0 = Debug|Any CPU
+ {4FEDF286-0252-4EBC-9E75-879CCA3B85DC}.Proto|x86.ActiveCfg = Debug|Any CPU
+ {4FEDF286-0252-4EBC-9E75-879CCA3B85DC}.Proto|x86.Build.0 = Debug|Any CPU
+ {4FEDF286-0252-4EBC-9E75-879CCA3B85DC}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {4FEDF286-0252-4EBC-9E75-879CCA3B85DC}.Release|Any CPU.Build.0 = Release|Any CPU
+ {4FEDF286-0252-4EBC-9E75-879CCA3B85DC}.Release|x86.ActiveCfg = Release|Any CPU
+ {4FEDF286-0252-4EBC-9E75-879CCA3B85DC}.Release|x86.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
@@ -223,6 +251,8 @@ Global
{81B9FE26-C976-4FC7-B6CC-C7DB5903CAA7} = {3840F2E7-3898-45F7-8CF7-1E6829E56DB8}
{99B3F4A5-80B4-41D9-A073-117DB6D7DBBA} = {B8DDA694-7939-42E3-95E5-265C2217C142}
{C97819B0-B428-4B96-9CD7-497D2D1C738C} = {CFE3259A-2D30-4EB0-80D5-E8B5F3D01449}
+ {6771860A-614D-4FDD-A655-4C70EBCC91B0} = {B8DDA694-7939-42E3-95E5-265C2217C142}
+ {4FEDF286-0252-4EBC-9E75-879CCA3B85DC} = {CFE3259A-2D30-4EB0-80D5-E8B5F3D01449}
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {BD5177C7-1380-40E7-94D2-7768E1A8B1B8}
diff --git a/VisualFSharp.sln b/VisualFSharp.sln
index 4a1e75f446a..6e4587ddb62 100644
--- a/VisualFSharp.sln
+++ b/VisualFSharp.sln
@@ -158,7 +158,11 @@ Project("{6EC3EE1D-3C4E-46DD-8F32-0CC8E7565705}") = "FSharp.Compiler.LanguageSer
EndProject
Project("{6EC3EE1D-3C4E-46DD-8F32-0CC8E7565705}") = "FSharp.Compiler.LanguageServer.UnitTests", "tests\FSharp.Compiler.LanguageServer.UnitTests\FSharp.Compiler.LanguageServer.UnitTests.fsproj", "{AAF2D233-1C38-4090-8FFA-F7C545625E06}"
EndProject
-Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "FSharp.Editor.Helpers", "vsintegration\src\FSharp.Editor.Helpers\FSharp.Editor.Helpers.csproj", "{79255A92-ED00-40BA-9D64-12FCC664A976}"
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "FSharp.Editor.Helpers", "vsintegration\src\FSharp.Editor.Helpers\FSharp.Editor.Helpers.csproj", "{79255A92-ED00-40BA-9D64-12FCC664A976}"
+EndProject
+Project("{6EC3EE1D-3C4E-46DD-8F32-0CC8E7565705}") = "FSharp.Compiler.Scripting", "src\fsharp\FSharp.Compiler.Scripting\FSharp.Compiler.Scripting.fsproj", "{20B7BC36-CF51-4D75-9E13-66681C07977F}"
+EndProject
+Project("{6EC3EE1D-3C4E-46DD-8F32-0CC8E7565705}") = "FSharp.Compiler.Scripting.UnitTests", "tests\FSharp.Compiler.Scripting.UnitTests\FSharp.Compiler.Scripting.UnitTests.fsproj", "{09F56540-AFA5-4694-B7A6-0DBF6D4618C2}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
@@ -926,6 +930,30 @@ Global
{79255A92-ED00-40BA-9D64-12FCC664A976}.Release|Any CPU.Build.0 = Release|Any CPU
{79255A92-ED00-40BA-9D64-12FCC664A976}.Release|x86.ActiveCfg = Release|Any CPU
{79255A92-ED00-40BA-9D64-12FCC664A976}.Release|x86.Build.0 = Release|Any CPU
+ {20B7BC36-CF51-4D75-9E13-66681C07977F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {20B7BC36-CF51-4D75-9E13-66681C07977F}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {20B7BC36-CF51-4D75-9E13-66681C07977F}.Debug|x86.ActiveCfg = Debug|Any CPU
+ {20B7BC36-CF51-4D75-9E13-66681C07977F}.Debug|x86.Build.0 = Debug|Any CPU
+ {20B7BC36-CF51-4D75-9E13-66681C07977F}.Proto|Any CPU.ActiveCfg = Debug|Any CPU
+ {20B7BC36-CF51-4D75-9E13-66681C07977F}.Proto|Any CPU.Build.0 = Debug|Any CPU
+ {20B7BC36-CF51-4D75-9E13-66681C07977F}.Proto|x86.ActiveCfg = Debug|Any CPU
+ {20B7BC36-CF51-4D75-9E13-66681C07977F}.Proto|x86.Build.0 = Debug|Any CPU
+ {20B7BC36-CF51-4D75-9E13-66681C07977F}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {20B7BC36-CF51-4D75-9E13-66681C07977F}.Release|Any CPU.Build.0 = Release|Any CPU
+ {20B7BC36-CF51-4D75-9E13-66681C07977F}.Release|x86.ActiveCfg = Release|Any CPU
+ {20B7BC36-CF51-4D75-9E13-66681C07977F}.Release|x86.Build.0 = Release|Any CPU
+ {09F56540-AFA5-4694-B7A6-0DBF6D4618C2}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {09F56540-AFA5-4694-B7A6-0DBF6D4618C2}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {09F56540-AFA5-4694-B7A6-0DBF6D4618C2}.Debug|x86.ActiveCfg = Debug|Any CPU
+ {09F56540-AFA5-4694-B7A6-0DBF6D4618C2}.Debug|x86.Build.0 = Debug|Any CPU
+ {09F56540-AFA5-4694-B7A6-0DBF6D4618C2}.Proto|Any CPU.ActiveCfg = Debug|Any CPU
+ {09F56540-AFA5-4694-B7A6-0DBF6D4618C2}.Proto|Any CPU.Build.0 = Debug|Any CPU
+ {09F56540-AFA5-4694-B7A6-0DBF6D4618C2}.Proto|x86.ActiveCfg = Debug|Any CPU
+ {09F56540-AFA5-4694-B7A6-0DBF6D4618C2}.Proto|x86.Build.0 = Debug|Any CPU
+ {09F56540-AFA5-4694-B7A6-0DBF6D4618C2}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {09F56540-AFA5-4694-B7A6-0DBF6D4618C2}.Release|Any CPU.Build.0 = Release|Any CPU
+ {09F56540-AFA5-4694-B7A6-0DBF6D4618C2}.Release|x86.ActiveCfg = Release|Any CPU
+ {09F56540-AFA5-4694-B7A6-0DBF6D4618C2}.Release|x86.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
@@ -1001,6 +1029,8 @@ Global
{60BAFFA5-6631-4328-B044-2E012AB76DCA} = {B8DDA694-7939-42E3-95E5-265C2217C142}
{AAF2D233-1C38-4090-8FFA-F7C545625E06} = {CFE3259A-2D30-4EB0-80D5-E8B5F3D01449}
{79255A92-ED00-40BA-9D64-12FCC664A976} = {4C7B48D7-19AF-4AE7-9D1D-3BB289D5480D}
+ {20B7BC36-CF51-4D75-9E13-66681C07977F} = {B8DDA694-7939-42E3-95E5-265C2217C142}
+ {09F56540-AFA5-4694-B7A6-0DBF6D4618C2} = {CFE3259A-2D30-4EB0-80D5-E8B5F3D01449}
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {48EDBBBE-C8EE-4E3C-8B19-97184A487B37}
diff --git a/azure-pipelines.yml b/azure-pipelines.yml
index 53288c73d3f..891be559808 100644
--- a/azure-pipelines.yml
+++ b/azure-pipelines.yml
@@ -56,6 +56,7 @@ stages:
- group: DotNet-Blob-Feed
- group: DotNet-Symbol-Server-Pats
- group: DotNet-DevDiv-Insertion-Workflow-Variables
+ - group: 'F# MyGet API Keys'
- name: _SignType
value: Real
- name: _DotNetPublishToBlobFeed
@@ -130,6 +131,13 @@ stages:
DropFolder: '$(Build.SourcesDirectory)\artifacts\VSSetup\$(_BuildConfig)\Insertion'
AccessToken: $(dn-bot-devdiv-drop-rw-code-rw)
condition: succeeded()
+ - pwsh: .\eng\publish-myget.ps1
+ -artifactsDir "$(Build.SourcesDirectory)\artifacts"
+ -apiKey "$(FSharpMyGetAPIKey)"
+ -configuration $(_BuildConfig)
+ displayName: Upload MyGet packages
+ continueOnError: true
+ condition: and(succeeded(), eq('$(Build.SourceBranch)', 'master'))
#-------------------------------------------------------------------------------------------------------------------#
# PR builds #
diff --git a/eng/Build.ps1 b/eng/Build.ps1
index 75f0178b7d2..f6a3f289c48 100644
--- a/eng/Build.ps1
+++ b/eng/Build.ps1
@@ -337,6 +337,7 @@ try {
if ($testDesktop -and -not $noVisualStudio) {
TestUsingNUnit -testProject "$RepoRoot\tests\FSharp.Compiler.UnitTests\FSharp.Compiler.UnitTests.fsproj" -targetFramework $desktopTargetFramework
TestUsingNUnit -testProject "$RepoRoot\tests\FSharp.Compiler.LanguageServer.UnitTests\FSharp.Compiler.LanguageServer.UnitTests.fsproj" -targetFramework $desktopTargetFramework
+ TestUsingNUnit -testProject "$RepoRoot\tests\FSharp.Compiler.Scripting.UnitTests\FSharp.Compiler.Scripting.UnitTests.fsproj" -targetFramework $desktopTargetFramework
TestUsingNUnit -testProject "$RepoRoot\tests\FSharp.Build.UnitTests\FSharp.Build.UnitTests.fsproj" -targetFramework $desktopTargetFramework
TestUsingNUnit -testProject "$RepoRoot\tests\FSharp.Core.UnitTests\FSharp.Core.UnitTests.fsproj" -targetFramework $desktopTargetFramework
TestUsingNUnit -testProject "$RepoRoot\tests\fsharp\FSharpSuite.Tests.fsproj" -targetFramework $desktopTargetFramework
@@ -345,6 +346,7 @@ try {
if ($testCoreClr) {
TestUsingNUnit -testProject "$RepoRoot\tests\FSharp.Compiler.UnitTests\FSharp.Compiler.UnitTests.fsproj" -targetFramework $coreclrTargetFramework
TestUsingNUnit -testProject "$RepoRoot\tests\FSharp.Compiler.LanguageServer.UnitTests\FSharp.Compiler.LanguageServer.UnitTests.fsproj" -targetFramework $coreclrTargetFramework
+ TestUsingNUnit -testProject "$RepoRoot\tests\FSharp.Compiler.Scripting.UnitTests\FSharp.Compiler.Scripting.UnitTests.fsproj" -targetFramework $coreclrTargetFramework
TestUsingNUnit -testProject "$RepoRoot\tests\FSharp.Build.UnitTests\FSharp.Build.UnitTests.fsproj" -targetFramework $coreclrTargetFramework
TestUsingNUnit -testProject "$RepoRoot\tests\FSharp.Core.UnitTests\FSharp.Core.UnitTests.fsproj" -targetFramework $coreclrTargetFramework
TestUsingNUnit -testProject "$RepoRoot\tests\fsharp\FSharpSuite.Tests.fsproj" -targetFramework $coreclrTargetFramework
diff --git a/eng/build.sh b/eng/build.sh
index e806856c556..164dd747f36 100755
--- a/eng/build.sh
+++ b/eng/build.sh
@@ -272,6 +272,7 @@ if [[ "$test_core_clr" == true ]]; then
coreclrtestframework=netcoreapp3.0
TestUsingNUnit --testproject "$repo_root/tests/FSharp.Compiler.UnitTests/FSharp.Compiler.UnitTests.fsproj" --targetframework $coreclrtestframework
TestUsingNUnit --testproject "$repo_root/tests/FSharp.Compiler.LanguageServer.UnitTests/FSharp.Compiler.LanguageServer.UnitTests.fsproj" --targetframework $coreclrtestframework
+ TestUsingNUnit --testproject "$repo_root/tests/FSharp.Compiler.Scripting.UnitTests/FSharp.Compiler.Scripting.UnitTests.fsproj" --targetframework $coreclrtestframework
TestUsingNUnit --testproject "$repo_root/tests/FSharp.Build.UnitTests/FSharp.Build.UnitTests.fsproj" --targetframework $coreclrtestframework
TestUsingNUnit --testproject "$repo_root/tests/FSharp.Core.UnitTests/FSharp.Core.UnitTests.fsproj" --targetframework $coreclrtestframework
fi
diff --git a/eng/publish-myget.ps1 b/eng/publish-myget.ps1
new file mode 100644
index 00000000000..23f3aa81c9a
--- /dev/null
+++ b/eng/publish-myget.ps1
@@ -0,0 +1,17 @@
+[CmdletBinding(PositionalBinding=$false)]
+param (
+ [string]$artifactsDir,
+ [string]$apiKey,
+ [string]$configuration
+)
+
+Set-StrictMode -version 2.0
+$ErrorActionPreference = "Stop"
+
+$shippingDir = "$artifactsDir\packages\$configuration\Shipping"
+$packages = @(
+ "$shippingDir\FSharp.Compiler.Scripting.*.nupkg",
+ "$shippingDir\FSharp.Core.*.nupkg"
+)
+
+. (Join-Path $PSScriptRoot "publish-myget.ps1") -apiKey "$apiKey" -feedUrl "https://dotnet.myget.org/F/fsharp/api/v2/package" $packages
diff --git a/eng/publish.ps1 b/eng/publish.ps1
new file mode 100644
index 00000000000..214d69b3c9f
--- /dev/null
+++ b/eng/publish.ps1
@@ -0,0 +1,21 @@
+[CmdletBinding(PositionalBinding=$false)]
+param (
+ [string]$apiKey,
+ [string]$feedUrl,
+
+ [parameter(ValueFromRemainingArguments=$true)][string[]]$packages
+)
+
+Set-StrictMode -version 2.0
+$ErrorActionPreference = "Stop"
+
+$failedPackages = 0
+foreach ($package in $packages) {
+ $response = Invoke-WebRequest -Uri $feedUrl -Headers @{"X-NuGet-ApiKey"=$apiKey} -ContentType "multipart/form-data" -InFile "$package" -Method Post -UseBasicParsing
+ if ($response.StatusCode -ne 201) {
+ Write-Error "Failed to upload package. Upload failed with status code: $response.StatusCode."
+ $failedPackages++
+ }
+}
+
+exit $failedPackages
diff --git a/src/fsharp/FSharp.Compiler.Scripting/Directory.Build.props b/src/fsharp/FSharp.Compiler.Scripting/Directory.Build.props
new file mode 100644
index 00000000000..7cd41381b5d
--- /dev/null
+++ b/src/fsharp/FSharp.Compiler.Scripting/Directory.Build.props
@@ -0,0 +1,9 @@
+
+
+
+ true
+
+
+
+
+
diff --git a/src/fsharp/FSharp.Compiler.Scripting/FSharp.Compiler.Scripting.fsproj b/src/fsharp/FSharp.Compiler.Scripting/FSharp.Compiler.Scripting.fsproj
new file mode 100644
index 00000000000..23c51fdf157
--- /dev/null
+++ b/src/fsharp/FSharp.Compiler.Scripting/FSharp.Compiler.Scripting.fsproj
@@ -0,0 +1,27 @@
+
+
+
+ netstandard2.0
+ true
+ Provides embedding F# language scripting.
+ FSharp.Compiler.Scripting.nuspec
+ true
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/fsharp/FSharp.Compiler.Scripting/FSharp.Compiler.Scripting.nuspec b/src/fsharp/FSharp.Compiler.Scripting/FSharp.Compiler.Scripting.nuspec
new file mode 100644
index 00000000000..c5c1756f6e1
--- /dev/null
+++ b/src/fsharp/FSharp.Compiler.Scripting/FSharp.Compiler.Scripting.nuspec
@@ -0,0 +1,20 @@
+
+
+
+ $CommonMetadataElements$
+ en-US
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/fsharp/FSharp.Compiler.Scripting/FSharpScript.fs b/src/fsharp/FSharp.Compiler.Scripting/FSharpScript.fs
new file mode 100644
index 00000000000..114e1e6cd33
--- /dev/null
+++ b/src/fsharp/FSharp.Compiler.Scripting/FSharpScript.fs
@@ -0,0 +1,57 @@
+// Copyright (c) Microsoft Corporation. All Rights Reserved. See License.txt in the project root for license information.
+
+namespace FSharp.Compiler.Scripting
+
+open System
+open FSharp.Compiler.Interactive.Shell
+
+type FSharpScript(?captureInput: bool, ?captureOutput: bool) as this =
+ let outputProduced = Event()
+ let errorProduced = Event()
+
+ // handle stdin/stdout
+ let stdin = new CapturedTextReader()
+ let stdout = new EventedTextWriter()
+ let stderr = new EventedTextWriter()
+ do stdout.LineWritten.Add outputProduced.Trigger
+ do stderr.LineWritten.Add errorProduced.Trigger
+ let captureInput = defaultArg captureInput false
+ let captureOutput = defaultArg captureOutput false
+ let savedInput = Console.In
+ let savedOutput = Console.Out
+ let savedError = Console.Error
+ do (fun () ->
+ if captureInput then
+ Console.SetIn(stdin)
+ if captureOutput then
+ Console.SetOut(stdout)
+ Console.SetError(stderr)
+ ())()
+
+ let config = FsiEvaluationSession.GetDefaultConfiguration()
+ let argv = [| this.GetType().Assembly.Location; "--noninteractive"; "--targetprofile:netcore"; "--quiet" |]
+ let fsi = FsiEvaluationSession.Create (config, argv, stdin, stdout, stderr, collectible=true)
+
+ member __.ProvideInput = stdin.ProvideInput
+
+ member __.OutputProduced = outputProduced.Publish
+
+ member __.ErrorProduced = errorProduced.Publish
+
+ member __.Eval(code: string) =
+ let ch, errors = fsi.EvalExpressionNonThrowing code
+ match ch with
+ | Choice1Of2(Some(value)) -> Ok(value), errors
+ | Choice1Of2 None -> Error(Exception("No value produced")), errors
+ | Choice2Of2 ex -> Error(ex), errors
+
+ interface IDisposable with
+ member __.Dispose() =
+ if captureInput then
+ Console.SetIn(savedInput)
+ if captureOutput then
+ Console.SetOut(savedOutput)
+ Console.SetError(savedError)
+ stdin.Dispose()
+ stdout.Dispose()
+ stderr.Dispose()
diff --git a/src/fsharp/FSharp.Compiler.Scripting/TextHelpers.fs b/src/fsharp/FSharp.Compiler.Scripting/TextHelpers.fs
new file mode 100644
index 00000000000..87e846b4009
--- /dev/null
+++ b/src/fsharp/FSharp.Compiler.Scripting/TextHelpers.fs
@@ -0,0 +1,36 @@
+// Copyright (c) Microsoft Corporation. All Rights Reserved. See License.txt in the project root for license information.
+
+namespace FSharp.Compiler.Scripting
+
+open System.Collections.Generic
+open System.IO
+open System.Text
+
+type internal CapturedTextReader() =
+ inherit TextReader()
+ let queue = Queue()
+ member __.ProvideInput(text: string) =
+ for c in text.ToCharArray() do
+ queue.Enqueue(c)
+ override __.Peek() =
+ if queue.Count > 0 then queue.Peek() |> int
+ else -1
+ override __.Read() =
+ if queue.Count > 0 then queue.Dequeue() |> int
+ else -1
+
+type internal EventedTextWriter() =
+ inherit TextWriter()
+ let sb = StringBuilder()
+ let lineWritten = Event()
+ member __.LineWritten = lineWritten.Publish
+ override __.Encoding = Encoding.UTF8
+ override __.Write(c: char) =
+ if c = '\n' then
+ let line =
+ let v = sb.ToString()
+ if v.EndsWith("\r") then v.Substring(0, v.Length - 1)
+ else v
+ sb.Clear() |> ignore
+ lineWritten.Trigger(line)
+ else sb.Append(c) |> ignore
diff --git a/tests/FSharp.Compiler.Scripting.UnitTests/Directory.Build.props b/tests/FSharp.Compiler.Scripting.UnitTests/Directory.Build.props
new file mode 100644
index 00000000000..7cd41381b5d
--- /dev/null
+++ b/tests/FSharp.Compiler.Scripting.UnitTests/Directory.Build.props
@@ -0,0 +1,9 @@
+
+
+
+ true
+
+
+
+
+
diff --git a/tests/FSharp.Compiler.Scripting.UnitTests/FSharp.Compiler.Scripting.UnitTests.fsproj b/tests/FSharp.Compiler.Scripting.UnitTests/FSharp.Compiler.Scripting.UnitTests.fsproj
new file mode 100644
index 00000000000..56f4523740d
--- /dev/null
+++ b/tests/FSharp.Compiler.Scripting.UnitTests/FSharp.Compiler.Scripting.UnitTests.fsproj
@@ -0,0 +1,22 @@
+
+
+
+
+ net472;netcoreapp3.0
+ netcoreapp3.0
+ Library
+ true
+ nunit
+ true
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/tests/FSharp.Compiler.Scripting.UnitTests/FSharpScriptTests.fs b/tests/FSharp.Compiler.Scripting.UnitTests/FSharpScriptTests.fs
new file mode 100644
index 00000000000..38d5c3d431a
--- /dev/null
+++ b/tests/FSharp.Compiler.Scripting.UnitTests/FSharpScriptTests.fs
@@ -0,0 +1,48 @@
+// Copyright (c) Microsoft Corporation. All Rights Reserved. See License.txt in the project root for license information.
+
+namespace FSharp.Compiler.Scripting.UnitTests
+
+open System
+open System.Threading
+open FSharp.Compiler.Interactive.Shell
+open FSharp.Compiler.Scripting
+open FSharp.Compiler.SourceCodeServices
+open NUnit.Framework
+
+[]
+type InteractiveTests() =
+
+ let getValue ((value: Result), (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
+
+ []
+ member __.``Eval object value``() =
+ use fsi = new FSharpScript()
+ let value = fsi.Eval("1+1") |> getValue
+ Assert.AreEqual(typeof, value.ReflectionType)
+ Assert.AreEqual(2, value.ReflectionValue :?> int)
+
+ []
+ member __.``Capture console input``() =
+ use script = new FSharpScript(captureInput=true)
+ script.ProvideInput "stdin:1234\r\n"
+ let value = script.Eval("System.Console.ReadLine()") |> getValue
+ Assert.AreEqual(typeof, value.ReflectionType)
+ Assert.AreEqual("stdin:1234", value.ReflectionValue)
+
+ []
+ member __.``Capture console output/error``() =
+ use script = new FSharpScript(captureOutput=true)
+ use sawOutputSentinel = new ManualResetEvent(false)
+ 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
+ Assert.AreEqual(typeof, 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")