Reading and updating OpenXML in F# - f#

I can't get this F# code to read and update Content Control text fields inside Word documents.
The second function does absolutely nothing and the first one produces this error: An unhandled exception of type 'System.InvalidOperationException' occurred in System.Core.dll
Additional information: Sequence contains no elements
namespace OpenXML
open DocumentFormat.OpenXml
open DocumentFormat.OpenXml.Packaging
open DocumentFormat.OpenXml.Wordprocessing
open System.Linq
// Add the DocumentFormat.OpenXml assembly
// Add the WindowsBase assembly
module public Word =
let query_plain_text_content_control document_path_and_file_name content_control_tag =
use theDoc = WordprocessingDocument.Open((document_path_and_file_name :string), true)
let mainPart = theDoc.MainDocumentPart
let block = mainPart.Document.Body.Descendants<SdtElement>().Where(fun r -> r.SdtProperties.GetFirstChild<Tag>().Val = content_control_tag).Single()
let t = block.Descendants<Text>().FirstOrDefault()
t.Text
let update_plain_text_content_control document_path_and_file_name content_control_tag new_text = async {
use theDoc = WordprocessingDocument.Open((document_path_and_file_name :string), true)
let mainPart = theDoc.MainDocumentPart
let block = mainPart.Document.Body.Descendants<SdtElement>().Where(fun r -> r.SdtProperties.GetFirstChild<Tag>().Val = content_control_tag).Single()
let t = block.Descendants<Text>().FirstOrDefault()
t.Text <- new_text
mainPart.Document.Save() |> ignore
}

Seems to work fine, with a couple of tweaks:
#r#"DocumentFormat.OpenXml.dll"
#r"WindowsBase.dll"
open DocumentFormat.OpenXml
open DocumentFormat.OpenXml.Packaging
open DocumentFormat.OpenXml.Wordprocessing
open System.Linq
// Add the DocumentFormat.OpenXml assembly
// Add the WindowsBase assembly
module public Word =
let query_plain_text_content_control document_path_and_file_name content_control_tag =
use theDoc = WordprocessingDocument.Open((document_path_and_file_name :string), true)
let mainPart = theDoc.MainDocumentPart
let block = mainPart.Document.Body.Descendants<SdtElement>().Where(fun r -> r.SdtProperties.GetFirstChild<Tag>().Val.ToString() = content_control_tag).Single()
let t = block.Descendants<Text>().FirstOrDefault()
t.Text
let update_plain_text_content_control document_path_and_file_name content_control_tag new_text = async {
use theDoc = WordprocessingDocument.Open((document_path_and_file_name :string), true)
let mainPart = theDoc.MainDocumentPart
let block = mainPart.Document.Body.Descendants<SdtElement>().Where(fun r -> r.SdtProperties.GetFirstChild<Tag>().Val.ToString() = content_control_tag).Single()
let t = block.Descendants<Text>().FirstOrDefault()
t.Text <- new_text
mainPart.Document.Save() |> ignore
}
let oldtext = query_plain_text_content_control #".\text.docx" "ctrltag"
let update = update_plain_text_content_control #".\text.docx" "ctrltag" "new text"
Async.RunSynchronously(update)
let newtext = query_plain_text_content_control #".\text.docx" "ctrltag"
.. on a document containing a single plaintext content control, with tag of 'ctrltag', with content 'old text', I get:
val oldtext : string = "Old text"
val update : Async<unit>
val newtext : string = "new text"
Without calling .ToString() on 'r.SdtProperties.GetFirstChild().Val', I got this error:
The type 'string' is not compatible with the type 'StringValue'.
Perhaps there is a problem with your document? The error you are getting would seem to suggest that there are no content controls with the specified Tag.

Related

FSCL error on a simple example

I am trying to use openCL with FSCL on F# but I am obtaining some errors that I don't understand
open FSCL.Compiler
open FSCL.Language
open FSCL.Runtime
open Microsoft.FSharp.Linq.RuntimeHelpers
open System.Runtime.InteropServices
[<StructLayout(LayoutKind.Sequential)>]
type gpu_point2 =
struct
val mutable x: float32
val mutable y: float32
new ( q ,w) = {x=q; y=w}
end
[<ReflectedDefinition>]
let PointSum(a:gpu_point2,b:gpu_point2) =
let sx =(a.x+b.x)
let sy =(a.y+b.y)
gpu_point2(sx,sy)
[<ReflectedDefinition;Kernel>]
let Modgpu(b:float32[], c:float32[],wi:WorkItemInfo) =
let gid = wi.GlobalID(0)
let arp = Array.zeroCreate<gpu_point2> b.Length
let newpoint = gpu_point2(b.[gid],c.[gid])
arp.[gid] <- newpoint
arp
[<ReflectedDefinition;Kernel>]
let ModSum(a:gpu_point2[],b:gpu_point2[],wi:WorkItemInfo) =
let gid = wi.GlobalID(0)
let cadd = Array.zeroCreate<gpu_point2> a.Length
let newsum = PointSum(a.[gid],b.[gid])
cadd.[gid] <- newsum
cadd
[<ReflectedDefinition;Kernel>]
let ModSum2(a:gpu_point2[],b:gpu_point2[],wi:WorkItemInfo) =
let gid = wi.GlobalID(0)
let cadd = Array.zeroCreate<gpu_point2> a.Length
let newsum = gpu_point2(a.[gid].x+b.[gid].x,a.[gid].y+b.[gid].y)
cadd.[gid] <- newsum
cadd
let ws = WorkSize(64L)
let arr_s1= <# Modgpu([|0.f..63.f|],[|63.f..(-1.f)..0.f|],ws)#>.Run()
let arr_s2 = <# Modgpu([|63.f..(-1.f)..0.f|],[|0.f..63.f|],ws)#>.Run()
With this code when I try to use ModSum as
let rsum = <# ModSum(arr_s1,arr_s2,ws)#>.Run()
doesn't work, but instead when I use ModSum2 works perfectly
let rsum = <# ModSum2(arr_s1,arr_s2,ws)#>.Run()
The error I obtain the first time I run it is
FSCL.Compiler.CompilerException: Unrecognized construct in kernel body NewObject (gpu_point2, sx, sy)
and if I re-run the fsi console says
System.NullReferenceException: Object reference not set to an instance of an object.
The only thing I know is that the error doesn't comes from the use of another function since I can define a dot product function that works.
[<ReflectedDefinition>]
let PointProd(a:gpu_point2,b:gpu_point2) =
let f = (a.x*b.x)
let s = (a.y*b.y)
f+s
Thus, I guess the problem comes from the return type of PointSum, but is there a way to create such a function to sum two points and return the point type? And Why is not working?
Edit/Update:
Also with a record happens the same if I define the type as :
[<StructLayout(LayoutKind.Sequential)>]
type gpu_point_2 = {x:float32; y:float32}
If I try to create a function that directly sums two gpu_point_2 on a function works, but if I call a second function it raises the same error as using a struct.
Try to add [<ReflectedDefinition>] on the constructor of gpu_point2:
[<StructLayout(LayoutKind.Sequential)>]
type gpu_point2 =
struct
val mutable x: float32
val mutable y: float32
[<ReflectedDefinition>] new (q, w) = {x=q; y=w}
end
Normally each code that is called from the device need this attribute, constructors included.

F# Error compiling

I have the following F# Code that is causing a compile error:
persistence.fs(32,21): error FS0072: Lookup on object of indeterminate type based on information prior to this program point. A type annotation may be needed prior to this program point to constrain the type of the object. This may allow the lookup to be resolved.
The error is at the line "serializer.write...."
Any help would be appreciated.
namespace persisitence
open System.Collections.Generic
open System
open System.IO
type LocalData<'T> =
struct
val mutable elements_ : 'T list
val mutable lock_ : obj
new(e: 'T list) = { elements_ = e ; lock_ = new obj() }
end
type BinaryPersistenceOut<'T, ^W when ^W: (member write : ('T * BinaryWriter) -> unit)>(fn: string, serializer: ^W) as this =
let writer_ = new BinaryWriter(File.Open(fn, FileMode.Append))
let mutable localdata_ = new LocalData<'T>([])
let serializer_ = serializer
let NUM_SECS_IN_MIN = 60
let NUM_MSECS_IN_SEC = 1000
let NUM_MIN_BETWEEN_COMMITS = 2
let TIME_TO_WAIT = 15
let closed_ = false
let freq_ = NUM_MIN_BETWEEN_COMMITS * NUM_SECS_IN_MIN * NUM_MSECS_IN_SEC
let path_ = fn
let timer_ = new System.Timers.Timer((float) (NUM_MIN_BETWEEN_COMMITS * NUM_MSECS_IN_SEC) )
let writetofile =
fun (arg: Timers.ElapsedEventArgs ) ->
lock localdata_.lock_ ( fun () ->
if closed_ = false then
for elem in localdata_.elements_ do
serializer.write(elem, writer_)
)
do
timer_.Elapsed.Add(writetofile)
Although it'd be nice if you could invoke the write function like serializer.write(elem, writer_), you can't. You have to invoke it like this instead:
(^W: (member write : ('T * BinaryWriter) -> unit) (serializer, (elem, writer_)))
Full code block:
type BinaryPersistenceOut<'T, ^W when ^W: (member write : ('T * BinaryWriter) -> unit)> (fn: string, serializer: ^W) as this =
let writer_ = new BinaryWriter(File.Open(fn, FileMode.Append))
let mutable localdata_ = new LocalData<'T>([])
let serializer_ = serializer
let NUM_SECS_IN_MIN = 60
let NUM_MSECS_IN_SEC = 1000
let NUM_MIN_BETWEEN_COMMITS = 2
let TIME_TO_WAIT = 15
let closed_ = false
let freq_ = NUM_MIN_BETWEEN_COMMITS * NUM_SECS_IN_MIN * NUM_MSECS_IN_SEC
let path_ = fn
let timer_ = new System.Timers.Timer((float) (NUM_MIN_BETWEEN_COMMITS * NUM_MSECS_IN_SEC) )
let writetofile =
fun (arg: Timers.ElapsedEventArgs ) ->
lock localdata_.lock_ ( fun () ->
if closed_ = false then
for elem in localdata_.elements_ do
(^W: (member write : ('T * BinaryWriter) -> unit) (serializer, (elem, writer_)))
)
do
timer_.Elapsed.Add(writetofile)
Caveat: this compiles, but I have no idea if it does what you want it to do.

Creating an F# record or union type (not instance thereof) at runtime

What is the best way to create an instance of System.Type representing an F# record or union at runtime? That is, I am looking for an equivalent of FSharpType.MakeTupleType for records and unions.
Just to clarify, I am not interested in creating an instance (i.e. FSharpValue.MakeRecord or FSharpValue.MakeUnion).
I am not aware of an equivalent to FSharpType.MakeTupleType for records and unions in the F# library.
One way to create record or union type like structures at runtime is to use Reflection.Emit. A record type is analogous to a sealed class and a union type is an abstract base class with sealed classes for each case.
For example the following function generates a minimal F# record type:
open System
open System.Reflection
open System.Reflection.Emit
let MakeRecord(typeName:string, fields:(string * Type)[]) =
let name = "GeneratedAssembly"
let domain = AppDomain.CurrentDomain
let assembly = domain.DefineDynamicAssembly(AssemblyName(name), AssemblyBuilderAccess.RunAndSave)
let name = "GeneratedModule"
let dm = assembly.DefineDynamicModule(name, name+".dll")
let attributes = TypeAttributes.Public ||| TypeAttributes.Class ||| TypeAttributes.Sealed
let typeBuilder = dm.DefineType(typeName, attributes)
let con = typeof<CompilationMappingAttribute>.GetConstructor([|typeof<SourceConstructFlags>|])
let customBuilder = CustomAttributeBuilder(con, [|SourceConstructFlags.RecordType|])
typeBuilder.SetCustomAttribute(customBuilder)
let makeField name t =
let attributes = FieldAttributes.Assembly
let fieldBuilder = typeBuilder.DefineField(name+"#", t, attributes)
let attributes = PropertyAttributes.None
let propertyBuilder = typeBuilder.DefineProperty(name, attributes, t, [||])
let customBuilder = CustomAttributeBuilder(con, [|SourceConstructFlags.Field|])
propertyBuilder.SetCustomAttribute(customBuilder)
let attributes = MethodAttributes.Public ||| MethodAttributes.HideBySig ||| MethodAttributes.SpecialName
let methodBuilder = typeBuilder.DefineMethod("get_"+name, attributes, t, [||])
let il = methodBuilder.GetILGenerator()
il.Emit(OpCodes.Ldarg_0)
il.Emit(OpCodes.Ldfld, fieldBuilder)
il.Emit(OpCodes.Ret)
propertyBuilder.SetGetMethod(methodBuilder)
fieldBuilder
let types = fields |> Array.map snd
let cb = typeBuilder.DefineConstructor(MethodAttributes.Public, CallingConventions.Standard, types)
let il = cb.GetILGenerator()
il.Emit(OpCodes.Ldarg_0)
il.Emit(OpCodes.Call, typeof<obj>.GetConstructor(Type.EmptyTypes))
fields |> Array.iteri (fun i (name, t) ->
let paramName = name.Substring(0,1).ToLower()+name.Substring(1)
let param = cb.DefineParameter(i+1, ParameterAttributes.In, paramName)
let fieldBuilder = makeField name t
il.Emit(OpCodes.Ldarg_0)
il.Emit(OpCodes.Ldarg, param.Position)
il.Emit(OpCodes.Stfld, fieldBuilder)
)
il.Emit(OpCodes.Ret)
let t = typeBuilder.CreateType()
assembly.Save("GeneratedModule.dll")
t
let r = MakeRecord("MyRecord", [|"Alpha",typeof<int>;"Beta",typeof<string>|])
Note the expected interfaces for a Record type may also need to be generated, i.e. implementations of IEquatable, IStructuralEquatable, IComparable and IStructuralComparable are missing.
Update
Extension methods MakeTupleType and MakeUnionType based on the code sample above are now available in the open source Fil (F# to IL Compiler) project (alpha).

block, following after 'let' is not finished. Statement required

First of all I want to point out that I could translate the error message in uncorrect way... What is this error about? How should I write my code?
[EntryPoint]
let Main (args:string[]) =
let start = startServer (args.[0])
Console.Read()
I do not understand what should I do to let compiler be happy. Is the following code snippet correct?
let rec handle =
let handler = socket.Accept()
let rec recieveData =
let bytesRec = handler.Receive(bytes)
let data = Encoding.ASCII.GetString(bytes,0,bytesRec)
Console.WriteLine( "Text received : {0}", data)
Console.Read()
0
I can't tell what your code is supposed to do because it has outside dependencies, but at a minimum your problem is indentation: whitespace in F# is significant and in particular plays a significant role in determining lexical scope. So for starters you need to fix indentation, something like
[EntryPoint]
let Main (args:string[]) =
let start = startServer (args.[0])
Console.Read()
let rec handle =
let handler = socket.Accept()
let rec recieveData =
let bytesRec = handler.Receive(bytes)
let data = Encoding.ASCII.GetString(bytes,0,bytesRec)
Console.WriteLine( "Text received : {0}", data)
Console.Read()
0
Also, your employment of rec values appears unnecessary if not incorrect. And it's odd that you perform a bunch of work in the body of the handle let expression only to bind it to 0... do you mean handle or recieveData to be functions? If so maybe you intended something more like
let handle socket = //make handle a function with socket an explicit dependency
let handler = socket.Accept()
let bytesRec = handler.Receive(bytes)
let data = Encoding.ASCII.GetString(bytes,0,bytesRec)
Console.WriteLine( "Text received : {0}", data)
Console.Read() |> ignore //probably you are using Read to wait for user interaction to continue, but just ignore the result (returning unit) instead of returning 0
[<EntryPoint>]
let Main (args : string[]) =
let start = startServer args.[0]
Console.Read()

Why is the member dictionary in this f# code always empty?

I want to scrape a page for all the urls and put them in the dictionary. I created an class with an dictionary. But I can't seem to add elements into it.
type crawler =
new()= {}
member this.urls = new Dictionary<string,string>()
member this.start (url : string)=
let hw = new HtmlWeb()
let doc = hw.Load(url)
let docNode = doc.DocumentNode
let links = docNode.SelectNodes(".//a")
for aLink in links do
let href = aLink.GetAttributeValue("href"," ")
if href.StartsWith("http://") && href.EndsWith(".html") then
this.urls.Add(href, href)
Why is the dictionary urls empty?
because urls here is property that returns new dictionary on every call.
type Crawler() =
let urls = new Dictionary<string,string>()
member this.Urls = urls
member this.Start (url : string)=
let hw = new HtmlWeb()
let doc = hw.Load(url)
let docNode = doc.DocumentNode
let links = docNode.SelectNodes(".//a")
for aLink in links do
let href = aLink.GetAttributeValue("href"," ")
if href.StartsWith("http://") && href.EndsWith(".html") then
urls.Add(href, href)
This wasn't your question, but if you're interested in taking a more functional approach, here's one way to do it:
type Crawler =
{ Urls : Set<string> }
[<CompilationRepresentation(CompilationRepresentationFlags.ModuleSuffix)>]
module Crawler =
[<CompiledName("Start")>]
let start crawler (url:string) =
let { Urls = oldUrls } = crawler
let newUrls =
HtmlWeb().Load(url).DocumentNode.SelectNodes(".//a")
|> Seq.cast<HtmlNode>
|> Seq.choose (fun link ->
match link.GetAttributeValue("href"," ") with
| href when href.StartsWith("http://") && href.EndsWith(".html") -> Some href
| _ -> None)
|> Set.ofSeq
|> Set.union oldUrls
{ crawler with Urls = newUrls }
Your data and behaviors are now separate. Crawler is an immutable record type. start accepts a Crawler and returns a new one with the updated list of urls. I replaced Dictionary with Set, since the keys and values are the same; eliminated unused let bindings, and snuck in some pattern matching. This should have a relatively friendly interface in C# also.

Resources