Skip to content

Commit

Permalink
File: Add with-input-from-file and with-output-to-file
Browse files Browse the repository at this point in the history
This also fixes a few logic and error messaging bugs in other related
methods.
  • Loading branch information
paulirwin committed Aug 24, 2023
1 parent 445fdb8 commit fdb9959
Show file tree
Hide file tree
Showing 3 changed files with 63 additions and 3 deletions.
38 changes: 36 additions & 2 deletions Colibri.Core/Macros/PortMacros.cs
Original file line number Diff line number Diff line change
Expand Up @@ -206,7 +206,7 @@ public static object ReadU8(ColibriRuntime runtime, Scope scope, object?[] args)

public static object ReadBytevector(ColibriRuntime runtime, Scope scope, object?[] args)
{
if (args.Length > 1)
if (args.Length is 0 or > 2)
{
throw new ArgumentException("read-bytevector requires one or two arguments");
}
Expand Down Expand Up @@ -320,7 +320,7 @@ public static object Read(ColibriRuntime runtime, Scope scope, object?[] args)
{
if (args.Length > 1)
{
throw new ArgumentException("read-line requires zero or one arguments");
throw new ArgumentException("read requires zero or one arguments");
}

object? port = GetInputPort(runtime, scope, args.Length == 1 ? args[0] : null);
Expand Down Expand Up @@ -587,4 +587,38 @@ public static object WriteBytevector(ColibriRuntime runtime, Scope scope, object

return Nil.Value;
}

public static object? WithInputFromFile(ColibriRuntime runtime, Scope scope, object?[] args)
{
if (args.Length != 2 || runtime.Evaluate(scope, args[0]) is not string filename)
{
throw new ArgumentException("with-input-from-file requires one filename argument and one thunk argument");
}

var thunk = runtime.Evaluate(scope, args[1]);

using var fileStream = File.OpenRead(filename);

var childScope = scope.CreateChildScope();
childScope.Define("current-input-port", new Parameter(fileStream));

return runtime.InvokePossibleTailCallExpression(childScope, thunk, Array.Empty<object?>());
}

public static object? WithOutputToFile(ColibriRuntime runtime, Scope scope, object?[] args)
{
if (args.Length != 2 || runtime.Evaluate(scope, args[0]) is not string filename)
{
throw new ArgumentException("with-output-to-file requires one filename argument and one thunk argument");
}

var thunk = runtime.Evaluate(scope, args[1]);

using var fileStream = File.OpenWrite(filename);

var childScope = scope.CreateChildScope();
childScope.Define("current-output-port", new Parameter(fileStream));

return runtime.InvokePossibleTailCallExpression(childScope, thunk, Array.Empty<object?>());
}
}
3 changes: 2 additions & 1 deletion Colibri.Core/StandardLibraries.cs
Original file line number Diff line number Diff line change
Expand Up @@ -379,7 +379,8 @@ static StandardLibraries()
["open-binary-output-file"] = PortExpressions.OpenBinaryOutputFile,
["open-input-file"] = PortExpressions.OpenInputFile,
["open-output-file"] = PortExpressions.OpenOutputFile,
// TODO: write-input-from-file, write-output-to-file
["with-input-from-file"] = (MacroExpression)PortMacros.WithInputFromFile,
["with-output-to-file"] = (MacroExpression)PortMacros.WithOutputToFile,
}, additionalExports: new List<string>
{
"call-with-input-file",
Expand Down
25 changes: 25 additions & 0 deletions Colibri.Tests/PortTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -88,4 +88,29 @@ public void CallWithPortTests(string input, object expected)
{
TestHelper.DefaultTest(input, expected);
}

[Fact]
public void WithInputOutputTest()
{
const string program = @"
(define msg ""Hello world!"")
(with-output-to-file ""WithInputOutputTest.txt""
(lambda ()
(write-bytevector (string->utf8 msg) (current-output-port))))
(define result (with-input-from-file ""WithInputOutputTest.txt""
(lambda ()
(utf8->string (read-bytevector (string-length msg) (current-input-port))))))
(delete-file ""WithInputOutputTest.txt"")
result
";

var runtime = new ColibriRuntime();

var result = runtime.EvaluateProgram(program);

Assert.Equal("Hello world!", result);
}
}

0 comments on commit fdb9959

Please sign in to comment.