3 people like it.
Like the snippet!
Code Kata: Writing Markdown parser
Sample for Coding Kata
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
16:
17:
18:
19:
20:
21:
22:
23:
24:
25:
26:
27:
28:
29:
30:
31:
32:
33:
34:
35:
36:
37:
38:
39:
40:
41:
42:
43:
44:
45:
46:
47:
48:
49:
50:
51:
52:
53:
54:
55:
56:
57:
58:
59:
60:
61:
62:
63:
64:
65:
66:
67:
68:
69:
70:
71:
72:
73:
74:
75:
76:
77:
78:
79:
80:
81:
82:
83:
84:
85:
86:
87:
88:
|
#r "nunit.framework.dll"
open System
open NUnit.Framework
// ----------------------------------------------------------------------------
let toString chars =
System.String(chars |> Array.ofList)
// TODO: Read until the end of inline code
let rec parseInlineBody acc chars =
failwith "!"
// TODO: Match beginning and read until the end
let parseInline chars =
failwith "!"
// ----------------------------------------------------------------------------
// Definition of markdown span element
type MarkdownSpan =
| Literal of string
| InlineCode of string
// Parse spans of the input
let rec parseSpans acc chars = seq {
// emit literal if we skipped some characters
let emitLiteral() = seq {
if acc <> [] then
yield acc |> List.rev |> toString |> Literal }
// try parsing inline
match parseInline chars, chars with
| Some(body, chars), _ ->
yield! emitLiteral ()
// TODO: Produce single 'InlineCode' element
// TODO: Continue recursively
| _, c::chars ->
// TODO: Add 'c' to unconsumed characters
// and continue recursively
| _, [] ->
// Finished, emit the remaining unprocessed characters
yield! emitLiteral () }
// ----------------------------------------------------------------------------
[<TestFixture>]
module Tests =
// parseInlineBody
[<Test>]
let ``End of inline is found`` () =
let res = "aa`bb" |> List.ofSeq |> parseInlineBody []
Assert.That((res = (['a'; 'a'], ['b'; 'b'])))
[<Test>]
let ``All input is consumed`` () =
let res = "aa" |> List.ofSeq |> parseInlineBody []
Assert.That((res = (['a'; 'a'], [])))
// parseInline
[<Test>]
let ``Needs backtick`` () =
let res = "aa" |> List.ofSeq |> parseInline
Assert.That((res = None))
[<Test>]
let ``Finds inline code`` () =
let res = "`aa`bb" |> List.ofSeq |> parseInline
Assert.That((res = Some (['a'; 'a'], ['b'; 'b'])))
// parseSpans
[<Test>]
let ``Parse two inline snippets`` () =
let res = "`a` `c`" |> List.ofSeq |> parseSpans [] |> List.ofSeq
Assert.That((res = [InlineCode "a"; Literal " "; InlineCode "c"]))
// ----------------------------------------------------------------------------
do
``End of inline is found``()
``All input is consumed``()
``Needs backtick`` ()
``Finds inline code`` ()
``Parse two inline snippets`` ()
|
namespace System
val toString : chars:char list -> String
Full name: Script.toString
val chars : char list
Multiple items
type String =
new : value:char -> string + 7 overloads
member Chars : int -> char
member Clone : unit -> obj
member CompareTo : value:obj -> int + 1 overload
member Contains : value:string -> bool
member CopyTo : sourceIndex:int * destination:char[] * destinationIndex:int * count:int -> unit
member EndsWith : value:string -> bool + 2 overloads
member Equals : obj:obj -> bool + 2 overloads
member GetEnumerator : unit -> CharEnumerator
member GetHashCode : unit -> int
...
Full name: System.String
--------------------
String(value: nativeptr<char>) : unit
String(value: nativeptr<sbyte>) : unit
String(value: char []) : unit
String(c: char, count: int) : unit
String(value: nativeptr<char>, startIndex: int, length: int) : unit
String(value: nativeptr<sbyte>, startIndex: int, length: int) : unit
String(value: char [], startIndex: int, length: int) : unit
String(value: nativeptr<sbyte>, startIndex: int, length: int, enc: Text.Encoding) : unit
type Array =
member Clone : unit -> obj
member CopyTo : array:Array * index:int -> unit + 1 overload
member GetEnumerator : unit -> IEnumerator
member GetLength : dimension:int -> int
member GetLongLength : dimension:int -> int64
member GetLowerBound : dimension:int -> int
member GetUpperBound : dimension:int -> int
member GetValue : [<ParamArray>] indices:int[] -> obj + 7 overloads
member Initialize : unit -> unit
member IsFixedSize : bool
...
Full name: System.Array
val ofList : list:'T list -> 'T []
Full name: Microsoft.FSharp.Collections.Array.ofList
val parseInlineBody : acc:'a -> chars:'b -> 'c
Full name: Script.parseInlineBody
val acc : 'a
val chars : 'b
val failwith : message:string -> 'T
Full name: Microsoft.FSharp.Core.Operators.failwith
val parseInline : chars:'a -> 'b
Full name: Script.parseInline
val chars : 'a
type MarkdownSpan =
| Literal of string
| InlineCode of string
Full name: Script.MarkdownSpan
Multiple items
union case MarkdownSpan.Literal: string -> MarkdownSpan
--------------------
type LiteralAttribute =
inherit Attribute
new : unit -> LiteralAttribute
Full name: Microsoft.FSharp.Core.LiteralAttribute
--------------------
new : unit -> LiteralAttribute
Multiple items
val string : value:'T -> string
Full name: Microsoft.FSharp.Core.Operators.string
--------------------
type string = String
Full name: Microsoft.FSharp.Core.string
union case MarkdownSpan.InlineCode: string -> MarkdownSpan
val parseSpans : acc:char list -> chars:'a list -> seq<MarkdownSpan>
Full name: Script.parseSpans
val acc : char list
val chars : 'a list
Multiple items
val seq : sequence:seq<'T> -> seq<'T>
Full name: Microsoft.FSharp.Core.Operators.seq
--------------------
type seq<'T> = Collections.Generic.IEnumerable<'T>
Full name: Microsoft.FSharp.Collections.seq<_>
val emitLiteral : (unit -> seq<MarkdownSpan>)
Multiple items
module List
from Microsoft.FSharp.Collections
--------------------
type List<'T> =
| ( [] )
| ( :: ) of Head: 'T * Tail: 'T list
interface IEnumerable
interface IEnumerable<'T>
member Head : 'T
member IsEmpty : bool
member Item : index:int -> 'T with get
member Length : int
member Tail : 'T list
static member Cons : head:'T * tail:'T list -> 'T list
static member Empty : 'T list
Full name: Microsoft.FSharp.Collections.List<_>
val rev : list:'T list -> 'T list
Full name: Microsoft.FSharp.Collections.List.rev
union case Option.Some: Value: 'T -> Option<'T>
val body : obj
val chars : obj
val c : 'a
module Tests
from Script
val ( End of inline is found ) : unit -> 'a
Full name: Script.Tests.( End of inline is found )
val res : obj
val ofSeq : source:seq<'T> -> 'T list
Full name: Microsoft.FSharp.Collections.List.ofSeq
val ( All input is consumed ) : unit -> 'a
Full name: Script.Tests.( All input is consumed )
val ( Needs backtick ) : unit -> 'a
Full name: Script.Tests.( Needs backtick )
union case Option.None: Option<'T>
val ( Finds inline code ) : unit -> 'a
Full name: Script.Tests.( Finds inline code )
val ( Parse two inline snippets ) : unit -> 'a
Full name: Script.Tests.( Parse two inline snippets )
val res : MarkdownSpan list
More information