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
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
|
# -*- coding: utf-8 -*- #
# frozen_string_literal: true
module Rouge
module Lexers
class XPath < RegexLexer
title 'XPath'
desc 'XML Path Language (XPath) 3.1'
tag 'xpath'
filenames '*.xpath'
# Terminal literals:
# https://www.w3.org/TR/xpath-31/#terminal-symbols
def self.digits
@digits ||= %r/[0-9]+/
end
def self.decimalLiteral
@decimalLiteral ||= %r/\.#{digits}|#{digits}\.[0-9]*/
end
def self.doubleLiteral
@doubleLiteral ||= %r/(\.#{digits})|#{digits}(\.[0-9]*)?[eE][+-]?#{digits}/
end
def self.stringLiteral
@stringLiteral ||= %r/("(("")|[^"])*")|('(('')|[^'])*')/
end
def self.ncName
@ncName ||= %r/[a-z_][a-z_\-.0-9]*/i
end
def self.qName
@qName ||= %r/(?:#{ncName})(?::#{ncName})?/
end
def self.uriQName
@uriQName ||= %r/Q\{[^{}]*\}#{ncName}/
end
def self.eqName
@eqName ||= %r/(?:#{uriQName}|#{qName})/
end
def self.commentStart
@commentStart ||= %r/\(:/
end
def self.openParen
@openParen ||= %r/\((?!:)/
end
# Terminal symbols:
# https://www.w3.org/TR/xpath-30/#id-terminal-delimitation
def self.kindTest
@kindTest ||= Regexp.union %w(
element attribute schema-element schema-attribute
comment text node document-node namespace-node
)
end
def self.kindTestForPI
@kindTestForPI ||= Regexp.union %w(processing-instruction)
end
def self.axes
@axes ||= Regexp.union %w(
child descendant attribute self descendant-or-self
following-sibling following namespace
parent ancestor preceding-sibling preceding ancestor-or-self
)
end
def self.operators
@operators ||= Regexp.union %w(, => = := : >= >> > <= << < - * != + // / || |)
end
def self.keywords
@keywords ||= Regexp.union %w(let for some every if then else return in satisfies)
end
def self.word_operators
@word_operators ||= Regexp.union %w(
and or eq ge gt le lt ne is
div mod idiv
intersect except union
to
)
end
def self.constructorTypes
@constructorTypes ||= Regexp.union %w(function array map empty-sequence)
end
# Mixin states:
state :commentsAndWhitespace do
rule XPath.commentStart, Comment, :comment
rule %r/\s+/m, Text::Whitespace
end
# Lexical states:
# https://www.w3.org/TR/xquery-xpath-parsing/#XPath-lexical-states
# https://lists.w3.org/Archives/Public/public-qt-comments/2004Aug/0127.html
# https://www.w3.org/TR/xpath-30/#id-revision-log
# https://www.w3.org/TR/xpath-31/#id-revision-log
state :root do
mixin :commentsAndWhitespace
# Literals
rule XPath.doubleLiteral, Num::Float
rule XPath.decimalLiteral, Num::Float
rule XPath.digits, Num
rule XPath.stringLiteral, Literal::String
# Variables
rule %r/\$/, Name::Variable, :varname
# Operators
rule XPath.operators, Operator
rule %r/#{XPath.word_operators}\b/, Operator::Word
rule %r/#{XPath.keywords}\b/, Keyword
rule %r/[?,{}()\[\]]/, Punctuation
# Functions
rule %r/(function)(\s*)(#{XPath.openParen})/ do # function declaration
groups Keyword, Text::Whitespace, Punctuation
end
rule %r/(map|array|empty-sequence)/, Keyword # constructors
rule %r/(#{XPath.kindTest})(\s*)(#{XPath.openParen})/ do # kindtest
groups Keyword, Text::Whitespace, Punctuation
push :kindtest
end
rule %r/(#{XPath.kindTestForPI})(\s*)(#{XPath.openParen})/ do # processing instruction kindtest
groups Keyword, Text::Whitespace, Punctuation
push :kindtestforpi
end
rule %r/(#{XPath.eqName})(\s*)(#{XPath.openParen})/ do # function call
groups Name::Function, Text::Whitespace, Punctuation
end
rule %r/(#{XPath.eqName})(\s*)(#)(\s*)(\d+)/ do # namedFunctionRef
groups Name::Function, Text::Whitespace, Name::Function, Text::Whitespace, Name::Function
end
# Type commands
rule %r/(cast|castable)(\s+)(as)/ do
groups Keyword, Text::Whitespace, Keyword
push :singletype
end
rule %r/(treat)(\s+)(as)/ do
groups Keyword, Text::Whitespace, Keyword
push :itemtype
end
rule %r/(instance)(\s+)(of)/ do
groups Keyword, Text::Whitespace, Keyword
push :itemtype
end
rule %r/(as)\b/ do
token Keyword
push :itemtype
end
# Paths
rule %r/(#{XPath.ncName})(\s*)(:)(\s*)(\*)/ do
groups Name::Tag, Text::Whitespace, Punctuation, Text::Whitespace, Operator
end
rule %r/(\*)(\s*)(:)(\s*)(#{XPath.ncName})/ do
groups Operator, Text::Whitespace, Punctuation, Text::Whitespace, Name::Tag
end
rule %r/(#{XPath.axes})(\s*)(::)/ do
groups Keyword, Text::Whitespace, Operator
end
rule %r/\.\.|\.|\*/, Operator
rule %r/@/, Name::Attribute, :attrname
rule XPath.eqName, Name::Tag
end
state :singletype do
mixin :commentsAndWhitespace
# Type name
rule XPath.eqName do
token Keyword::Type
pop!
end
end
state :itemtype do
mixin :commentsAndWhitespace
# Type tests
rule %r/(#{XPath.kindTest})(\s*)(#{XPath.openParen})/ do
groups Keyword::Type, Text::Whitespace, Punctuation
# go to kindtest then occurrenceindicator
goto :occurrenceindicator
push :kindtest
end
rule %r/(#{XPath.kindTestForPI})(\s*)(#{XPath.openParen})/ do
groups Keyword::Type, Text::Whitespace, Punctuation
# go to kindtestforpi then occurrenceindicator
goto :occurrenceindicator
push :kindtestforpi
end
rule %r/(item)(\s*)(#{XPath.openParen})(\s*)(\))/ do
groups Keyword::Type, Text::Whitespace, Punctuation, Text::Whitespace, Punctuation
goto :occurrenceindicator
end
rule %r/(#{XPath.constructorTypes})(\s*)(#{XPath.openParen})/ do
groups Keyword::Type, Text::Whitespace, Punctuation
end
# Type commands
rule %r/(cast|castable)(\s+)(as)/ do
groups Keyword, Text::Whitespace, Keyword
goto :singletype
end
rule %r/(treat)(\s+)(as)/ do
groups Keyword, Text::Whitespace, Keyword
goto :itemtype
end
rule %r/(instance)(\s+)(of)/ do
groups Keyword, Text::Whitespace, Keyword
goto :itemtype
end
rule %r/(as)\b/, Keyword
# Operators
rule XPath.operators do
token Operator
pop!
end
rule %r/#{XPath.word_operators}\b/ do
token Operator::Word
pop!
end
rule %r/#{XPath.keywords}\b/ do
token Keyword
pop!
end
rule %r/[\[),]/ do
token Punctuation
pop!
end
# Other types (e.g. xs:double)
rule XPath.eqName do
token Keyword::Type
goto :occurrenceindicator
end
end
# For pseudo-parameters for the KindTest productions
state :kindtest do
mixin :commentsAndWhitespace
# Pseudo-parameters:
rule %r/[?*]/, Operator
rule %r/,/, Punctuation
rule %r/(element|schema-element)(\s*)(#{XPath.openParen})/ do
groups Keyword::Type, Text::Whitespace, Punctuation
push :kindtest
end
rule XPath.eqName, Name::Tag
# End of pseudo-parameters
rule %r/\)/, Punctuation, :pop!
end
# Similar to :kindtest, but recognizes NCNames instead of EQNames
state :kindtestforpi do
mixin :commentsAndWhitespace
# Pseudo-parameters
rule XPath.ncName, Name
rule XPath.stringLiteral, Literal::String
# End of pseudo-parameters
rule %r/\)/, Punctuation, :pop!
end
state :occurrenceindicator do
mixin :commentsAndWhitespace
# Occurrence indicator
rule %r/[?*+]/ do
token Operator
pop!
end
# Otherwise, lex it in root state:
rule %r/(?![?*+])/ do
pop!
end
end
state :varname do
mixin :commentsAndWhitespace
# Function call
rule %r/(#{XPath.eqName})(\s*)(#{XPath.openParen})/ do
groups Name::Variable, Text::Whitespace, Punctuation
pop!
end
# Variable name
rule XPath.eqName, Name::Variable, :pop!
end
state :attrname do
mixin :commentsAndWhitespace
# Attribute name
rule XPath.eqName, Name::Attribute, :pop!
rule %r/\*/, Operator, :pop!
end
state :comment do
# Comment end
rule %r/:\)/, Comment, :pop!
# Nested comment
rule XPath.commentStart, Comment, :comment
# Comment contents
rule %r/[^:(]+/m, Comment
rule %r/[:(]/, Comment
end
end
end
end
|