-
Notifications
You must be signed in to change notification settings - Fork 44
/
ParserBuilder.scala
193 lines (167 loc) · 7.2 KB
/
ParserBuilder.scala
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
/*
* Copyright 2012-2020 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package laika.api.bundle
import laika.ast.{ Block, Span }
import laika.parse.Parser
import laika.parse.markup.{ EscapedTextParsers, RecursiveParsers, RecursiveSpanParsers }
import laika.parse.text.PrefixedParser
/** Base trait for `SpanParserBuilder` and `BlockParserBuilder` APIs.
*/
sealed trait ParserBuilder[T <: ParserDefinition[_]] {
/** Builds a block parser definition lazily by passing the recursive parsers
* of the host language.
*
* This indirection is needed when supplying parser implementations as
* many parsers recursively parse child elements. A list parser for example
* needs to be able to detect any other block or span element within a list
* item. But since it has no way of knowing which extensions a user might
* have added to the host language, those parsers are supplied from the
* outside by the parser engine.
*/
def createParser(recursiveParsers: RecursiveParsers): T
}
/** Builder API for span parsers.
*/
class SpanParserBuilder private (
parserFactory: RecursiveSpanParsers => PrefixedParser[Span],
recursive: Boolean,
precedence: Precedence
) extends ParserBuilder[SpanParserDefinition] {
def createParser(recursiveParsers: RecursiveParsers): SpanParserDefinition = {
val p = parserFactory(recursiveParsers)
new SpanParserDefinition(p.startChars, p.underlying, recursive, precedence)
}
/** Indicates that this parser should only be applied after all built-in
* parsers have failed on a specific markup element.
*/
def withLowPrecedence: SpanParserBuilder =
new SpanParserBuilder(parserFactory, recursive, Precedence.Low)
}
/** Entry points for the builder API for span parsers.
*
* The two entry points are either `recursive` if your parser implementation
* needs to recursively parse child spans defined by the host language
* or `standalone` if it doesn't.
*/
object SpanParserBuilder {
/** Creates a parser definition for a parser that is independent from the parsers
* of the host languages.
*/
def standalone(parser: PrefixedParser[Span]): SpanParserBuilder =
new SpanParserBuilder(_ => parser, recursive = false, Precedence.High)
/** Creates a parser definition for a parser that depends on the parsers
* of the host languages for recursively parsing child elements.
*/
def recursive(factory: RecursiveSpanParsers => PrefixedParser[Span]): SpanParserBuilder =
new SpanParserBuilder(factory, recursive = true, Precedence.High)
}
/** Builder API for block parsers.
*
* Builds a span parser definition lazily by passing the recursive parsers
* of the host language.
*
* This indirection is needed when supplying parser implementations as
* many parsers recursively parse child elements. A list parser for example
* needs to be able to detect any other block or span element within a list
* item. But since it has no way of knowing which extensions a user might
* have added to the host language, those parsers are supplied from the
* outside by the parser engine.
*/
class BlockParserBuilder private (
parserFactory: RecursiveParsers => Parser[Block],
recursive: Boolean = false,
position: BlockPosition = BlockPosition.Any,
precedence: Precedence = Precedence.High,
paragraphLineCheck: Option[PrefixedParser[Any]] = None
) extends ParserBuilder[BlockParserDefinition] {
def createParser(recursiveParsers: RecursiveParsers): BlockParserDefinition = {
val p = parserFactory(recursiveParsers)
val startChars = p match {
case pp: PrefixedParser[_] => pp.startChars.toSortedSet
case _ => Set.empty[Char]
}
new BlockParserDefinition(startChars, p, recursive, position, precedence, paragraphLineCheck)
}
/** Indicates that this parser should only be applied after all built-in
* parsers have failed on a specific markup element.
*/
def withLowPrecedence: BlockParserBuilder =
new BlockParserBuilder(parserFactory, recursive, position, Precedence.Low, paragraphLineCheck)
/** Indicates that this parser should only be applied for top level block items,
* but not for blocks nested within other blocks.
*/
def rootOnly: BlockParserBuilder =
new BlockParserBuilder(
parserFactory,
recursive,
BlockPosition.RootOnly,
precedence,
paragraphLineCheck
)
/** Indicates that this parser should only be applied for blocks nested within other blocks.
*/
def nestedOnly: BlockParserBuilder =
new BlockParserBuilder(
parserFactory,
recursive,
BlockPosition.NestedOnly,
precedence,
paragraphLineCheck
)
/** Provides a test for the start of each line in plain paragraphs that indicates whether the line might
* be the start of a block identified by this parser.
* Without providing such a test the type of block produced by this parser can only occur after a blank line.
*/
def interruptsParagraphWith(lineCheck: PrefixedParser[Any]): BlockParserBuilder =
new BlockParserBuilder(
parserFactory,
recursive,
position,
precedence,
Some(lineCheck)
)
}
/** Entry points for the builder API for block parsers.
*
* The entry points provide access to the parsers for child blocks (`recursive`),
* child spans (`withSpans`) or escape sequences (`withEscapedText`). These
* are methods with decreasing power, as the parser for recursive blocks does
* also provide the span parsers.
*
* If your parser implementation is completely independent from the host markup
* language you can use the `standalone` method.
*/
object BlockParserBuilder {
/** Creates a parser definition for a parser that is independent from the parsers
* of the host languages.
*/
def standalone(parser: Parser[Block]): BlockParserBuilder = new BlockParserBuilder(_ => parser)
/** Creates a parser definition for a parser that depends on the parsers
* of the host languages for recursively parsing child blocks.
*/
def recursive(factory: RecursiveParsers => Parser[Block]): BlockParserBuilder =
new BlockParserBuilder(factory, recursive = true)
/** Creates a parser definition for a parser that depends on the span parsers
* of the host languages for recursively parsing spans inside block elements.
*/
def withSpans(factory: RecursiveSpanParsers => Parser[Block]): BlockParserBuilder =
new BlockParserBuilder(factory)
/** Creates a parser definition for a parser that depends on the parsers for escape sequences
* of the host languages for parsing text.
*/
def withEscapedText(factory: EscapedTextParsers => Parser[Block]): BlockParserBuilder =
new BlockParserBuilder(factory)
}