@@ -9,6 +9,8 @@ knowledge of parsing is required, and we will explain the basic structure of how
9parsing works in a purely functional language such as Hoon before moving on to
9parsing works in a purely functional language such as Hoon before moving on to
10how it is implemented in Hoon.
10how it is implemented in Hoon.
12+**Note:** For JSON printing/parsing and encoding/decoding, see the [JSON Guide](/docs/hoon/guides/json-guide).
12## What is parsing? {#what-is-parsing}
14## What is parsing? {#what-is-parsing}
14A program which takes a raw sequence of characters as an input and produces a data
16A program which takes a raw sequence of characters as an input and produces a data
@@ -42,20 +44,24 @@ the parsing routine on the string `"1"` and looks for an integer, and so it
42returns the integer `1`. However, taking into account what was said above about
44returns the integer `1`. However, taking into account what was said above about
43parsers returning the unparsed portion of the string as well, we should
45parsers returning the unparsed portion of the string as well, we should
44represent this return as a tuple. So we should expect something like this:
46represent this return as a tuple. So we should expect something like this:
46> (parse "1" integer)
49> (parse "1" integer)
48> (parse "123" integer)
51> (parse "123" integer)
51What if we wish to parse the rest of the string? We would need to apply the
55What if we wish to parse the rest of the string? We would need to apply the
52`parse` function again:
56`parse` function again:
54> (parse (parse "123" integer) integer)
59> (parse (parse "123" integer) integer)
56> (parse (parse (parse "123" integer) integer) integer)
61> (parse (parse (parse "123" integer) integer) integer)
59So we see that we can parse strings larger than one character by stringing
65So we see that we can parse strings larger than one character by stringing
60together parsing functions for single characters. Thus in addition to parsing
66together parsing functions for single characters. Thus in addition to parsing
61functions for single input characters, we want _parser combinators_ that
67functions for single input characters, we want _parser combinators_ that
@@ -75,15 +81,14 @@ are quite a few), but ought to be
75sufficient to get started with parsing in Hoon and be equipped to discover the
81sufficient to get started with parsing in Hoon and be equipped to discover the
76remainder yourself.
82remainder yourself.
79## Basic types
84## Basic types
81In this section we discuss the types most commonly used for Hoon parsers. In short:
86In this section we discuss the types most commonly used for Hoon parsers. In short:
83− - A `hair` is the position in the text the parser is at,
88+- A `hair` is the position in the text the parser is at,
84− - A `nail` is parser input,
89+- A `nail` is parser input,
85− - An `edge` is parser output,
90+- An `edge` is parser output,
86− - A `rule` is a parser.
91+- A `rule` is a parser.
@@ -102,6 +107,7 @@ is in case it hits something unexpected during parsing.
103++ nail [p=hair q=tape]
108++ nail [p=hair q=tape]
105We recall from our [discussion above](#what-is-parsing) that parsing functions must keep
111We recall from our [discussion above](#what-is-parsing) that parsing functions must keep
106track of both what has been parsed and what has yet to be parsed. Thus a `nail`
112track of both what has been parsed and what has yet to be parsed. Thus a `nail`
107consists of both a `hair`, giving the line and column up to which the input
113consists of both a `hair`, giving the line and column up to which the input
@@ -122,6 +128,7 @@ what has theoretically already been parsed up to that point.
123++ edge [p=hair q=(unit [p=* q=nail])]
129++ edge [p=hair q=(unit [p=* q=nail])]
125An `edge` is the output of a parser. If parsing succeeded, `p` is the location
132An `edge` is the output of a parser. If parsing succeeded, `p` is the location
126of the original input `tape `up to which the text has been parsed. If parsing
133of the original input `tape `up to which the text has been parsed. If parsing
127failed, `p` will be the first `hair` at which parsing failed.
134failed, `p` will be the first `hair` at which parsing failed.
@@ -136,6 +143,7 @@ of what is to be parsed. If `q` is not null, `p` and `p.q.q` are identical.
137++ rule _|:($:nail $:edge)
144++ rule _|:($:nail $:edge)
139A `rule` is a gate which takes in a `nail` and returns an `edge` - in other
147A `rule` is a gate which takes in a `nail` and returns an `edge` - in other
140words, a parser.
148words, a parser.
@@ -160,6 +168,7 @@ of the input `nail`.
161[p=[p=1 q=2] q=[~ [p='a' q=[p=[p=1 q=2] q="bc"]]]]
169[p=[p=1 q=2] q=[~ [p='a' q=[p=[p=1 q=2] q="bc"]]]]
163We note that `p.edg` is `[p=1 q=2]`, indicating that the next character to be
172We note that `p.edg` is `[p=1 q=2]`, indicating that the next character to be
164parsed is in line 1, column 2. `q.edg` is not null, indicating that parsing
173parsed is in line 1, column 2. `q.edg` is not null, indicating that parsing
165succeeded. `p.q.edg` is `'a'`, which is the result of the parse. `p.q.q.edg` is the same as `p.edg`, which is always the case for
174succeeded. `p.q.edg` is `'a'`, which is the result of the parse. `p.q.q.edg` is the same as `p.edg`, which is always the case for
@@ -167,11 +176,13 @@ succeeded. `p.q.edg` is `'a'`, which is the result of the parse. `p.q.q.edg` is
167`q.q.edg` is `"bc"`, which is the part of the input `tape` that has yet to be parsed.
176`q.q.edg` is `"bc"`, which is the part of the input `tape` that has yet to be parsed.
169Now let's see what happens when parsing fails.
178Now let's see what happens when parsing fails.
171> =edg ((just 'b') [[1 1] "abc"])
181> =edg ((just 'b') [[1 1] "abc"])
173[p=[p=1 q=1] q=~]
183[p=[p=1 q=1] q=~]
175Now we have that `p.edg` is the same as the input `hair`, `[1 1]`, meaning the
186Now we have that `p.edg` is the same as the input `hair`, `[1 1]`, meaning the
176parser has not advanced since parsing failed. `q.edg` is null, indicating that
187parser has not advanced since parsing failed. `q.edg` is null, indicating that
177parsing has failed.
188parsing has failed.
@@ -186,11 +197,13 @@ produces a `rule` that attempts to match that `cord` against the beginning of
186the input.
197the input.
188Let's see what happens when we successfully parse the entire input `tape`.
199Let's see what happens when we successfully parse the entire input `tape`.
190> =edg ((jest 'abc') [[1 1] "abc"])
202> =edg ((jest 'abc') [[1 1] "abc"])
192[p=[p=1 q=4] q=[~ [p='abc' q=[p=[p=1 q=4] q=""]]]]
204[p=[p=1 q=4] q=[~ [p='abc' q=[p=[p=1 q=4] q=""]]]]
194`p.edg` is `[p=1 q=4]`, indicating that the next character to be parsed is at
207`p.edg` is `[p=1 q=4]`, indicating that the next character to be parsed is at
195line 1, column 4. Of course, this does not exist since the input `tape` was only
208line 1, column 4. Of course, this does not exist since the input `tape` was only
1963 characters long, so this actually indicates that the entire `tape` has been
2093 characters long, so this actually indicates that the entire `tape` has been
@@ -199,21 +212,25 @@ successfully parsed (since the `hair` does not advance in the case of failure).
199remains to be parsed.
212remains to be parsed.
201What happens if we only match some of the input `tape`?
214What happens if we only match some of the input `tape`?
203> =edg ((jest 'ab') [[1 1] "abc"])
217> =edg ((jest 'ab') [[1 1] "abc"])
205[p=[p=1 q=3] q=[~ [p='ab' q=[p=[p=1 q=3] q="c"]]]]
219[p=[p=1 q=3] q=[~ [p='ab' q=[p=[p=1 q=3] q="c"]]]]
207Now we have that the result, `p.q.edg`, is `'ab'`, while the remainder `q.q.q.edg`
222Now we have that the result, `p.q.edg`, is `'ab'`, while the remainder `q.q.q.edg`
208is `"c"`. So `+jest` has successfully parsed the first two characters, while the
223is `"c"`. So `+jest` has successfully parsed the first two characters, while the
209last character remains. Furthermore, we still have the information that the
224last character remains. Furthermore, we still have the information that the
210remaining character was in line 1 column 3 from `p.edg` and `p.q.q.edg`.
225remaining character was in line 1 column 3 from `p.edg` and `p.q.q.edg`.
212What happens when `+jest` fails?
227What happens when `+jest` fails?
214> ((jest 'bc') [[1 1] "abc"])
230> ((jest 'bc') [[1 1] "abc"])
215[p=[p=1 q=1] q=~]
231[p=[p=1 q=1] q=~]
217Despite the fact that `'bc'` appears in `"abc"`, because it was not at the
234Despite the fact that `'bc'` appears in `"abc"`, because it was not at the
218beginning the parse failed. We will see in [parser
235beginning the parse failed. We will see in [parser
219combinators](#parser-combinators) how to modify this `rule` so that it
236combinators](#parser-combinators) how to modify this `rule` so that it
@@ -237,6 +254,7 @@ replaces the parsing result with `cus`.
238Here we see that `p.q` of the `edge` returned by the `rule` created with `+cold`
255Here we see that `p.q` of the `edge` returned by the `rule` created with `+cold`
239is `%foo`.
256is `%foo`.
241> ((cold %foo (just 'a')) [[1 1] "abc"])
259> ((cold %foo (just 'a')) [[1 1] "abc"])
242[p=[p=1 q=2] q=[~ u=[p=%foo q=[p=[p=1 q=2] q="bc"]]]]
260[p=[p=1 q=2] q=[~ u=[p=%foo q=[p=[p=1 q=2] q="bc"]]]]
@@ -246,7 +264,6 @@ One common scenario where `+cold` sees play is when writing [command line
246interface (CLI) apps](/docs/hoon/guides/cli-tutorial). We usher the
264interface (CLI) apps](/docs/hoon/guides/cli-tutorial). We usher the
247reader there to find an example where `+cold` is used.
265reader there to find an example where `+cold` is used.
250### `+knee`
267### `+knee`
252Another important function in the parser builder library is `+knee`, used for building
269Another important function in the parser builder library is `+knee`, used for building
@@ -254,7 +271,6 @@ recursive parsers. We delay discussion of `+knee` to the
254[section below](#recursive-parsers) as more context is needed to explain it
271[section below](#recursive-parsers) as more context is needed to explain it
258## Outside callers
274## Outside callers
260Since `hair`s, `nail`s, etc. are only utilized within the context of writing
276Since `hair`s, `nail`s, etc. are only utilized within the context of writing
@@ -295,7 +311,6 @@ For additional information including additional examples see [4g: Parsing (Outsi
295For the remainder of this tutorial we will make use of `+scan` so that we do not
311For the remainder of this tutorial we will make use of `+scan` so that we do not
296need to deal directly with `nail`s except where it is ilustrative to do so.
312need to deal directly with `nail`s except where it is ilustrative to do so.
299### Parsing atoms
314### Parsing atoms
301[Recall](/docs/hoon/hoon-school/lists) that `cord`s are atoms with the aura
316[Recall](/docs/hoon/hoon-school/lists) that `cord`s are atoms with the aura
@@ -312,7 +327,6 @@ The standard library provides a number of gates that take a `rule` and
312produce a new modified `rule` according to some process. We call these _parser modifiers_. These are
327produce a new modified `rule` according to some process. We call these _parser modifiers_. These are
313documented among the [parser builders](/docs/hoon/reference/stdlib/4f).
328documented among the [parser builders](/docs/hoon/reference/stdlib/4f).
316### `+ifix`
330### `+ifix`
318`+ifix` modifies a `rule` so that it matches that `rule` only when it is
332`+ifix` modifies a `rule` so that it matches that `rule` only when it is
@@ -322,11 +336,11 @@ surrounded on both sides by text that matches a pair of `rule`s, which is discar
322> (scan "(42)" (ifix [pal par] (jest '42')))
336> (scan "(42)" (ifix [pal par] (jest '42')))
325`+pal` and `+par` are shorthand for `(just '(')` and `(just ')')`, respectively. All
340`+pal` and `+par` are shorthand for `(just '(')` and `(just ')')`, respectively. All
326ASCII glyphs have counterparts of this sort, documented
341ASCII glyphs have counterparts of this sort, documented
327[here](/docs/hoon/reference/stdlib/4h).
342[here](/docs/hoon/reference/stdlib/4h).
330### `+star` {#star}
344### `+star` {#star}
332`+star` is used to apply a `rule` repeatedly. Recall that `+just` only parses
346`+star` is used to apply a `rule` repeatedly. Recall that `+just` only parses
@@ -343,7 +357,9 @@ We can use `+star` to get the rest of the `tape`:
343> ((star (just 'a')) [[1 1] "aaa"])
357> ((star (just 'a')) [[1 1] "aaa"])
344[p=[p=1 q=4] q=[~ [p=[i='a' t=<|a a|>] q=[p=[p=1 q=4] q=""]]]]
358[p=[p=1 q=4] q=[~ [p=[i='a' t=<|a a|>] q=[p=[p=1 q=4] q=""]]]]
346and we note that the parsing ceases when it fails.
361and we note that the parsing ceases when it fails.
348> ((star (just 'a')) [[1 1] "aaab"])
364> ((star (just 'a')) [[1 1] "aaab"])
349[p=[p=1 q=4] q=[~ [p=[i='a' t=<|a a|>] q=[p=[p=1 q=4] q="b"]]]]
365[p=[p=1 q=4] q=[~ [p=[i='a' t=<|a a|>] q=[p=[p=1 q=4] q="b"]]]]
@@ -358,9 +374,11 @@ combinators](/docs/hoon/reference/stdlib/4e). First we introduce a few
358combinators, then we examine more closely how `;~` is used to chain them together.
374combinators, then we examine more closely how `;~` is used to chain them together.
360The syntax to combine `rule`s is
376The syntax to combine `rule`s is
362;~(combinator rule1 rule2 ... ruleN)
379;~(combinator rule1 rule2 ... ruleN)
364The `rule`s are composed together using the combinator as an
382The `rule`s are composed together using the combinator as an
365intermediate function, which takes product of a `rule` (an `edge`) and a `rule` and turns
383intermediate function, which takes product of a `rule` (an `edge`) and a `rule` and turns
366it into a sample (a `nail`) for the next `rule` to handle. We elaborate on this
384it into a sample (a `nail`) for the next `rule` to handle. We elaborate on this
@@ -409,42 +427,46 @@ take this opportunity to think about it carefully.
409The `rule` created by `;~(combinator [list rule])` may be understood
427The `rule` created by `;~(combinator [list rule])` may be understood
410inductively. To do this, let's consider the base case where our `[list rule]` has only a
428inductively. To do this, let's consider the base case where our `[list rule]` has only a
411single entry.
429single entry.
413> (scan "star" ;~(plug (jest 'star')))
432> (scan "star" ;~(plug (jest 'star')))
416Our output is identical to that given by `scan "star" (jest 'star')`. This is to
436Our output is identical to that given by `scan "star" (jest 'star')`. This is to
417be expected. The combinator `+plug` is specifically used for chaining together
437be expected. The combinator `+plug` is specifically used for chaining together
418`rule`s in the `[list rule]`, but if there is only one `rule`, there is nothing
438`rule`s in the `[list rule]`, but if there is only one `rule`, there is nothing
419to chain. Thus, swapping out `+plug` for another combinator makes no difference here:
439to chain. Thus, swapping out `+plug` for another combinator makes no difference here:
421> (scan "star" ;~(pose (jest 'star')))
442> (scan "star" ;~(pose (jest 'star')))
423> (scan "star" ;~((glue com) (jest 'star')))
444> (scan "star" ;~((glue com) (jest 'star')))
426`;~` and the combinator only begin to play a role once the `[list rule]` has at
448`;~` and the combinator only begin to play a role once the `[list rule]` has at
427least two elements. So let's look at an example done with `+plug`, the simplest
449least two elements. So let's look at an example done with `+plug`, the simplest
428combinator.
450combinator.
430> (scan "star" ;~(plug (jest 'st') (jest 'ar')))
453> (scan "star" ;~(plug (jest 'st') (jest 'ar')))
431['st' 'ar']
454['st' 'ar']
433Our return suggests that we first parsed `"star"` with the `rule` `(jest 'st')` and passed
457Our return suggests that we first parsed `"star"` with the `rule` `(jest 'st')` and passed
434−the resulting `edge` to `(jest 'ar')` - in other words, we called `+plug` on `(jest
458+the resulting `edge` to `(jest 'ar')` - in other words, we called `+plug` on `(jest 'st')` and the `edge` returned once it had been used to parse `"star"`. Thus
435−'st')` and the `edge` returned once it had been used to parse `"star"`. Thus
436`+plug` was the glue that allowed us to join the two `rule`s, and `;~` performed
459`+plug` was the glue that allowed us to join the two `rule`s, and `;~` performed
437the gluing operation. And so, swapping `+plug` for `+pose` results in a crash,
460the gluing operation. And so, swapping `+plug` for `+pose` results in a crash,
438which clues us into the fact that the combinator now has an effect since there
461which clues us into the fact that the combinator now has an effect since there
439is more than one `rule`.
462is more than one `rule`.
441> (scan "star" ;~(pose (jest 'st') (jest 'ar')))
465> (scan "star" ;~(pose (jest 'st') (jest 'ar')))
443syntax error
467syntax error
448## Parsing numbers
470## Parsing numbers
450Functions for parsing numbers are documented in [4j: Parsing (Bases and Base
472Functions for parsing numbers are documented in [4j: Parsing (Bases and Base
@@ -461,15 +483,19 @@ for parsing decimal numbers.
461## Recursive parsers
483## Recursive parsers
463Naively attempting to write a recursive `rule`, i.e. like
485Naively attempting to write a recursive `rule`, i.e. like
465> |-(;~(pose den ;~(pose $ (easy ~))))
488> |-(;~(pose den ;~(pose $ (easy ~))))
467results in an error.
491results in an error.
469-find.,.+6
494-find.,.+6
470-find.,.+6
495-find.,.+6
473Thus some special sauce is required, the `+knee` function.
499Thus some special sauce is required, the `+knee` function.
475`+knee` takes in a noun that is the default value of the parser, typically given
501`+knee` takes in a noun that is the default value of the parser, typically given
@@ -494,7 +520,6 @@ the parser to improve performance. In [parsing arithmetic
494expressions](#parsing-arithmetic-expressions) we will be writing a recursive
520expressions](#parsing-arithmetic-expressions) we will be writing a recursive
495parser making use of `+knee` and `~+` throughout.
521parser making use of `+knee` and `~+` throughout.
498# Parsing arithmetic expressions
523# Parsing arithmetic expressions
500In this section we will be applying what we have learned to write a parser for
525In this section we will be applying what we have learned to write a parser for
@@ -514,6 +539,7 @@ for arithmetic expressions informally.
514First lets look at the code we're going to use, and then dive into explaining
539First lets look at the code we're going to use, and then dive into explaining
515it. If you'd like to follow along, save the following as `expr-parse.hoon` in
540it. If you'd like to follow along, save the following as `expr-parse.hoon` in
516your `gen/` folder.
541your `gen/` folder.
518:: expr-parse: parse arithmetic expressions
544:: expr-parse: parse arithmetic expressions
@@ -544,9 +570,10 @@ your `gen/` folder.
546Informally, the grammar here is:
572Informally, the grammar here is:
547− - A factor is either an integer or an expression surrounded by parentheses.
574+- A factor is either an integer or an expression surrounded by parentheses.
548− - A term is either a factor or a factor times a term.
575+- A term is either a factor or a factor times a term.
549− - An expression is either a term or a term plus an expression.
576+- An expression is either a term or a term plus an expression.
551### Factors, terms, and expressions
578### Factors, terms, and expressions
@@ -568,8 +595,7 @@ for expressions.
568A _factor_ is either a decimal number or an expression surrounded by parentheses. Put
595A _factor_ is either a decimal number or an expression surrounded by parentheses. Put
569into Hoon terms, a decimal number is parsed by the `rule` `+dem` and an
596into Hoon terms, a decimal number is parsed by the `rule` `+dem` and an
570expression is parsed by removing the surrounding parentheses and then passing
597expression is parsed by removing the surrounding parentheses and then passing
571−the result to the expression parser arm `+expr`, given by the `rule` `(ifix [pal
598+the result to the expression parser arm `+expr`, given by the `rule` `(ifix [pal par] expr)`. Since we want to parse our expression with one or the other, we
572−par] expr)`. Since we want to parse our expression with one or the other, we
573chain these two `rule`s together using the monadic applicator rune `;~` along
599chain these two `rule`s together using the monadic applicator rune `;~` along
574with `+pose`, which says to try each rule in succession until one of them works.
600with `+pose`, which says to try each rule in succession until one of them works.
@@ -578,6 +604,7 @@ recursive rule. Thus we need to make use of `+knee`. The first argument for
578`+knee` is `*@ud`, since our final answer should be a `@ud`.
604`+knee` is `*@ud`, since our final answer should be a `@ud`.
580Then follows the definition of the gate utilized by `+knee`:
606Then follows the definition of the gate utilized by `+knee`:
@@ -585,6 +612,7 @@ Then follows the definition of the gate utilized by `+knee`:
585 (ifix [pal par] expr)
612 (ifix [pal par] expr)
588`~+` is used to cache the parser, so that it does not need to be computed over
616`~+` is used to cache the parser, so that it does not need to be computed over
589and over again. Then it follows the `rule` we described above.
617and over again. Then it follows the `rule` we described above.
@@ -615,7 +643,6 @@ accomplish this:
615The rest of the `+expr` arm is structured just like how `+factor` is, and for
643The rest of the `+expr` arm is structured just like how `+factor` is, and for
616the same reasons.
644the same reasons.
619#### Parsing terms
646#### Parsing terms
621A _term_ is either a factor times a term or a factor. This is handled similarly
648A _term_ is either a factor times a term or a factor. This is handled similarly