Skip to content

Commit

Permalink
Fix for literals with spaces (Issue #4)
Browse files Browse the repository at this point in the history
Supports using numbers in templates (Issue #5)
Improved parsing logic
  • Loading branch information
Dzoukr committed May 29, 2017
1 parent 56f9b72 commit 5dcc8b8
Show file tree
Hide file tree
Showing 6 changed files with 180 additions and 22 deletions.
63 changes: 63 additions & 0 deletions .gitattributes
@@ -0,0 +1,63 @@
###############################################################################
# Set default behavior to automatically normalize line endings.
###############################################################################
* text=auto

###############################################################################
# Set default behavior for command prompt diff.
#
# This is need for earlier builds of msysgit that does not have it on by
# default for csharp files.
# Note: This is only used by command line
###############################################################################
#*.cs diff=csharp

###############################################################################
# Set the merge driver for project and solution files
#
# Merging from the command prompt will add diff markers to the files if there
# are conflicts (Merging from VS is not affected by the settings below, in VS
# the diff markers are never inserted). Diff markers may cause the following
# file extensions to fail to load in VS. An alternative would be to treat
# these files as binary and thus will always conflict and require user
# intervention with every merge. To do so, just uncomment the entries below
###############################################################################
#*.sln merge=binary
#*.csproj merge=binary
#*.vbproj merge=binary
#*.vcxproj merge=binary
#*.vcproj merge=binary
#*.dbproj merge=binary
#*.fsproj merge=binary
#*.lsproj merge=binary
#*.wixproj merge=binary
#*.modelproj merge=binary
#*.sqlproj merge=binary
#*.wwaproj merge=binary

###############################################################################
# behavior for image files
#
# image files are treated as binary by default.
###############################################################################
#*.jpg binary
#*.png binary
#*.gif binary

###############################################################################
# diff behavior for common document formats
#
# Convert binary document formats to text before diffing them. This feature
# is only available from the command line. Turn it on by uncommenting the
# entries below.
###############################################################################
#*.doc diff=astextplain
#*.DOC diff=astextplain
#*.docx diff=astextplain
#*.DOCX diff=astextplain
#*.dot diff=astextplain
#*.DOT diff=astextplain
#*.pdf diff=astextplain
#*.PDF diff=astextplain
#*.rtf diff=astextplain
#*.RTF diff=astextplain
5 changes: 5 additions & 0 deletions RELEASE_NOTES.md
@@ -1,3 +1,8 @@
### 1.3.0 - May 29 2017
* Fix for literals with spaces (Issue #4)
* Supports using numbers in templates (Issue #5)
* Improved parsing logic

### 1.2.0 - March 10 2017
* Added support for direct tuples destructuring in fs-for cycle

Expand Down
8 changes: 4 additions & 4 deletions src/Fue/AssemblyInfo.fs
Expand Up @@ -5,13 +5,13 @@ open System.Reflection
[<assembly: AssemblyTitleAttribute("Fue")>]
[<assembly: AssemblyProductAttribute("Fue")>]
[<assembly: AssemblyDescriptionAttribute("F# templating library with simple syntax designed for smooth work with F# types")>]
[<assembly: AssemblyVersionAttribute("1.2.0")>]
[<assembly: AssemblyFileVersionAttribute("1.2.0")>]
[<assembly: AssemblyVersionAttribute("1.3.0")>]
[<assembly: AssemblyFileVersionAttribute("1.3.0")>]
do ()

module internal AssemblyVersionInformation =
let [<Literal>] AssemblyTitle = "Fue"
let [<Literal>] AssemblyProduct = "Fue"
let [<Literal>] AssemblyDescription = "F# templating library with simple syntax designed for smooth work with F# types"
let [<Literal>] AssemblyVersion = "1.2.0"
let [<Literal>] AssemblyFileVersion = "1.2.0"
let [<Literal>] AssemblyVersion = "1.3.0"
let [<Literal>] AssemblyFileVersion = "1.3.0"
81 changes: 63 additions & 18 deletions src/Fue/Parser.fs
@@ -1,6 +1,7 @@
module Fue.Parser

open Core
open System
open StringUtils
open System.Text.RegularExpressions
open HtmlAgilityPack
Expand All @@ -17,7 +18,10 @@ let private (==>) regex value =
regex.Match(value).Groups

let private splitByCurrying parseFn t =
let f,s = t |> splitToFirstAndList ' '

let regex = new Regex(""""[^"]+"?|'[^']+'?|[^'"\s]+""", RegexOptions.IgnoreCase ||| RegexOptions.Singleline)
let matches = [ for m in regex.Matches(t) do yield m.Groups.[0].Value ]
let f,s = (matches |> List.head |> clean),(matches |> List.tail |> List.map clean)
f, (s |> List.map parseFn)

let private (|TwoPartsMatch|_|) (groups:GroupCollection) =
Expand All @@ -33,26 +37,67 @@ let private (|OnePartMatch|_|) (groups:GroupCollection) =
| 2 -> groups.[1].Value |> clean |> Some
| _ -> None

let private numberOrSimple value =
match Int32.TryParse(value, Globalization.NumberStyles.Any, Globalization.NumberFormatInfo.InvariantInfo) with
| true, value -> Literal(value)
| _ ->
match Decimal.TryParse(value, Globalization.NumberStyles.Any, Globalization.NumberFormatInfo.InvariantInfo) with
| true, value -> Literal(value)
| _ ->
match Double.TryParse(value, Globalization.NumberStyles.Any, Globalization.NumberFormatInfo.InvariantInfo) with
| true, value -> Literal(value)
| _ -> SimpleValue(value)

let parseTemplateValue text =
let rec parse t =

let pipedFn parseFn t =
match "(.+)\|\>(.+)" ==> t with
| TwoPartsMatch(parts, fnName) ->
let f,p = fnName |> splitByCurrying parse
Function(f, p @ [parts |> parse])
| _ ->
match "(.+?)\((.*)\)" ==> t, "(.+?)\s+(.*)" ==> (t |> clean) with
| TwoPartsMatch(fnName, parts), _ ->
let parts = parts |> splitToFunctionParams |> List.map parse
Function(fnName, parts)
| _, TwoPartsMatch(fnName, parts) ->
let parts = parts |> split ' ' |> List.map parse
Function(fnName, parts)
| _ ->
match "\"(.+)\"" ==> t, "'(.+)'" ==> t with
| OnePartMatch(constant), _
| _, OnePartMatch(constant) -> Literal(constant)
| _ -> t |> SimpleValue
parse text
let f,p = fnName |> splitByCurrying parseFn
Function(f, p @ [parts |> parseFn]) |> Some
| _ -> None

let bracketFn parseFn t =
match """(.+?)\((.*)\)""" ==> t with
| TwoPartsMatch(fnName, parts) ->
let parts = parts |> splitToFunctionParams |> List.map parseFn
Function(fnName, parts) |> Some
| _ -> None

let plainFn parseFn t =
match """(.+?)\s+(.*)""" ==> (t |> clean) with
| TwoPartsMatch(fnName, parts) ->
let parts = parts |> split ' ' |> List.map parseFn
Function(fnName, parts) |> Some
| _ -> None

let literalSQFn parseFn t =
match """^'([^']+)'$""" ==> (t |> clean) with
| OnePartMatch(constant) -> Literal(constant) |> Some
| _ -> None

let literalDQFn parseFn t =
match """^"([^"]+)"$""" ==> (t |> clean) with
| OnePartMatch(constant) -> Literal(constant) |> Some
| _ -> None

let parseFns = [literalSQFn;literalDQFn;pipedFn;bracketFn;plainFn;]

let rec newParse t =
let foldFn (acc:TemplateValue option) item =
if acc.IsSome then acc
else t |> item

let chainResult =
parseFns
|> List.map (fun x -> x newParse)
|> List.fold foldFn None

match chainResult with
| Some v -> v
| None -> t |> numberOrSimple

newParse text

let parseForCycleAttribute forAttr =
match "(.+) in (.+)" ==> forAttr with
Expand Down
1 change: 1 addition & 0 deletions tests/Fue.Tests/Fue.Tests.fsproj
Expand Up @@ -62,6 +62,7 @@
<Content Include="App.config" />
<Compile Include="Parser.fs" />
<Compile Include="Compiler.fs" />
<Compile Include="Issues.fs" />
<None Include="SimplePage.html">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
Expand Down
44 changes: 44 additions & 0 deletions tests/Fue.Tests/Issues.fs
@@ -0,0 +1,44 @@
module Fue.Tests.Issues

open NUnit.Framework
open FsUnit
open Fue.Data
open Fue.Compiler
open Fue.Parser
open Fue.Core

[<Test>]
let ``Supports numbers in literals (Issue #5)``() =
let html = "{{{multiply 2}}}"
init
|> add "multiply" (fun x -> x * 2)
|> fromText html
|> should equal "4"

[<Test>]
let ``Supports numbers in literals with more params (Issue #5)``() =
let html = "{{{multiply 2 5}}}"
init
|> add "multiply" (fun x y -> x * y)
|> fromText html
|> should equal "10"

[<Test>]
let ``Combines numbers and literals (Issue #5)``() =
let html = "{{{multiply 'someText' 2 5}}}"
init
|> add "multiply" (fun text x y -> x * y |> sprintf "This is %s and %i" text)
|> fromText html
|> should equal "This is someText and 10"


[<Test>]
let ``Parses correctly with spaces (Issue #4)``() =
""" now() |> fmtDate "MMMM yyyy" """
|> parseTemplateValue
|> should equal (
TemplateValue.Function("fmtDate",
[
TemplateValue.Literal("MMMM yyyy")
TemplateValue.Function("now", [])
]))

0 comments on commit 5dcc8b8

Please sign in to comment.