• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

satisfactory-dev / lunr.js / 20899312197

11 Jan 2026 05:45PM UTC coverage: 98.511% (+1.8%) from 96.746%
20899312197

push

github

SignpostMarv
more or less migrated to es6 re: olivernn/lunr.js#401

234 of 262 branches covered (89.31%)

Branch coverage included in aggregate %.

824 of 850 new or added lines in 21 files covered. (96.94%)

4264 of 4304 relevant lines covered (99.07%)

159.43 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

89.74
/lib/query_parser.mjs
1
/*!
1✔
2
 * QueryParser
1✔
3
 * Copyright (C) 2020 Oliver Nightingale
1✔
4
 * Copyright (C) @YEAR SignpostMarv
1✔
5
 */
1✔
6

1✔
7
import {
1✔
8
  // eslint-disable-next-line no-unused-vars
1✔
9
  Query,
1✔
10
  QueryClause,
1✔
11
  QueryPresence
1✔
12
} from './query.mjs'
1✔
13

1✔
14
import {
1✔
15
  // eslint-disable-next-line no-unused-vars
1✔
16
  QueryLexeme,
1✔
17
  QueryLexer
1✔
18
} from './query_lexer.mjs'
1✔
19

1✔
20
import {
1✔
21
  QueryParseError
1✔
22
} from './query_parse_error.mjs'
1✔
23

1✔
24
export class QueryParser {
1✔
25
  /**
1✔
26
   * @type {QueryLexer}
1✔
27
   */
1✔
28
  lexer
1✔
29

179✔
30
  /**
179✔
31
   * @type {Query}
179✔
32
   */
179✔
33
  query
179✔
34

179✔
35
  /**
179✔
36
   * @type {QueryClause}
179✔
37
   */
179✔
38
  currentClause
179✔
39

179✔
40
  /**
179✔
41
   * @type {number}
179✔
42
   */
179✔
43
  lexemeIdx
179✔
44

179✔
45
  /**
179✔
46
   * @type {QueryLexeme[]|undefined}
179✔
47
   */
179✔
48
  lexemes
179✔
49

1✔
50
  /**
1✔
51
   * @param {string} str
1✔
52
   * @param {Query} query
1✔
53
   */
1✔
54
  constructor (str, query) {
1✔
55
    this.lexer = new QueryLexer (str)
1✔
56
    this.query = query
1✔
57
    this.currentClause = {}
1✔
58
    this.lexemeIdx = 0
1✔
59
  }
1✔
60

1✔
61
  /**
1✔
62
   * @return {Query}
1✔
63
   */
1✔
64
  parse () {
1✔
65
    this.lexer.run()
1✔
66
    this.lexemes = this.lexer.lexemes
1✔
67

1✔
68
    var state = QueryParser.parseClause
1✔
69

1✔
70
    while (state) {
1✔
71
      state = state(this)
1✔
72
    }
1✔
73

1✔
74
    return this.query
1✔
75
  }
1✔
76

1✔
77
  peekLexeme () {
1✔
78
    return this.lexemes[this.lexemeIdx]
1✔
79
  }
1✔
80

1✔
81
  consumeLexeme () {
1✔
82
    var lexeme = this.peekLexeme()
1✔
83
    this.lexemeIdx += 1
1✔
84
    return lexeme
1✔
85
  }
1✔
86

1✔
87
  nextClause () {
1✔
88
    var completedClause = this.currentClause
1✔
89
    this.query.clause(completedClause)
1✔
90
    this.currentClause = new QueryClause
1✔
91
  }
1✔
92

1✔
93
  static parseClause (parser) {
1✔
94
    var lexeme = parser.peekLexeme()
1✔
95

1✔
96
    if (lexeme == undefined) {
1✔
97
      return
1!
98
    }
1✔
99

1✔
100
    switch (lexeme.type) {
1✔
101
      case QueryLexer.PRESENCE:
1✔
102
        return QueryParser.parsePresence
51✔
103
      case QueryLexer.FIELD:
1✔
104
        return QueryParser.parseField
26✔
105
      case QueryLexer.TERM:
1✔
106
        return QueryParser.parseTerm
100✔
107
      default:
1✔
108
        var errorMessage = "expected either a field or a term, found " + lexeme.type
2✔
109

2✔
110
        if (lexeme.str.length >= 1) {
2✔
111
          errorMessage += " with value '" + lexeme.str + "'"
2✔
112
        }
2✔
113

2✔
114
        throw new QueryParseError (errorMessage, lexeme.start, lexeme.end)
2✔
115
    }
1✔
116
  }
1✔
117

1✔
118
  static parsePresence (parser) {
1✔
119
    var lexeme = parser.consumeLexeme()
1✔
120

1✔
121
    if (lexeme == undefined) {
1✔
122
      return
1!
123
    }
1✔
124

1✔
125
    switch (lexeme.str) {
1✔
126
      case "-":
1✔
127
        parser.currentClause.presence = QueryPresence.PROHIBITED
25✔
128
        break
25✔
129
      case "+":
1✔
130
        parser.currentClause.presence = QueryPresence.REQUIRED
46✔
131
        break
46✔
132
      default:
1!
133
        var errorMessage = "unrecognised presence operator'" + lexeme.str + "'"
×
NEW
134
        throw new QueryParseError (errorMessage, lexeme.start, lexeme.end)
×
135
    }
1✔
136

1✔
137
    var nextLexeme = parser.peekLexeme()
1✔
138

1✔
139
    if (nextLexeme == undefined) {
1✔
140
      var errorMessage = "expecting term or field, found nothing"
1✔
141
      throw new QueryParseError (errorMessage, lexeme.start, lexeme.end)
1✔
142
    }
1✔
143

1✔
144
    switch (nextLexeme.type) {
1✔
145
      case QueryLexer.FIELD:
1✔
146
        return QueryParser.parseField
27✔
147
      case QueryLexer.TERM:
1✔
148
        return QueryParser.parseTerm
44✔
149
      default:
1!
150
        var errorMessage = "expecting term or field, found '" + nextLexeme.type + "'"
×
NEW
151
        throw new QueryParseError (errorMessage, nextLexeme.start, nextLexeme.end)
×
152
    }
1✔
153
  }
1✔
154

1✔
155
  static parseField (parser) {
1✔
156
    var lexeme = parser.consumeLexeme()
1✔
157

1✔
158
    if (lexeme == undefined) {
1✔
159
      return
1!
160
    }
1✔
161

1✔
162
    if (parser.query.allFields.indexOf(lexeme.str) == -1) {
1✔
163
      var possibleFields = parser.query.allFields.map(function (f) { return "'" + f + "'" }).join(', '),
1✔
164
          errorMessage = "unrecognised field '" + lexeme.str + "', possible fields: " + possibleFields
1✔
165

1✔
166
      throw new QueryParseError (errorMessage, lexeme.start, lexeme.end)
1✔
167
    }
1✔
168

1✔
169
    parser.currentClause.fields = [lexeme.str]
1✔
170

1✔
171
    var nextLexeme = parser.peekLexeme()
1✔
172

1✔
173
    if (nextLexeme == undefined) {
1✔
174
      var errorMessage = "expecting term, found nothing"
1✔
175
      throw new QueryParseError (errorMessage, lexeme.start, lexeme.end)
1✔
176
    }
1✔
177

1✔
178
    switch (nextLexeme.type) {
1✔
179
      case QueryLexer.TERM:
1✔
180
        return QueryParser.parseTerm
1✔
181
      default:
1!
182
        var errorMessage = "expecting term, found '" + nextLexeme.type + "'"
×
NEW
183
        throw new QueryParseError (errorMessage, nextLexeme.start, nextLexeme.end)
×
184
    }
1✔
185
  }
1✔
186

1✔
187
  static parseTerm (parser) {
1✔
188
    var lexeme = parser.consumeLexeme()
1✔
189

1✔
190
    if (lexeme == undefined) {
1✔
191
      return
1!
192
    }
1✔
193

1✔
194
    parser.currentClause.term = lexeme.str.toLowerCase()
1✔
195

1✔
196
    if (lexeme.str.indexOf("*") != -1) {
1✔
197
      parser.currentClause.usePipeline = false
1✔
198
    }
1✔
199

1✔
200
    var nextLexeme = parser.peekLexeme()
1✔
201

1✔
202
    if (nextLexeme == undefined) {
1✔
203
      parser.nextClause()
1✔
204
      return
1✔
205
    }
1✔
206

1✔
207
    switch (nextLexeme.type) {
1✔
208
      case QueryLexer.TERM:
1✔
209
        parser.nextClause()
39✔
210
        return QueryParser.parseTerm
39✔
211
      case QueryLexer.FIELD:
1✔
212
        parser.nextClause()
4✔
213
        return QueryParser.parseField
4✔
214
      case QueryLexer.EDIT_DISTANCE:
1✔
215
        return QueryParser.parseEditDistance
25✔
216
      case QueryLexer.BOOST:
1✔
217
        return QueryParser.parseBoost
31✔
218
      case QueryLexer.PRESENCE:
1✔
219
        parser.nextClause()
12✔
220
        return QueryParser.parsePresence
12✔
221
      default:
1!
222
        var errorMessage = "Unexpected lexeme type '" + nextLexeme.type + "'"
×
NEW
223
        throw new QueryParseError (errorMessage, nextLexeme.start, nextLexeme.end)
×
224
    }
1✔
225
  }
1✔
226

1✔
227
  static parseEditDistance (parser) {
1✔
228
    var lexeme = parser.consumeLexeme()
1✔
229

1✔
230
    if (lexeme == undefined) {
1✔
231
      return
1!
232
    }
1✔
233

1✔
234
    var editDistance = parseInt(lexeme.str, 10)
1✔
235

1✔
236
    if (isNaN(editDistance)) {
1✔
237
      var errorMessage = "edit distance must be numeric"
1✔
238
      throw new QueryParseError (errorMessage, lexeme.start, lexeme.end)
1✔
239
    }
1✔
240

1✔
241
    parser.currentClause.editDistance = editDistance
1✔
242

1✔
243
    var nextLexeme = parser.peekLexeme()
1✔
244

1✔
245
    if (nextLexeme == undefined) {
1✔
246
      parser.nextClause()
1✔
247
      return
1✔
248
    }
1✔
249

1✔
250
    switch (nextLexeme.type) {
1✔
251
      case QueryLexer.TERM:
1✔
252
        parser.nextClause()
4✔
253
        return QueryParser.parseTerm
4✔
254
      case QueryLexer.FIELD:
1!
255
        parser.nextClause()
×
NEW
256
        return QueryParser.parseField
×
257
      case QueryLexer.EDIT_DISTANCE:
1!
NEW
258
        return QueryParser.parseEditDistance
×
259
      case QueryLexer.BOOST:
1!
NEW
260
        return QueryParser.parseBoost
×
261
      case QueryLexer.PRESENCE:
1✔
262
        parser.nextClause()
4✔
263
        return QueryParser.parsePresence
4✔
264
      default:
1!
265
        var errorMessage = "Unexpected lexeme type '" + nextLexeme.type + "'"
×
NEW
266
        throw new QueryParseError (errorMessage, nextLexeme.start, nextLexeme.end)
×
267
    }
1✔
268
  }
1✔
269

1✔
270
  static parseBoost (parser) {
1✔
271
    var lexeme = parser.consumeLexeme()
1✔
272

1✔
273
    if (lexeme == undefined) {
1✔
274
      return
1!
275
    }
1✔
276

1✔
277
    var boost = parseInt(lexeme.str, 10)
1✔
278

1✔
279
    if (isNaN(boost)) {
1✔
280
      var errorMessage = "boost must be numeric"
1✔
281
      throw new QueryParseError (errorMessage, lexeme.start, lexeme.end)
1✔
282
    }
1✔
283

1✔
284
    parser.currentClause.boost = boost
1✔
285

1✔
286
    var nextLexeme = parser.peekLexeme()
1✔
287

1✔
288
    if (nextLexeme == undefined) {
1✔
289
      parser.nextClause()
1✔
290
      return
1✔
291
    }
1✔
292

1✔
293
    switch (nextLexeme.type) {
1✔
294
      case QueryLexer.TERM:
1✔
295
        parser.nextClause()
4✔
296
        return QueryParser.parseTerm
4✔
297
      case QueryLexer.FIELD:
1!
298
        parser.nextClause()
×
NEW
299
        return QueryParser.parseField
×
300
      case QueryLexer.EDIT_DISTANCE:
1✔
301
        return QueryParser.parseEditDistance
5✔
302
      case QueryLexer.BOOST:
1!
NEW
303
        return QueryParser.parseBoost
×
304
      case QueryLexer.PRESENCE:
1✔
305
        parser.nextClause()
4✔
306
        return QueryParser.parsePresence
4✔
307
      default:
1!
308
        var errorMessage = "Unexpected lexeme type '" + nextLexeme.type + "'"
×
NEW
309
        throw new QueryParseError (errorMessage, nextLexeme.start, nextLexeme.end)
×
310
    }
1✔
311
  }
1✔
312
}
1✔
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc