All files / src/compiler/utils compile_diagnostic.js

85.04% Statements 91/107
100% Branches 11/11
83.33% Functions 5/6
84.9% Lines 90/106

Press n or j to go to the next uncovered block, b, p or k for the previous block.

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 1072x 2x 2x 2x 2x 2x 2x 2x 9773x 9773x 9773x 2x 2x 2x 2x 2x 2x 1797x 1797x 1797x 1797x 1797x 1797x 1797x 1797x 7976x 7976x 7976x 1797x 1797x 1797x 1797x 6179x 1797x 1797x 1797x 2x 2x 2x 2x 2x 2x 2x 2x 2x 2x 2x 2x 2x 2x 2x 1802x 1802x 1802x 1802x 1802x 1802x 1802x 1802x 1802x 1802x 1802x 1802x 1799x 1799x 1802x 1802x 1797x 1797x 1797x 1797x 1797x 1797x 1797x 1802x 1802x 1802x                                 1802x 1802x 945x 945x 945x 945x 945x 945x 945x 945x 945x 945x 1802x  
/** @import { Location } from 'locate-character' */
import * as state from '../state.js';
 
const regex_tabs = /^\t+/;
 
/**
 * @param {string} str
 */
function tabs_to_spaces(str) {
	return str.replace(regex_tabs, (match) => match.split('\t').join('  '));
}
 
/**
 * @param {string} source
 * @param {number} line
 * @param {number} column
 */
function get_code_frame(source, line, column) {
	const lines = source.split('\n');
	const frame_start = Math.max(0, line - 2);
	const frame_end = Math.min(line + 3, lines.length);
	const digits = String(frame_end + 1).length;
	return lines
		.slice(frame_start, frame_end)
		.map((str, i) => {
			const is_error_line = frame_start + i === line;
			const line_num = String(i + frame_start + 1).padStart(digits, ' ');
			if (is_error_line) {
				const indicator =
					' '.repeat(digits + 2 + tabs_to_spaces(str.slice(0, column)).length) + '^';
				return `${line_num}: ${tabs_to_spaces(str)}\n${indicator}`;
			}
			return `${line_num}: ${tabs_to_spaces(str)}`;
		})
		.join('\n');
}
 
/**
 * @typedef {{
 * 	code: string;
 * 	message: string;
 * 	filename?: string;
 * 	start?: Location;
 * 	end?: Location;
 * 	position?: [number, number];
 * 	frame?: string;
 * }} ICompileDiagnostic
 */
 
/** @implements {ICompileDiagnostic} */
export class CompileDiagnostic {
	name = 'CompileDiagnostic';
 
	/**
	 * @param {string} code
	 * @param {string} message
	 * @param {[number, number] | undefined} position
	 */
	constructor(code, message, position) {
		this.code = code;
		this.message = message;
 
		if (state.filename) {
			this.filename = state.filename;
		}
 
		if (position) {
			this.position = position;
			this.start = state.locator(position[0]);
			this.end = state.locator(position[1]);
			if (this.start && this.end) {
				this.frame = get_code_frame(state.source, this.start.line - 1, this.end.column);
			}
		}
	}
 
	toString() {
		let out = `${this.code}: ${this.message}`;

		if (this.filename) {
			out += `\n${this.filename}`;

			if (this.start) {
				out += `:${this.start.line}:${this.start.column}`;
			}
		}

		if (this.frame) {
			out += `\n${this.frame}`;
		}

		return out;
	}
 
	toJSON() {
		return {
			code: this.code,
			message: this.message,
			filename: this.filename,
			start: this.start,
			end: this.end,
			position: this.position,
			frame: this.frame
		};
	}
}