{"cells":[{"cell_type":"markdown","metadata":{},"source":["# Parser (Polyglot)"]},{"cell_type":"code","execution_count":1,"metadata":{"duration_ms":9688,"language":"fsharp"},"outputs":[],"source":["#!import ../../lib/fsharp/Notebooks.livemd"]},{"cell_type":"code","execution_count":2,"metadata":{"duration_ms":187,"language":"fsharp"},"outputs":[],"source":["#!import ../../lib/fsharp/Testing.livemd"]},{"cell_type":"code","execution_count":3,"metadata":{"duration_ms":34,"language":"fsharp"},"outputs":[],"source":["#!import ../../lib/fsharp/Common.fs"]},{"cell_type":"code","execution_count":4,"metadata":{"duration_ms":13,"language":"fsharp"},"outputs":[],"source":["open Common"]},{"cell_type":"markdown","metadata":{},"source":["### TextInput"]},{"cell_type":"code","execution_count":5,"metadata":{"duration_ms":42,"language":"fsharp"},"outputs":[],"source":["type Position =\n","    {\n","        line : int\n","        column : int\n","    }"]},{"cell_type":"code","execution_count":6,"metadata":{"duration_ms":11,"language":"fsharp"},"outputs":[],"source":["let initialPos = { line = 0; column = 0 }"]},{"cell_type":"code","execution_count":7,"metadata":{"duration_ms":11,"language":"fsharp"},"outputs":[],"source":["let inline incrCol (pos : Position) =\n","    { pos with column = pos.column + 1 }"]},{"cell_type":"code","execution_count":8,"metadata":{"duration_ms":12,"language":"fsharp"},"outputs":[],"source":["let inline incrLine pos =\n","    { line = pos.line + 1; column = 0 }"]},{"cell_type":"code","execution_count":9,"metadata":{"duration_ms":23,"language":"fsharp"},"outputs":[],"source":["type InputState =\n","    {\n","        lines : string[]\n","        position : Position\n","    }"]},{"cell_type":"code","execution_count":10,"metadata":{"duration_ms":12,"language":"fsharp"},"outputs":[],"source":["let inline fromStr str =\n","    {\n","        lines =\n","            if str |> String.IsNullOrEmpty\n","            then [||]\n","            else str |> SpiralSm.split_string [| \"\\r\\n\"; \"\\n\" |]\n","        position = initialPos\n","    }"]},{"cell_type":"code","execution_count":11,"metadata":{"duration_ms":50,"language":"fsharp"},"outputs":[{"name":"stdout","output_type":"stream","text":["{ lines = [||]\n","  position = { line = 0\n","               column = 0 } }"]}],"source":["///- --test\n","\n","fromStr \"\" |> _assertEqual {\n","    lines = [||]\n","    position = { line = 0; column = 0 }\n","}"]},{"cell_type":"code","execution_count":12,"metadata":{"duration_ms":18,"language":"fsharp"},"outputs":[{"name":"stdout","output_type":"stream","text":["{ lines = [|\"Hello \"; \" World\"|]\n","  position = { line = 0\n","               column = 0 } }"]}],"source":["///- --test\n","\n","fromStr \"Hello \\n World\" |> _assertEqual {\n","    lines = [| \"Hello \"; \" World\" |]\n","    position = { line = 0; column = 0 }\n","}"]},{"cell_type":"code","execution_count":13,"metadata":{"duration_ms":13,"language":"fsharp"},"outputs":[],"source":["let inline currentLine inputState =\n","    let linePos = inputState.position.line\n","    if linePos < inputState.lines.Length\n","    then inputState.lines.[linePos]\n","    else \"end of file\""]},{"cell_type":"code","execution_count":14,"metadata":{"duration_ms":18,"language":"fsharp"},"outputs":[],"source":["let inline nextChar input =\n","    let linePos = input.position.line\n","    let colPos = input.position.column\n","\n","    if linePos >= input.lines.Length\n","    then input, None\n","    else\n","        let currentLine = currentLine input\n","        if colPos < currentLine.Length then\n","            let char = currentLine.[colPos]\n","            let newPos = incrCol input.position\n","            let newState = { input with position = newPos }\n","            newState, Some char\n","        else\n","            let char = '\\n'\n","            let newPos = incrLine input.position\n","            let newState = { input with position = newPos }\n","            newState, Some char"]},{"cell_type":"code","execution_count":15,"metadata":{"duration_ms":25,"language":"fsharp"},"outputs":[{"name":"stdout","output_type":"stream","text":["{ lines = [|\"Hello World\"|]\n","  position = { line = 0\n","               column = 1 } }\n","\n","Some 'H'"]}],"source":["///- --test\n","\n","let newInput, charOpt = fromStr \"Hello World\" |> nextChar\n","\n","newInput |> _assertEqual {\n","    lines = [| \"Hello World\" |]\n","    position = { line = 0; column = 1 }\n","}\n","charOpt |> _assertEqual (Some 'H')"]},{"cell_type":"code","execution_count":16,"metadata":{"duration_ms":23,"language":"fsharp"},"outputs":[{"name":"stdout","output_type":"stream","text":["{ lines = [|\"Hello\"; \"\"; \"World\"|]\n","  position = { line = 0\n","               column = 1 } }\n","\n","Some 'H'"]}],"source":["///- --test\n","\n","let newInput, charOpt = fromStr \"Hello\\n\\nWorld\" |> nextChar\n","\n","newInput |> _assertEqual {\n","    lines = [| \"Hello\"; \"\"; \"World\" |]\n","    position = { line = 0; column = 1 }\n","}\n","charOpt |> _assertEqual (Some 'H')"]},{"cell_type":"markdown","metadata":{},"source":["### Parser"]},{"cell_type":"code","execution_count":17,"metadata":{"duration_ms":61,"language":"fsharp"},"outputs":[],"source":["type Input = InputState\n","type ParserLabel = string\n","type ParserError = string\n","\n","type ParserPosition =\n","    {\n","        currentLine : string\n","        line : int\n","        column : int\n","    }\n","\n","type ParseResult<'a> =\n","    | Success of 'a\n","    | Failure of ParserLabel * ParserError * ParserPosition\n","\n","type Parser<'a> =\n","    {\n","        label : ParserLabel\n","        parseFn : Input -> ParseResult<'a * Input>\n","    }"]},{"cell_type":"code","execution_count":18,"metadata":{"duration_ms":17,"language":"fsharp"},"outputs":[],"source":["let inline printResult result =\n","    match result with\n","    | Success (value, input) ->\n","        printfn $\"%A{value}\"\n","    | Failure (label, error, parserPos) ->\n","        let errorLine = parserPos.currentLine\n","        let colPos = parserPos.column\n","        let linePos = parserPos.line\n","        let failureCaret = $\"{' ' |> string |> String.replicate colPos}^{error}\"\n","        printfn $\"Line:%i{linePos} Col:%i{colPos} Error parsing %s{label}\\n%s{errorLine}\\n%s{failureCaret}\""]},{"cell_type":"code","execution_count":19,"metadata":{"duration_ms":23,"language":"fsharp"},"outputs":[],"source":["let inline runOnInput parser input =\n","    parser.parseFn input"]},{"cell_type":"code","execution_count":20,"metadata":{"duration_ms":23,"language":"fsharp"},"outputs":[],"source":["let inline run parser inputStr =\n","    runOnInput parser (fromStr inputStr)"]},{"cell_type":"code","execution_count":21,"metadata":{"duration_ms":21,"language":"fsharp"},"outputs":[],"source":["let inline parserPositionFromInputState (inputState : Input) =\n","    {\n","        currentLine = currentLine inputState\n","        line = inputState.position.line\n","        column = inputState.position.column\n","    }"]},{"cell_type":"code","execution_count":22,"metadata":{"duration_ms":11,"language":"fsharp"},"outputs":[],"source":["let inline getLabel parser =\n","    parser.label"]},{"cell_type":"code","execution_count":23,"metadata":{"duration_ms":16,"language":"fsharp"},"outputs":[],"source":["let inline setLabel parser newLabel =\n","    {\n","        label = newLabel\n","        parseFn = fun input ->\n","            match parser.parseFn input with\n","            | Success s -> Success s\n","            | Failure (oldLabel, err, pos) -> Failure (newLabel, err, pos)\n","    }"]},{"cell_type":"code","execution_count":24,"metadata":{"duration_ms":13,"language":"fsharp"},"outputs":[],"source":["let (<?>) = setLabel"]},{"cell_type":"code","execution_count":25,"metadata":{"duration_ms":20,"language":"fsharp"},"outputs":[],"source":["let inline satisfy predicate label =\n","    {\n","        label = label\n","        parseFn = fun input ->\n","            let remainingInput, charOpt = nextChar input\n","            match charOpt with\n","            | None ->\n","                let err = \"No more input\"\n","                let pos = parserPositionFromInputState input\n","                Failure (label, err, pos)\n","            | Some first ->\n","                if predicate first\n","                then Success (first, remainingInput)\n","                else\n","                    let err = $\"Unexpected '%c{first}'\"\n","                    let pos = parserPositionFromInputState input\n","                    Failure (label, err, pos)\n","    }"]},{"cell_type":"code","execution_count":26,"metadata":{"duration_ms":26,"language":"fsharp"},"outputs":[{"name":"stdout","output_type":"stream","text":["Success ('H', { lines = [|\"Hello\"|]\n","                position = { line = 0\n","                             column = 1 } })"]}],"source":["///- --test\n","\n","let input = fromStr \"Hello\"\n","let parser = satisfy (fun c -> c = 'H') \"H\"\n","runOnInput parser input |> _assertEqual (\n","    Success (\n","        'H',\n","        {\n","            lines = [| \"Hello\" |]\n","            position = { line = 0; column = 1 }\n","        }\n","    )\n",")"]},{"cell_type":"code","execution_count":27,"metadata":{"duration_ms":24,"language":"fsharp"},"outputs":[{"name":"stdout","output_type":"stream","text":["Failure (\"H\", \"Unexpected 'W'\", { currentLine = \"World\"\n","                                  line = 0\n","                                  column = 0 })"]}],"source":["///- --test\n","\n","let input = fromStr \"World\"\n","let parser = satisfy (fun c -> c = 'H') \"H\"\n","runOnInput parser input |> _assertEqual (\n","    Failure (\n","        \"H\",\n","        \"Unexpected 'W'\",\n","        {\n","            currentLine = \"World\"\n","            line = 0\n","            column = 0\n","        }\n","    )\n",")"]},{"cell_type":"code","execution_count":28,"metadata":{"duration_ms":15,"language":"fsharp"},"outputs":[],"source":["let inline bindP f p =\n","    {\n","        label = \"unknown\"\n","        parseFn = fun input ->\n","            match runOnInput p input with\n","            | Failure (label, err, pos) -> Failure (label, err, pos)\n","            | Success (value1, remainingInput) -> runOnInput (f value1) remainingInput\n","    }"]},{"cell_type":"code","execution_count":29,"metadata":{"duration_ms":13,"language":"fsharp"},"outputs":[],"source":["let inline (>>=) p f = bindP f p"]},{"cell_type":"code","execution_count":30,"metadata":{"duration_ms":29,"language":"fsharp"},"outputs":[{"name":"stdout","output_type":"stream","text":["Success ('e', { lines = [|\"Hello\"|]\n","                position = { line = 0\n","                             column = 2 } })"]}],"source":["///- --test\n","\n","let input = fromStr \"Hello\"\n","let parser = satisfy (fun c -> c = 'H') \"H\"\n","let parser2 = parser >>= fun c -> satisfy (fun c -> c = 'e') \"e\"\n","runOnInput parser2 input |> _assertEqual (\n","    Success (\n","        'e',\n","        {\n","            lines = [| \"Hello\" |]\n","            position = { line = 0; column = 2 }\n","        }\n","    )\n",")"]},{"cell_type":"code","execution_count":31,"metadata":{"duration_ms":29,"language":"fsharp"},"outputs":[{"name":"stdout","output_type":"stream","text":["Failure (\"e\", \"Unexpected 'o'\", { currentLine = \"World\"\n","                                  line = 0\n","                                  column = 1 })"]}],"source":["///- --test\n","\n","let input = fromStr \"World\"\n","let parser = satisfy (fun c -> c = 'W') \"W\"\n","let parser2 = parser >>= fun c -> satisfy (fun c -> c = 'e') \"e\"\n","runOnInput parser2 input |> _assertEqual (\n","    Failure (\n","        \"e\",\n","        \"Unexpected 'o'\",\n","        {\n","            currentLine = \"World\"\n","            line = 0\n","            column = 1\n","        }\n","    )\n",")"]},{"cell_type":"code","execution_count":32,"metadata":{"duration_ms":14,"language":"fsharp"},"outputs":[],"source":["let inline returnP x =\n","    {\n","        label = $\"%A{x}\"\n","        parseFn = fun input -> Success (x, input)\n","    }"]},{"cell_type":"code","execution_count":33,"metadata":{"duration_ms":21,"language":"fsharp"},"outputs":[{"name":"stdout","output_type":"stream","text":["Success (\"Hello\", { lines = [|\"Hello\"|]\n","                    position = { line = 0\n","                                 column = 0 } })"]}],"source":["///- --test\n","\n","let input = fromStr \"Hello\"\n","let parser = returnP \"Hello\"\n","runOnInput parser input |> _assertEqual (\n","    Success (\n","        \"Hello\",\n","        {\n","            lines = [| \"Hello\" |]\n","            position = { line = 0; column = 0 }\n","        }\n","    )\n",")"]},{"cell_type":"code","execution_count":34,"metadata":{"duration_ms":15,"language":"fsharp"},"outputs":[],"source":["let inline mapP f =\n","    bindP (f >> returnP)"]},{"cell_type":"code","execution_count":35,"metadata":{"duration_ms":15,"language":"fsharp"},"outputs":[],"source":["let (<!>) = mapP"]},{"cell_type":"code","execution_count":36,"metadata":{"duration_ms":12,"language":"fsharp"},"outputs":[],"source":["let inline (|>>) x f = f <!> x"]},{"cell_type":"code","execution_count":37,"metadata":{"duration_ms":28,"language":"fsharp"},"outputs":[{"name":"stdout","output_type":"stream","text":["Success (\"H\", { lines = [|\"Hello\"|]\n","                position = { line = 0\n","                             column = 1 } })"]}],"source":["///- --test\n","\n","let input = fromStr \"Hello\"\n","let parser = satisfy (fun c -> c = 'H') \"H\"\n","let parser2 = parser |>> string\n","runOnInput parser2 input |> _assertEqual (\n","    Success (\n","        \"H\",\n","        {\n","            lines = [| \"Hello\" |]\n","            position = { line = 0; column = 1 }\n","        }\n","    )\n",")"]},{"cell_type":"code","execution_count":38,"metadata":{"duration_ms":40,"language":"fsharp"},"outputs":[],"source":["let inline applyP fP xP =\n","    fP >>=\n","        fun f ->\n","            xP >>=\n","                fun x ->\n","                    returnP (f x)"]},{"cell_type":"code","execution_count":39,"metadata":{"duration_ms":17,"language":"fsharp"},"outputs":[],"source":["let (<*>) = applyP"]},{"cell_type":"code","execution_count":40,"metadata":{"duration_ms":13,"language":"fsharp"},"outputs":[],"source":["let inline lift2 f xP yP =\n","    returnP f <*> xP <*> yP"]},{"cell_type":"code","execution_count":41,"metadata":{"duration_ms":38,"language":"fsharp"},"outputs":[{"name":"stdout","output_type":"stream","text":["Success (\"He\", { lines = [|\"Hello\"|]\n","                 position = { line = 0\n","                              column = 2 } })"]}],"source":["///- --test\n","\n","let input = fromStr \"Hello\"\n","let parser = satisfy (fun c -> c = 'H') \"H\"\n","let parser2 = satisfy (fun c -> c = 'e') \"e\"\n","let parser3 = lift2 (fun c1 c2 -> string c1 + string c2) parser parser2\n","runOnInput parser3 input |> _assertEqual (\n","    Success (\n","        \"He\",\n","        {\n","            lines = [| \"Hello\" |]\n","            position = { line = 0; column = 2 }\n","        }\n","    )\n",")"]},{"cell_type":"code","execution_count":42,"metadata":{"duration_ms":18,"language":"fsharp"},"outputs":[],"source":["let inline andThen p1 p2 =\n","    p1 >>=\n","        fun p1Result ->\n","            p2 >>=\n","                fun p2Result ->\n","                    returnP (p1Result, p2Result)\n","    <?> $\"{getLabel p1} andThen {getLabel p2}\""]},{"cell_type":"code","execution_count":43,"metadata":{"duration_ms":22,"language":"fsharp"},"outputs":[],"source":["let (.>>.) = andThen"]},{"cell_type":"code","execution_count":44,"metadata":{"duration_ms":36,"language":"fsharp"},"outputs":[{"name":"stdout","output_type":"stream","text":["Success (('H', 'e'), { lines = [|\"Hello\"|]\n","                       position = { line = 0\n","                                    column = 2 } })"]}],"source":["///- --test\n","\n","let input = fromStr \"Hello\"\n","let parser = satisfy (fun c -> c = 'H') \"H\"\n","let parser2 = satisfy (fun c -> c = 'e') \"e\"\n","let parser3 = parser .>>. parser2\n","runOnInput parser3 input |> _assertEqual (\n","    Success (\n","        ('H', 'e'),\n","        {\n","            lines = [| \"Hello\" |]\n","            position = { line = 0; column = 2 }\n","        }\n","    )\n",")"]},{"cell_type":"code","execution_count":45,"metadata":{"duration_ms":14,"language":"fsharp"},"outputs":[],"source":["let inline orElse p1 p2 =\n","    {\n","        label = $\"{getLabel p1} orElse {getLabel p2}\"\n","        parseFn = fun input ->\n","            match runOnInput p1 input with\n","            | Success _ as result -> result\n","            | Failure _ -> runOnInput p2 input\n","    }"]},{"cell_type":"code","execution_count":46,"metadata":{"duration_ms":13,"language":"fsharp"},"outputs":[],"source":["let (<|>) = orElse"]},{"cell_type":"code","execution_count":47,"metadata":{"duration_ms":30,"language":"fsharp"},"outputs":[{"name":"stdout","output_type":"stream","text":["Success ('h', { lines = [|\"hello\"|]\n","                position = { line = 0\n","                             column = 1 } })"]}],"source":["///- --test\n","\n","let input = fromStr \"hello\"\n","let parser = satisfy (fun c -> c = 'H') \"H\"\n","let parser2 = satisfy (fun c -> c = 'h') \"h\"\n","let parser3 = parser <|> parser2\n","runOnInput parser3 input |> _assertEqual (\n","    Success (\n","        'h',\n","        {\n","            lines = [| \"hello\" |]\n","            position = { line = 0; column = 1 }\n","        }\n","    )\n",")"]},{"cell_type":"code","execution_count":48,"metadata":{"duration_ms":12,"language":"fsharp"},"outputs":[],"source":["let inline choice listOfParsers =\n","    listOfParsers |> List.reduce (<|>)"]},{"cell_type":"code","execution_count":49,"metadata":{"duration_ms":28,"language":"fsharp"},"outputs":[{"name":"stdout","output_type":"stream","text":["Success ('h', { lines = [|\"hello\"|]\n","                position = { line = 0\n","                             column = 1 } })"]}],"source":["///- --test\n","\n","let input = fromStr \"hello\"\n","let parser = satisfy (fun c -> c = 'H') \"H\"\n","let parser2 = satisfy (fun c -> c = 'h') \"h\"\n","let parser3 = choice [ parser; parser2 ]\n","runOnInput parser3 input |> _assertEqual (\n","    Success (\n","        'h',\n","        {\n","            lines = [| \"hello\" |]\n","            position = { line = 0; column = 1 }\n","        }\n","    )\n",")"]},{"cell_type":"code","execution_count":50,"metadata":{"duration_ms":20,"language":"fsharp"},"outputs":[],"source":["let rec sequence parserList =\n","    match parserList with\n","    | [] -> returnP []\n","    | head :: tail -> (lift2 cons) head (sequence tail)"]},{"cell_type":"code","execution_count":51,"metadata":{"duration_ms":38,"language":"fsharp"},"outputs":[{"name":"stdout","output_type":"stream","text":["Success (['H'; 'e'], { lines = [|\"Hello\"|]\n","                       position = { line = 0\n","                                    column = 2 } })"]}],"source":["///- --test\n","\n","let input = fromStr \"Hello\"\n","let parser = satisfy (fun c -> c = 'H') \"H\"\n","let parser2 = satisfy (fun c -> c = 'e') \"e\"\n","let parser3 = sequence [ parser; parser2 ]\n","runOnInput parser3 input |> _assertEqual (\n","    Success (\n","        [ 'H'; 'e' ],\n","        {\n","            lines = [| \"Hello\" |]\n","            position = { line = 0; column = 2 }\n","        }\n","    )\n",")"]},{"cell_type":"code","execution_count":52,"metadata":{"duration_ms":15,"language":"fsharp"},"outputs":[],"source":["let rec parseZeroOrMore parser input =\n","    match runOnInput parser input with\n","    | Failure (_, _, _) ->\n","        [], input\n","    | Success (firstValue, inputAfterFirstParse) ->\n","        let subsequentValues, remainingInput = parseZeroOrMore parser inputAfterFirstParse\n","        firstValue :: subsequentValues, remainingInput"]},{"cell_type":"code","execution_count":53,"metadata":{"duration_ms":14,"language":"fsharp"},"outputs":[],"source":["let inline many parser =\n","    {\n","        label = $\"many {getLabel parser}\"\n","        parseFn = fun input -> Success (parseZeroOrMore parser input)\n","    }"]},{"cell_type":"code","execution_count":54,"metadata":{"duration_ms":27,"language":"fsharp"},"outputs":[{"name":"stdout","output_type":"stream","text":["Success ([], { lines = [|\"hello\"|]\n","               position = { line = 0\n","                            column = 0 } })"]}],"source":["///- --test\n","\n","let input = fromStr \"hello\"\n","let parser = satisfy (fun c -> c = 'H') \"H\"\n","let parser2 = many parser\n","runOnInput parser2 input |> _assertEqual (\n","    Success (\n","        [],\n","        {\n","            lines = [| \"hello\" |]\n","            position = { line = 0; column = 0 }\n","        }\n","    )\n",")"]},{"cell_type":"code","execution_count":55,"metadata":{"duration_ms":21,"language":"fsharp"},"outputs":[],"source":["let inline many1 p =\n","    p >>=\n","        fun head ->\n","            many p >>=\n","                fun tail ->\n","                    returnP (head :: tail)\n","    <?> $\"many1 {getLabel p}\""]},{"cell_type":"code","execution_count":56,"metadata":{"duration_ms":51,"language":"fsharp"},"outputs":[{"name":"stdout","output_type":"stream","text":["Failure (\"many1 H\", \"Unexpected 'h'\", { currentLine = \"hello\"\n","                                        line = 0\n","                                        column = 0 })"]}],"source":["///- --test\n","\n","let input = fromStr \"hello\"\n","let parser = satisfy (fun c -> c = 'H') \"H\"\n","let parser2 = many1 parser\n","runOnInput parser2 input |> _assertEqual (\n","    Failure (\n","        \"many1 H\",\n","        \"Unexpected 'h'\",\n","        {\n","            currentLine = \"hello\"\n","            line = 0\n","            column = 0\n","        }\n","    )\n",")"]},{"cell_type":"code","execution_count":57,"metadata":{"duration_ms":19,"language":"fsharp"},"outputs":[],"source":["let inline opt p =\n","    let some = p |>> Some\n","    let none = returnP None\n","    (some <|> none)\n","    <?> $\"opt {getLabel p}\""]},{"cell_type":"code","execution_count":58,"metadata":{"duration_ms":31,"language":"fsharp"},"outputs":[{"name":"stdout","output_type":"stream","text":["Success (None, { lines = [|\"hello\"|]\n","                 position = { line = 0\n","                              column = 0 } })"]}],"source":["///- --test\n","\n","let input = fromStr \"hello\"\n","let parser = satisfy (fun c -> c = 'H') \"H\"\n","let parser2 = opt parser\n","runOnInput parser2 input |> _assertEqual (\n","    Success (\n","        None,\n","        {\n","            lines = [| \"hello\" |]\n","            position = { line = 0; column = 0 }\n","        }\n","    )\n",")"]},{"cell_type":"code","execution_count":59,"metadata":{"duration_ms":19,"language":"fsharp"},"outputs":[],"source":["let inline (.>>) p1 p2 =\n","    p1 .>>. p2\n","    |> mapP fst"]},{"cell_type":"code","execution_count":60,"metadata":{"duration_ms":19,"language":"fsharp"},"outputs":[],"source":["let inline (>>.) p1 p2 =\n","    p1 .>>. p2\n","    |> mapP snd"]},{"cell_type":"code","execution_count":61,"metadata":{"duration_ms":22,"language":"fsharp"},"outputs":[],"source":["let inline between p1 p2 p3 =\n","    p1 >>. p2 .>> p3"]},{"cell_type":"code","execution_count":62,"metadata":{"duration_ms":65,"language":"fsharp"},"outputs":[{"name":"stdout","output_type":"stream","text":["Success (['H'; 'e'; 'l'; 'l'; 'o'], { lines = [|\"[Hello]\"|]\n","                                      position = { line = 0\n","                                                   column = 7 } })"]}],"source":["///- --test\n","\n","let input = fromStr \"[Hello]\"\n","let parser =\n","    between\n","        (satisfy (fun c -> c = '[') \"[\")\n","        (many (satisfy (fun c -> [ 'a' .. 'z' ] @ [ 'A' .. 'Z' ] |> List.contains c) \"letter\"))\n","        (satisfy (fun c -> c = ']') \"]\")\n","runOnInput parser input |> _assertEqual (\n","    Success (\n","        [ 'H'; 'e'; 'l'; 'l'; 'o' ],\n","        {\n","            lines = [| \"[Hello]\" |]\n","            position = { line = 0; column = 7 }\n","        }\n","    )\n",")"]},{"cell_type":"code","execution_count":63,"metadata":{"duration_ms":30,"language":"fsharp"},"outputs":[],"source":["let inline sepBy1 p sep =\n","    let sepThenP = sep >>. p\n","    p .>>. many sepThenP\n","    |>> fun (p, pList) -> p :: pList"]},{"cell_type":"code","execution_count":64,"metadata":{"duration_ms":24,"language":"fsharp"},"outputs":[],"source":["let inline sepBy p sep =\n","    sepBy1 p sep <|> returnP []"]},{"cell_type":"code","execution_count":65,"metadata":{"duration_ms":52,"language":"fsharp"},"outputs":[{"name":"stdout","output_type":"stream","text":["Success ([['H'; 'e'; 'l'; 'l'; 'o']; ['W'; 'o'; 'r'; 'l'; 'd'; '\\010']], { lines = [|\"Hello,World\"|]\n","                                                                           position = { line = 1\n","                                                                                        column = 0 } })"]}],"source":["///- --test\n","\n","let input = fromStr \"Hello,World\"\n","let parser = sepBy (many (satisfy (fun c -> c <> ',') \"not comma\")) (satisfy (fun c -> c = ',') \"comma\")\n","runOnInput parser input |> _assertEqual (\n","    Success (\n","        [ [ 'H'; 'e'; 'l'; 'l'; 'o' ]; [ 'W'; 'o'; 'r'; 'l'; 'd'; '\\n' ] ],\n","        {\n","            lines = [| \"Hello,World\" |]\n","            position = { line = 1; column = 0 }\n","        }\n","    )\n",")"]},{"cell_type":"code","execution_count":66,"metadata":{"duration_ms":20,"language":"fsharp"},"outputs":[],"source":["let inline pchar charToMatch =\n","    satisfy ((=) charToMatch) $\"%c{charToMatch}\""]},{"cell_type":"code","execution_count":67,"metadata":{"duration_ms":22,"language":"fsharp"},"outputs":[],"source":["let inline anyOf listOfChars =\n","    listOfChars\n","    |> List.map pchar\n","    |> choice\n","    <?> $\"anyOf %A{listOfChars}\""]},{"cell_type":"code","execution_count":68,"metadata":{"duration_ms":34,"language":"fsharp"},"outputs":[{"name":"stdout","output_type":"stream","text":["Success (['H'; 'e'; 'l'; 'l'; 'o'], { lines = [|\"Hello\"|]\n","                                      position = { line = 0\n","                                                   column = 5 } })"]}],"source":["///- --test\n","\n","let input = fromStr \"Hello\"\n","let parser = anyOf [ 'H'; 'e'; 'l'; 'o' ] |> many\n","runOnInput parser input |> _assertEqual (\n","    Success (\n","        [ 'H'; 'e'; 'l'; 'l'; 'o' ],\n","        {\n","            lines = [| \"Hello\" |]\n","            position = { line = 0; column = 5 }\n","        }\n","    )\n",")"]},{"cell_type":"code","execution_count":69,"metadata":{"duration_ms":19,"language":"fsharp"},"outputs":[],"source":["let inline charListToStr charList =\n","    charList |> List.toArray |> String"]},{"cell_type":"code","execution_count":70,"metadata":{"duration_ms":17,"language":"fsharp"},"outputs":[],"source":["let inline manyChars cp =\n","    many cp\n","    |>> charListToStr"]},{"cell_type":"code","execution_count":71,"metadata":{"duration_ms":25,"language":"fsharp"},"outputs":[],"source":["let inline manyChars1 cp =\n","    many1 cp\n","    |>> charListToStr"]},{"cell_type":"code","execution_count":72,"metadata":{"duration_ms":39,"language":"fsharp"},"outputs":[{"name":"stdout","output_type":"stream","text":["Success (\"Hello\", { lines = [|\"Hello\"|]\n","                    position = { line = 0\n","                                 column = 5 } })"]}],"source":["///- --test\n","\n","let input = fromStr \"Hello\"\n","let parser = manyChars1 (anyOf [ 'H'; 'e'; 'l'; 'o' ])\n","runOnInput parser input |> _assertEqual (\n","    Success (\n","        \"Hello\",\n","        {\n","            lines = [| \"Hello\" |]\n","            position = { line = 0; column = 5 }\n","        }\n","    )\n",")"]},{"cell_type":"code","execution_count":73,"metadata":{"duration_ms":28,"language":"fsharp"},"outputs":[],"source":["let inline pstring str =\n","    str\n","    |> List.ofSeq\n","    |> List.map pchar\n","    |> sequence\n","    |> mapP charListToStr\n","    <?> str"]},{"cell_type":"code","execution_count":74,"metadata":{"duration_ms":38,"language":"fsharp"},"outputs":[{"name":"stdout","output_type":"stream","text":["Success (\"Hello\", { lines = [|\"Hello\"|]\n","                    position = { line = 0\n","                                 column = 5 } })"]}],"source":["///- --test\n","\n","let input = fromStr \"Hello\"\n","let parser = pstring \"Hello\"\n","runOnInput parser input |> _assertEqual (\n","    Success (\n","        \"Hello\",\n","        {\n","            lines = [| \"Hello\" |]\n","            position = { line = 0; column = 5 }\n","        }\n","    )\n",")"]},{"cell_type":"code","execution_count":75,"metadata":{"duration_ms":41,"language":"fsharp"},"outputs":[],"source":["let whitespaceChar =\n","    satisfy Char.IsWhiteSpace \"whitespace\""]},{"cell_type":"code","execution_count":76,"metadata":{"duration_ms":14,"language":"fsharp"},"outputs":[],"source":["let spaces = many whitespaceChar"]},{"cell_type":"code","execution_count":77,"metadata":{"duration_ms":20,"language":"fsharp"},"outputs":[],"source":["let spaces1 = many1 whitespaceChar"]},{"cell_type":"code","execution_count":78,"metadata":{"duration_ms":40,"language":"fsharp"},"outputs":[{"name":"stdout","output_type":"stream","text":["Success (([' '; ' '], \"Hello\"), { lines = [|\"  Hello\"|]\n","                                  position = { line = 0\n","                                               column = 7 } })"]}],"source":["///- --test\n","\n","let input = fromStr \"  Hello\"\n","let parser = spaces1 .>>. pstring \"Hello\"\n","runOnInput parser input |> _assertEqual (\n","    Success (\n","        ([ ' '; ' ' ], \"Hello\"),\n","        {\n","            lines = [| \"  Hello\" |]\n","            position = { line = 0; column = 7 }\n","        }\n","    )\n",")"]},{"cell_type":"code","execution_count":79,"metadata":{"duration_ms":21,"language":"fsharp"},"outputs":[],"source":["let digitChar =\n","    satisfy Char.IsDigit \"digit\""]},{"cell_type":"code","execution_count":80,"metadata":{"duration_ms":27,"language":"fsharp"},"outputs":[{"name":"stdout","output_type":"stream","text":["Failure (\"digit\", \"Unexpected 'H'\", { currentLine = \"Hello\"\n","                                      line = 0\n","                                      column = 0 })"]}],"source":["///- --test\n","\n","let input = fromStr \"Hello\"\n","let parser = digitChar\n","runOnInput parser input |> _assertEqual (\n","    Failure (\n","        \"digit\",\n","        \"Unexpected 'H'\",\n","        {\n","            currentLine = \"Hello\"\n","            line = 0\n","            column = 0\n","        }\n","    )\n",")"]},{"cell_type":"code","execution_count":81,"metadata":{"duration_ms":47,"language":"fsharp"},"outputs":[],"source":["let pint =\n","    let inline resultToInt (sign, digits) =\n","        let i = int digits\n","        match sign with\n","        | Some ch -> -i\n","        | None -> i\n","\n","    let digits = manyChars1 digitChar\n","\n","    opt (pchar '-') .>>. digits\n","    |> mapP resultToInt\n","    <?> \"integer\""]},{"cell_type":"code","execution_count":82,"metadata":{"duration_ms":37,"language":"fsharp"},"outputs":[{"name":"stdout","output_type":"stream","text":["Success (-123, { lines = [|\"-123\"|]\n","                 position = { line = 0\n","                              column = 4 } })"]}],"source":["///- --test\n","\n","run pint \"-123\"\n","|> _assertEqual (\n","    Success (\n","        -123,\n","        {\n","            lines = [| \"-123\" |]\n","            position = { line = 0; column = 4 }\n","        }\n","    )\n",")"]},{"cell_type":"code","execution_count":83,"metadata":{"duration_ms":50,"language":"fsharp"},"outputs":[],"source":["let pfloat =\n","    let inline resultToFloat (((sign, digits1), point), digits2) =\n","        let fl = float $\"{digits1}.{digits2}\"\n","        match sign with\n","        | Some ch -> -fl\n","        | None -> fl\n","\n","    let digits = manyChars1 digitChar\n","\n","    opt (pchar '-') .>>. digits .>>. pchar '.' .>>. digits\n","    |> mapP resultToFloat\n","    <?> \"float\""]},{"cell_type":"code","execution_count":84,"metadata":{"duration_ms":33,"language":"fsharp"},"outputs":[{"name":"stdout","output_type":"stream","text":["Success (-123.45, { lines = [|\"-123.45\"|]\n","                    position = { line = 0\n","                                 column = 7 } })"]}],"source":["///- --test\n","\n","run pfloat \"-123.45\"\n","|> _assertEqual (\n","    Success (\n","        -123.45,\n","        {\n","            lines = [| \"-123.45\" |]\n","            position = { line = 0; column = 7 }\n","        }\n","    )\n",")"]},{"cell_type":"code","execution_count":85,"metadata":{"duration_ms":21,"language":"fsharp"},"outputs":[],"source":["let inline createParserForwardedToRef<'a> () =\n","    let mutable parserRef : Parser<'a> =\n","        {\n","            label = \"unknown\"\n","            parseFn = fun _ -> failwith \"unfixed forwarded parser\"\n","        }\n","\n","    let wrapperParser =\n","        { parserRef with\n","            parseFn = fun input -> runOnInput parserRef input\n","        }\n","\n","    wrapperParser, (fun v -> parserRef <- v)"]},{"cell_type":"code","execution_count":86,"metadata":{"duration_ms":18,"language":"fsharp"},"outputs":[],"source":["let inline (>>%) p x =\n","    p\n","    |>> fun _ -> x"]}],"metadata":{"kernelspec":{"display_name":"Spiral","language":"spiral","name":"spiral"}},"nbformat":4,"nbformat_minor":5}