Skip to content

Commit

Permalink
Move Writer Functions to Serefa
Browse files Browse the repository at this point in the history
Move the `display` and `newline` builtins to the Serefa library. Here
we can hopefully extend them with better support for displaying external
representation and add a `write` impl too.
  • Loading branch information
iwillspeak committed Oct 26, 2020
1 parent 1743285 commit 02ca7e0
Show file tree
Hide file tree
Showing 5 changed files with 76 additions and 73 deletions.
72 changes: 1 addition & 71 deletions src/Feersum/Builtins.fs
Original file line number Diff line number Diff line change
Expand Up @@ -165,74 +165,6 @@ let private createBuiltins (assm: AssemblyDefinition) (ty: TypeDefinition) =

meth

/// Display builtin. This is intended for user-readable output rather than
/// any machine readable round tripping. Printing out strings & chars should
/// display their raw form. All other objects is up to the implementation.
///
/// This implementation calls `ToString` on the underlying .NET object and
/// uses that directly.
let displayBuiltin =
let meth, il = declareBuiltinMethod "display"
let fail = il.Create(OpCodes.Nop)
let hasValue = il.Create(OpCodes.Nop)
let print = il.Create(OpCodes.Dup)

il.Emit(OpCodes.Ldarg_0)
il.Emit(OpCodes.Ldlen)
il.Emit(OpCodes.Ldc_I4_1)
il.Emit(OpCodes.Bne_Un, fail)

// null check
il.Emit(OpCodes.Ldarg_0)
il.Emit(OpCodes.Ldc_I4_0)
il.Emit(OpCodes.Ldelem_Ref)
il.Emit(OpCodes.Brtrue_S, hasValue)

// If null use empty string
il.Emit(OpCodes.Ldstr, "")
il.Emit(OpCodes.Br, print)

// convert to string
il.Append(hasValue)
let toStr = typeof<obj>.GetMethod("ToString", BindingFlags.Public ||| BindingFlags.Instance)
let toStr = assm.MainModule.ImportReference toStr
il.Emit(OpCodes.Ldarg_0)
il.Emit(OpCodes.Ldc_I4_0)
il.Emit(OpCodes.Ldelem_Ref)
il.Emit(OpCodes.Callvirt, toStr)

il.Append(print)

let write = typeof<Console>.GetMethod("Write", [| typeof<string> |])
let write = assm.MainModule.ImportReference write
il.Emit(OpCodes.Call, write)
il.Emit(OpCodes.Ret)

il.Append(fail)
emitThrow il assm "`dsiplay` expects a single argument"

meth

let newlineBuiltin =
let meth, il = declareBuiltinMethod "newline"
let fail = il.Create(OpCodes.Nop)

il.Emit(OpCodes.Ldarg_0)
il.Emit(OpCodes.Ldlen)
il.Emit(OpCodes.Ldc_I4_0)
il.Emit(OpCodes.Bne_Un, fail)

let write = typeof<Console>.GetMethod("WriteLine", [| |])
let write = assm.MainModule.ImportReference write
il.Emit(OpCodes.Call, write)
il.Emit(OpCodes.Ldnull)
il.Emit(OpCodes.Ret)

il.Append(fail)
emitThrow il assm "`newline` expects no arguments"

meth

[ ("+", createArithBuiltin "arithadd" OpCodes.Add 0.0)
; ("-", createArithBuiltin "arithsub" OpCodes.Sub 0.0)
; ("/", createArithBuiltin "arithdiv" OpCodes.Div 1.0)
Expand All @@ -241,9 +173,7 @@ let private createBuiltins (assm: AssemblyDefinition) (ty: TypeDefinition) =
; (">", createCompBuiltin "arithgt" OpCodes.Bgt)
; ("<", createCompBuiltin "arithlt" OpCodes.Blt)
; (">=", createCompBuiltin "arithgte" OpCodes.Bge)
; ("<=", createCompBuiltin "arithlte" OpCodes.Ble)
; ("newline", newlineBuiltin)
; ("display", displayBuiltin) ]
; ("<=", createCompBuiltin "arithlte" OpCodes.Ble) ]
|> Seq.map(fun (name, method) -> (name, method :> MethodReference))
|> Map.ofSeq

Expand Down
10 changes: 10 additions & 0 deletions src/Serehfa/ArgHelpers.cs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,16 @@ namespace Serehfa
{
public class ArgHelpers
{
public static void CheckNoArgs(object[] args)
{
if (args.Length != 0)
{
throw new ArgumentException(
$"Expected no arguments, but received {args.Length}",
nameof(args));
}
}

public static T UnpackArgs<T>(object[] args)
{
if (args.Length != 1)
Expand Down
24 changes: 24 additions & 0 deletions src/Serehfa/Undefined.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
using System;

namespace Serehfa
{
/// <summary>
/// The unspecified / undefined value. Instances of this type are returned
/// from scheme functions where no specific return value is specified in
/// the standard.
/// </summary>
public class Undefined
{
private static Lazy<Undefined> s_Instance = new Lazy<Undefined>();
public static Undefined Instance => s_Instance.Value;

public override string ToString() => "; Unspecified value";

/// <summary>
/// Instances of these values are never equal to anything else.
/// </summary>
public override bool Equals(object obj) => false;

public override int GetHashCode() => base.GetHashCode();
}
}
39 changes: 39 additions & 0 deletions src/Serehfa/Write.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
namespace Serehfa
{
using System;
using static ArgHelpers;

public static class Write
{
[LispBuiltin("newline")]
public static object Newline(object[] args)
{
CheckNoArgs(args);

Console.WriteLine();

return Undefined.Instance;
}

/// <summary>
/// Display builtin. This is intended for user-readable output rather than
/// any machine readable round tripping. Printing out strings &amp; chars should
/// display their raw form. All other objects is up to the implementation.
///
/// This implementation calls `ToString` on the underlying .NET object and
/// uses that directly.
/// </summary>
[LispBuiltin("display")]
public static object Display(object[] args)
{
var obj = UnpackArgs<object>(args);

var repr = obj == null ?
"'()" : obj.ToString();

Console.Write(repr);

return Undefined.Instance;
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,7 @@
"Exit": 0
},
"display.scm": {
"Output": "\n\n123\n",
"Output": "\n'()\n123\n",
"Error": "",
"Exit": 0
},
Expand Down Expand Up @@ -157,7 +157,7 @@
"Exit": 0
},
"quotes.scm": {
"Output": "1\nhello world\n\n(1 2 3)\n1\nhello world\n\n(1 2 3)\n(quote (quote 123))\nd\n| |\n|test\\x9;ident|\n|hello world|\nTrue\n",
"Output": "1\nhello world\n'()\n(1 2 3)\n1\nhello world\n'()\n(1 2 3)\n(quote (quote 123))\nd\n| |\n|test\\x9;ident|\n|hello world|\nTrue\n",
"Error": "",
"Exit": 0
},
Expand Down

0 comments on commit 02ca7e0

Please sign in to comment.