Skip to content

Commit

Permalink
Code generation (#32)
Browse files Browse the repository at this point in the history
* Add code generation

* Add unit tests for code generation

* Do minor changes (WIP)

* Add requested changes

* Update tests according to request

* Use `ReplaceLineEndingsMethod()` instead of own functions
  • Loading branch information
omaus authored May 14, 2024
1 parent a0c0284 commit b4c655a
Show file tree
Hide file tree
Showing 10 changed files with 1,108 additions and 0 deletions.
14 changes: 14 additions & 0 deletions OBO.NET.sln
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,10 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".ci", ".ci", "{C8635ABF-202
.github\workflows\build-and-test.yml = .github\workflows\build-and-test.yml
EndProjectSection
EndProject
Project("{6EC3EE1D-3C4E-46DD-8F32-0CC8E7565705}") = "OBO.NET.CodeGeneration", "src\OBO.NET.CodeGeneration\OBO.NET.CodeGeneration.fsproj", "{67194ACF-4022-4B1A-B849-E574C74CC3E3}"
EndProject
Project("{F2A71F9B-5D33-465A-A702-920D77279786}") = "OBO.NET.CodeGeneration.Tests", "tests\OBO.NET.CodeGeneration.Tests\OBO.NET.CodeGeneration.Tests.fsproj", "{75C2BDF9-A623-4C77-8382-798271D772B4}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Expand All @@ -52,6 +56,14 @@ Global
{56A33646-9976-4155-A731-224F688D9F6F}.Debug|Any CPU.Build.0 = Debug|Any CPU
{56A33646-9976-4155-A731-224F688D9F6F}.Release|Any CPU.ActiveCfg = Release|Any CPU
{56A33646-9976-4155-A731-224F688D9F6F}.Release|Any CPU.Build.0 = Release|Any CPU
{67194ACF-4022-4B1A-B849-E574C74CC3E3}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{67194ACF-4022-4B1A-B849-E574C74CC3E3}.Debug|Any CPU.Build.0 = Debug|Any CPU
{67194ACF-4022-4B1A-B849-E574C74CC3E3}.Release|Any CPU.ActiveCfg = Release|Any CPU
{67194ACF-4022-4B1A-B849-E574C74CC3E3}.Release|Any CPU.Build.0 = Release|Any CPU
{75C2BDF9-A623-4C77-8382-798271D772B4}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{75C2BDF9-A623-4C77-8382-798271D772B4}.Debug|Any CPU.Build.0 = Debug|Any CPU
{75C2BDF9-A623-4C77-8382-798271D772B4}.Release|Any CPU.ActiveCfg = Release|Any CPU
{75C2BDF9-A623-4C77-8382-798271D772B4}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
Expand All @@ -60,6 +72,8 @@ Global
{9B04A234-E7AC-4960-99A2-3E1B7B05F32A} = {BC99056C-0431-4FBE-8396-DAB1FFFF2A8F}
{0A4EF63F-C659-4AB6-AF88-CB444A98C434} = {A989E8E9-BC2B-4E53-97EC-FE0E9ED342F9}
{56A33646-9976-4155-A731-224F688D9F6F} = {5FA53C60-C472-4A99-A3F7-8098B4606CBE}
{67194ACF-4022-4B1A-B849-E574C74CC3E3} = {BC99056C-0431-4FBE-8396-DAB1FFFF2A8F}
{75C2BDF9-A623-4C77-8382-798271D772B4} = {A989E8E9-BC2B-4E53-97EC-FE0E9ED342F9}
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {54B9E6DB-7B93-4B11-AF6D-F46EF21F9781}
Expand Down
1 change: 1 addition & 0 deletions build/ProjectInfo.fs
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ let project = "OBO.NET"
let testProjects =
[
"tests/OBO.NET.Tests/OBO.NET.Tests.fsproj"
"tests/OBO.NET.CodeGeneration.Tests/OBO.NET.CodeGeneration.Tests.fsproj"
]

let solutionFile = $"{project}.sln"
Expand Down
37 changes: 37 additions & 0 deletions playground.fsx
Original file line number Diff line number Diff line change
@@ -1,3 +1,40 @@
#I "src/OBO.NET/bin/Debug/netstandard2.0"
#I "src/OBO.NET/bin/Release/netstandard2.0"
#I "src/OBO.NET.CodeGeneration/bin/Debug/netstandard2.0"
#I "src/OBO.NET.CodeGeneration/bin/Release/netstandard2.0"

#r "OBO.NET.dll"
#r "OBO.NET.CodeGeneration.dll"

open OBO.NET
open OBO.NET.CodeGeneration

#r "nuget: FSharpAux"
#r "nuget: ARCTokenization"

open FSharpAux
open ARCTokenization.Terms

open type System.Environment

let expected =
$"namespace ARCTokenization.StructuralOntology{NewLine}{NewLine} open ControlledVocabulary{NewLine}{NewLine} module Investigation ={NewLine}{NewLine} let Investigation_Metadata = CvTerm.create(\"INVMSO:00000001\", \"Investigation Metadata\", \"INVMSO\"){NewLine}{NewLine} let ONTOLOGY_SOURCE_REFERENCE = CvTerm.create(\"INVMSO:00000002\", \"ONTOLOGY SOURCE REFERENCE\", \"INVMSO\"){NewLine}{NewLine} let Term_Source_Name = CvTerm.create(\"INVMSO:00000003\", \"Term Source Name\", \"INVMSO\")"
|> String.replace "\r" ""
let actual =
CodeGeneration.toSourceCode "Investigation" InvestigationMetadata.ontology
|> String.splitS NewLine
|> Array.take 11
|> String.concat "\n"
|> String.replace "\r" ""

// OBO.NET.OboOntology.toFile @"C:\Repos\CSBiology\OBO.NET\tests\OBO.NET.CodeGeneration.Tests\References\ReferenceOboFile.obo" InvestigationMetadata.ontology

CodeGeneration.toFile "InvestigationMetadata" InvestigationMetadata.ontology @"C:\Repos\CSBiology\OBO.NET\tests\OBO.NET.CodeGeneration.Tests\References\ReferenceSourceFile2.fs"


// DEPRECATED


#I "src/FsOboParser/bin/Debug/netstandard2.0"
#I "src/FsOboParser/bin/Release/netstandard2.0"
#r "FsOboParser.dll"
Expand Down
61 changes: 61 additions & 0 deletions src/OBO.NET.CodeGeneration/CodeGeneration.fs
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
namespace OBO.NET.CodeGeneration


open OBO.NET
open FSharpAux
open type System.Environment


module CodeGeneration =

[<Literal>]
let baseString = """namespace ARCTokenization.StructuralOntology
open ControlledVocabulary
module <name> =
"""

/// Takes an OboTerm and returns its name but with all spaces replaced by underscores.
let toUnderscoredName (term : OboTerm) =
term.Name
|> String.replace " " "_"

/// Returns true if a string contains special characters or starts with a number.
let checkForSpecialCharacters str =
let spChs = System.Text.RegularExpressions.Regex(@"(^\d|[^a-zA-Z0-9_])")
(spChs.Match str).Success

/// Takes a string and returns it with back ticks ("``") at the beginning and the end.
let addBackTicks str =
$"``{str}``"

/// Takes an OboTerm and returns its TermSourceRef as string.
let toTermSourceRef (term : OboTerm) =
term.Id
|> String.takeWhile ((<>) ':')

/// Takes an OboTerm and transforms it into an F# code string for structural ontology libraries.
let toCodeString (term : OboTerm) =
let underscoredName = toUnderscoredName term
let curatedName =
if checkForSpecialCharacters underscoredName then
addBackTicks underscoredName
else underscoredName
$" let {curatedName} = CvTerm.create(\"{term.Id}\", \"{term.Name}\", \"{toTermSourceRef term}\"){NewLine}{NewLine}"

/// Takes a module name and an OboOntology and returns the F# code of the whole term list for structural ontology libraries.
let toSourceCode moduleName (onto : OboOntology) =
let concattedSingleValues = String.init onto.Terms.Length (fun i -> $"{toCodeString onto.Terms[i]}")
let updatedBaseString = String.replace "<name>" moduleName baseString
$"{updatedBaseString}{concattedSingleValues}"

/// Takes a module name and an OboOntology and writes the ontology's terms as F# code for structural ontology libraries as a source file at the given path.
let toFile moduleName (onto : OboOntology) path =
System.IO.File.WriteAllText(path, toSourceCode moduleName onto)

/// Takes a module name and the path to an OBO file and writes the ontology's terms as F# code for structural ontology libraries as a source file at the given output path.
let fromOboFileToSourceFile moduleName inputPath outputPath =
OboOntology.fromFile false inputPath
|> fun o -> toFile moduleName o outputPath
36 changes: 36 additions & 0 deletions src/OBO.NET.CodeGeneration/OBO.NET.CodeGeneration.fsproj
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup>
<TargetFramework>netstandard2.0</TargetFramework>
<GenerateDocumentationFile>true</GenerateDocumentationFile>
<!-- Optional: Publish the repository URL in the built .nupkg (in the NuSpec <Repository> element) -->
<PublishRepositoryUrl>true</PublishRepositoryUrl>
<!-- Optional: Embed source files that are not tracked by the source control manager in the PDB -->
<EmbedUntrackedSources>true</EmbedUntrackedSources>
<!-- Optional: Build symbol package (.snupkg) to distribute the PDB containing Source Link -->
<IncludeSymbols>true</IncludeSymbols>
<SymbolPackageFormat>snupkg</SymbolPackageFormat>
</PropertyGroup>

<ItemGroup>
<ProjectReference Include="..\OBO.NET\OBO.NET.fsproj" />
</ItemGroup>

<ItemGroup>
<Compile Include="CodeGeneration.fs" />
</ItemGroup>

<PropertyGroup>
<Authors>Oliver Maus, F# open source contributors</Authors>
<Description>An OBO file format to F# source code generator, written in F#.</Description>
<Summary>An OBO file format to F# source code generator, written in F#.</Summary>
<PackageLicenseExpression>MIT</PackageLicenseExpression>
<PackageProjectUrl>https://github.com/CSBiology/OBO.NET</PackageProjectUrl>
<PackageTags>ontology fsharp file obo codegeneration code generation</PackageTags>
<RepositoryUrl>https://github.com/CSBiology/OBO.NET</RepositoryUrl>
<RepositoryType>git</RepositoryType>
<FsDocsLicenseLink>https://github.com/CSBiology/OBO.NET/blob/main/LICENSE</FsDocsLicenseLink>
<FsDocsReleaseNotesLink>https://github.com/CSBiology/OBO.NET/blob/main/RELEASE_NOTES.md</FsDocsReleaseNotesLink>
</PropertyGroup>

</Project>
51 changes: 51 additions & 0 deletions tests/OBO.NET.CodeGeneration.Tests/CodeGeneration.Tests.fs
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
namespace OBO.NET.CodeGeneration.Tests

open Expecto
open OBO.NET
open OBO.NET.CodeGeneration
open FSharpAux
open ARCTokenization.StructuralOntology
open ARCTokenization.Terms
open System.IO
open type System.Environment


module CodeGenerationTests =

let refObo = OboOntology.fromFile false (Path.Combine(__SOURCE_DIRECTORY__, "References/ReferenceOboFile.obo"))
let refSF = File.ReadAllText (Path.Combine(__SOURCE_DIRECTORY__, "References/ReferenceSourceFile.fs"))

let toUnderscoredNameTest =
testList "toUnderscoredName" [
testCase "returns correct underscored name" <| fun _ ->
let expected = "Investigation_Metadata"
let actual = List.head refObo.Terms |> CodeGeneration.toUnderscoredName
Expect.equal actual expected "underscored name is not correct"
]

let toTermSourceRefTest =
testList "toTermSourceRef" [
testCase "returns correct TermSourceRef" <| fun _ ->
let expected = "INVMSO"
let actual = List.head refObo.Terms |> CodeGeneration.toTermSourceRef
Expect.equal actual expected "TermSourceRef is not correct"
]

let toCodeStringTest =
testList "toCodeString" [
testCase "returns correct F# code" <| fun _ ->
let expected = $" let Investigation_Metadata = CvTerm.create(\"INVMSO:00000001\", \"Investigation Metadata\", \"INVMSO\"){NewLine}{NewLine}"
let actual = List.head refObo.Terms |> CodeGeneration.toCodeString
Expect.equal actual expected "F# code is not correct"
]

let toSourceCodeTest =
testList "toSourceCode" [
testCase "returns correct source code" <| fun _ ->
let expected = refSF.ReplaceLineEndings()
let actual = (CodeGeneration.toSourceCode "Investigation" refObo).ReplaceLineEndings()
Expect.equal actual expected "Source code is not correct"
]

[<Tests>]
let all = testList "CodeGeneration" [toUnderscoredNameTest; toTermSourceRefTest; toCodeStringTest; toSourceCodeTest]
7 changes: 7 additions & 0 deletions tests/OBO.NET.CodeGeneration.Tests/Main.fs
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
open OBO.NET.CodeGeneration.Tests

open Expecto


[<EntryPoint>]
let main argv = Tests.runTestsInAssemblyWithCLIArgs [] argv
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup>
<TargetFramework>net6.0</TargetFramework>

<IsPackable>false</IsPackable>
<GenerateProgramFile>false</GenerateProgramFile>
<IsTestProject>true</IsTestProject>
</PropertyGroup>

<ItemGroup>
<None Include="references\ReferenceOboFile.obo" />
<Compile Include="References\ReferenceSourceFile.fs" />
<Compile Include="CodeGeneration.Tests.fs" />
<Compile Include="Main.fs" />
</ItemGroup>

<ItemGroup />

<ItemGroup>
<ProjectReference Include="..\..\src\OBO.NET\OBO.NET.fsproj" />
<ProjectReference Include="..\..\src\OBO.NET.CodeGeneration\OBO.NET.CodeGeneration.fsproj" />
<PackageReference Include="Expecto" Version="10.1.0" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.5.0" />
<PackageReference Include="MSTest.TestAdapter" Version="2.2.10" />
<PackageReference Include="MSTest.TestFramework" Version="2.2.10" />
<PackageReference Include="coverlet.collector" Version="3.2.0" />
<PackageReference Include="YoloDev.Expecto.TestSdk" Version="0.14.1" />
<PackageReference Include="ARCTokenization" Version="6.0.0" />
</ItemGroup>

</Project>
Loading

0 comments on commit b4c655a

Please sign in to comment.