1
1
package konnik.json
2
2
3
+ /* ----------------------------------------------------------------------------
4
+ *
5
+ * PUBLIC INTERFACE
6
+ *
7
+ * ----------------------------------------------------------------------------
8
+ */
9
+
3
10
/* *
4
11
* Algebraic Data Type representing a parsed JSON-value.
5
12
*/
@@ -27,7 +34,138 @@ fun parseJson(input: String): JsonValue? =
27
34
}
28
35
29
36
30
- /*
37
+ /* ----------------------------------------------------------------------------
38
+ *
39
+ * IMPLEMENTATION
40
+ *
41
+ * ----------------------------------------------------------------------------
42
+ */
43
+
44
+ /* *
45
+ * Data type representing a parser that parses a some input string and produces
46
+ * a value of type A.
47
+ *
48
+ * If the parser succeeds the parsed value is returned together with the remainder of the input
49
+ * that has not been consumed.
50
+ *
51
+ * If the parser fails null is returned.
52
+ *
53
+ */
54
+ private typealias Parser <A > = (String ) -> Pair <A , String >?
55
+
56
+
57
+ /* ----------------------------------------------------------------------------
58
+ *
59
+ * COMBINATORS
60
+ *
61
+ * ----------------------------------------------------------------------------
62
+ */
63
+
64
+ /* *
65
+ * Transforms the value of a successful parse to another value.
66
+ *
67
+ * Fun fact: this means that Parser is a Functor and corresponds to fmap in Haskell.
68
+ */
69
+ private fun <A : Any , B : Any > Parser<A>.map (transform : (A ) -> B ): Parser <B > = { input ->
70
+ this (input)?.let { transform(it.first) to it.second }
71
+ }
72
+
73
+ /* *
74
+ * A parser that always succeeds with the provided value without consuming any input.
75
+ *
76
+ * Fun fact: this corresponds to pure (from type class Applicative) in Haskell.
77
+ */
78
+
79
+ private fun <T : Any > succeed (value : T ): Parser <T > = { input -> value to input }
80
+
81
+ /* *
82
+ * Chain two parsers together where the second parser depends on the produced value
83
+ * if the first.
84
+ *
85
+ * This function is often called flatMap, but I think andThen is a more intuitive name. It's
86
+ * also the name used in the Elm, the best programming language ever created so let's go with that.
87
+ *
88
+ * Fun fact: This is bind / >>= in Haskell and makes our Parser a Monad.
89
+ */
90
+ private fun <A : Any , B : Any > Parser<A>.andThen (aToParserB : (A ) -> Parser <B >): Parser <B > = { input ->
91
+ this (input)?.let { a -> aToParserB(a.first)(a.second) }
92
+ }
93
+
94
+ /* *
95
+ * Chain two parsers together but keep only the value from de second parser.
96
+ *
97
+ * Fun fact: This is Applicative *> (or Monad >>) in Haskell
98
+ */
99
+ private fun <A : Any , B : Any > Parser<A>.keep (parserB : Parser <B >): Parser <B > =
100
+ this .andThen { parserB }
101
+
102
+
103
+ /* *
104
+ * Chain two parsers together but keep only the value from de first parser.
105
+ *
106
+ * Fun fact: This is Applicative <* in Haskell
107
+ */
108
+ private fun <A : Any , B : Any > Parser<A>.skip (parserB : Parser <B >): Parser <A > =
109
+ this .andThen { a -> parserB.map { a } }
110
+
111
+ /* *
112
+ * Combine multiple parsers into a parser that returns the value of the first
113
+ * parser that succeeds.
114
+ */
115
+ private fun <A : Any > oneOf (vararg parsers : Parser <A >): Parser <A > = { input ->
116
+ parsers.firstNotNullOfOrNull { it(input) }
117
+ }
118
+
119
+ /* *
120
+ * Make the construction of a parser lazy. This is sometimes useful for
121
+ * preventing stack overflows when defining parsers recursively.
122
+ */
123
+ private fun <A : Any > lazy (parser : () -> Parser <A >): Parser <A > = { input ->
124
+ parser()(input)
125
+ }
126
+
127
+ /* *
128
+ * Combine to parsers into one that concatenates the string results of the
129
+ * individual parsers into one string.
130
+ */
131
+ private operator fun Parser<String>.plus (parserB : Parser <String >): Parser <String > =
132
+ this .andThen { valueA -> parserB.map { valueB -> valueA + valueB } }
133
+
134
+
135
+ /* ----------------------------------------------------------------------------
136
+ *
137
+ * PRIMITIVE PARSERS
138
+ *
139
+ * ----------------------------------------------------------------------------
140
+ */
141
+
142
+ /* *
143
+ * Exact match of a string literal.
144
+ */
145
+ private fun s (str : String ): Parser <String > = { input ->
146
+ when {
147
+ input.startsWith(str) -> str to input.drop(str.length)
148
+ else -> null
149
+ }
150
+ }
151
+
152
+ /* *
153
+ * Match a character using the provided predicate.
154
+ */
155
+ private fun match (test : (Char ) -> Boolean ): Parser <Char > = { input ->
156
+ input.firstOrNull()?.let {
157
+ when {
158
+ test(it) -> input.first() to input.drop(1 )
159
+ else -> null
160
+ }
161
+ }
162
+ }
163
+
164
+
165
+
166
+ /* ------------------------------------------------------------------------------------------
167
+ * GRAMMAR
168
+ *
31
169
* Implementation of the grammar from https://www.json.org/json-en.html follows.
32
170
*
33
171
* The parsers defined below are given names that follows the grammar but due to Kotlin
@@ -38,6 +176,7 @@ fun parseJson(input: String): JsonValue? =
38
176
*
39
177
* To be able to define some recursive parsers a "lazy" variant of some parsers was needed,
40
178
* in the form of a function that lazily returns the corresponding parser.
179
+ * ------------------------------------------------------------------------------------------
41
180
*/
42
181
43
182
0 commit comments