mirror of
https://github.com/Smaug123/WoofWare.Myriad
synced 2025-10-26 22:29:01 +00:00
Compare commits
3 Commits
WoofWare.M
...
1e1176bec5
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
1e1176bec5 | ||
|
|
16daa1b7ca | ||
|
|
ef4a83ae61 |
180
ConsumePlugin/Catamorphism.fs
Normal file
180
ConsumePlugin/Catamorphism.fs
Normal file
@@ -0,0 +1,180 @@
|
|||||||
|
namespace ConsumePlugin
|
||||||
|
|
||||||
|
open WoofWare.Myriad.Plugins
|
||||||
|
|
||||||
|
type Const =
|
||||||
|
| Int of int
|
||||||
|
| String of string
|
||||||
|
|
||||||
|
type PairOpKind =
|
||||||
|
| NormalSeq
|
||||||
|
| ThenDoSeq
|
||||||
|
|
||||||
|
[<CreateCatamorphism>]
|
||||||
|
type Expr =
|
||||||
|
| Const of Const
|
||||||
|
| Pair of Expr * Expr * PairOpKind
|
||||||
|
| Sequential of Expr list
|
||||||
|
| Builder of Expr * ExprBuilder
|
||||||
|
|
||||||
|
and [<CreateCatamorphism>] ExprBuilder =
|
||||||
|
| Child of ExprBuilder
|
||||||
|
| Parent of Expr
|
||||||
|
|
||||||
|
// Say that CreateCatamorphism-tagged types form the set T.
|
||||||
|
// Assert that each U in T is a discriminated union.
|
||||||
|
// For each type U in T, assign a generic parameter 'ret<U>.
|
||||||
|
// For each U:
|
||||||
|
// * Define the type [U]Cata, generic on all the parameters {'ret<U> : U in T}.
|
||||||
|
// * For each DU case C in type U:
|
||||||
|
// * create a method in [U]Cata, whose return value is 'ret<U> and whose args are the fields of the case C
|
||||||
|
// * any occurrence in a field of an input value of type equal to any element of T (say type V) is replaced by 'ret<V>
|
||||||
|
// Finally, define a type Cata<{'ret<U> for U in T}>
|
||||||
|
// with one member for each U, namely of type [U]Cata<{'ret<U> for U in T}>.
|
||||||
|
type ExprCata<'builderRet, 'ret> =
|
||||||
|
abstract Const : Const -> 'ret
|
||||||
|
abstract Pair : 'ret -> 'ret -> PairOpKind -> 'ret
|
||||||
|
abstract Sequential : 'ret list -> 'ret
|
||||||
|
abstract Builder : 'ret -> 'builderRet -> 'ret
|
||||||
|
|
||||||
|
type ExprBuilderCata<'builderRet, 'ret> =
|
||||||
|
abstract Child : 'builderRet -> 'builderRet
|
||||||
|
abstract Parent : 'ret -> 'builderRet
|
||||||
|
|
||||||
|
type Cata<'bret, 'ret> =
|
||||||
|
{
|
||||||
|
Expr : ExprCata<'bret, 'ret>
|
||||||
|
Builder : ExprBuilderCata<'bret, 'ret>
|
||||||
|
}
|
||||||
|
|
||||||
|
// Then we can create the noddy non-tail-rec implementation of `apply`.
|
||||||
|
// For each U in T, define apply{U}, generic on every {'ret<U> for U in T}, taking a Cata and a U and returning a 'ret<U>.
|
||||||
|
// The body of apply{U} is given by matching on the cases of U.
|
||||||
|
module Cata =
|
||||||
|
let rec apply<'bret, 'ret> (cata : Cata<'bret, 'ret>) (e : Expr) : 'ret =
|
||||||
|
match e with
|
||||||
|
| Const c -> cata.Expr.Const c
|
||||||
|
| Pair (expr, expr1, pairOpKind) -> cata.Expr.Pair (apply cata expr) (apply cata expr1) pairOpKind
|
||||||
|
| Sequential exprs -> exprs |> List.map (apply cata) |> cata.Expr.Sequential
|
||||||
|
| Builder (expr, exprBuilder) -> cata.Expr.Builder (apply cata expr) (applyB cata exprBuilder)
|
||||||
|
|
||||||
|
and applyB<'bret, 'ret> (cata : Cata<'bret, 'ret>) (e : ExprBuilder) : 'bret =
|
||||||
|
match e with
|
||||||
|
| Child b -> cata.Builder.Child (applyB cata b)
|
||||||
|
| Parent p -> cata.Builder.Parent (apply cata p)
|
||||||
|
|
||||||
|
// The tail-recursive version is harder.
|
||||||
|
module TailRecCata =
|
||||||
|
[<RequireQualifiedAccess>]
|
||||||
|
type private Instruction =
|
||||||
|
| ProcessExpr of Expr
|
||||||
|
| ProcessBuilder of ExprBuilder
|
||||||
|
| Pair of PairOpKind
|
||||||
|
| Sequential of int
|
||||||
|
| Builder
|
||||||
|
| Child
|
||||||
|
| Parent
|
||||||
|
|
||||||
|
let private loop (cata : Cata<_, _>) (instructions : ResizeArray<_>) =
|
||||||
|
let resultsStack = ResizeArray ()
|
||||||
|
let builderResultsStack = ResizeArray ()
|
||||||
|
|
||||||
|
while instructions.Count > 0 do
|
||||||
|
let currentInstruction = instructions.[instructions.Count - 1]
|
||||||
|
instructions.RemoveAt (instructions.Count - 1)
|
||||||
|
|
||||||
|
match currentInstruction with
|
||||||
|
| Instruction.ProcessBuilder builder ->
|
||||||
|
match builder with
|
||||||
|
| Child exprBuilder ->
|
||||||
|
instructions.Add Instruction.Child
|
||||||
|
instructions.Add (Instruction.ProcessBuilder exprBuilder)
|
||||||
|
| Parent expr ->
|
||||||
|
instructions.Add Instruction.Parent
|
||||||
|
instructions.Add (Instruction.ProcessExpr expr)
|
||||||
|
| Instruction.ProcessExpr currentExpr ->
|
||||||
|
match currentExpr with
|
||||||
|
| Const c -> resultsStack.Add (cata.Expr.Const c)
|
||||||
|
| Pair (expr, expr1, pairOpKind) ->
|
||||||
|
instructions.Add (Instruction.Pair pairOpKind)
|
||||||
|
instructions.Add (Instruction.ProcessExpr expr1)
|
||||||
|
instructions.Add (Instruction.ProcessExpr expr)
|
||||||
|
| Sequential exprs ->
|
||||||
|
instructions.Add (Instruction.Sequential (List.length exprs))
|
||||||
|
|
||||||
|
for expr in exprs do
|
||||||
|
instructions.Add (Instruction.ProcessExpr expr)
|
||||||
|
| Builder (expr, exprBuilder) ->
|
||||||
|
instructions.Add Instruction.Builder
|
||||||
|
instructions.Add (Instruction.ProcessExpr expr)
|
||||||
|
instructions.Add (Instruction.ProcessBuilder exprBuilder)
|
||||||
|
| Instruction.Pair pairOpKind ->
|
||||||
|
let expr = resultsStack.[resultsStack.Count - 1]
|
||||||
|
let expr1 = resultsStack.[resultsStack.Count - 2]
|
||||||
|
resultsStack.RemoveRange (resultsStack.Count - 2, 2)
|
||||||
|
cata.Expr.Pair expr expr1 pairOpKind |> resultsStack.Add
|
||||||
|
| Instruction.Sequential count ->
|
||||||
|
let values =
|
||||||
|
seq {
|
||||||
|
for i = resultsStack.Count - 1 downto resultsStack.Count - count do
|
||||||
|
yield resultsStack.[i]
|
||||||
|
}
|
||||||
|
|> Seq.toList
|
||||||
|
|
||||||
|
resultsStack.RemoveRange (resultsStack.Count - count, count)
|
||||||
|
cata.Expr.Sequential values |> resultsStack.Add
|
||||||
|
| Instruction.Builder ->
|
||||||
|
let expr = resultsStack.[resultsStack.Count - 1]
|
||||||
|
resultsStack.RemoveAt (resultsStack.Count - 1)
|
||||||
|
let exprBuilder = builderResultsStack.[builderResultsStack.Count - 1]
|
||||||
|
builderResultsStack.RemoveAt (builderResultsStack.Count - 1)
|
||||||
|
cata.Expr.Builder expr exprBuilder |> resultsStack.Add
|
||||||
|
| Instruction.Child ->
|
||||||
|
let exprBuilder = builderResultsStack.[builderResultsStack.Count - 1]
|
||||||
|
builderResultsStack.RemoveAt (builderResultsStack.Count - 1)
|
||||||
|
cata.Builder.Child exprBuilder |> builderResultsStack.Add
|
||||||
|
| Instruction.Parent ->
|
||||||
|
let expr = resultsStack.[resultsStack.Count - 1]
|
||||||
|
resultsStack.RemoveAt (resultsStack.Count - 1)
|
||||||
|
cata.Builder.Parent expr |> builderResultsStack.Add
|
||||||
|
|
||||||
|
resultsStack, builderResultsStack
|
||||||
|
|
||||||
|
let run (cata : Cata<'bret, 'ret>) (e : Expr) : 'ret =
|
||||||
|
let instructions = ResizeArray ()
|
||||||
|
instructions.Add (Instruction.ProcessExpr e)
|
||||||
|
|
||||||
|
let resultsStack, builderResultsStack = loop cata instructions
|
||||||
|
|
||||||
|
if builderResultsStack.Count > 0 then
|
||||||
|
failwith "logic error"
|
||||||
|
|
||||||
|
Seq.exactlyOne resultsStack
|
||||||
|
|
||||||
|
let runBuilder (cata : Cata<'bret, 'ret>) (e : ExprBuilder) : 'bret =
|
||||||
|
let instructions = ResizeArray ()
|
||||||
|
instructions.Add (Instruction.ProcessBuilder e)
|
||||||
|
|
||||||
|
let resultsStack, builderResultsStack = loop cata instructions
|
||||||
|
|
||||||
|
if resultsStack.Count > 0 then
|
||||||
|
failwith "logic error"
|
||||||
|
|
||||||
|
Seq.exactlyOne builderResultsStack
|
||||||
|
|
||||||
|
module CataExample =
|
||||||
|
let id =
|
||||||
|
{
|
||||||
|
Expr =
|
||||||
|
{ new ExprCata<_, _> with
|
||||||
|
member _.Const x = Const x
|
||||||
|
member _.Pair x y z = Pair (x, y, z)
|
||||||
|
member _.Sequential xs = Sequential xs
|
||||||
|
member _.Builder x b = Builder (x, b)
|
||||||
|
}
|
||||||
|
Builder =
|
||||||
|
{ new ExprBuilderCata<_, _> with
|
||||||
|
member _.Child x = Child x
|
||||||
|
member _.Parent x = Parent x
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -39,6 +39,10 @@
|
|||||||
<Compile Include="GeneratedSerde.fs">
|
<Compile Include="GeneratedSerde.fs">
|
||||||
<MyriadFile>SerializationAndDeserialization.fs</MyriadFile>
|
<MyriadFile>SerializationAndDeserialization.fs</MyriadFile>
|
||||||
</Compile>
|
</Compile>
|
||||||
|
<Compile Include="Catamorphism.fs" />
|
||||||
|
<Compile Include="GeneratedCatamorphism.fs">
|
||||||
|
<MyriadFile>Catamorphism.fs</MyriadFile>
|
||||||
|
</Compile>
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
|
|||||||
39
ConsumePlugin/GeneratedCatamorphism.fs
Normal file
39
ConsumePlugin/GeneratedCatamorphism.fs
Normal file
@@ -0,0 +1,39 @@
|
|||||||
|
//------------------------------------------------------------------------------
|
||||||
|
// This code was generated by myriad.
|
||||||
|
// Changes to this file will be lost when the code is regenerated.
|
||||||
|
//------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
namespace ConsumePlugin
|
||||||
|
|
||||||
|
open WoofWare.Myriad.Plugins
|
||||||
|
|
||||||
|
/// Catamorphism
|
||||||
|
[<RequireQualifiedAccess>]
|
||||||
|
module ExprCata =
|
||||||
|
[<RequireQualifiedAccess>]
|
||||||
|
type private Instruction =
|
||||||
|
| ProcessExpr of Expr
|
||||||
|
| ProcessExprBuilder of ExprBuilder
|
||||||
|
| ExprPair of PairOpKind
|
||||||
|
| ExprSequential of int
|
||||||
|
| ExprBuilder
|
||||||
|
| ExprBuilderChild
|
||||||
|
| ExprBuilderParent
|
||||||
|
|
||||||
|
/// Execute the catamorphism.
|
||||||
|
let runExpr (cata : Cata<'ExprRet, 'ExprBuilderRet>) (x : Expr) : 'ExprRet =
|
||||||
|
let instructions = ResizeArray ()
|
||||||
|
instructions.Add (Instruction.ProcessExpr x)
|
||||||
|
let ExprRetStack, ExprBuilderRetStack = loop cata instructions
|
||||||
|
Seq.exactlyOne ExprRetStack
|
||||||
|
|
||||||
|
/// Execute the catamorphism.
|
||||||
|
let runExprBuilder (cata : Cata<'ExprRet, 'ExprBuilderRet>) (x : ExprBuilder) : 'ExprBuilderRet =
|
||||||
|
let instructions = ResizeArray ()
|
||||||
|
instructions.Add (Instruction.ProcessExprBuilder x)
|
||||||
|
let ExprRetStack, ExprBuilderRetStack = loop cata instructions
|
||||||
|
Seq.exactlyOne ExprBuilderRetStack
|
||||||
@@ -62,3 +62,8 @@ type JsonParseAttribute (isExtensionMethod : bool) =
|
|||||||
/// i.e. to stamp out HTTP REST clients from interfaces defining the API.
|
/// i.e. to stamp out HTTP REST clients from interfaces defining the API.
|
||||||
type HttpClientAttribute () =
|
type HttpClientAttribute () =
|
||||||
inherit Attribute ()
|
inherit Attribute ()
|
||||||
|
|
||||||
|
/// Attribute indicating a DU type to which the "create catamorphism" Myriad
|
||||||
|
/// generator should apply during build.
|
||||||
|
type CreateCatamorphismAttribute () =
|
||||||
|
inherit Attribute ()
|
||||||
|
|||||||
403
WoofWare.Myriad.Plugins/CataGenerator.fs
Normal file
403
WoofWare.Myriad.Plugins/CataGenerator.fs
Normal file
@@ -0,0 +1,403 @@
|
|||||||
|
namespace WoofWare.Myriad.Plugins
|
||||||
|
|
||||||
|
open System
|
||||||
|
open System.Text
|
||||||
|
open Fantomas.FCS.Syntax
|
||||||
|
open Fantomas.FCS.SyntaxTrivia
|
||||||
|
open Fantomas.FCS.Xml
|
||||||
|
open Myriad.Core
|
||||||
|
|
||||||
|
[<RequireQualifiedAccess>]
|
||||||
|
module internal CataGenerator =
|
||||||
|
open Fantomas.FCS.Text.Range
|
||||||
|
open Myriad.Core.Ast
|
||||||
|
|
||||||
|
/// Returns a function:
|
||||||
|
/// let run{Case} (cata : Cata<{typars}>) (x : {Case}) : {TyPar} =
|
||||||
|
/// let instructions = ResizeArray ()
|
||||||
|
/// instructions.Add (Instruction.Process{Case} e)
|
||||||
|
/// let {typar1}Results, {typar2}Results, ... = loop cata instructions
|
||||||
|
/// { for all non-relevant typars: }
|
||||||
|
/// if {typar}Results.Count > 0 then failwith "logic error"
|
||||||
|
/// Seq.exactlyOne {relevantTypar}Stack
|
||||||
|
let createRunFunction (allTypars : SynType list) (relevantTypar : SynType) (unionType : SynTypeDefn) : SynBinding =
|
||||||
|
let relevantTypeName =
|
||||||
|
match unionType with
|
||||||
|
| SynTypeDefn.SynTypeDefn (SynComponentInfo.SynComponentInfo (longId = id), _, _, _, _, _) -> List.last id
|
||||||
|
|
||||||
|
let allTyparNames =
|
||||||
|
allTypars
|
||||||
|
|> List.map (fun ty ->
|
||||||
|
match ty with
|
||||||
|
| SynType.Var (SynTypar.SynTypar (ident = ident), _) -> ident
|
||||||
|
| _ -> failwith "logic error in generator"
|
||||||
|
)
|
||||||
|
|
||||||
|
let relevantTyparName =
|
||||||
|
match relevantTypar with
|
||||||
|
| SynType.Var (SynTypar.SynTypar (ident = ident), _) -> ident
|
||||||
|
| _ -> failwith "logic error in generator"
|
||||||
|
|
||||||
|
SynBinding.SynBinding (
|
||||||
|
None,
|
||||||
|
SynBindingKind.Normal,
|
||||||
|
false,
|
||||||
|
false,
|
||||||
|
[],
|
||||||
|
PreXmlDoc.Create " Execute the catamorphism.",
|
||||||
|
SynValData.SynValData (
|
||||||
|
None,
|
||||||
|
SynValInfo.SynValInfo (
|
||||||
|
[ [ SynArgInfo.CreateIdString "cata" ] ; [ SynArgInfo.CreateIdString "x" ] ],
|
||||||
|
SynArgInfo.SynArgInfo ([], false, None)
|
||||||
|
),
|
||||||
|
None
|
||||||
|
),
|
||||||
|
SynPat.CreateLongIdent (
|
||||||
|
SynLongIdent.CreateString ("run" + relevantTypeName.idText),
|
||||||
|
[
|
||||||
|
SynPat.CreateParen (
|
||||||
|
SynPat.CreateTyped (
|
||||||
|
SynPat.CreateNamed (Ident.Create "cata"),
|
||||||
|
SynType.App (
|
||||||
|
SynType.CreateLongIdent "Cata",
|
||||||
|
Some range0,
|
||||||
|
allTypars,
|
||||||
|
List.replicate (allTypars.Length - 1) range0,
|
||||||
|
Some range0,
|
||||||
|
false,
|
||||||
|
range0
|
||||||
|
)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
SynPat.CreateParen (
|
||||||
|
SynPat.CreateTyped (
|
||||||
|
SynPat.CreateNamed (Ident.Create "x"),
|
||||||
|
SynType.CreateLongIdent (SynLongIdent.CreateFromLongIdent [ relevantTypeName ])
|
||||||
|
)
|
||||||
|
)
|
||||||
|
]
|
||||||
|
),
|
||||||
|
Some (SynBindingReturnInfo.Create relevantTypar),
|
||||||
|
SynExpr.CreateTyped (
|
||||||
|
SynExpr.LetOrUse (
|
||||||
|
false,
|
||||||
|
false,
|
||||||
|
[
|
||||||
|
SynBinding.Let (
|
||||||
|
valData = SynValData.SynValData (None, SynValInfo.Empty, None),
|
||||||
|
pattern = SynPat.CreateNamed (Ident.Create "instructions"),
|
||||||
|
expr =
|
||||||
|
SynExpr.CreateApp (
|
||||||
|
SynExpr.CreateIdentString "ResizeArray",
|
||||||
|
SynExpr.CreateConst SynConst.Unit
|
||||||
|
)
|
||||||
|
)
|
||||||
|
],
|
||||||
|
SynExpr.CreateSequential
|
||||||
|
[
|
||||||
|
SynExpr.CreateApp (
|
||||||
|
SynExpr.CreateLongIdent (SynLongIdent.Create [ "instructions" ; "Add" ]),
|
||||||
|
SynExpr.CreateParen (
|
||||||
|
SynExpr.CreateApp (
|
||||||
|
SynExpr.CreateLongIdent (
|
||||||
|
SynLongIdent.Create [ "Instruction" ; "Process" + relevantTypeName.idText ]
|
||||||
|
),
|
||||||
|
SynExpr.CreateLongIdent (SynLongIdent.CreateString "x")
|
||||||
|
)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
SynExpr.LetOrUse (
|
||||||
|
false,
|
||||||
|
false,
|
||||||
|
[
|
||||||
|
SynBinding.Let (
|
||||||
|
valData = SynValData.SynValData (None, SynValInfo.Empty, None),
|
||||||
|
pattern =
|
||||||
|
SynPat.Tuple (
|
||||||
|
false,
|
||||||
|
List.map
|
||||||
|
(fun (t : Ident) ->
|
||||||
|
SynPat.CreateNamed (Ident.Create (t.idText + "Stack"))
|
||||||
|
)
|
||||||
|
allTyparNames,
|
||||||
|
List.replicate (allTypars.Length - 1) range0,
|
||||||
|
range0
|
||||||
|
),
|
||||||
|
expr =
|
||||||
|
SynExpr.CreateApp (
|
||||||
|
SynExpr.CreateApp (
|
||||||
|
SynExpr.CreateIdentString "loop",
|
||||||
|
SynExpr.CreateIdentString "cata"
|
||||||
|
),
|
||||||
|
SynExpr.CreateIdentString "instructions"
|
||||||
|
)
|
||||||
|
)
|
||||||
|
],
|
||||||
|
// TODO: add the "all other stacks are empty" sanity checks
|
||||||
|
SynExpr.CreateApp (
|
||||||
|
SynExpr.CreateLongIdent (SynLongIdent.Create [ "Seq" ; "exactlyOne" ]),
|
||||||
|
SynExpr.CreateIdent (Ident.Create (relevantTyparName.idText + "Stack"))
|
||||||
|
),
|
||||||
|
range0,
|
||||||
|
{
|
||||||
|
SynExprLetOrUseTrivia.InKeyword = None
|
||||||
|
}
|
||||||
|
)
|
||||||
|
],
|
||||||
|
range0,
|
||||||
|
{
|
||||||
|
InKeyword = None
|
||||||
|
}
|
||||||
|
),
|
||||||
|
relevantTypar
|
||||||
|
),
|
||||||
|
range0,
|
||||||
|
DebugPointAtBinding.NoneAtLet,
|
||||||
|
{
|
||||||
|
LeadingKeyword = SynLeadingKeyword.Let range0
|
||||||
|
InlineKeyword = None
|
||||||
|
EqualsRange = Some range0
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
let getName (ty : SynTypeDefn) : LongIdent =
|
||||||
|
match ty with
|
||||||
|
| SynTypeDefn.SynTypeDefn (SynComponentInfo.SynComponentInfo (_, _, _, id, _, _, _, _), _, _, _, _, _) -> id
|
||||||
|
|
||||||
|
type UnionField =
|
||||||
|
{
|
||||||
|
Type : SynType
|
||||||
|
Name : Ident option
|
||||||
|
}
|
||||||
|
|
||||||
|
type UnionCase =
|
||||||
|
{
|
||||||
|
Name : SynIdent
|
||||||
|
Fields : UnionField list
|
||||||
|
}
|
||||||
|
|
||||||
|
let getCases (SynTypeDefn.SynTypeDefn (_, repr, _, _, _, _)) : UnionCase list =
|
||||||
|
match repr with
|
||||||
|
| SynTypeDefnRepr.Simple (SynTypeDefnSimpleRepr.Union (_, cases, _), range0) ->
|
||||||
|
cases
|
||||||
|
|> List.map (fun (SynUnionCase.SynUnionCase (_, ident, kind, _, _, _, _)) ->
|
||||||
|
match kind with
|
||||||
|
| SynUnionCaseKind.FullType _ -> failwith "FullType union cases not supported"
|
||||||
|
| SynUnionCaseKind.Fields fields ->
|
||||||
|
{
|
||||||
|
Name = ident
|
||||||
|
Fields =
|
||||||
|
fields
|
||||||
|
|> List.map (fun (SynField.SynField (_, _, id, ty, _, _, _, _, _)) ->
|
||||||
|
{
|
||||||
|
Type = ty
|
||||||
|
Name = id
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
)
|
||||||
|
| _ -> failwithf "Failed to get union cases for type that was: %+A" repr
|
||||||
|
|
||||||
|
/// Given the input `| Pair of Expr * Expr * PairOpKind`,
|
||||||
|
/// strips out any members which contain recursive calls.
|
||||||
|
/// TODO: support lists and other compound types.
|
||||||
|
let createInstructionCases (allUnionTypes : SynTypeDefn list) (case : UnionCase) : UnionField list option =
|
||||||
|
let hasRecursion, cases =
|
||||||
|
((false, []), case.Fields)
|
||||||
|
||> List.fold (fun (hasRecursion, cases) field ->
|
||||||
|
match SynType.stripOptionalParen field.Type with
|
||||||
|
| ListType ty ->
|
||||||
|
match ty with
|
||||||
|
| SynType.LongIdent (SynLongIdent.SynLongIdent (ty, _, _)) ->
|
||||||
|
let isListOfSelf =
|
||||||
|
allUnionTypes
|
||||||
|
|> List.exists (fun unionTy -> List.last(getName unionTy).idText = List.last(ty).idText)
|
||||||
|
|
||||||
|
if isListOfSelf then
|
||||||
|
// store an int which is the length of the list
|
||||||
|
true, SynType.Int () :: cases
|
||||||
|
else
|
||||||
|
hasRecursion, field.Type :: cases
|
||||||
|
| _ -> hasRecursion, field.Type :: cases
|
||||||
|
| PrimitiveType _ -> hasRecursion, field.Type :: cases
|
||||||
|
| SynType.LongIdent (SynLongIdent.SynLongIdent (ty, _, _)) ->
|
||||||
|
let isSelf =
|
||||||
|
allUnionTypes
|
||||||
|
|> List.exists (fun unionTy -> List.last(getName unionTy).idText = List.last(ty).idText)
|
||||||
|
|
||||||
|
if isSelf then
|
||||||
|
true, cases
|
||||||
|
else
|
||||||
|
hasRecursion, field.Type :: cases
|
||||||
|
| _ -> failwithf "Unrecognised type: %+A" field.Type
|
||||||
|
)
|
||||||
|
|
||||||
|
if hasRecursion then
|
||||||
|
cases
|
||||||
|
|> List.rev
|
||||||
|
|> List.map (fun ty ->
|
||||||
|
{
|
||||||
|
Name = None
|
||||||
|
Type = ty
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|> Some
|
||||||
|
else
|
||||||
|
None
|
||||||
|
|
||||||
|
let createInstructionType (allUnionTypes : SynTypeDefn list) : SynTypeDefn =
|
||||||
|
// One union case for each union type, and then
|
||||||
|
// a union case for each union case which contains a recursive reference.
|
||||||
|
let casesFromProcess : SynUnionCase list =
|
||||||
|
allUnionTypes
|
||||||
|
|> List.map (fun unionType ->
|
||||||
|
let name = getName unionType
|
||||||
|
|
||||||
|
SynUnionCase.Create (
|
||||||
|
Ident.Create ("Process" + (List.last name).idText),
|
||||||
|
[
|
||||||
|
SynField.Create (SynType.CreateLongIdent (SynLongIdent.CreateFromLongIdent name))
|
||||||
|
]
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
let casesFromCases =
|
||||||
|
allUnionTypes
|
||||||
|
|> List.collect (fun unionType ->
|
||||||
|
getCases unionType
|
||||||
|
|> List.choose (fun case ->
|
||||||
|
let fields = createInstructionCases allUnionTypes case
|
||||||
|
|
||||||
|
match fields with
|
||||||
|
| None -> None
|
||||||
|
| Some fields ->
|
||||||
|
let name =
|
||||||
|
match case.Name with
|
||||||
|
| SynIdent.SynIdent (ident, _) ->
|
||||||
|
(List.last (getName unionType)).idText + ident.idText |> Ident.Create
|
||||||
|
|
||||||
|
SynUnionCase.Create (name, fields |> List.map (fun field -> SynField.Create field.Type))
|
||||||
|
|> Some
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
let cases = casesFromProcess @ casesFromCases
|
||||||
|
|
||||||
|
SynTypeDefn.SynTypeDefn (
|
||||||
|
SynComponentInfo.SynComponentInfo (
|
||||||
|
[ SynAttributeList.Create [ SynAttribute.RequireQualifiedAccess () ] ],
|
||||||
|
None,
|
||||||
|
[],
|
||||||
|
[ Ident.Create "Instruction" ],
|
||||||
|
PreXmlDoc.Empty,
|
||||||
|
false,
|
||||||
|
Some (SynAccess.Private range0),
|
||||||
|
range0
|
||||||
|
),
|
||||||
|
SynTypeDefnRepr.Simple (SynTypeDefnSimpleRepr.Union (None, cases, range0), range0),
|
||||||
|
[],
|
||||||
|
None,
|
||||||
|
range0,
|
||||||
|
{
|
||||||
|
LeadingKeyword = SynTypeDefnLeadingKeyword.Type range0
|
||||||
|
EqualsRange = Some range0
|
||||||
|
WithKeyword = None
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
let createModule
|
||||||
|
(opens : SynOpenDeclTarget list)
|
||||||
|
(ns : LongIdent)
|
||||||
|
(taggedType : SynTypeDefn)
|
||||||
|
(allUnionTypes : SynTypeDefn list)
|
||||||
|
: SynModuleOrNamespace
|
||||||
|
=
|
||||||
|
let moduleName : LongIdent =
|
||||||
|
List.last (getName taggedType)
|
||||||
|
|> fun x -> x.idText + "Cata"
|
||||||
|
|> Ident.Create
|
||||||
|
|> List.singleton
|
||||||
|
|
||||||
|
let attribs = [ SynAttributeList.Create (SynAttribute.RequireQualifiedAccess ()) ]
|
||||||
|
|
||||||
|
let modInfo =
|
||||||
|
SynComponentInfo.Create (
|
||||||
|
moduleName,
|
||||||
|
attributes = attribs,
|
||||||
|
xmldoc = PreXmlDoc.Create " Catamorphism" // TODO: better docstring
|
||||||
|
)
|
||||||
|
|
||||||
|
let allTypars =
|
||||||
|
allUnionTypes
|
||||||
|
|> List.map (fun unionType ->
|
||||||
|
List.last (getName unionType)
|
||||||
|
|> fun x -> x.idText
|
||||||
|
|> fun s -> s + "Ret"
|
||||||
|
|> Ident.Create
|
||||||
|
|> fun x -> SynTypar.SynTypar (x, TyparStaticReq.None, false)
|
||||||
|
|> fun x -> SynType.Var (x, range0)
|
||||||
|
)
|
||||||
|
|
||||||
|
let runFunctions =
|
||||||
|
List.zip allUnionTypes allTypars
|
||||||
|
|> List.map (fun (unionType, relevantTypar) -> createRunFunction allTypars relevantTypar unionType)
|
||||||
|
|
||||||
|
SynModuleOrNamespace.CreateNamespace (
|
||||||
|
ns,
|
||||||
|
decls =
|
||||||
|
[
|
||||||
|
for openStatement in opens do
|
||||||
|
yield SynModuleDecl.CreateOpen openStatement
|
||||||
|
yield
|
||||||
|
SynModuleDecl.CreateNestedModule (
|
||||||
|
modInfo,
|
||||||
|
[
|
||||||
|
SynModuleDecl.Types ([ createInstructionType allUnionTypes ], range0)
|
||||||
|
SynModuleDecl.CreateLet runFunctions
|
||||||
|
]
|
||||||
|
)
|
||||||
|
]
|
||||||
|
)
|
||||||
|
|
||||||
|
/// Myriad generator that provides an HTTP client for an interface type using RestEase annotations.
|
||||||
|
[<MyriadGenerator("create-catamorphism")>]
|
||||||
|
type CreateCatamorphismGenerator () =
|
||||||
|
|
||||||
|
interface IMyriadGenerator with
|
||||||
|
member _.ValidInputExtensions = [ ".fs" ]
|
||||||
|
|
||||||
|
member _.Generate (context : GeneratorContext) =
|
||||||
|
let ast, _ =
|
||||||
|
Ast.fromFilename context.InputFilename |> Async.RunSynchronously |> Array.head
|
||||||
|
|
||||||
|
let types = Ast.extractTypeDefn ast
|
||||||
|
|
||||||
|
let opens = AstHelper.extractOpens ast
|
||||||
|
|
||||||
|
let namespaceAndTypes =
|
||||||
|
types
|
||||||
|
|> List.choose (fun (ns, types) ->
|
||||||
|
match types |> List.tryFind Ast.hasAttribute<CreateCatamorphismAttribute> with
|
||||||
|
| Some taggedType ->
|
||||||
|
let anyNonUnion =
|
||||||
|
types
|
||||||
|
|> List.exists (fun (SynTypeDefn.SynTypeDefn (_, repr, _, _, _, _)) ->
|
||||||
|
match repr with
|
||||||
|
| SynTypeDefnRepr.Simple (SynTypeDefnSimpleRepr.Union _, _) -> false
|
||||||
|
| _ -> true
|
||||||
|
)
|
||||||
|
|
||||||
|
if anyNonUnion then
|
||||||
|
failwith
|
||||||
|
"Error: all types recursively defined together with a CreateCatamorphism type must be discriminated unions"
|
||||||
|
|
||||||
|
Some (ns, taggedType, types)
|
||||||
|
| _ -> None
|
||||||
|
)
|
||||||
|
|
||||||
|
let modules =
|
||||||
|
namespaceAndTypes
|
||||||
|
|> List.map (fun (ns, taggedType, types) -> CataGenerator.createModule opens ns taggedType types)
|
||||||
|
|
||||||
|
Output.Ast modules
|
||||||
@@ -34,6 +34,7 @@
|
|||||||
<Compile Include="JsonSerializeGenerator.fs"/>
|
<Compile Include="JsonSerializeGenerator.fs"/>
|
||||||
<Compile Include="JsonParseGenerator.fs"/>
|
<Compile Include="JsonParseGenerator.fs"/>
|
||||||
<Compile Include="HttpClientGenerator.fs"/>
|
<Compile Include="HttpClientGenerator.fs"/>
|
||||||
|
<Compile Include="CataGenerator.fs" />
|
||||||
<EmbeddedResource Include="version.json"/>
|
<EmbeddedResource Include="version.json"/>
|
||||||
<EmbeddedResource Include="SurfaceBaseline.txt"/>
|
<EmbeddedResource Include="SurfaceBaseline.txt"/>
|
||||||
<None Include="..\README.md">
|
<None Include="..\README.md">
|
||||||
|
|||||||
Reference in New Issue
Block a user