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

DanielXMoore / Civet / 23913590751

02 Apr 2026 05:36PM UTC coverage: 92.941% (+1.4%) from 91.513%
23913590751

push

github

web-flow
Using Civet in parser.hera (#1876)

* Using Civet in parser.hera

---------

Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>

5924 of 6293 branches covered (94.14%)

Branch coverage included in aggregate %.

156 of 158 new or added lines in 2 files covered. (98.73%)

236 existing lines in 3 files now uncovered.

24991 of 26970 relevant lines covered (92.66%)

36762.26 hits per line

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

94.52
/source/sourcemap.civet
1
/** A source map entry from the spec with deltas for all fields */
1✔
2
export type SourceMapEntry =
1✔
3
| [generatedColumnDelta: number, sourceFileDelta: number, sourceLineDelta: number, sourceColumnDelta: number, sourceNameDelta: number]
1✔
4
| [generatedColumnDelta: number, sourceFileDelta: number, sourceLineDelta: number, sourceColumnDelta: number]
1✔
5
| [generatedColumnDelta: number]
1✔
6

1✔
7
/** A source map entry with absolute source lines and columns, the other fields are still deltas */
1✔
8
export type ResolvedSourceMapEntry =
1✔
9
| [generatedColumnDelta: number, sourceFileDelta: number, sourceLine: number, sourceColumn: number, sourceNameDelta: number]
1✔
10
| [generatedColumnDelta: number, sourceFileDelta: number, sourceLine: number, sourceColumn: number]
1✔
11
| [generatedColumnDelta: number]
1✔
12

1✔
13
export type SourceMapLine = ResolvedSourceMapEntry[]
1✔
14
export type SourceMapLines = SourceMapLine[]
1✔
15

1✔
16
export type SourceMapJSON =
1✔
17
  /** The version of the source map format */
1✔
18
  version: 3
1✔
19
  /** The name of the output file. */
1✔
20
  file: string
1✔
21
  /** The list of source files. */
1✔
22
  sources: string[]
1✔
23
  /** The mappings between generated code and source files. */
1✔
24
  mappings: string
1✔
25
  /** The list of names. */
1✔
26
  names: string[]
1✔
27
  /** The list of source contents. */
1✔
28
  sourcesContent: string[]
1✔
29

1✔
30
// Utility function to create a line/column lookup table for an input string
1✔
31
export function locationTable(input: string): number[]
1✔
32
  linesRe := /([^\r\n]*)(\r\n|\r|\n|$)/y
87✔
33
  lines := []
87✔
34
  line .= 0
87✔
35
  pos .= 0
87✔
36

87✔
37
  while result := linesRe.exec(input)
87✔
38
    pos += result[0].length
488✔
39
    lines[line++] = pos
488✔
40

488✔
41
    break if pos is input.length
488✔
42

87✔
43
  return lines
87✔
44

1✔
45
export function lookupLineColumn(table: number[], pos: number)
1✔
46
  l .= 0
3,853✔
47
  prevEnd .= 0
3,853✔
48

3,853✔
49
  while table[l] <= pos
3,853✔
50
    prevEnd = table[l++]
446,989✔
51

3,853✔
52
  // [line, column]; zero based
3,853✔
53
  return [l, pos - prevEnd]
3,853✔
54

1✔
55
EOL := /\r?\n|\r/
1✔
56
export class SourceMap
1✔
57
  lines: SourceMapLines
1✔
58
  line: number
85✔
59
  colOffset: number  // relative to previous entry
85✔
60
  srcLine: number
85✔
61
  srcColumn: number
85✔
62
  srcTable: number[]
85✔
63

85✔
64
  @(@source: string)
1✔
65
    @lines = [[]]
85✔
66
    @line = 0
85✔
67
    @colOffset = 0 // relative to previous entry
85✔
68
    @srcLine = 0
85✔
69
    @srcColumn = 0
85✔
70
    @srcTable = locationTable @source
85✔
71

1✔
72
  renderMappings(): string
5✔
73
    lastSourceLine .= 0
5✔
74
    lastSourceColumn .= 0
5✔
75

5✔
76
    for each line of @lines
5✔
77
      for each entry of line
5✔
78
        if entry.length is 4
96✔
79
          [colDelta, sourceFileIndex, srcLine, srcCol] .= entry
83✔
80
          lineDelta := srcLine - lastSourceLine
83✔
81
          colDelta = srcCol - lastSourceColumn
83✔
82
          lastSourceLine = srcLine
83✔
83
          lastSourceColumn = srcCol
83✔
84
          `${encodeVlq(entry[0])}${encodeVlq(sourceFileIndex)}${encodeVlq(lineDelta)}${encodeVlq(colDelta)}`
83✔
85
        else
13✔
86
          encodeVlq entry[0]
13✔
87
      .join(",")
5✔
88
    .join(";")
5✔
89

1✔
90
  json(srcFileName: string, outFileName: string)
5✔
91
    version: 3
5✔
92
    file: outFileName
5✔
93
    sources: [srcFileName]
5✔
94
    mappings: @renderMappings()
5✔
95
    names: []
5✔
96
    sourcesContent: [@source]
5✔
97
    toString: ->
5✔
98
      JSON.stringify this
1✔
99

1✔
100
  /** Generate a comment with the source mapping URL. */
1✔
101
  comment(srcFileName: string, outFileName: string)
3✔
102
    // NOTE: be sure to keep comment split up so as not to trigger tools from confusing it with the actual sourceMappingURL
3✔
103
    `//${'#'} sourceMappingURL=data:application/json;base64,${base64Encode JSON.stringify(@json(srcFileName, outFileName))}`
3✔
104

1✔
105
  updateSourceMap(outputStr: string, inputPos?: number, colOffset=0)
4,395✔
106
    outLines := outputStr.split(EOL)
4,395✔
107

4,395✔
108
    let srcLine: number, srcCol: number
4,395✔
109

4,395✔
110
    if inputPos?
4,395✔
111
      [srcLine, srcCol] = lookupLineColumn @srcTable, inputPos
3,847✔
112
      srcCol += colOffset
3,847✔
113
      @srcLine = srcLine
3,847✔
114
      @srcColumn = srcCol
3,847✔
115

4,395✔
116
    for each line, i of outLines
4,395✔
117
      if i > 0
4,923✔
118
        @line++
528✔
119
        @srcLine++
528✔
120
        @colOffset = 0
528✔
121
        @lines[@line] = []
528✔
122
        @srcColumn = srcCol = colOffset
528✔
123

4,923✔
124
      l := @colOffset
4,923✔
125
      @colOffset = line.length
4,923✔
126
      @srcColumn += line.length
4,923✔
127

4,923✔
128
      if inputPos?
4,923✔
129
        // srcLine and srcCol are absolute here
4,238✔
130
        @lines[@line].push [l, 0, srcLine!+i, srcCol!]
4,238✔
131
      else if l != 0
685✔
132
        @lines[@line].push [l]
427✔
133

4,395✔
134
    return
4,395✔
135

1✔
136
  /**
1✔
137
  Remap a string with compiled code and a source map to use a new source map
1✔
138
  referencing upstream source files.
1✔
139
  This modifies the upstream map in place.
1✔
140
  */
1✔
141
  @remap := (codeWithSourceMap: string, upstreamMap: SourceMap, sourcePath: string, targetPath: string) =>
1✔
142
    let sourceMapText?: string
1✔
143
    codeWithoutSourceMap := codeWithSourceMap.replace smRegexp, (_match, sm) =>
1✔
144
      sourceMapText = sm
1✔
145
      ""
1✔
146

1✔
147
    if sourceMapText
1✔
148
      parsed := @parseWithLines sourceMapText
1✔
149
      composedLines := @composeLines upstreamMap.lines, parsed.lines
1✔
150
      upstreamMap.lines = composedLines
1✔
151

1✔
152
    remappedCodeWithSourceMap := `${codeWithoutSourceMap}\n${upstreamMap.comment(sourcePath, targetPath)}`
1✔
153
    return remappedCodeWithSourceMap
1✔
154

4✔
155
  /**
4✔
156
  Compose lines from an upstream source map with lines from a downstream source map.
4✔
157
  */
4✔
158
  @composeLines := (upstreamMapping: SourceMapLines, lines: SourceMapLines): SourceMapLines =>
4✔
159
    lines.map (line) =>
1✔
160
      line.map (entry) =>
2✔
161
        if entry.length is 1
3✔
162
          return entry
×
163

3✔
164
        [colDelta, sourceFileIndex, srcLine, srcCol] := entry
3✔
165
        srcPos := remapPosition [srcLine, srcCol], upstreamMapping
3✔
166

3✔
167
        if !srcPos
3✔
UNCOV
168
          return [entry[0]]
×
169

3✔
170
        [ upstreamLine, upstreamCol ] := srcPos
3✔
171

3✔
172
        if entry.length is 4
3✔
173
          return [colDelta, sourceFileIndex, upstreamLine, upstreamCol]
3✔
174

×
175
        // length is 5
×
176
        return [colDelta, sourceFileIndex, upstreamLine, upstreamCol, entry[4]]
×
177

4✔
178
  /**
4✔
179
  Parse a base64 encoded source map string into a SourceMapJSON object with lines.
4✔
180
  */
4✔
181
  @parseWithLines := (base64encodedJSONstr: string) =>
4✔
182
    json: SourceMapJSON := JSON.parse Buffer.from(base64encodedJSONstr, "base64").toString("utf8")
3✔
183
    sourceLine .= 0
3✔
184
    sourceColumn .= 0
3✔
185

3✔
186
    lines: SourceMapLines := json.mappings.split(";").map (line) =>
3✔
187
      if line.length is 0
8✔
188
        return []
1✔
189

7✔
190
      line.split(",").map (entry) =>
7✔
191
        result := decodeVLQ entry
40✔
192

40✔
193
        switch result.length
40✔
194
          when 1
40✔
195
          when 4, 5
40✔
196
            // convert deltas to absolute values
39✔
197
            sourceLine += result[2]
39✔
198
            result[2] = sourceLine
39✔
199
            sourceColumn += result[3]
39✔
200
            result[3] = sourceColumn
39✔
201
          else
40!
202
            throw new Error(`Unknown source map entry ${JSON.stringify(result)}`)
×
203

40✔
204
        result
40✔
205

3✔
206
    return { ...json, lines }
3✔
207

1✔
208
smRegexp := /(?:\r?\n|\r)\/\/# sourceMappingURL=data:application\/json;(?:charset=[^;]*;)?base64,([+a-zA-Z0-9\/]*=?=?)(?:\s*)$/
1✔
209

1✔
210
/* c8 ignore start */
1✔
211
// write a formatted error message to the console displaying the source code with line numbers
1✔
212
// and the error underlined
1✔
213
//@ts-expect-error
1✔
214
prettySourceExcerpt := (source: string, location: {line: number, column: number}, length: number) ->
1✔
215
  lines := source.split(/\r?\n|\r/)
1✔
216
  lineNum := location.line
1✔
217
  colNum := location.column
1✔
218

1✔
219
  // print the source code above and below the error location with line numbers and underline from location to length
1✔
220
  for i of [lineNum - 2 .. lineNum + 2]
1✔
221
    continue unless 0 <= i < lines.length
1✔
222

1✔
223
    line := lines[i]
1✔
224
    lineNumStr .= (i + 1).toString()
1✔
225
    lineNumStr = " " + lineNumStr while lineNumStr.length < 4
1✔
226

1✔
227
    if i is lineNum
1✔
228
      console.log `${lineNumStr}: ${line}`
1✔
229
      console.log " ".repeat(lineNumStr.length + 2 + colNum) + "^".repeat(length)
1✔
230
    else
1✔
231
      console.log `${lineNumStr}: ${line}`
1✔
232

1✔
233
  return
1✔
234
/* c8 ignore stop */
1✔
235

1✔
236
VLQ_SHIFT            := 5
1✔
237
VLQ_CONTINUATION_BIT := 1 << VLQ_SHIFT             // 0010 0000
1✔
238
VLQ_VALUE_MASK       := VLQ_CONTINUATION_BIT - 1   // 0001 1111
1✔
239

1✔
240
encodeVlq := (value: number) ->
1✔
241
  answer .= ''
345✔
242

345✔
243
  // Least significant bit represents the sign.
345✔
244
  signBit := if value < 0 then 1 else 0
345✔
245

345✔
246
  // The next bits are the actual value.
345✔
247
  valueToEncode .= (Math.abs(value) << 1) + signBit
345✔
248

345✔
249
  // Make sure we encode at least one character, even if valueToEncode is 0.
345✔
250
  while valueToEncode or !answer
345✔
251
    nextChunk .= valueToEncode & VLQ_VALUE_MASK
358✔
252
    valueToEncode = valueToEncode >> VLQ_SHIFT
358✔
253
    nextChunk |= VLQ_CONTINUATION_BIT if valueToEncode
358✔
254
    answer += BASE64_CHARS[nextChunk]
358✔
255

345✔
256
  return answer
345✔
257

1✔
258
BASE64_CHARS := 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/'
1✔
259

1✔
260
export base64Encode = (src: string) ->
1✔
261
  if Buffer !<? 'undefined'
4✔
262
    Buffer.from(src).toString('base64')
4✔
263
  else
×
264
    bytes := new TextEncoder().encode(src)
×
265
    binaryString := String.fromCodePoint(...bytes)
×
266
    btoa(binaryString)
×
267

1✔
268
// Accelerate VLQ decoding with a lookup table
1✔
269
vlqTable := new Uint8Array(128)
1✔
270
vlqChars := 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/'
1✔
271

1✔
272
do
1✔
273
  i .= 0
1✔
274
  l .= vlqTable.length
1✔
275
  while i < l
1✔
276
    vlqTable[i] = 0xFF
512✔
277
    i++
512✔
278
  i = 0
1✔
279
  l = vlqChars.length
1✔
280
  while i < l
1✔
281
    vlqTable[vlqChars.charCodeAt(i)] = i
256✔
282
    i++
256✔
283

1✔
284
decodeError := (message: string) ->
1✔
285
  throw new Error(message)
4✔
286

1✔
287
// reference: https://github.com/evanw/source-map-visualization/blob/gh-pages/code.js#L199
1✔
288
export decodeVLQ := (mapping: string): SourceMapEntry =>
1✔
289
  i .= 0
48✔
290
  l .= mapping.length
48✔
291
  result .= []
48✔
292

48✔
293
  // Scan over the input
48✔
294
  while i < l
48✔
295
    shift .= 0
169✔
296
    vlq .= 0
169✔
297
    v .= 0
169✔
298

169✔
299
    while true
169✔
300
      if i >= l
171✔
301
        decodeError 'Unexpected early end of mapping data'
1✔
302
      // Read a byte
170✔
303
      c := mapping.charCodeAt(i)
170✔
304
      if (c & 0x7F) != c
170✔
305
        decodeError `Invalid mapping character: ${JSON.stringify(String.fromCharCode(c))}`
2✔
306
      index := vlqTable[c & 0x7F]
168✔
307
      if (index is 0xFF)
168✔
308
        decodeError `Invalid mapping character: ${JSON.stringify(String.fromCharCode(c))}`
1✔
309
      i++
167✔
310

167✔
311
      // Decode the byte
167✔
312
      vlq |= (index & 31) << shift
167✔
313
      shift += 5
167✔
314

167✔
315
      // Stop if there's no continuation bit
167✔
316
      break if (index & 32) is 0
171✔
317

165✔
318
    // Recover the signed value
165✔
319
    if vlq & 1
165✔
320
      v = -(vlq >> 1)
6✔
321
    else
159✔
322
      v = vlq >> 1
159✔
323

165✔
324
    result.push v
165✔
325

44✔
326
  return result as SourceMapEntry
44✔
327

1✔
328
/**
1✔
329
Take a position in generated code and map it into a position in source code.
1✔
330
Reverse mapping.
1✔
331

1✔
332
Returns undefined if there is not an exact match
1✔
333
*/
1✔
334
remapPosition := (position: [number, number], sourcemapLines: SourceMapLines) =>
1✔
335
  [ line, character ] := position
3✔
336

3✔
337
  textLine := sourcemapLines[line]
3✔
338
  // Return undefined if no mapping at this line
3✔
339
  if (!textLine?.length)
3✔
340
    return undefined
×
341

3✔
342
  i .= 0
3✔
343
  p .= 0
3✔
344
  l := textLine.length
3✔
345
  lastMapping .= undefined
3✔
346
  lastMappingPosition .= 0
3✔
347

3✔
348
  while i < l
3✔
349
    mapping := textLine[i]
12✔
350
    p += mapping[0]
12✔
351

12✔
352
    if mapping.length is 4
12✔
353
      lastMapping = mapping
12✔
354
      lastMappingPosition = p
12✔
355

12✔
356
    if p >= character
12✔
357
      break
3✔
358

9✔
359
    i++
9✔
360

3✔
361
  if character - lastMappingPosition != 0
3✔
UNCOV
362
    return undefined
×
363

3✔
364
  if lastMapping
3✔
365
    [lastMapping[2], lastMapping[3]]
3✔
366

×
367
  else
×
368
    // console.error("no mapping for ", position)
×
369
    return undefined
×
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE TRIAL · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc