on master help sign in
add parser library documentation
commitbdb7405130f4796db5e55bd0e3d67086ef5689a7 authormartinbaker <git29f185@martinb.com> dateSat 28 Dec 2019, 18:31 (7 years ago) parentd86f82e
3 files changed+443−404
docs/parserLibrary/introduction.rst+17−190 docs/parserLibrary/lexer.rst+166−38 docs/parserLibrary/parser.rst+260−176
docs/parserLibrary/introduction.rst+17−190
@@ -1,209 +1,36 @@
1−.. _parserLibrary:
1+.. _parserLibraryIntro:
22
33***************************
44Parser Library Introduction
55***************************
66
7−The Idris elaborator is responsible for converting high-level Idris code into the core language.
8−It is implemented as a kind of embedded tactic language in Haskell, where tactic scripts are written in an *elaboration monad* that provides error handling and a proof state.
9−For details, see `Edwin Brady's 2013 paper in the Journal of Functional
10−Programming <https://eb.host.cs.st-andrews.ac.uk/drafts/impldtp.pdf>`_.
7+The parser library is used in a similar way to Parsec which is a library for writing parsers in Haskell. It is based on higher-order parser combinators, so a complicated parser can be made out of many smaller ones.
118
12−Elaborator reflection makes the elaboration type as well as a selection of its tactics available to Idris code.
13−This means that metaprograms written in Idris can have complete control over the elaboration process, generating arbitrary code, and they have access to all of the facilities available in the elaborator, such as higher-order unification, type checking, and emitting auxiliary definitions.
9+.. list-table::
1410
15−The Elaborator State
16−====================
11+ * - This is a two stage process:
12+ - Lexer - This takes the input string and turns it into a list of Tokens.
13+ - Parser - This takes the list of tokens and outputs the code.
1714
18−The elaborator state contains information about the ongoing elaboration process.
19−In particular, it contains a *goal type*, which is to be filled by an under-construction *proof term*.
20−The proof term can contain *holes*, each of which has a scope in which it is valid and a type.
21−Some holes may additionally contain *guesses*, which can be substituted in the scope of the hole.
22−The holes are tracked in a *hole queue*, and one of them is *focused*.
23−In addition to the goal type, proof term, and holes, the elaborator state contains a collection of unsolved unification problems that can affect elaboration.
15+.. image:: ../image/parserTopLevel.png
16+ :width: 484px
17+ :height: 147px
18+ :alt: diagram illustrating these stages of lexer and parser
2419
25−The elaborator state is not directly available to Idris programs.
26−Instead, it is modified through the use of *tactics*, which are operations that affect the elaborator state.
27−A tactic that returns a value of type ``a``, potentially modifying the elaborator state, has type ``Elab a``.
28−The default tactics are all in the namespace ``Language.Reflection.Elab.Tactics``.
20+The advantage of using two stages, like this, is that things like whitespace and comments don't need to be considered in every parser rule.
2921
22+The Idris parser library differs from Parsec in that you need to say in the Recogniser whether a rule is guaranteed to consume input. This means that the Idris type system prevents the construction of recognisers/rules that can't consume the input.
3023
31−Running Elaborator Scripts
32−==========================
24+The Lexer is similar but works at the level of characters rather than tokens, and you need to provide a TokenMap which says how to build a token representation from a string when a rule is matched.
3325
34−On their own, tactics have no effect.
35−The meta-operation ``%runElab script`` runs ``script`` in the current elaboration context.
36−Before you can use ``%runElab``, you will have to enable the language extension by adding
37−``%language ElabReflection`` in your file (or by passing ``-X ElabReflection`` to the
38−``idris`` executable from your command line).
39−For example, the following script constructs the identity function at type ``Nat``:
4026
41−.. code-block:: idris
4227
43− idNat : Nat -> Nat
44− idNat = %runElab (do intro `{{x}}
45− fill (Var `{{x}})
46− solve)
4728
29+.. image:: ../image/parserModules.png
30+ :width: 484px
31+ :height: 147px
32+ :alt: diagram illustrating these stages of lexer and parser
4833
49−On the right-hand side, the Idris elaborator has the goal ``Nat -> Nat``.
50−When it encounters the ``%runElab`` directive, it fulfills this goal by running the provided script.
51−The first tactic, ``intro``, constructs a lambda that binds the name ``x``.
52−The name argument is optional because a default name can be taken from the function type.
53−Now, the proof term is of the form ``\x : Nat => {hole}``.
54−The second tactic, ``fill``, fills this hole with a guess, giving the term ``\x : Nat => {hole≈x}``.
55−Finally, the ``solve`` tactic instantiates the guess, giving the result ``\x : Nat => x``.
5634
57−Because elaborator scripts are ordinary Idris expressions, it is also possible to use them in multiple contexts.
58−Note that there is nothing ``Nat``-specific about the above script.
59−We can generate identity functions at any concrete type using the same script:
6035
61−.. code-block:: idris
6236
63− mkId : Elab ()
64− mkId = do intro `{{x}}
65− fill (Var `{{x}})
66− solve
67−
68− idNat : Nat -> Nat
69− idNat = %runElab mkId
70−
71− idUnit : () -> ()
72− idUnit = %runElab mkId
73−
74− idString : String -> String
75− idString = %runElab mkId
76−
77−
78−Interactively Building Elab Scripts
79−===================================
80−
81−You can build an ``Elab`` script interactively at the REPL.
82−Use the command ``:metavars``, or ``:m`` for short, to list the available holes.
83−Then, issue the ``:elab <hole>`` command at the REPL
84−to enter the elaboration shell.
85−
86−At the shell, you can enter proof tactics to alter the proof state.
87−You can view the system-provided tactics prior to entering the shell
88−by issuing the REPL command ``:browse Language.Reflection.Elab.Tactics``.
89−When you have discharged all goals, you can complete the proof
90−using the ``:qed`` command and receive in return an elaboration script
91−that fills the hole.
92−
93−The interactive elaboration shell accepts a limited number of commands,
94−including a subset of the commands understood by the normal Idris REPL
95−as well as some elaboration-specific commands. It also supports the
96−``do``-syntax, meaning you can write ``res <- command`` to bind the result of
97−``command`` to variable ``res``.
98−
99−General-purpose commands:
100−
101−- ``:eval <EXPR>``, or ``:e <EXPR>`` for short, evaluates the provided expression
102− and prints the result.
103−
104−- ``:type <EXPR>``, or ``:t <EXPR>`` for short, prints the provided expression
105− together with its type.
106−
107−- ``:search <TYPE>`` searches for definitions having the provided type.
108−
109−- ``:doc <NAME>`` searches for definitions with the provided name and prints their
110− documentation.
111−
112−
113−Commands for viewing the proof state:
114−
115−- ``:state`` displays the current state of the term being constructed. It lists both
116− other goals and the current goal.
117−
118−- ``:term`` displays the current proof term as well as its yet-to-be-filled holes.
119−
120−
121−Commands for manipulating the proof state:
122−
123−- ``:undo`` undoes the effects of the last tactic.
124−
125−- ``:abandon`` gives up on proving the current lemma and quits the elaboration shell.
126−
127−- ``:qed`` finishes the script and exits the elaboration shell. The shell will only accept
128− this command once it reports, "No more goals." On exit, it will print out the finished
129− elaboration script for you to copy into your program.
130−
131−
132−Failure
133−=======
134−
135−Some tactics may *fail*.
136−For example, ``intro`` will fail if the focused hole does not have a function type, ``solve`` will fail if the current hole does not contain a guess, and ``fill`` will fail if the term to be filled in has the wrong type.
137−Scripts can also fail explicitly using the ``fail`` tactic.
138−
139−To account for failure, there is an ``Alternative`` implementation for ``Elab``.
140−The ``<|>`` operator first tries the script to its left.
141−If that script fails, any changes that it made to the state are undone and the right argument is executed.
142−If the first argument succeeds, then the second argument is not executed.
143−
144−Querying the Elaboration State
145−==============================
146−
147−``Elab`` includes operations to query the elaboration state, allowing scripts to use information about their environment to steer the elaboration process.
148−The ordinary Idris bind syntax can be used to propagate this information.
149−For example, a tactic that solves the current goal when it is the unit type might look like this:
150−
151−.. code-block:: idris
152−
153− triv : Elab ()
154− triv = do compute
155− g <- getGoal
156− case (snd g) of
157− `(() : Type) => do fill `(() : ())
158− solve
159− otherGoal => fail [ TermPart otherGoal
160− , TextPart "is not trivial"
161− ]
162−
163−
164−The tactic ``compute`` normalises the type of its goal with respect to the current context.
165−While not strictly necessary, this allows ``triv`` to be used in contexts where the triviality of the goal is not immediately apparent.
166−Then, ``getGoal`` is used, and its result is bound to ``g``.
167−Because it returns a pair consisting of the current goal's name and type, we case-split on its second projection.
168−If the goal type turns out to have been the unit type, we fill using the unit constructor and solve the goal.
169−Otherwise, we fail with an error message informing the user that the current goal is not trivial.
170−
171−Additionally, the elaboration state can be dumped into an error message with the ``debug`` tactic.
172−A variant, ``debugMessage``, allows arbitrary messages to be included with the state, allowing for a kind of "``printf`` debugging" of elaboration scripts.
173−The message format used by ``debugMessage`` is the same for errors produced by the error reflection mechanism, allowing the re-use of the Idris pretty-printer when rendering messages.
174−
175−Changing the Global Context
176−===========================
177−
178−``Elab`` scripts can modify the global context during execution.
179−Just as the Idris elaborator produces auxiliary definitions to implement features such as ``where``-blocks and ``case`` expressions, user elaboration scripts may need to define functions.
180−Furthermore, this allows ``Elab`` reflection to be used to implement features such as interface deriving.
181−The operations ``declareType``, ``defineFunction``, and ``addImplementation`` allow ``Elab`` scripts to modify the global context.
182−
183−Using Idris's Features
184−======================
185−
186−The Idris compiler has a number of ways to automate the construction of terms.
187−On its own, the ``Elab`` state and its interactions with the unifier allow implicits to be solved using unification.
188−Additional operations use further features of Idris.
189−In particular, ``resolveTC`` solves the current goal using interface resolution, ``search`` invokes the proof search mechanism, and ``sourceLocation`` finds the context in the original file at which the elaboration script is invoked.
190−
191−
192−Recursive Elaboration
193−=====================
194−
195−The elaboration mechanism can be invoked recursively using the ``runElab`` tactic.
196−This tactic takes a goal type and an elaboration script as arguments and runs the script in a fresh lexical environment to create an inhabitant of the provided goal type.
197−This is primarily useful for code generation, particularly for generating pattern-matching clauses, where variable scope needs to be one that isn't the present local context.
198−
199−Learn More
200−==========
201−
202−Some tactics are introduced in the :ref:`proofs-index` section with further details, of those most relevant to elaborator reflection, on the following pages.
203−
204−The list of built-in tactics can be obtained using the ``:browse`` command in an Idris REPL or the corresponding feature in one of the graphical IDE clients to explore the ``Language.Reflection.Elab.Tactics`` namespace.
205−All of the built-in tactics contain documentation strings.
206−
207−For alternative ways to extend the Idris language see the :ref:`reference-index` section.
208−
209−The following pages explain more about the theory and practice of elaborator reflection.
docs/parserLibrary/lexer.rst+166−38
@@ -1,60 +1,188 @@
11Lexer
22=====
33
4−Idris provides a mechanism to modify the language without having to recompile Idris itself. We can think of this in terms of metaprogramming or domain specific languages or just building in new capabilities.
4+On this page we will implement a lexer to lex a very simple expression, on the
5+next page we will go on to implement a parser for it.
6+
7+.. code-block:: idris
8+
9+ module ParserExample
10+
11+ import Text.Lexer
12+ import public Text.Parser.Core
13+ import public Text.Parser
14+
15+
16+to run:
17+
18+.. code-block:: idris
19+
20+ cd Idris-dev/libs/contrib
21+ idris -p contrib parserEx.idr
22+ ____ __ _
23+ / _/___/ /____(_)____
24+ / // __ / ___/ / ___/ Version 1.3.2
25+ _/ // /_/ / / / (__ ) http://www.idris-lang.org/
26+ /___/\__,_/_/ /_/____/ Type :? for help
27+
28+ Idris is free software with ABSOLUTELY NO WARRANTY.
29+ For details type :warranty.
30+ Type checking ./Text/Token.idr
31+ Type checking ./Text/Quantity.idr
32+ Type checking ./Control/Delayed.idr
33+ Type checking ./Data/Bool/Extra.idr
34+ Type checking ./Text/Lexer/Core.idr
35+ Type checking ./Text/Lexer.idr
36+ Type checking ./parserEx.idr
37+
38+.. code-block:: idris
39+
40+ *parserEx> lex expressionTokens "1+2"
41+ ([MkToken 0 0 (Number 1),
42+ MkToken 0
43+ (case fspan (\ARG => not (intToBool (prim__eqChar ARG '\n'))) "1" of
44+ (incol, "") => c + cast (length incol)
45+ (incol, b) => cast (length incol))
46+ (Operator "+"),
47+ MkToken 0
48+ (case fspan (\ARG => not (intToBool (prim__eqChar ARG '\n'))) "+" of
49+ (incol, "") => c + cast (length incol)
50+ (incol, b) => cast (length incol))
51+ (Number 2)],
52+ 0,
53+ case fspan (\ARG => not (intToBool (prim__eqChar ARG '\n'))) "2" of
54+ (incol, "") => c + cast (length incol)
55+ (incol, b) => cast (length incol),
56+ getString (MkStrLen "" 0)) : (List (TokenData ExpressionToken),
57+ Int,
58+ Int,
59+ String)
60+ *parserEx>
61+
62+The lexer uses potentially infinite data structures. It has recursive arguments (codata type) so code is lazy.
63+
64+.. code-block:: idris
65+
66+ %default total
67+
68+ public export
69+ data ExpressionToken = Number Integer
70+ | Operator String
71+ | OParen
72+ | CParen
73+ | EndInput
74+
75+.. code-block:: idris
76+
77+ export
78+ Show ExpressionToken where
79+ show (Number x) = "number " ++ show x
80+ show (Operator x) = "operator " ++ x
81+ show OParen = "("
82+ show CParen = ")"
83+ show EndInput = "end of input"
84+
85+.. code-block:: idris
86+
87+ export
88+ Show (TokenData ExpressionToken) where
89+ show (MkToken l c t) = "line=" ++ show l ++ " col=" ++ show c ++ "tok=" ++ show t
90+
91+.. code-block:: idris
92+
93+ -- integer arithmetic operators plus, minus and multiply.
94+ export
95+ opChars : String
96+ opChars = "+-*"
97+
98+ operator : Lexer
99+ operator = some (oneOf opChars)
100+
101+.. code-block:: idris
102+
103+ toInt' : String -> Integer
104+ toInt' = cast
105+
106+ expressionTokens : TokenMap ExpressionToken
107+ expressionTokens =
108+ [(digits, \x => Number (toInt' x)),
109+ (operator, \x => Operator x),
110+ (is '(' ,\x => OParen),
111+ (is ')' ,\x => CParen)]
5112
6−In order to extend the language we need to know something about how Idris is compiled. This page explains only what is needed to customise the elaboration. For more information about the compiler's implementation see `Edwin Brady's 2013 paper`_ and for customising the elaboration process see `Elaborator reflection: extending Idris in Idris`_ and `David Christiansen's PhD thesis`_.
7−
8−Compilation of Idris proceeds through a number of stages.
9−
10−- First, Idris is desugared by inserting placeholders for terms to be guessed by the compiler and replacing certain syntactic forms, such as do-notation, with the functions that implement them.
11−- Then, this desugared Idris is translated into a much simpler core language, called TT. This translation process is called elaboration.
12−- Finally, TT is type checked a second time to rule out errors, and then compiled into the target language.
113+Lexer
114+-----
13115
14−.. image:: ../image/idrisTopLevel.png
116+.. image:: ../image/tokenise.png
15117 :width: 484px
16118 :height: 147px
17− :alt: diagram illustrating these stages of Idris compilation
119+ :alt: diagram illustrating these stages of lexer and parser
120+
121+To lex some string to a list of tokens we define the structures using recognisers:
122+
123+img src="recogniser.png" alt="recognisers" width="487" height="249"
124+
125+There are constructors and combinators to allow the construction of the lexer definition:
126+
127+A simple recogniser is 'Pred' which uses a predicate (Char -> Bool) to test whether to accept the character. It can be constructed using the 'is' function:
128+
129+.. code-block:: idris
130+
131+ Idris> :module Lexer2
132+ *Lexer2> is 'a'
133+ Pred (\ARG =>
134+ intToBool (prim__eqChar ARG 'a'))
135+ : Recognise True
18136
19−TT is a core language which is syntactically very simple. This makes it easy for computers to process but very verbose and hard for humans to read. The Idris elaborator is written in Haskell using an elaboration library that was inspired by the tactics in interactive proof assistants such as Coq.
137+Recognisers can be combined, for example,
20138
21139.. list-table::
22140
23− * - There are some similarities with a proof assistant but in Idris the elaborator is an interpreter of Idris source in the elaboration monad, where each syntactic construct of Idris is interpreted as a sequence of tactics.
24− - .. image:: ../image/compareToProofAssist.png
25− :width: 206px
26− :height: 112px
27− :alt: diagram comparing elaboration with proof assistant
141+ * - <+> means sequence two recognisers. If either consumes a character, the sequence
142+ is guaranteed to consume a character.
28143
29−The primitives in the elaboration library are not just useful for the implementors of Idris itself. They can also be used by authors of extensions to the compiler, using a mechanism known as elaborator reflection.
30−During elaboration TT (Raw) structure contains:
144+ .. code-block:: idris
31145
32−- holes - placeholders for terms that have not yet been filled in.
33−- guesses - similar to let bindings, except with no reduction rule, so that elaboration programs can control the precise shape of terms that are under construction.
146+ *Lexer2> is 'a' <+> is 'b'
147+ SeqEat (Pred (\ARG => intToBool (prim__eqChar ARG 'a')))
148+ (Delay (is 'b')) : Recognise True
149+ *Lexer2>
34150
35−For more information about holes and guesses see `Dependently Typed Functional Programs and their Proofs by McBride 1999`_.
151+ - <|> means if both consume, the combination is guaranteed
152+ to consumer a character:
36153
37−The following diagram is intended to illustrate a high level view of the tactics and how this eventually results in the TT language being generated. It is not necessary to understand the details at this stage. The intention is to help build up some intuition so that, when we get into the details, we can recognise how this fits into the big picture.
154+ .. code-block:: idris
38155
39−.. image:: ../image/elabOverview.png
40− :width: 410px
41− :height: 282px
42− :alt: diagram illustrating overview of TT language being generated from tactics.
156+ *Lexer2> is 'a' <|> is 'b'
157+ Alt (Pred (\ARG => intToBool (prim__eqChar ARG 'a')))
158+ (Pred (\ARG => intToBool (prim__eqChar ARG 'b'))) : Recognise True
159+ *Lexer2>
43160
44−As already mentioned the TT core language is kept syntactically very simple, for instance, here are the binders in TT with corresponding code and logic type validity rules:
161+So far, this is static code, to define the lexical structure. To lex a given text we need to pass this to the runtime code.
45162
46−.. list-table::
163+However this can only cut up the string into a list of substrings, these must be converted into tokens so we need a way to construct tokens. This will also depend on the lexical structure we require.
164+
165+TokenMap
166+--------
167+
168+We then need to generate a TokenMap. This is a mapping from lexers to the tokens they produce. This is a list of pairs:
169+
170+.. code-block:: idris
171+
172+ (Lexer, String -> tokenType)
173+
174+For each Lexer in the list, if a substring in the input matches, run
175+the associated function to produce a token of type `tokenType`
176+
177+from Core:
178+
179+.. code-block:: idris
47180
48− * - This diagram illustrates the basis of the compilation process in logic (in this case for binders). It is not necessary to be an expert logician to understand elaborator reflection. However, when learning about tactics, they may appear arbitrary without knowing some theory. For more information about this see `Edwin Brady's 2013 paper`_.
49− - .. image:: ../image/binders.png
50− :width: 310px
51− :height: 203px
52− :alt: diagram illustrating basis of code in logic
181+ TokenMap : (tokenType : Type) -> Type
182+ TokenMap tokenType = List (Lexer, String -> tokenType)
53183
54−.. target-notes::
55−.. _`Edwin Brady's 2013 paper`: https://eb.host.cs.st-andrews.ac.uk/drafts/impldtp.pdf
56−.. _`Elaborator reflection: extending Idris in Idris`: https://dl.acm.org/citation.cfm?doid=2951913.2951932
57−.. _`David Christiansen's PhD thesis`: https://davidchristiansen.dk/david-christiansen-phd.pdf
58−.. _`Dependently Typed Functional Programs and their Proofs by McBride 1999`: https://www.era.lib.ed.ac.uk/handle/1842/374
184+Here is the code that generates the TokenMap for Idris2 lexer from
59185
186+_`TokenMap for Idris2 lexer`: https://github.com/edwinb/Idris2/blob/master/src/Parser/Lexer.idr">src/Parser/Lexer.idr
60187
188+So, for our code, we can then turn this into a tokenMap by using a function like this:.
docs/parserLibrary/parser.rst+260−176
@@ -1,192 +1,276 @@
1−Elaborator Reflection - Identity Example
2−========================================
1+Parser
2+======
33
4−.. list-table::
4+On the previous page we implemented a lexer to lex a very simple expression, on this
5+page we will go on to implement a parser for it.
56
6− * - This example of elaborator reflection steps through this metaprogram that generates the identity function:
7− - .. code-block:: idris
7+To run our parser we call 'doParse'. This requires the output from the lexer (a list of tokens) and a Grammar.
88
9− %language ElabReflection
9+.. code-block:: idris
1010
11− idNat : Nat -> Nat
12− idNat = %runElab (do intro `{{x}}
13− fill (Var `{{x}})
14− solve)
11+ doParse : {c : Bool} ->
12+ (commit : Bool) -> (xs : List tok) -> (act : Grammar tok c ty) ->
13+ ParseResult xs c ty
1514
16−.. list-table::
17− :widths: 200 100
15+So we need to define the Grammar for our parser
1816
19− * - At the beginning of executing the elaboration script, the initial state consists of a single hole of type Nat -> Nat.
17+.. code-block:: idris
2018
21− As a first approximation, the state consists of a term with holes in it, an indicator of which hole is focused, a queue of the next holes to focus on, and miscellaneous information like a source of fresh names. The intro tactic modifies this state, replacing the focused hole with a lambda and focusing on the lambda's body.
22− - .. image:: ../image/tree.png
23− :width: 119px
24− :height: 109px
19+ ||| Description of a language's grammar. The `tok` parameter is the type
20+ ||| of tokens, and the `consumes` flag is True if the language is guaranteed
21+ ||| to be non-empty - that is, successfully parsing the language is guaranteed
22+ ||| to consume some input.
23+ public export
24+ data Grammar : (tok : Type) -> (consumes : Bool) -> Type -> Type where
25+ Empty : (val : ty) -> Grammar tok False ty
26+ Terminal : String -> (tok -> Maybe a) -> Grammar tok True a
27+ NextIs : String -> (tok -> Bool) -> Grammar tok False tok
28+ EOF : Grammar tok False ()
2529
26−The following is a walkthough looking at the state after each tactic:
30+ Fail : Bool -> String -> Grammar tok c ty
31+ Commit : Grammar tok False ()
32+ MustWork : Grammar tok c a -> Grammar tok c a
2733
28−.. list-table::
34+ SeqEat : Grammar tok True a -> Inf (a -> Grammar tok c2 b) ->
35+ Grammar tok True b
36+ SeqEmpty : {c1, c2 : Bool} ->
37+ Grammar tok c1 a -> (a -> Grammar tok c2 b) ->
38+ Grammar tok (c1 || c2) b
39+ Alt : {c1, c2 : Bool} ->
40+ Grammar tok c1 ty -> Grammar tok c2 ty ->
41+ Grammar tok (c1 && c2) ty
2942
30− * - Start with the type signature like this:
31− - .. code-block:: idris
43+.. code-block:: idris
3244
33− %language ElabReflection
45+ -- from Idris2/src/Parser/Support.idr
46+ public export
47+ Rule : Type -> Type
48+ Rule ty = Grammar (TokenData ExpressionToken) True ty
3449
35− idNat : Nat -> Nat
36− idNat = %runElab (do
50+.. code-block:: idris
3751
38− * - In order to investigate how the program works this table shows the proof state at each stage as the tactics are applied. So here is the proof state at the start:
39− - .. image:: ../image/elabProofStateEx1_1.png
40− :width: 310px
41− :height: 115px
52+ export
53+ intLiteral : Rule Integer
54+ intLiteral
55+ = terminal --"Expected integer literal"
56+ (\x => case tok x of
57+ Number i => Just i
58+ _ => Nothing)
59+
60+In order to try this out, here is a temporary function, this calls
61+parse which takes two parameters:
62+* The grammar (in this case intLiteral)
63+* The token list from the lexer.
64+
65+.. code-block:: idris
66+
67+ test1 : String -> Either (ParseError (TokenData ExpressionToken))
68+ (Integer, List (TokenData ExpressionToken))
69+ test1 s = parse intLiteral (fst (lex expressionTokens s))
70+
71+As required, if we pass it a string which is a number literal then it will return the
72+number in the 'Right' option.
73+
74+.. code-block:: idris
75+
76+ *parserEx> test1 "123"
77+ Right (123, []) : Either (ParseError (TokenData ExpressionToken))
78+ (Integer, List (TokenData ExpressionToken))
79+
80+If we pass it a string which is not a number literal then it will return an
81+error message.
82+
83+.. code-block:: idris
84+
85+ *parserEx> test1 "a"
86+ Left (Error "End of input"
87+ []) : Either (ParseError (TokenData ExpressionToken))
88+ (Integer, List (TokenData ExpressionToken))
89+
90+If we pass it a number followed by something else, then it will still be
91+successful, this is because we are not specifically checking for end-of-file.
92+
93+.. code-block:: idris
94+
95+ *parserEx> test1 "123a"
96+ Right (123, []) : Either (ParseError (TokenData ExpressionToken))
97+ (Integer, List (TokenData ExpressionToken))
98+ *parserEx>
99+
100+.. code-block:: idris
101+
102+ ||| Succeeds if running the predicate on the next token returns Just x,
103+ ||| returning x. Otherwise fails.
104+ export
105+ terminal : (tok -> Maybe a) -> Grammar tok True a
106+ terminal = Terminal
107+
108+.. code-block:: idris
109+
110+ openParen : Rule Integer
111+ openParen = terminal (\x => case tok x of
112+ OParen => Just 0
113+ _ => Nothing)
114+
115+.. code-block:: idris
116+
117+ test2 : String -> Either (ParseError (TokenData ExpressionToken))
118+ (Integer, List (TokenData ExpressionToken))
119+ test2 s = parse openParen (fst (lex expressionTokens s))
120+
121+.. code-block:: idris
122+
123+ *parserEx> test2 "("
124+ Right (0, []) : Either (ParseError (TokenData ExpressionToken))
125+ (Integer, List (TokenData ExpressionToken))
126+ *parserEx> test2 "123"
127+ Left (Error "Unrecognised token"
128+ [MkToken 0
129+ 0
130+ (Number 123)]) : Either (ParseError (TokenData ExpressionToken))
131+ (Integer,
132+ List (TokenData ExpressionToken))
133+ *parserEx>
134+
135+.. code-block:: idris
136+
137+ test3 : String -> Either (ParseError (TokenData ExpressionToken))
138+ (Integer, List (TokenData ExpressionToken))
139+ test3 s = parse (map const openParen <*> intLiteral) (fst (lex expressionTokens s))
140+
141+.. code-block:: idris
142+
143+ *parserEx> test3 "(123"
144+ Right (0, []) : Either (ParseError (TokenData ExpressionToken))
145+ (Integer, List (TokenData ExpressionToken))
146+ *parserEx> test3 "(("
147+ Left (Error "Unrecognised token"
148+ [MkToken 0
149+ (case fspan (\ARG => not (intToBool (prim__eqChar ARG '\n')))
150+ "(" of
151+ (incol, "") => c + cast (length incol)
152+ (incol, b) => cast (length incol))
153+ OParen]) : Either (ParseError (TokenData ExpressionToken))
154+ (Integer, List (TokenData ExpressionToken))
155+
156+ *parserEx> test3 "123"
157+ Left (Error "Unrecognised token"
158+ [MkToken 0
159+ 0
160+ (Number 123)]) : Either (ParseError (TokenData ExpressionToken))
161+ (Integer,
162+ List (TokenData ExpressionToken))
163+ *parserEx> test3 "123("
164+ Left (Error "Unrecognised token"
165+ [MkToken 0 0 (Number 123),
166+ MkToken 0
167+ (case fspan (\ARG => not (intToBool (prim__eqChar ARG '\n')))
168+ "321" of
169+ (incol, "") => c + cast (length incol)
170+ (incol, b) => cast (length incol))
171+ OParen]) : Either (ParseError (TokenData ExpressionToken))
172+ (Integer, List (TokenData ExpressionToken))
173+ *parserEx>
174+
175+.. code-block:: idris
176+
177+ closeParen : Rule Integer
178+ closeParen = terminal (\x => case tok x of
179+ CParen => Just 0
180+ _ => Nothing)
181+
182+.. code-block:: idris
183+
184+ ||| Matches if this is an operator token and string matches, that is,
185+ ||| it is the required type of operator.
186+ op : String -> Rule Integer
187+ op s = terminal (\x => case tok x of
188+ (Operator s1) => if s==s1 then Just 0 else Nothing
189+ _ => Nothing)
190+
191+.. code-block:: idris
192+
193+ paren : Rule Integer -> Rule Integer
194+ paren exp = openParen *> exp <* closeParen
195+
196+.. code-block:: idris
197+
198+ addInt : Integer -> Integer -> Integer
199+ addInt a b = a+b
200+
201+ export
202+ add : Grammar tok c1 Integer ->
203+ Grammar tok c2 Integer ->
204+ Grammar tok c3 Integer ->
205+ Grammar tok ((c1 || c2) || c3) Integer
206+ add x y z = map addInt (x *> y) <*> z
207+
208+.. code-block:: idris
209+
210+ subInt : Integer -> Integer -> Integer
211+ subInt a b = a-b
212+
213+ export
214+ sub : Grammar tok c1 Integer ->
215+ Grammar tok c2 Integer ->
216+ Grammar tok c3 Integer ->
217+ Grammar tok ((c1 || c2) || c3) Integer
218+ sub x y z = map subInt (x *> y) <*> z
219+
220+
221+ multInt : Integer -> Integer -> Integer
222+ multInt a b = a*b
223+
224+ export
225+ mult : Grammar tok c1 Integer ->
226+ Grammar tok c2 Integer ->
227+ Grammar tok c3 Integer ->
228+ Grammar tok ((c1 || c2) || c3) Integer
229+ mult x y z = map multInt (x *> y) <*> z
230+
231+.. code-block:: idris
232+
233+ partial
234+ expr : Rule Integer
235+ expr = (add (op "+") expr expr)
236+ <|> (sub (op "-") expr expr)
237+ <|> (mult (op "*") expr expr)
238+ <|> intLiteral <|> (paren expr)
239+
240+.. code-block:: idris
241+
242+ *parserEx> parse expr (fst (lex expressionTokens "(1)"))
243+ Right (1, []) : Either (ParseError (TokenData ExpressionToken))
244+ (Integer, List (TokenData ExpressionToken))
245+ *parserEx>
246+
247+.. code-block:: idris
248+
249+ parse : (act : Grammar tok c ty) -> (xs : List tok)
250+
251+ parse intLiteral (fst (lex expressionTokens "1"))
252+
253+.. code-block:: idris
254+
255+ *parserEx> parse expr (fst (lex expressionTokens "1+2"))
256+ Right (1,
257+ [MkToken 0
258+ (case fspan (\ARG => not (intToBool (prim__eqChar ARG '\n'))) "1" of
259+ (incol, "") => c + cast (length incol)
260+ (incol, b) => cast (length incol))
261+ (Operator "+"),
262+ MkToken 0
263+ (case fspan (\ARG => not (intToBool (prim__eqChar ARG '\n'))) "+" of
264+ (incol, "") => c + cast (length incol)
265+ (incol, b) => cast (length incol))
266+ (Number 2)]) : Either (ParseError (TokenData ExpressionToken))
267+ (Integer, List (TokenData ExpressionToken))
268+ *parserEx>
42269
43− * - This table shows the hole types and what they depend on. The aim is to illustrate the types by analogy with proofs, as a line with the premises above it and the conclusion below it.
44− - .. image:: ../image/elabLogicEx1_1.png
45− :width: 277px
46− :height: 15px
270+.. code-block:: idris
47271
48− * - The term is:
49− - ?{hole_0} ≈ ? {hole_2} . {hole_2} . {hole_0}
272+ partial
273+ test : String -> Either (ParseError (TokenData ExpressionToken))
274+ (Integer, List (TokenData ExpressionToken))
275+ test s = parse expr (fst (lex expressionTokens s))
50276
51− * - It is possible to read the state from the script by calling getEnv, getGoal and getHoles.
52−
53− - The output of these calls contain structures with TT code. To show the results I hacked this: `my code`_. TT code is not really designed to be readable by humans, all the names are fully expanded, everything has a type down to universes (type of types). This is shown here to illustrate the information available.
54−
55− .. code-block:: idris
56−
57− getEnv=[]
58−
59− getGoal=(hole_2, __pi_arg:(Nat.["Nat", "Prelude"]:{
60− type constructor tag=8 number=0}.Type:U=(20:./Prelude/Nat.idr)->.
61− {name ref{type constructor tag=8 number=0}Nat.["Nat","Prelude"]:
62− Type:U=(20:./Prelude/Nat.idr)
63− })
64− })
65−
66− getHoles=[{hole_2},{hole_0}]
67−
68− * - getGuess
69− - error no guess
70−
71− * - Introduce a lambda binding around the current hole and focus on the body.
72− - intro \`{{x}}
73−
74− * - The state now looks like this:
75− - .. image:: ../image/elabProofStateEx1_2.png
76− :width: 312px
77− :height: 84px
78−
79− * - The hole types now looks like this:
80− - .. image:: ../image/elabLogicEx1_2.png
81− :width: 279px
82− :height: 26px
83−
84− * - The term now looks like this:
85− - ?{hole_0} ≈ λ x . ? {hole_2} . {hole_2} . {hole_0}
86−
87− * - Again we can check the state by calling getEnv, getGoal and getHoles: see `my code`_
88−
89− - .. code-block:: idris
90−
91− getEnv=[(x, {λ (Nat.["Nat", "Prelude"]:{
92− type constructor tag=8 number=0}).
93− Type:U=(20:./Prelude/Nat.idr)
94− })]
95−
96− getGoal=(hole_2, {name ref{type constructor tag=8 number=0}
97− Nat.["Nat","Prelude"]:Type:U=(20:./Prelude/Nat.idr)
98− })
99−
100− getHoles=[{hole_2},{hole_0}]
101−
102− * - getGuess
103− - error no guess
104−
105− * - Place a term into a hole, unifying its type
106− - fill (Var \`{{x}})
107−
108− * - The state still looks like this:
109− - .. image:: ../image/elabProofStateEx1_3.png
110− :width: 312px
111− :height: 57px
112−
113− * - The hole types now looks like this:
114− - .. image:: ../image/elabLogicEx1_3.png
115− :width: 290px
116− :height: 26px
117−
118− * - The term now looks like this:
119− - ?{hole_0} ≈ λ x . ?{hole_2} ≈ x . {hole_2} . {hole_0}
120−
121− * - Again we can check the state by calling getEnv, getGoal and getHoles: see `my code`_
122−
123− - .. code-block:: idris
124−
125− getEnv=[(x, {λ (Nat.["Nat", "Prelude"]:
126− {type constructor tag=8 number=0}).
127− Type:U=(20:./Prelude/Nat.idr)
128− })]
129−
130− getGoal=(hole_2, {name ref{type constructor tag=8 number=0}
131− Nat.["Nat","Prelude"]:Type:U=(20:./Prelude/Nat.idr)
132− })
133−
134− getHoles=[{hole_2}, {hole_0}]
135−
136− * - getGuess
137− - .. code-block:: idris
138−
139− {name ref bound x:
140− {name ref{type constructor tag=8 number=0}
141− Nat.["Nat","Prelude"]:Type:U=(20:./Prelude/Nat.idr)
142− }
143− }
144−
145− * - Substitute a guess into a hole.
146− - solve
147−
148− * - The hole types now looks like this:
149− - .. image:: ../image/elabLogicEx1_4.png
150− :width: 131px
151− :height: 14px
152−
153− * - The term now looks like this:
154− - ?{hole_0} ≈ λ x . x . {hole_0}
155−
156− * - getEnv
157−
158− getGoal
159−
160− getHoles
161−
162− - .. code-block:: idris
163−
164− getEnv=[]
165−
166− getGoal=(hole_0, __pi_arg:(Nat.["Nat", "Prelude"]:{
167− type constructor tag=8 number=0}.
168− Type:U=(20:./Prelude/Nat.idr)
169− ->.{name ref
170− {type constructor tag=8 number=0}
171− Nat.["Nat","Prelude"]:Type:U=(20:./Prelude/Nat.idr)
172− })
173− })
174−
175− getHoles=[{hole_0}]
176−
177− * - getGuess
178− - .. code-block:: idris
179−
180− x:({λ (Nat.["Nat", "Prelude"]:{
181− type constructor tag=8 number=0}).
182− Type:U=(20:./Prelude/Nat.idr)
183− }.{
184− name ref bound
185− x:{name ref {type constructor tag=8 number=0}
186− Nat.["Nat","Prelude"]:Type:U=(20:./Prelude/Nat.idr)
187− }
188− })
189− }
190−
191−.. target-notes::
192−.. _`my code`: https://github.com/martinbaker/Idris-dev/blob/uglyTTPrinter/libs/prelude/Language/Reflection/TTPrinter.idr