Compare commits

...

5 Commits

Author SHA1 Message Date
Smaug123
1793e9490f More 2024-02-16 19:23:15 +00:00
Smaug123
a524c1104d Bit more 2024-02-16 15:36:06 +00:00
Smaug123
d651aae6fb Add match clause 2024-02-16 14:26:41 +00:00
Smaug123
b7f7db8c11 Generate outside of loop 2024-02-16 14:07:15 +00:00
Smaug123
65d2263a6c Generate the cata types 2024-02-16 12:50:57 +00:00
8 changed files with 860 additions and 133 deletions

View File

@@ -17,7 +17,7 @@ type Expr =
| Sequential of Expr list
| Builder of Expr * ExprBuilder
and [<CreateCatamorphism>] ExprBuilder =
and ExprBuilder =
| Child of ExprBuilder
| Parent of Expr
@@ -75,7 +75,7 @@ module TailRecCata =
| Child
| Parent
let private loop (cata : Cata<_, _>) (instructions : ResizeArray<_>) =
let private loop (cata : Cata<_, _>) (instructions : ResizeArray<Instruction>) =
let resultsStack = ResizeArray ()
let builderResultsStack = ResizeArray ()

View File

@@ -11,29 +11,83 @@ namespace ConsumePlugin
open WoofWare.Myriad.Plugins
/// Catamorphism
/// Description of how to combine cases during a fold
type ExprCata<'Expr, 'ExprBuilder> =
/// How to operate on the Const case
abstract Const : Const -> 'Expr
/// How to operate on the Pair case
abstract Pair : 'Expr -> 'Expr -> PairOpKind -> 'Expr
/// How to operate on the Sequential case
abstract Sequential : 'Expr list -> 'Expr
/// How to operate on the Builder case
abstract Builder : 'Expr -> 'ExprBuilder -> 'Expr
/// Description of how to combine cases during a fold
type ExprBuilderCata<'Expr, 'ExprBuilder> =
/// How to operate on the Child case
abstract Child : 'ExprBuilder -> 'ExprBuilder
/// How to operate on the Parent case
abstract Parent : 'Expr -> 'ExprBuilder
/// Specifies how to perform a fold (catamorphism) over the type Expr.
type Cata<'Expr, 'ExprBuilder> =
{
/// TODO: doc
Expr : ExprCata<'Expr, 'ExprBuilder>
/// TODO: doc
ExprBuilder : ExprBuilderCata<'Expr, 'ExprBuilder>
}
/// Methods to perform a catamorphism over the type Expr
[<RequireQualifiedAccess>]
module ExprCata =
[<RequireQualifiedAccess>]
type private Instruction =
| ProcessExpr of Expr
| ProcessExprBuilder of ExprBuilder
| ExprPair of PairOpKind
| ExprSequential of int
| ExprBuilder
| ExprBuilderChild
| ExprBuilderParent
| Process__Expr of Expr
| Process__ExprBuilder of ExprBuilder
| Expr_Pair of PairOpKind
| Expr_Sequential of int
| Expr_Builder
| ExprBuilder_Child
| ExprBuilder_Parent
let private loop (cata : Cata<_, _>) (instructions : ResizeArray<Instruction>) =
let exprBuilderStack = ResizeArray ()
let exprStack = ResizeArray ()
while instructions.Count > 0 do
let currentInstruction = instructions.[instructions.Count - 1]
instructions.RemoveAt (instructions.Count - 1)
match currentInstruction with
| Instruction.Process__Expr x ->
match x with
| Expr.Const (arg0) -> cata.Expr.Const arg0 |> exprStack.Add
| Expr.Pair (arg0, arg1, arg2) -> ()
| Expr.Sequential (arg0) -> ()
| Expr.Builder (arg0, arg1) -> ()
| Instruction.Process__ExprBuilder x ->
match x with
| ExprBuilder.Child (arg0) -> ()
| ExprBuilder.Parent (arg0) -> ()
| Instruction.Expr_Pair (arg2) -> ()
| Instruction.Expr_Sequential (n) -> ()
| Instruction.Expr_Builder -> ()
| Instruction.ExprBuilder_Child -> ()
| Instruction.ExprBuilder_Parent -> ()
exprStack, exprBuilderStack
/// 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
instructions.Add (Instruction.Process__Expr 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
instructions.Add (Instruction.Process__ExprBuilder x)
let exprRetStack, exprBuilderRetStack = loop cata instructions
Seq.exactlyOne exprBuilderRetStack

View File

@@ -70,6 +70,18 @@ type internal RecordType =
Accessibility : SynAccess option
}
type UnionField =
{
Type : SynType
Name : Ident option
}
type UnionCase =
{
Name : SynIdent
Fields : UnionField list
}
[<RequireQualifiedAccess>]
module internal AstHelper =
@@ -383,6 +395,27 @@ module internal AstHelper =
Accessibility = accessibility
}
let getUnionCases (SynTypeDefn.SynTypeDefn (_, repr, _, _, _, _)) : UnionCase list =
match repr with
| SynTypeDefnRepr.Simple (SynTypeDefnSimpleRepr.Union (_, cases, _), _) ->
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
[<AutoOpen>]
module internal SynTypePatterns =

View File

@@ -1,7 +1,6 @@
namespace WoofWare.Myriad.Plugins
open System
open System.Text
open System.Transactions
open Fantomas.FCS.Syntax
open Fantomas.FCS.SyntaxTrivia
open Fantomas.FCS.Xml
@@ -101,7 +100,8 @@ module internal CataGenerator =
SynExpr.CreateParen (
SynExpr.CreateApp (
SynExpr.CreateLongIdent (
SynLongIdent.Create [ "Instruction" ; "Process" + relevantTypeName.idText ]
SynLongIdent.Create
[ "Instruction" ; "Process__" + relevantTypeName.idText ]
),
SynExpr.CreateLongIdent (SynLongIdent.CreateString "x")
)
@@ -118,7 +118,9 @@ module internal CataGenerator =
false,
List.map
(fun (t : Ident) ->
SynPat.CreateNamed (Ident.Create (t.idText + "Stack"))
SynPat.CreateNamed (
Ident.Create (t.idText + "Stack") |> Ident.lowerFirstLetter
)
)
allTyparNames,
List.replicate (allTypars.Length - 1) range0,
@@ -137,7 +139,9 @@ module internal CataGenerator =
// 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"))
SynExpr.CreateIdent (
Ident.Create (relevantTyparName.idText + "Stack") |> Ident.lowerFirstLetter
)
),
range0,
{
@@ -154,132 +158,158 @@ module internal CataGenerator =
),
range0,
DebugPointAtBinding.NoneAtLet,
{
LeadingKeyword = SynLeadingKeyword.Let range0
InlineKeyword = None
EqualsRange = Some range0
}
SynExpr.synBindingTriviaZero false
)
let getName (ty : SynTypeDefn) : LongIdent =
match ty with
| SynTypeDefn.SynTypeDefn (SynComponentInfo.SynComponentInfo (_, _, _, id, _, _, _, _), _, _, _, _, _) -> id
type UnionField =
{
Type : SynType
Name : Ident option
}
let getNameKey (ty : SynTypeDefn) : string =
getName ty |> List.map _.idText |> String.concat "/"
type UnionCase =
let getNameKeyUnion (unionType : SynType) : string =
match unionType with
| SynType.LongIdent (SynLongIdent.SynLongIdent (name, _, _)) -> name |> List.map _.idText |> String.concat "/"
| _ -> failwithf "unrecognised type: %+A" unionType
type FieldDescription =
| ListSelf of SynType
/// One of the union types itself
| Self of SynType
| NonRecursive of SynType
let analyse (allUnionTypes : SynTypeDefn list) (case : UnionCase) : (Ident option * FieldDescription) list =
let rec go (ty : SynType) : FieldDescription =
let stripped = SynType.stripOptionalParen ty
match stripped with
| ListType child ->
let gone = go child
match gone with
| FieldDescription.NonRecursive ty -> FieldDescription.NonRecursive stripped
| FieldDescription.Self ty -> FieldDescription.ListSelf ty
| FieldDescription.ListSelf _ -> failwith "Deeply nested lists not currently supported"
| PrimitiveType _ -> NonRecursive stripped
| SynType.LongIdent (SynLongIdent.SynLongIdent (ty, _, _)) ->
let key = ty |> List.map _.idText |> String.concat "/"
let isSelf = allUnionTypes |> List.exists (fun unionTy -> getNameKey unionTy = key)
if isSelf then
FieldDescription.Self stripped
else
FieldDescription.NonRecursive stripped
| _ -> failwithf "Unrecognised type: %+A" stripped
case.Fields |> List.map (fun x -> x.Name, go x.Type)
/// Returns whether this type recursively contains a Self, and the emitted TODO
let rec toInstructionCase (field : FieldDescription) : bool * SynType option =
match field with
| FieldDescription.NonRecursive ty -> false, Some ty
| FieldDescription.Self ty -> true, None
| FieldDescription.ListSelf ty ->
// store the length of the list
true, Some (SynType.Int ())
type InstructionCase =
{
Name : SynIdent
Name : Ident
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
let getInstructionCaseName (thisUnionType : SynTypeDefn) (case : UnionCase) =
match case.Name with
| SynIdent.SynIdent (ident, _) ->
(List.last (getName thisUnionType)).idText + "_" + ident.idText |> Ident.Create
/// 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 =
/// Stores a list as an int which is "the length of the list".
/// TODO: support other compound types.
let getRecursiveInstruction
(allUnionTypes : SynTypeDefn list)
(thisUnionType : SynTypeDefn)
(case : UnionCase)
: InstructionCase option
=
let analysed = analyse allUnionTypes case
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)
((false, []), analysed)
||> List.fold (fun (hasRecursion, cases) (fieldName, field) ->
let newHasRecursion, case = toInstructionCase field
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)
let cases =
match case with
| None -> cases
| Some case -> (fieldName, case) :: cases
if isSelf then
true, cases
else
hasRecursion, field.Type :: cases
| _ -> failwithf "Unrecognised type: %+A" field.Type
hasRecursion || newHasRecursion, cases
)
let name = getInstructionCaseName thisUnionType case
if hasRecursion then
cases
|> List.rev
|> List.map (fun ty ->
{
Name = None
Type = ty
}
)
let fields =
cases
|> List.rev
|> List.map (fun (name, ty) ->
{
Name = name
Type = ty
}
)
{
Name = name
Fields = fields
}
|> Some
else
None
/// The instruction to "process an Expr"; the loop will have to descend
/// into this Expr and break it down to discover what recursive calls
/// and calls to the cata this will imply making.
let baseCases (allUnionTypes : SynTypeDefn list) : InstructionCase list =
allUnionTypes
|> List.map (fun unionType ->
let name = getName unionType
{
Name = Ident.Create ("Process__" + (List.last name).idText)
Fields =
{
Name = None
Type = SynType.CreateLongIdent (SynLongIdent.CreateFromLongIdent (getName unionType))
}
|> List.singleton
}
)
let recursiveCases (allUnionTypes : SynTypeDefn list) : InstructionCase list =
allUnionTypes
|> List.collect (fun unionType ->
AstHelper.getUnionCases unionType
|> List.choose (fun case -> getRecursiveInstruction allUnionTypes unionType case)
)
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))
]
)
baseCases allUnionTypes
|> List.map (fun unionCase ->
SynUnionCase.Create (unionCase.Name, unionCase.Fields |> List.map (fun f -> SynField.Create f.Type))
)
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
)
recursiveCases allUnionTypes
|> List.map (fun case ->
SynUnionCase.Create (case.Name, case.Fields |> List.map (fun field -> SynField.Create field.Type))
)
let cases = casesFromProcess @ casesFromCases
@@ -306,6 +336,585 @@ module internal CataGenerator =
}
)
let createCataStructure (allUnionTypes : SynTypeDefn list) : SynTypeDefn list =
let generics =
allUnionTypes
|> List.map (fun defn ->
let name = getName defn |> List.map _.idText |> String.concat "" |> Ident.Create
SynTypar.SynTypar (name, TyparStaticReq.None, false)
)
let tyToGenericMap =
let names = allUnionTypes |> List.map getNameKey
List.zip names generics |> Map.ofList
allUnionTypes
|> List.map (fun unionType ->
let name =
match getName unionType |> List.rev with
| [] -> failwith "empty name"
| head :: rest -> Ident.Create (head.idText + "Cata") :: rest |> List.rev
let componentInfo =
let generics = generics |> List.map (fun ty -> SynTyparDecl.SynTyparDecl ([], ty))
SynComponentInfo.SynComponentInfo (
[],
Some (SynTyparDecls.PostfixList (generics, [], range0)),
[],
name,
// TODO: better docstring
PreXmlDoc.Create " Description of how to combine cases during a fold",
false,
None,
range0
)
let slots =
let ourGenericName = tyToGenericMap.[getNameKey unionType]
let flags =
{
SynMemberFlags.IsInstance = true
SynMemberFlags.IsDispatchSlot = true
SynMemberFlags.IsOverrideOrExplicitImpl = false
SynMemberFlags.IsFinal = false
SynMemberFlags.GetterOrSetterIsCompilerGenerated = false
SynMemberFlags.MemberKind = SynMemberKind.Member
}
AstHelper.getUnionCases unionType
|> List.map (fun case ->
let caseName =
match case.Name with
| SynIdent.SynIdent (name, _) -> name
let arity =
SynValInfo.SynValInfo (
case.Fields |> List.map (fun field -> [ SynArgInfo.Empty ]),
SynArgInfo.Empty
)
let ty =
// TODO: we should only have called this once; pass the resulting
// data structure in, rather than rederiving it
let analysed = analyse allUnionTypes case
(SynType.Var (ourGenericName, range0), List.rev analysed)
||> List.fold (fun acc (_name, field) ->
let place : SynType =
match field with
| FieldDescription.Self ty ->
SynType.Var (tyToGenericMap.[getNameKeyUnion ty], range0)
| FieldDescription.ListSelf ty ->
SynType.CreateApp (
SynType.CreateLongIdent "list",
[ SynType.Var (tyToGenericMap.[getNameKeyUnion ty], range0) ],
true
)
| FieldDescription.NonRecursive ty -> ty
SynType.Fun (
place,
acc,
range0,
{
ArrowRange = range0
}
)
)
let slot =
SynValSig.SynValSig (
[],
case.Name,
SynValTyparDecls.SynValTyparDecls (None, true),
ty,
arity,
false,
false,
PreXmlDoc.Create $" How to operate on the %s{caseName.idText} case",
None,
None,
range0,
{
EqualsRange = None
WithKeyword = None
InlineKeyword = None
LeadingKeyword = SynLeadingKeyword.Abstract range0
}
)
SynMemberDefn.AbstractSlot (
slot,
flags,
range0,
{
GetSetKeywords = None
}
)
)
let repr = SynTypeDefnRepr.ObjectModel (SynTypeDefnKind.Unspecified, slots, range0)
SynTypeDefn.SynTypeDefn (
componentInfo,
repr,
[],
None,
range0,
{
LeadingKeyword = SynTypeDefnLeadingKeyword.Type range0
EqualsRange = Some range0
WithKeyword = None
}
)
)
let createCataRecord (allUnionTypes : SynTypeDefn list) : SynTypeDefn =
let nameForDoc = List.last (getName allUnionTypes.[0]) |> _.idText
let generics =
allUnionTypes
|> List.map (fun defn ->
let name = getName defn |> List.map _.idText |> String.concat "" |> Ident.Create
SynTypar.SynTypar (name, TyparStaticReq.None, false)
)
let fields =
allUnionTypes
|> List.map (fun unionType ->
let doc = PreXmlDoc.Create " TODO: doc"
let name = getName unionType
let ty =
SynType.App (
SynType.CreateLongIdent (SynLongIdent.CreateString (List.last(name).idText + "Cata")),
Some range0,
generics |> List.map (fun v -> SynType.Var (v, range0)),
List.replicate (generics.Length - 1) range0,
Some range0,
false,
range0
)
SynField.SynField (
[],
false,
Some (List.last name),
ty,
false,
doc,
None,
range0,
{
LeadingKeyword = None
}
)
)
let componentInfo =
SynComponentInfo.SynComponentInfo (
[],
Some (
SynTyparDecls.PostfixList (
generics |> List.map (fun ty -> SynTyparDecl.SynTyparDecl ([], ty)),
[],
range0
)
),
[],
[ Ident.Create "Cata" ], // TODO: better name
PreXmlDoc.Create $" Specifies how to perform a fold (catamorphism) over the type %s{nameForDoc}.",
false,
None,
range0
)
SynTypeDefn.SynTypeDefn (
componentInfo,
SynTypeDefnRepr.Simple (SynTypeDefnSimpleRepr.Record (None, fields, range0), range0),
[],
None,
range0,
{
LeadingKeyword = SynTypeDefnLeadingKeyword.Type range0
WithKeyword = None
EqualsRange = Some range0
}
)
let createLoopFunction (allUnionTypes : SynTypeDefn list) : SynBinding =
let valData =
SynValData.SynValData (
None,
SynValInfo.SynValInfo (
[
[ SynArgInfo.SynArgInfo ([], false, Some (Ident.Create "cata")) ]
[ SynArgInfo.SynArgInfo ([], false, Some (Ident.Create "instructions")) ]
],
SynArgInfo.Empty
),
None
)
let headPat =
SynPat.LongIdent (
SynLongIdent.CreateString "loop",
None,
None,
SynArgPats.Pats
[
SynPat.CreateParen (
SynPat.CreateTyped (
SynPat.CreateNamed (Ident.Create "cata"),
SynType.App (
// TODO: better type name
SynType.CreateLongIdent "Cata",
Some range0,
List.replicate allUnionTypes.Length (SynType.Anon range0),
List.replicate (allUnionTypes.Length - 1) range0,
Some range0,
false,
range0
)
)
)
SynPat.CreateParen (
SynPat.CreateTyped (
SynPat.CreateNamed (Ident.Create "instructions"),
SynType.App (
SynType.CreateLongIdent "ResizeArray",
Some range0,
[ SynType.CreateLongIdent "Instruction" ],
[],
Some range0,
false,
range0
)
)
)
],
Some (SynAccess.Private range0),
range0
)
let stackNames =
allUnionTypes
|> List.map (fun ty ->
// TODO this is jank
List.last(getName ty).idText + "Stack" |> Ident.Create |> Ident.lowerFirstLetter
)
// A clause for each type, splitting it into its cases:
let baseMatchClauses =
List.zip stackNames allUnionTypes
|> List.map (fun (stackName, unionType) ->
let cases = AstHelper.getUnionCases unionType
let bodyMatch =
SynExpr.CreateMatch (
SynExpr.CreateIdentString "x",
cases
|> List.map (fun case ->
let name =
match case.Name with
| SynIdent (ident, _) -> ident
let analysis = analyse allUnionTypes case
let matchBody =
if
analysis
|> List.forall (
function
| _, FieldDescription.NonRecursive ty -> true
| _ -> false
)
then
// directly call the cata
((0,
SynExpr.CreateLongIdent (
SynLongIdent.CreateFromLongIdent (
Ident.Create "cata" :: getName unionType @ [ name ]
)
)),
List.rev case.Fields)
||> List.fold (fun (i, body) field ->
let fieldName =
match field.Name with
| Some n -> n
| None -> Ident.Create $"arg%i{i}"
let body = SynExpr.CreateApp (body, SynExpr.CreateIdent fieldName)
(i + 1, body)
)
|> snd
|> SynExpr.pipeThroughFunction (
SynExpr.CreateLongIdent (
SynLongIdent.CreateFromLongIdent (stackName :: [ Ident.Create "Add" ])
)
)
else
// there's a recursive type in here, so we'll have to make some calls
// and then come back.
// TODO
SynExpr.CreateConst SynConst.Unit
SynMatchClause.SynMatchClause (
SynPat.CreateLongIdent (
SynLongIdent.CreateFromLongIdent (getName unionType @ [ name ]),
[
SynPat.CreateParen (
SynPat.Tuple (
false,
case.Fields
|> List.mapi (fun i field ->
let name =
match field.Name with
| None -> Ident.Create $"arg%i{i}"
| Some n -> n
SynPat.CreateNamed name
),
List.replicate (case.Fields.Length - 1) range0,
range0
)
)
]
),
None,
matchBody,
range0,
DebugPointAtTarget.Yes,
{
ArrowRange = Some range0
BarRange = Some range0
}
)
)
)
SynMatchClause.SynMatchClause (
SynPat.LongIdent (
// TODO this is also jank; should unify with DU generator
SynLongIdent.Create [ "Instruction" ; "Process__" + (List.last (getName unionType)).idText ],
None,
None,
SynArgPats.Pats [ SynPat.CreateNamed (Ident.Create "x") ],
None,
range0
),
None,
bodyMatch,
range0,
DebugPointAtTarget.Yes,
{
ArrowRange = Some range0
BarRange = Some range0
}
)
)
// And a clause for each case with a recursive reference.
let recMatchClauses : SynMatchClause list =
allUnionTypes
|> List.collect (fun unionType ->
let cases = AstHelper.getUnionCases unionType
cases
|> List.choose (fun case ->
let analysis = analyse allUnionTypes case
// We already know there is a recursive reference somewhere
// in `analysis`.
if
analysis
|> List.exists (fun (_, ty) ->
match ty with
| NonRecursive _ -> false
| _ -> true
)
then
Some (case, analysis)
else
None
)
|> List.map (fun (case, analysis) ->
let lhsNames =
analysis
|> Seq.mapi (fun i x -> (i, x))
|> Seq.choose (fun (i, (name, desc)) ->
match desc with
| FieldDescription.NonRecursive _ ->
match name with
| None -> Ident.Create $"arg%i{i}"
| Some name -> name
|> SynPat.CreateNamed
|> Some
| FieldDescription.ListSelf _ -> Ident.Create "n" |> SynPat.CreateNamed |> Some
| FieldDescription.Self _ -> None
)
|> Seq.toList
let lhs =
match lhsNames with
| [] -> []
| lhsNames ->
SynPat.Tuple (false, lhsNames, List.replicate (lhsNames.Length - 1) range0, range0)
|> SynPat.CreateParen
|> List.singleton
let pat =
SynPat.LongIdent (
SynLongIdent.CreateFromLongIdent
[ Ident.Create "Instruction" ; getInstructionCaseName unionType case ],
None,
None,
SynArgPats.Pats lhs,
None,
range0
)
let body = [ SynExpr.CreateConst SynConst.Unit ] |> SynExpr.CreateSequential
SynMatchClause.SynMatchClause (
pat,
None,
body,
range0,
DebugPointAtTarget.Yes,
{
ArrowRange = Some range0
BarRange = Some range0
}
)
)
)
let matchStatement =
SynExpr.CreateMatch (SynExpr.CreateIdentString "currentInstruction", baseMatchClauses @ recMatchClauses)
let body =
SynExpr.CreateSequential
[
SynExpr.CreateApp (
SynExpr.CreateLongIdent (SynLongIdent.Create [ "instructions" ; "RemoveAt" ]),
SynExpr.CreateParen (SynExpr.minusN (SynLongIdent.Create [ "instructions" ; "Count" ]) 1)
)
matchStatement
]
let body =
SynExpr.LetOrUse (
false,
false,
[
SynBinding.SynBinding (
None,
SynBindingKind.Normal,
false,
false,
[],
PreXmlDoc.Empty,
SynValData.SynValData (None, SynValInfo.SynValInfo ([], SynArgInfo.Empty), None),
SynPat.CreateNamed (Ident.Create "currentInstruction"),
None,
SynExpr.DotIndexedGet (
SynExpr.CreateIdentString "instructions",
SynExpr.minusN (SynLongIdent.Create [ "instructions" ; "Count" ]) 1,
range0,
range0
),
range0,
DebugPointAtBinding.Yes range0,
SynExpr.synBindingTriviaZero false
)
],
body,
range0,
{
InKeyword = None
}
)
let body =
SynExpr.CreateSequential
[
SynExpr.While (
DebugPointAtWhile.Yes range0,
SynExpr.CreateApp (
SynExpr.CreateAppInfix (
SynExpr.CreateLongIdent (
SynLongIdent.SynLongIdent (
[ Ident.Create "op_GreaterThan" ],
[],
[ Some (IdentTrivia.OriginalNotation ">") ]
)
),
SynExpr.CreateLongIdent (SynLongIdent.Create [ "instructions" ; "Count" ])
),
SynExpr.CreateConst (SynConst.Int32 0)
),
body,
range0
)
SynExpr.CreateTuple (
stackNames
|> List.map (List.singleton >> SynLongIdent.CreateFromLongIdent >> SynExpr.CreateLongIdent)
)
]
let body =
(body, List.zip stackNames allUnionTypes)
||> List.fold (fun body (stackName, unionType) ->
SynExpr.LetOrUse (
false,
false,
[
SynBinding.SynBinding (
None,
SynBindingKind.Normal,
false,
false,
[],
PreXmlDoc.Empty,
SynValData.SynValData (None, SynValInfo.Empty, None),
SynPat.Named (SynIdent.SynIdent (stackName, None), false, None, range0),
None,
SynExpr.CreateApp (
SynExpr.CreateLongIdent (SynLongIdent.CreateString "ResizeArray"),
SynExpr.CreateConst SynConst.Unit
),
range0,
DebugPointAtBinding.Yes range0,
SynExpr.synBindingTriviaZero false
)
],
body,
range0,
{
SynExprLetOrUseTrivia.InKeyword = None
}
)
)
SynBinding.SynBinding (
Some (SynAccess.Private range0),
SynBindingKind.Normal,
false,
false,
[],
PreXmlDoc.Empty,
valData,
headPat,
None,
body,
range0,
DebugPointAtBinding.NoneAtLet,
trivia = SynExpr.synBindingTriviaZero false
)
let createModule
(opens : SynOpenDeclTarget list)
(ns : LongIdent)
@@ -313,11 +922,8 @@ module internal CataGenerator =
(allUnionTypes : SynTypeDefn list)
: SynModuleOrNamespace
=
let moduleName : LongIdent =
List.last (getName taggedType)
|> fun x -> x.idText + "Cata"
|> Ident.Create
|> List.singleton
let parentName = List.last (getName taggedType) |> _.idText
let moduleName : LongIdent = parentName + "Cata" |> Ident.Create |> List.singleton
let attribs = [ SynAttributeList.Create (SynAttribute.RequireQualifiedAccess ()) ]
@@ -325,15 +931,14 @@ module internal CataGenerator =
SynComponentInfo.Create (
moduleName,
attributes = attribs,
xmldoc = PreXmlDoc.Create " Catamorphism" // TODO: better docstring
xmldoc = PreXmlDoc.Create $" Methods to perform a catamorphism over the type {parentName}"
)
let allTypars =
allUnionTypes
|> List.map (fun unionType ->
List.last (getName unionType)
|> fun x -> x.idText
|> fun s -> s + "Ret"
|> fun x -> x.idText + "Ret"
|> Ident.Create
|> fun x -> SynTypar.SynTypar (x, TyparStaticReq.None, false)
|> fun x -> SynType.Var (x, range0)
@@ -343,18 +948,28 @@ module internal CataGenerator =
List.zip allUnionTypes allTypars
|> List.map (fun (unionType, relevantTypar) -> createRunFunction allTypars relevantTypar unionType)
let cataStructures =
createCataStructure allUnionTypes
|> List.map (fun repr -> SynModuleDecl.Types ([ repr ], range0))
let loopFunction = createLoopFunction allUnionTypes
let cataRecord = SynModuleDecl.Types ([ createCataRecord allUnionTypes ], range0)
SynModuleOrNamespace.CreateNamespace (
ns,
decls =
[
for openStatement in opens do
yield SynModuleDecl.CreateOpen openStatement
yield! cataStructures
yield cataRecord
yield
SynModuleDecl.CreateNestedModule (
modInfo,
[
SynModuleDecl.Types ([ createInstructionType allUnionTypes ], range0)
SynModuleDecl.CreateLet runFunctions
SynModuleDecl.CreateLet (loopFunction :: runFunctions)
]
)
]

View File

@@ -756,12 +756,6 @@ module internal HttpClientGenerator =
| _ -> None
)
let lowerFirstLetter (x : Ident) : Ident =
let result = StringBuilder x.idText.Length
result.Append (Char.ToLowerInvariant x.idText.[0]) |> ignore
result.Append x.idText.[1..] |> ignore
Ident.Create ((result : StringBuilder).ToString ())
let createModule
(opens : SynOpenDeclTarget list)
(ns : LongIdent)
@@ -891,7 +885,7 @@ module internal HttpClientGenerator =
Some (SynBindingReturnInfo.Create pi.Type),
SynExpr.CreateApp (
SynExpr.CreateLongIdent (
SynLongIdent.CreateFromLongIdent [ lowerFirstLetter pi.Identifier ]
SynLongIdent.CreateFromLongIdent [ Ident.lowerFirstLetter pi.Identifier ]
),
SynExpr.CreateConst SynConst.Unit
),
@@ -927,7 +921,7 @@ module internal HttpClientGenerator =
properties
|> List.map (fun (_, pi) ->
SynPat.CreateTyped (
SynPat.CreateNamed (lowerFirstLetter pi.Identifier),
SynPat.CreateNamed (Ident.lowerFirstLetter pi.Identifier),
SynType.CreateFun (SynType.CreateLongIdent "unit", pi.Type)
)
|> SynPat.CreateParen

View File

@@ -0,0 +1,14 @@
namespace WoofWare.Myriad.Plugins
open System
open System.Text
open Fantomas.FCS.Syntax
open Myriad.Core
[<RequireQualifiedAccess>]
module internal Ident =
let lowerFirstLetter (x : Ident) : Ident =
let result = StringBuilder x.idText.Length
result.Append (Char.ToLowerInvariant x.idText.[0]) |> ignore
result.Append x.idText.[1..] |> ignore
Ident.Create ((result : StringBuilder).ToString ())

View File

@@ -275,3 +275,19 @@ module internal SynExpr =
else
SynLeadingKeyword.Let range0
}
/// {ident} - {n}
let minusN (ident : SynLongIdent) (n : int) : SynExpr =
SynExpr.CreateApp (
SynExpr.CreateAppInfix (
SynExpr.CreateLongIdent (
SynLongIdent.SynLongIdent (
[ Ident.Create "op_Subtraction" ],
[],
[ Some (IdentTrivia.OriginalNotation "-") ]
)
),
SynExpr.CreateLongIdent ident
),
SynExpr.CreateConst (SynConst.Int32 n)
)

View File

@@ -25,6 +25,7 @@
<ItemGroup>
<Compile Include="List.fs"/>
<Compile Include="Ident.fs" />
<Compile Include="AstHelper.fs"/>
<Compile Include="SynExpr.fs"/>
<Compile Include="SynType.fs"/>