feat(decls): add algebraic data type and record declaration registry
This commit is contained in:
1 parent
8c555e99e0
commit
f7d69f3067
1 file changed
+134
@@ -0,0 +1,134 @@
|
||||
module Fml.Core.Decls
|
||||
|
||||
open Fml.Core.Ast
|
||||
open Fml.Core.Types
|
||||
|
||||
// Global registries for user-declared algebraic data types and records.
|
||||
// They mirror the mutable-global style already used for the inference counter
|
||||
// and are reset per session so runProgram stays reproducible.
|
||||
|
||||
type CtorInfo =
|
||||
{ Tag: string
|
||||
TypeName: string
|
||||
Params: string list
|
||||
ArgTypes: TyExpr list }
|
||||
|
||||
type FieldInfo =
|
||||
{ Name: string
|
||||
TypeName: string
|
||||
Params: string list
|
||||
FieldType: TyExpr
|
||||
AllFields: string list }
|
||||
|
||||
type TypeInfo =
|
||||
{ Name: string
|
||||
Params: string list
|
||||
Ctors: string list
|
||||
Fields: string list
|
||||
IsRecord: bool }
|
||||
|
||||
let mutable typeInfos: Map<string, TypeInfo> = Map.empty
|
||||
let mutable ctorInfos: Map<string, CtorInfo> = Map.empty
|
||||
let mutable fieldInfos: Map<string, FieldInfo> = Map.empty
|
||||
|
||||
let resetDecls () =
|
||||
typeInfos <- Map.empty
|
||||
ctorInfos <- Map.empty
|
||||
fieldInfos <- Map.empty
|
||||
|
||||
let lookupCtor (name: string) : CtorInfo option = Map.tryFind name ctorInfos
|
||||
let lookupField (name: string) : FieldInfo option = Map.tryFind name fieldInfos
|
||||
let lookupType (name: string) : TypeInfo option = Map.tryFind name typeInfos
|
||||
let ctorArity (name: string) : int =
|
||||
match Map.tryFind name ctorInfos with
|
||||
| Some info -> List.length info.ArgTypes
|
||||
| None -> raise (TypeError(sprintf "unknown constructor '%s'" name))
|
||||
|
||||
let registerDecl (decl: TypeDecl) =
|
||||
match decl.Body with
|
||||
| Variants variants ->
|
||||
let tags = variants |> List.map fst
|
||||
typeInfos <-
|
||||
Map.add decl.Name
|
||||
{ Name = decl.Name
|
||||
Params = decl.Params
|
||||
Ctors = tags
|
||||
Fields = []
|
||||
IsRecord = false }
|
||||
typeInfos
|
||||
for (tag, argTypes) in variants do
|
||||
ctorInfos <-
|
||||
Map.add tag
|
||||
{ Tag = tag
|
||||
TypeName = decl.Name
|
||||
Params = decl.Params
|
||||
ArgTypes = argTypes }
|
||||
ctorInfos
|
||||
| RecordDef fields ->
|
||||
let fieldNames = fields |> List.map fst
|
||||
typeInfos <-
|
||||
Map.add decl.Name
|
||||
{ Name = decl.Name
|
||||
Params = decl.Params
|
||||
Ctors = []
|
||||
Fields = fieldNames
|
||||
IsRecord = true }
|
||||
typeInfos
|
||||
for (fieldName, fieldTy) in fields do
|
||||
fieldInfos <-
|
||||
Map.add fieldName
|
||||
{ Name = fieldName
|
||||
TypeName = decl.Name
|
||||
Params = decl.Params
|
||||
FieldType = fieldTy
|
||||
AllFields = fieldNames }
|
||||
fieldInfos
|
||||
|
||||
let rec tyExprToTy (subst: Map<string, Ty>) (te: TyExpr) : Ty =
|
||||
match te with
|
||||
| TEVar name ->
|
||||
match Map.tryFind name subst with
|
||||
| Some ty -> ty
|
||||
| None -> raise (TypeError(sprintf "unbound type variable '%s" name))
|
||||
| TEList inner -> TList(tyExprToTy subst inner)
|
||||
| TETuple parts -> TTuple(List.map (tyExprToTy subst) parts)
|
||||
| TEArrow(a, b) -> TArrow(tyExprToTy subst a, tyExprToTy subst b)
|
||||
| TECon(name, args) ->
|
||||
match name, args with
|
||||
| "int", [] -> TInt
|
||||
| "bool", [] -> TBool
|
||||
| "string", [] -> TString
|
||||
| "unit", [] -> TUnit
|
||||
| "list", [ single ] -> TList(tyExprToTy subst single)
|
||||
| _ -> TCon(name, List.map (tyExprToTy subst) args)
|
||||
|
||||
// Fresh instantiation of a declared constructor: returns the argument types and
|
||||
// the built result type, all sharing freshly allocated type variables for the
|
||||
// type parameters.
|
||||
let instantiateCtor (info: CtorInfo) : Ty list * Ty =
|
||||
let subst = info.Params |> List.map (fun p -> p, newVar ()) |> Map.ofList
|
||||
let argTys = info.ArgTypes |> List.map (tyExprToTy subst)
|
||||
let resultTy = TCon(info.TypeName, info.Params |> List.map (fun p -> subst.[p]))
|
||||
argTys, resultTy
|
||||
|
||||
let instantiateField (info: FieldInfo) : Ty * Ty =
|
||||
let subst = info.Params |> List.map (fun p -> p, newVar ()) |> Map.ofList
|
||||
let fieldTy = tyExprToTy subst info.FieldType
|
||||
let recordTy = TCon(info.TypeName, info.Params |> List.map (fun p -> subst.[p]))
|
||||
fieldTy, recordTy
|
||||
|
||||
// Instantiates a whole record type at once so every field shares the same fresh
|
||||
// type parameters. Returns the per-field types and the record's own type.
|
||||
let instantiateRecordType (typeName: string) : Map<string, Ty> * Ty =
|
||||
match Map.tryFind typeName typeInfos with
|
||||
| Some info when info.IsRecord ->
|
||||
let subst = info.Params |> List.map (fun p -> p, newVar ()) |> Map.ofList
|
||||
let fieldTys =
|
||||
info.Fields
|
||||
|> List.map (fun fieldName ->
|
||||
let fieldInfo = fieldInfos.[fieldName]
|
||||
fieldName, tyExprToTy subst fieldInfo.FieldType)
|
||||
|> Map.ofList
|
||||
let recordTy = TCon(typeName, info.Params |> List.map (fun p -> subst.[p]))
|
||||
fieldTys, recordTy
|
||||
| _ -> raise (TypeError(sprintf "'%s' is not a record type" typeName))
|
||||
Reference in new issue
Block a user