squirreling 0.4.8 → 0.6.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,7 +1,7 @@
1
- import { syntaxError } from '../errors.js'
1
+ import { syntaxError } from '../parseErrors.js'
2
2
  import { isBinaryOp } from '../validation.js'
3
3
  import { parseAdditive, parseExpression, parseSubquery } from './expression.js'
4
- import { consume, current, expect, match, peekToken } from './state.js'
4
+ import { consume, current, expect, lastPosition, match, peekToken } from './state.js'
5
5
 
6
6
  /**
7
7
  * @import { ExprNode, ParserState } from '../types.js'
@@ -26,6 +26,8 @@ export function parseComparison(state) {
26
26
  type: 'unary',
27
27
  op: 'IS NOT NULL',
28
28
  argument: left,
29
+ positionStart: left.positionStart,
30
+ positionEnd: lastPosition(state),
29
31
  }
30
32
  }
31
33
  expect(state, 'keyword', 'NULL')
@@ -33,6 +35,8 @@ export function parseComparison(state) {
33
35
  type: 'unary',
34
36
  op: 'IS NULL',
35
37
  argument: left,
38
+ positionStart: left.positionStart,
39
+ positionEnd: lastPosition(state),
36
40
  }
37
41
  }
38
42
 
@@ -40,6 +44,7 @@ export function parseComparison(state) {
40
44
  if (tok.type === 'keyword' && tok.value === 'NOT') {
41
45
  const nextTok = peekToken(state, 1)
42
46
  if (nextTok.type === 'keyword' && nextTok.value === 'LIKE') {
47
+ const notPositionStart = tok.positionStart
43
48
  consume(state) // NOT
44
49
  consume(state) // LIKE
45
50
  const right = parseAdditive(state)
@@ -51,7 +56,11 @@ export function parseComparison(state) {
51
56
  op: 'LIKE',
52
57
  left,
53
58
  right,
59
+ positionStart: left.positionStart,
60
+ positionEnd: right.positionEnd,
54
61
  },
62
+ positionStart: notPositionStart,
63
+ positionEnd: right.positionEnd,
55
64
  }
56
65
  }
57
66
  }
@@ -64,6 +73,8 @@ export function parseComparison(state) {
64
73
  op: 'LIKE',
65
74
  left,
66
75
  right,
76
+ positionStart: left.positionStart,
77
+ positionEnd: right.positionEnd,
67
78
  }
68
79
  }
69
80
 
@@ -71,6 +82,7 @@ export function parseComparison(state) {
71
82
  if (tok.type === 'keyword' && tok.value === 'NOT') {
72
83
  const nextTok = peekToken(state, 1)
73
84
  if (nextTok.type === 'keyword' && nextTok.value === 'BETWEEN') {
85
+ const notPositionStart = tok.positionStart
74
86
  consume(state) // NOT
75
87
  consume(state) // BETWEEN
76
88
  const lower = parseAdditive(state)
@@ -80,8 +92,10 @@ export function parseComparison(state) {
80
92
  return {
81
93
  type: 'binary',
82
94
  op: 'OR',
83
- left: { type: 'binary', op: '<', left, right: lower },
84
- right: { type: 'binary', op: '>', left, right: upper },
95
+ left: { type: 'binary', op: '<', left, right: lower, positionStart: left.positionStart, positionEnd: lower.positionEnd },
96
+ right: { type: 'binary', op: '>', left, right: upper, positionStart: left.positionStart, positionEnd: upper.positionEnd },
97
+ positionStart: notPositionStart,
98
+ positionEnd: upper.positionEnd,
85
99
  }
86
100
  }
87
101
  }
@@ -95,8 +109,10 @@ export function parseComparison(state) {
95
109
  return {
96
110
  type: 'binary',
97
111
  op: 'AND',
98
- left: { type: 'binary', op: '>=', left, right: lower },
99
- right: { type: 'binary', op: '<=', left, right: upper },
112
+ left: { type: 'binary', op: '>=', left, right: lower, positionStart: left.positionStart, positionEnd: lower.positionEnd },
113
+ right: { type: 'binary', op: '<=', left, right: upper, positionStart: left.positionStart, positionEnd: upper.positionEnd },
114
+ positionStart: left.positionStart,
115
+ positionEnd: upper.positionEnd,
100
116
  }
101
117
  }
102
118
 
@@ -104,6 +120,7 @@ export function parseComparison(state) {
104
120
  if (tok.type === 'keyword' && tok.value === 'NOT') {
105
121
  const nextTok = peekToken(state, 1)
106
122
  if (nextTok.type === 'keyword' && nextTok.value === 'IN') {
123
+ const notPositionStart = tok.positionStart
107
124
  consume(state) // NOT
108
125
  consume(state) // IN
109
126
 
@@ -111,12 +128,13 @@ export function parseComparison(state) {
111
128
  // parseSubquery expects to consume the opening paren itself
112
129
  const parenTok = current(state)
113
130
  if (parenTok.type !== 'paren' || parenTok.value !== '(') {
114
- throw syntaxError({ expected: '(', received: `"${parenTok.value}"`, position: parenTok.position, after: 'IN' })
131
+ throw syntaxError({ expected: '(', received: `"${parenTok.value}"`, positionStart: parenTok.positionStart, positionEnd: parenTok.positionEnd, after: 'IN' })
115
132
  }
116
133
  const peekTok = peekToken(state, 1)
117
134
  if (peekTok.type === 'keyword' && peekTok.value === 'SELECT') {
118
135
  // Subquery - let parseSubquery handle the parens
119
136
  const subquery = parseSubquery(state)
137
+ const positionEnd = lastPosition(state)
120
138
  return {
121
139
  type: 'unary',
122
140
  op: 'NOT',
@@ -124,7 +142,11 @@ export function parseComparison(state) {
124
142
  type: 'in',
125
143
  expr: left,
126
144
  subquery,
145
+ positionStart: left.positionStart,
146
+ positionEnd,
127
147
  },
148
+ positionStart: notPositionStart,
149
+ positionEnd,
128
150
  }
129
151
  } else {
130
152
  // Parse list of values - we handle the parens
@@ -136,6 +158,7 @@ export function parseComparison(state) {
136
158
  if (!match(state, 'comma')) break
137
159
  }
138
160
  expect(state, 'paren', ')')
161
+ const positionEnd = lastPosition(state)
139
162
  return {
140
163
  type: 'unary',
141
164
  op: 'NOT',
@@ -143,7 +166,11 @@ export function parseComparison(state) {
143
166
  type: 'in valuelist',
144
167
  expr: left,
145
168
  values,
169
+ positionStart: left.positionStart,
170
+ positionEnd,
146
171
  },
172
+ positionStart: notPositionStart,
173
+ positionEnd,
147
174
  }
148
175
  }
149
176
  }
@@ -156,7 +183,7 @@ export function parseComparison(state) {
156
183
  // parseSubquery expects to consume the opening paren itself
157
184
  const parenTok = current(state)
158
185
  if (parenTok.type !== 'paren' || parenTok.value !== '(') {
159
- throw syntaxError({ expected: '(', received: `"${parenTok.value}"`, position: parenTok.position, after: 'IN' })
186
+ throw syntaxError({ expected: '(', received: `"${parenTok.value}"`, positionStart: parenTok.positionStart, positionEnd: parenTok.positionEnd, after: 'IN' })
160
187
  }
161
188
  const peekTok = peekToken(state, 1)
162
189
  if (peekTok.type === 'keyword' && peekTok.value === 'SELECT') {
@@ -166,6 +193,8 @@ export function parseComparison(state) {
166
193
  type: 'in',
167
194
  expr: left,
168
195
  subquery,
196
+ positionStart: left.positionStart,
197
+ positionEnd: lastPosition(state),
169
198
  }
170
199
  } else {
171
200
  // Parse list of values - we handle the parens
@@ -181,6 +210,8 @@ export function parseComparison(state) {
181
210
  type: 'in valuelist',
182
211
  expr: left,
183
212
  values,
213
+ positionStart: left.positionStart,
214
+ positionEnd: lastPosition(state),
184
215
  }
185
216
  }
186
217
  }
@@ -193,6 +224,8 @@ export function parseComparison(state) {
193
224
  op: tok.value,
194
225
  left,
195
226
  right,
227
+ positionStart: left.positionStart,
228
+ positionEnd: right.positionEnd,
196
229
  }
197
230
  }
198
231
 
@@ -3,11 +3,11 @@ import {
3
3
  missingClauseError,
4
4
  syntaxError,
5
5
  unknownFunctionError,
6
- } from '../errors.js'
7
- import { isAggregateFunc, isIntervalUnit, isStringFunc } from '../validation.js'
6
+ } from '../parseErrors.js'
7
+ import { isAggregateFunc, isIntervalUnit, isMathFunc, isStringFunc } from '../validation.js'
8
8
  import { parseComparison } from './comparison.js'
9
9
  import { parseSelectInternal } from './parse.js'
10
- import { consume, current, expect, expectIdentifier, match, peekToken } from './state.js'
10
+ import { consume, current, expect, expectIdentifier, lastPosition, match, peekToken } from './state.js'
11
11
 
12
12
  /**
13
13
  * @import { ExprNode, IntervalNode, ParserState, SelectStatement, WhenClause } from '../types.js'
@@ -18,6 +18,7 @@ import { consume, current, expect, expectIdentifier, match, peekToken } from './
18
18
  * @returns {IntervalNode}
19
19
  */
20
20
  function parseInterval(state) {
21
+ const { positionStart } = current(state)
21
22
  consume(state) // INTERVAL
22
23
 
23
24
  // Handle optional negative sign
@@ -39,11 +40,11 @@ function parseInterval(state) {
39
40
  consume(state)
40
41
  const parsed = parseFloat(valueTok.value)
41
42
  if (isNaN(parsed)) {
42
- throw invalidLiteralError({ type: 'interval value', value: valueTok.value, position: valueTok.position })
43
+ throw invalidLiteralError({ type: 'interval value', value: valueTok.value, positionStart: valueTok.positionStart, positionEnd: valueTok.positionEnd })
43
44
  }
44
45
  value = sign * parsed
45
46
  } else {
46
- throw syntaxError({ expected: 'interval value (number)', received: `"${valueTok.value}"`, position: valueTok.position })
47
+ throw syntaxError({ expected: 'interval value (number)', received: `"${valueTok.value}"`, positionStart: valueTok.positionStart, positionEnd: valueTok.positionEnd })
47
48
  }
48
49
 
49
50
  // Get unit keyword
@@ -52,13 +53,14 @@ function parseInterval(state) {
52
53
  throw invalidLiteralError({
53
54
  type: 'interval unit',
54
55
  value: unitTok.value,
55
- position: unitTok.position,
56
+ positionStart: unitTok.positionStart,
57
+ positionEnd: unitTok.positionEnd,
56
58
  validValues: 'DAY, MONTH, YEAR, HOUR, MINUTE, SECOND',
57
59
  })
58
60
  }
59
61
  consume(state)
60
62
 
61
- return { type: 'interval', value, unit: unitTok.value }
63
+ return { type: 'interval', value, unit: unitTok.value, positionStart, positionEnd: lastPosition(state) }
62
64
  }
63
65
 
64
66
  /**
@@ -75,6 +77,7 @@ export function parseExpression(state) {
75
77
  */
76
78
  export function parsePrimary(state) {
77
79
  const tok = current(state)
80
+ const { positionStart } = tok
78
81
 
79
82
  if (tok.type === 'paren' && tok.value === '(') {
80
83
  // Peek ahead to see if this is a scalar subquery
@@ -85,6 +88,8 @@ export function parsePrimary(state) {
85
88
  return {
86
89
  type: 'subquery',
87
90
  subquery,
91
+ positionStart,
92
+ positionEnd: lastPosition(state),
88
93
  }
89
94
  }
90
95
  // Regular grouped expression
@@ -109,6 +114,8 @@ export function parsePrimary(state) {
109
114
  type: 'cast',
110
115
  expr,
111
116
  toType: typeTok.value,
117
+ positionStart,
118
+ positionEnd: lastPosition(state),
112
119
  }
113
120
  }
114
121
 
@@ -117,8 +124,8 @@ export function parsePrimary(state) {
117
124
  const funcName = tok.value
118
125
 
119
126
  // validate function names
120
- if (!isStringFunc(funcName) && !isAggregateFunc(funcName)) {
121
- throw unknownFunctionError(funcName, tok.position)
127
+ if (!isStringFunc(funcName) && !isAggregateFunc(funcName) && !isMathFunc(funcName)) {
128
+ throw unknownFunctionError({ funcName, positionStart: tok.positionStart, positionEnd: tok.positionEnd })
122
129
  }
123
130
 
124
131
  consume(state) // function name
@@ -131,10 +138,13 @@ export function parsePrimary(state) {
131
138
  while (true) {
132
139
  // Handle COUNT(*) - treat * as a special identifier
133
140
  if (current(state).type === 'operator' && current(state).value === '*') {
141
+ const starTok = current(state)
134
142
  consume(state)
135
143
  args.push({
136
144
  type: 'identifier',
137
145
  name: '*',
146
+ positionStart: starTok.positionStart,
147
+ positionEnd: lastPosition(state),
138
148
  })
139
149
  } else {
140
150
  const arg = parseExpression(state)
@@ -150,6 +160,8 @@ export function parsePrimary(state) {
150
160
  type: 'function',
151
161
  name: funcName,
152
162
  args,
163
+ positionStart,
164
+ positionEnd: lastPosition(state),
153
165
  }
154
166
  }
155
167
 
@@ -161,6 +173,8 @@ export function parsePrimary(state) {
161
173
  type: 'function',
162
174
  name: tok.value,
163
175
  args: [],
176
+ positionStart,
177
+ positionEnd: lastPosition(state),
164
178
  }
165
179
  }
166
180
 
@@ -177,6 +191,8 @@ export function parsePrimary(state) {
177
191
  return {
178
192
  type: 'identifier',
179
193
  name,
194
+ positionStart,
195
+ positionEnd: lastPosition(state),
180
196
  }
181
197
  }
182
198
 
@@ -185,6 +201,8 @@ export function parsePrimary(state) {
185
201
  return {
186
202
  type: 'literal',
187
203
  value: tok.numericValue ?? null,
204
+ positionStart,
205
+ positionEnd: lastPosition(state),
188
206
  }
189
207
  }
190
208
 
@@ -193,21 +211,23 @@ export function parsePrimary(state) {
193
211
  return {
194
212
  type: 'literal',
195
213
  value: tok.value,
214
+ positionStart,
215
+ positionEnd: lastPosition(state),
196
216
  }
197
217
  }
198
218
 
199
219
  if (tok.type === 'keyword') {
200
220
  if (tok.value === 'TRUE') {
201
221
  consume(state)
202
- return { type: 'literal', value: true }
222
+ return { type: 'literal', value: true, positionStart, positionEnd: lastPosition(state) }
203
223
  }
204
224
  if (tok.value === 'FALSE') {
205
225
  consume(state)
206
- return { type: 'literal', value: false }
226
+ return { type: 'literal', value: false, positionStart, positionEnd: lastPosition(state) }
207
227
  }
208
228
  if (tok.value === 'NULL') {
209
229
  consume(state)
210
- return { type: 'literal', value: null }
230
+ return { type: 'literal', value: null, positionStart, positionEnd: lastPosition(state) }
211
231
  }
212
232
  if (tok.value === 'EXISTS') {
213
233
  consume(state) // EXISTS
@@ -215,6 +235,8 @@ export function parsePrimary(state) {
215
235
  return {
216
236
  type: 'exists',
217
237
  subquery,
238
+ positionStart,
239
+ positionEnd: lastPosition(state),
218
240
  }
219
241
  }
220
242
  if (tok.value === 'CASE') {
@@ -260,6 +282,8 @@ export function parsePrimary(state) {
260
282
  caseExpr,
261
283
  whenClauses,
262
284
  elseResult,
285
+ positionStart,
286
+ positionEnd: lastPosition(state),
263
287
  }
264
288
  }
265
289
  if (tok.value === 'INTERVAL') {
@@ -274,11 +298,13 @@ export function parsePrimary(state) {
274
298
  type: 'unary',
275
299
  op: '-',
276
300
  argument,
301
+ positionStart,
302
+ positionEnd: argument.positionEnd,
277
303
  }
278
304
  }
279
305
 
280
306
  const found = tok.type === 'eof' ? 'end of query' : `"${tok.originalValue ?? tok.value}"`
281
- throw syntaxError({ expected: 'expression', received: found, position: tok.position })
307
+ throw syntaxError({ expected: 'expression', received: found, positionStart: tok.positionStart, positionEnd: tok.positionEnd })
282
308
  }
283
309
 
284
310
  /**
@@ -294,6 +320,8 @@ function parseOr(state) {
294
320
  op: 'OR',
295
321
  left: node,
296
322
  right,
323
+ positionStart: node.positionStart,
324
+ positionEnd: right.positionEnd,
297
325
  }
298
326
  }
299
327
  return node
@@ -312,6 +340,8 @@ function parseAnd(state) {
312
340
  op: 'AND',
313
341
  left: node,
314
342
  right,
343
+ positionStart: node.positionStart,
344
+ positionEnd: right.positionEnd,
315
345
  }
316
346
  }
317
347
  return node
@@ -322,7 +352,9 @@ function parseAnd(state) {
322
352
  * @returns {ExprNode}
323
353
  */
324
354
  function parseNot(state) {
355
+ const tok = current(state)
325
356
  if (match(state, 'keyword', 'NOT')) {
357
+ const { positionStart } = tok
326
358
  // Check for NOT EXISTS
327
359
  const nextTok = current(state)
328
360
  if (nextTok.type === 'keyword' && nextTok.value === 'EXISTS') {
@@ -331,6 +363,8 @@ function parseNot(state) {
331
363
  return {
332
364
  type: 'not exists',
333
365
  subquery,
366
+ positionStart,
367
+ positionEnd: lastPosition(state),
334
368
  }
335
369
  }
336
370
  const argument = parseNot(state)
@@ -338,6 +372,8 @@ function parseNot(state) {
338
372
  type: 'unary',
339
373
  op: 'NOT',
340
374
  argument,
375
+ positionStart,
376
+ positionEnd: argument.positionEnd,
341
377
  }
342
378
  }
343
379
  return parseComparison(state)
@@ -359,6 +395,8 @@ export function parseAdditive(state) {
359
395
  op: tok.value,
360
396
  left: node,
361
397
  right,
398
+ positionStart: node.positionStart,
399
+ positionEnd: right.positionEnd,
362
400
  }
363
401
  } else {
364
402
  break
@@ -383,6 +421,8 @@ function parseMultiplicative(state) {
383
421
  op: tok.value,
384
422
  left: node,
385
423
  right,
424
+ positionStart: node.positionStart,
425
+ positionEnd: right.positionEnd,
386
426
  }
387
427
  } else {
388
428
  break
@@ -1,4 +1,4 @@
1
- import { syntaxError } from '../errors.js'
1
+ import { syntaxError } from '../parseErrors.js'
2
2
 
3
3
  /**
4
4
  * @import { ParserState, Token, TokenType } from '../types.js'
@@ -31,12 +31,22 @@ export function peekToken(state, offset) {
31
31
  */
32
32
  export function consume(state) {
33
33
  const tok = current(state)
34
+ state.lastPos = tok.positionEnd
34
35
  if (state.pos < state.tokens.length - 1) {
35
36
  state.pos += 1
36
37
  }
37
38
  return tok
38
39
  }
39
40
 
41
+ /**
42
+ * Gets the position after the last consumed token.
43
+ * @param {ParserState} state
44
+ * @returns {number}
45
+ */
46
+ export function lastPosition(state) {
47
+ return state.lastPos ?? 0
48
+ }
49
+
40
50
  /**
41
51
  * @param {ParserState} state
42
52
  * @param {TokenType} type
@@ -83,12 +93,12 @@ export function expectIdentifier(state) {
83
93
  * Helper function to create consistent parser error messages.
84
94
  * @param {ParserState} state
85
95
  * @param {string} expected - Description of what was expected
86
- * @returns {Error}
96
+ * @returns {import('../parseErrors.js').ParseError}
87
97
  */
88
98
  export function parseError(state, expected) {
89
99
  const tok = current(state)
90
100
  const prevToken = state.tokens[state.pos - 1]
91
101
  const after = prevToken ? prevToken.originalValue ?? prevToken.value : undefined
92
102
  const received = tok.type === 'eof' ? 'end of query' : `"${tok.originalValue ?? tok.value}"`
93
- return syntaxError({ expected, received, position: tok.position, after })
103
+ return syntaxError({ expected, received, positionStart: tok.positionStart, positionEnd: tok.positionEnd, after })
94
104
  }
@@ -2,7 +2,7 @@ import {
2
2
  invalidLiteralError,
3
3
  unexpectedCharError,
4
4
  unterminatedError,
5
- } from '../errors.js'
5
+ } from '../parseErrors.js'
6
6
 
7
7
  /**
8
8
  * @import { Token } from '../types.d.ts'
@@ -117,24 +117,26 @@ export function tokenize(sql) {
117
117
  return {
118
118
  type: 'number',
119
119
  value: text,
120
- position: startPos,
120
+ positionStart: startPos,
121
+ positionEnd: i,
121
122
  numericValue: BigInt(text.slice(0, -1)),
122
123
  }
123
124
  } catch {
124
- throw invalidLiteralError({ type: 'bigint', value: text.slice(0, -1), position: startPos })
125
+ throw invalidLiteralError({ type: 'bigint', value: text.slice(0, -1), positionStart: startPos, positionEnd: i })
125
126
  }
126
127
  }
127
128
  if (isAlpha(peek())) {
128
- throw invalidLiteralError({ type: 'number', value: text + peek(), position: startPos })
129
+ throw invalidLiteralError({ type: 'number', value: text + peek(), positionStart: startPos, positionEnd: i + 1 })
129
130
  }
130
131
  const num = parseFloat(text)
131
132
  if (isNaN(num)) {
132
- throw invalidLiteralError({ type: 'number', value: text, position: startPos })
133
+ throw invalidLiteralError({ type: 'number', value: text, positionStart: startPos, positionEnd: i })
133
134
  }
134
135
  return {
135
136
  type: 'number',
136
137
  value: text,
137
- position: startPos,
138
+ positionStart: startPos,
139
+ positionEnd: i,
138
140
  numericValue: num,
139
141
  }
140
142
  }
@@ -204,13 +206,15 @@ export function tokenize(sql) {
204
206
  type: 'keyword',
205
207
  value: upper,
206
208
  originalValue: text,
207
- position: pos,
209
+ positionStart: pos,
210
+ positionEnd: i,
208
211
  })
209
212
  } else {
210
213
  tokens.push({
211
214
  type: 'identifier',
212
215
  value: text,
213
- position: pos,
216
+ positionStart: pos,
217
+ positionEnd: i,
214
218
  })
215
219
  }
216
220
  continue
@@ -222,7 +226,7 @@ export function tokenize(sql) {
222
226
  let text = ''
223
227
  while (i <= length) {
224
228
  if (i === length) {
225
- throw unterminatedError('string', pos)
229
+ throw unterminatedError('string', pos, length)
226
230
  }
227
231
  const c = nextChar()
228
232
  if (c === quote) {
@@ -239,7 +243,8 @@ export function tokenize(sql) {
239
243
  tokens.push({
240
244
  type: 'string',
241
245
  value: text,
242
- position: pos,
246
+ positionStart: pos,
247
+ positionEnd: i,
243
248
  })
244
249
  continue
245
250
  }
@@ -250,7 +255,7 @@ export function tokenize(sql) {
250
255
  let text = ''
251
256
  while (i <= length) {
252
257
  if (i === length) {
253
- throw unterminatedError('identifier', pos)
258
+ throw unterminatedError('identifier', pos, length)
254
259
  }
255
260
  const c = nextChar()
256
261
  if (c === quote) {
@@ -267,7 +272,8 @@ export function tokenize(sql) {
267
272
  tokens.push({
268
273
  type: 'identifier',
269
274
  value: text,
270
- position: pos,
275
+ positionStart: pos,
276
+ positionEnd: i,
271
277
  })
272
278
  continue
273
279
  }
@@ -283,7 +289,8 @@ export function tokenize(sql) {
283
289
  tokens.push({
284
290
  type: 'operator',
285
291
  value: op,
286
- position: pos,
292
+ positionStart: pos,
293
+ positionEnd: i,
287
294
  })
288
295
  continue
289
296
  }
@@ -294,7 +301,8 @@ export function tokenize(sql) {
294
301
  tokens.push({
295
302
  type: 'operator',
296
303
  value: ch,
297
- position: pos,
304
+ positionStart: pos,
305
+ positionEnd: i,
298
306
  })
299
307
  continue
300
308
  }
@@ -304,7 +312,8 @@ export function tokenize(sql) {
304
312
  tokens.push({
305
313
  type: 'comma',
306
314
  value: ',',
307
- position: pos,
315
+ positionStart: pos,
316
+ positionEnd: i,
308
317
  })
309
318
  continue
310
319
  }
@@ -314,7 +323,8 @@ export function tokenize(sql) {
314
323
  tokens.push({
315
324
  type: 'dot',
316
325
  value: '.',
317
- position: pos,
326
+ positionStart: pos,
327
+ positionEnd: i,
318
328
  })
319
329
  continue
320
330
  }
@@ -324,7 +334,8 @@ export function tokenize(sql) {
324
334
  tokens.push({
325
335
  type: 'paren',
326
336
  value: ch,
327
- position: pos,
337
+ positionStart: pos,
338
+ positionEnd: i,
328
339
  })
329
340
  continue
330
341
  }
@@ -334,21 +345,23 @@ export function tokenize(sql) {
334
345
  tokens.push({
335
346
  type: 'semicolon',
336
347
  value: ';',
337
- position: pos,
348
+ positionStart: pos,
349
+ positionEnd: i,
338
350
  })
339
351
  continue
340
352
  }
341
353
 
342
354
  if (tokens.length === 0) {
343
- throw unexpectedCharError(ch, pos, true)
355
+ throw unexpectedCharError({ char: ch, positionStart: pos, expectsSelect: true })
344
356
  }
345
- throw unexpectedCharError(ch, pos)
357
+ throw unexpectedCharError({ char: ch, positionStart: pos })
346
358
  }
347
359
 
348
360
  tokens.push({
349
361
  type: 'eof',
350
362
  value: '',
351
- position: length,
363
+ positionStart: length,
364
+ positionEnd: length,
352
365
  })
353
366
 
354
367
  return tokens