-
Notifications
You must be signed in to change notification settings - Fork 14
/
position.scala
129 lines (122 loc) · 5.52 KB
/
position.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
/*
* Copyright 2020 Parsley Contributors <https://github.com/j-mie6/Parsley/graphs/contributors>
*
* SPDX-License-Identifier: BSD-3-Clause
*/
package parsley
import parsley.implicits.zipped.Zipped3
import parsley.internal.deepembedding.singletons
/** This module contains parsers that provide a way to extract position information during a parse.
*
* Position parsers can be important
* for when the final result of the parser needs to encode position information for later consumption:
* this is particularly useful for abstract syntax trees. Offset is also exposed by this interface, which
* may be useful for establishing a caret size in specialised error messages.
*
* @since 4.2.0
*/
object position {
/** This parser returns the current line number (starting at 1) of the input without having any other effect.
*
* When this combinator is ran, no input is required, nor consumed, and
* the current line number will always be successfully returned. It has no other
* effect on the state of the parser.
*
* @example {{{
* scala> import parsley.position.line, parsley.character.char
* scala> line.parse("")
* val res0 = Success(1)
* scala> (char('a') *> line).parse("a")
* val res0 = Success(1)
* scala> (char('\n') *> line).parse("\n")
* val res0 = Success(2)
* }}}
*
* @return a parser that returns the line number the parser is currently at.
* @group pos
*/
val line: Parsley[Int] = new Parsley(singletons.Line)
/** This parser returns the current column number (starting at 1) of the input without having any other effect.
*
* When this combinator is ran, no input is required, nor consumed, and
* the current column number will always be successfully returned. It has no other
* effect on the state of the parser.
*
* @example {{{
* scala> import parsley.position.col, parsley.character.char
* scala> col.parse("")
* val res0 = Success(1)
* scala> (char('a') *> col).parse("a")
* val res0 = Success(2)
* scala> (char('\n') *> col).parse("\n")
* val res0 = Success(1)
* }}}
*
* @return a parser that returns the column number the parser is currently at.
* @note in the presence of wide unicode characters, the value returned may be inaccurate.
* @group pos
*/
val col: Parsley[Int] = new Parsley(singletons.Col)
/** This parser returns the current line and column numbers (starting at 1) of the input without having any other effect.
*
* When this combinator is ran, no input is required, nor consumed, and
* the current line and column number will always be successfully returned. It has no other
* effect on the state of the parser.
*
* @example {{{
* scala> import parsley.position.pos, parsley.character.char
* scala> pos.parse("")
* val res0 = Success((1, 1))
* scala> (char('a') *> pos).parse("a")
* val res0 = Success((1, 2))
* scala> (char('\n') *> pos).parse("\n")
* val res0 = Success((2, 1))
* }}}
*
* @return a parser that returns the line and column number the parser is currently at.
* @note in the presence of wide unicode characters, the column value returned may be inaccurate.
* @group pos
*/
val pos: Parsley[(Int, Int)] = line <~> col
// this is subject to change at the slightest notice, do NOT expose
private [parsley] val internalOffset: Parsley[Int] = new Parsley(singletons.Offset)
/** This parser returns the current offset into the input (starting at 0) without having any other effect.
*
* When this combinator is ran, no input is required, nor consumed, and
* the current offset into the input will always be successfully returned. It has no other
* effect on the state of the parser.
*
* @example {{{
* scala> import parsley.position.offset, parsley.character.char
* scala> offset.parse("")
* val res0 = Success(0)
* scala> (char('a') *> offset).parse("a")
* val res0 = Success(1)
* scala> (char('\n') *> offset).parse("\n")
* val res0 = Success(1)
* }}}
*
* @return a parser that returns the offset the parser is currently at.
* @note offset does not take wide unicode codepoints into account.
*/
val offset: Parsley[Int] = internalOffset
private [parsley] def withSpan[A, S](end: Parsley[S])(p: Parsley[A]): Parsley[(S, A, S)] = (end, p, end).zipped
/** This combinator returns the result of a given parser and the number of characters it consumed.
*
* First records the initial `offset` on entry to given parser `p`, then executes `p`. If `p` succeeds,
* then the `offset` is taken again, and the two values are subtracted to give width `w`. The result of
* `p`, `x` is returned along with `w` as `(x, w)`. If `p` fails, this combinator will also fail.
*
* @example {{{
* scala> import parsley.position.withWidth, parsley.character.string
* scala> withWidth(string("abc")).parse("abc")
* val res0 = Success(("abc", 3))
* }}}
*
* @param p the parser to compute the width for
* @return a parser that pairs the result of the parser `p` with the number of characters it consumed
* @note the value returned is the number of 16-bit ''characters'' consumed, not unicode codepoints.
* @since 4.4.0
*/
def withWidth[A](p: Parsley[A]): Parsley[(A, Int)] = (offset, p, offset).zipped((s, x, e) => (x, e-s))
}