Skip to content

Instantly share code, notes, and snippets.

@latkin
Last active October 6, 2015 13:02
Show Gist options
  • Star 2 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save latkin/b1e7c066b1b9a2b72d08 to your computer and use it in GitHub Desktop.
Save latkin/b1e7c066b1b9a2b72d08 to your computer and use it in GitHub Desktop.
Static params to provided methods

Updated regex type provider code from the Six Quick Picks from F# 4.0 video. Modified from MSDN sample code.

To build, create an F# 4.0 library project with files in this order:

  • ProvidedTypes.fsi
  • ProvidedTypes.fs
  • RegexTP.fs

Note that the bulk of the content in ProvidedTypes.fs/fsi is pre-existing. Changes made are just h4x to add usage of the ITypeProvider2 interface for demo purposes. This is not meant to demonstrate best practices for type provider authoring.

(*
* all of ProvidedTypes.fs
* as of https://github.com/fsprojects/FSharp.TypeProviders.StarterPack/blob/245fe3b0126ac6a326a14a0f6b1ef569680b08b3/src/ProvidedTypes.fs
*)
// then add to the bottom
interface ITypeProvider2 with
member __.GetStaticParametersForMethod(methodWithoutArguments:MethodBase) =
match methodWithoutArguments.Name with
| "Match" -> [| ProvidedStaticParameter("pattern", typeof<string>) |]
| _ -> null
member __.ApplyStaticArgumentsForMethod(methodWithoutArguments : MethodBase, methodNameWithArguments : string, staticArguments : obj[]) =
match staticArguments with
| [| :? string as pattern|] ->
let r = Regex(pattern)
let rootTy =
match methodWithoutArguments.DeclaringType with
| :? ProvidedTypeDefinition as pTypeDef -> pTypeDef
| _ -> failwith "can't find root type"
let matchTy = RegexTPHelper.GetMatchTy(pattern, rootTy)
RegexTPHelper.GetStaticMatchMethod(methodNameWithArguments, matchTy, pattern, rootTy)
| _ -> failwith "expecting a single static method argument"
and RegexTPHelper =
static member GetMatchTy(pattern, parentTy : ProvidedTypeDefinition) =
let r = Regex(pattern)
let matchTy = ProvidedTypeDefinition(
sprintf "Obj%s" pattern,
baseType = Some typeof<obj>,
HideObjectMethods = true)
parentTy.AddMember matchTy
for group in r.GetGroupNames() do
// Ignore the group named 0, which represents all input.
if group <> "0" then
let prop = ProvidedProperty(
propertyName = group,
propertyType = typeof<string>,
GetterCode = fun args -> <@@ ((%%args.[0]:obj) :?> Match).Groups.[group].Value @@>)
prop.AddXmlDoc(sprintf @"Gets the ""%s"" group from this match" group)
matchTy.AddMember(prop)
matchTy
static member GetStaticMatchMethod(methodName, returnTy, pattern, parentTy : ProvidedTypeDefinition) =
let mthd =
ProvidedMethod(
methodName = methodName,
parameters = [ProvidedParameter("input", typeof<string>)],
returnType = returnTy,
IsStaticMethod = true,
InvokeCode = (fun args -> <@@ Regex.Match(%%args.[0], pattern) :> obj @@>))
parentTy.AddMember mthd
mthd :> MethodBase
(*
* all of ProvidedTypes.fsi
* as of https://github.com/fsprojects/FSharp.TypeProviders.StarterPack/blob/245fe3b0126ac6a326a14a0f6b1ef569680b08b3/src/ProvidedTypes.fsi
*)
// then add at the bottom
interface ITypeProvider2
[<Class>]
type RegexTPHelper =
static member GetMatchTy : pattern : string * parentTy : ProvidedTypeDefinition -> ProvidedTypeDefinition
static member GetStaticMatchMethod : methodName : string * returnTy : ProvidedTypeDefinition * pattern : string * parentTy : ProvidedTypeDefinition -> MethodBase
namespace TPDemo
open System.Reflection
open Microsoft.FSharp.Core.CompilerServices
open ProviderImplementation.ProvidedTypes
open System.Text.RegularExpressions
[<TypeProvider>]
type public CheckedRegexProvider() as this =
inherit TypeProviderForNamespaces()
let thisAssembly = Assembly.GetExecutingAssembly()
let rootNamespace = "Demo.TypeProviders"
let baseTy = typeof<obj>
let staticParams = [ProvidedStaticParameter("pattern", typeof<string>)]
let regexTy = ProvidedTypeDefinition(thisAssembly, rootNamespace, "RegexTyped", Some baseTy)
do
let defaultPattern = "(?<Value>.*)"
let initialMatchTy = RegexTPHelper.GetMatchTy(defaultPattern, regexTy)
RegexTPHelper.GetStaticMatchMethod("Match", initialMatchTy, defaultPattern, regexTy) |> ignore
do regexTy.DefineStaticParameters(
parameters=staticParams,
instantiationFunction=(fun typeName parameterValues ->
match parameterValues with
| [| :? string as pattern|] ->
let ty = ProvidedTypeDefinition(
thisAssembly,
rootNamespace,
typeName,
baseType = Some baseTy)
let r = System.Text.RegularExpressions.Regex(pattern)
let matchTy = RegexTPHelper.GetMatchTy(pattern, ty)
let matchMeth = ProvidedMethod(
methodName = "Match",
parameters = [ProvidedParameter("input", typeof<string>)],
returnType = matchTy,
InvokeCode = fun args -> <@@ ((%%args.[0]:obj) :?> Regex).Match(%%args.[1]) :> obj @@>)
ty.AddMember matchMeth
let ctor = ProvidedConstructor(
parameters = [],
InvokeCode = fun args -> <@@ Regex(pattern) :> obj @@>)
ty.AddMember ctor
ty
| _ -> failwith "unexpected parameter values"))
do this.AddNamespace(rootNamespace, [regexTy])
[<TypeProviderAssembly>]
do ()
#r "RegexTP.dll"
open Demo.TypeProviders
[<Literal>]
let phonePattern =
@"^(?:(?<AreaCode>\d{3})-)?(?<PhoneNumber>\d{3}-\d{4})$"
// traditional usage
type PhoneReg = RegexTyped<phonePattern>
let phone = PhoneReg()
phone.Match("800-374-2774").PhoneNumber
// new usage
RegexTyped.Match<phonePattern>("800-374-2774").PhoneNumber
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment