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 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 | 6x 6x 5x 5x 6x 5x 5x 6x 5x 6x 6x 6x 6x 6x 6x 6x 6x 6x 6x 4x 4x 6x 4x 6x 6x 6x 6x 6x 6x 6x 3x 3x 6x 1x 1x 6x 1x 6x 6x 6x 6x 6x 6x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 6x 6x 6x 6x 6x 6x 6x | import { getColumn, getLine, Token } from "lib/env/lexer";
import { NewlineType } from "lib/options";
export interface FileCoordinates {
line: number
column: number
position: number // TODO get rid of?
}
// Ts-jest doesn't support extending Error, so instead we have to implement it.
// Otherwise, we get this error thrown in our tests when trying to call
// setters on subclasses of Error: "(intermediate value).setToken is not a function"
//
// This is only an issue with ts-jest, but works fine in the code compiled by esbuild.
export abstract class FileError implements Error {
public readonly name: string = ''
public readonly message: string = ''
protected filePath: string
public setFilePath(filePath: string): this {
this.filePath = filePath
return this
}
public getFilePath(): string {
return this.filePath
}
}
export class FileNotFoundError extends FileError {}
export class LexicalError extends FileError {
protected char: string
protected coordinates: FileCoordinates
public setChar(char: string): this {
this.char = char
return this
}
public getChar(): string {
return this.char
}
public setCoordinates(coordinates: FileCoordinates): this {
this.coordinates = coordinates
return this
}
public getCoordinates(): FileCoordinates {
return this.coordinates
}
}
export abstract class ParseError extends FileError {
protected token: Token
public setToken(token: Token): this {
this.token = token
return this
}
public getToken(): Token {
return this.token
}
}
export class InvalidTokenAfterCommentError extends ParseError {}
export class ExpectedAssignmentAfterIdentifierError extends ParseError {}
export class UnexpectedTokenError extends ParseError {}
export class InvalidTokenAfterIdentifierError extends ParseError {}
export class DuplicateVariableError extends ParseError {}
export class InvalidArgumentError implements Error {
public readonly name: string = ''
public readonly message: string = ''
// TODO remove this and use "name"
protected argumentName: string
public setArgumentName(argumentName: string): this {
this.argumentName = argumentName
return this
}
public getArgumentName(): string {
return this.argumentName
}
}
export class InvalidNewlineTypeError extends InvalidArgumentError {}
export class MissingArgumentValueError implements Error {
public name: string = ''
public readonly message: string = ''
public setName (name: string): this {
this.name = name
return this
}
}
export const getMessageForError = (error: Error): string => {
const fileNotFound = error instanceof FileNotFoundError
Iif (fileNotFound) return getMessageForFileNotFoundError(error as FileNotFoundError)
const isLexicalError = error instanceof LexicalError
Iif (isLexicalError) return getMessageForLexicalError(error as LexicalError)
const isParseError = error instanceof ParseError
Iif (isParseError) return getMessageForParseError(error as ParseError)
const isInvalidArgumentError = error instanceof InvalidArgumentError
Iif (isInvalidArgumentError) return getMessageForInvalidArgumentError(error as InvalidArgumentError)
const isMissingArgumentValueError = error instanceof MissingArgumentValueError
Iif (isMissingArgumentValueError) return getMessageForMissingArgumentValueError(error as MissingArgumentValueError)
return `ERROR: ${(error).message}`
}
const getMessageForFileNotFoundError = (error: FileNotFoundError): string => {
const filePath = error.getFilePath()
return `Could not locate ${filePath}`
}
const getMessageForLexicalError = (error: LexicalError): string => {
const filePath = error.getFilePath()
const coordinates = error.getCoordinates()
const positionDescription = getPositionDescription(filePath, coordinates)
return `Unrecognized token ${positionDescription}`
}
const getMessageForParseError = (error: ParseError): string => {
const token = error.getToken()
const filePath = error.getFilePath()
const positionDescription = getPositionDescription(filePath, token)
const invalidTokenAfterComment = error instanceof InvalidTokenAfterCommentError
if (invalidTokenAfterComment) return `Expected newline or end of document after comment ${positionDescription}`
const expectedAssignmentAfterIdentifier = error instanceof ExpectedAssignmentAfterIdentifierError
if (expectedAssignmentAfterIdentifier) return `Expected = after variable "${token.value}" ${positionDescription}`
const unexpectedToken = error instanceof UnexpectedTokenError
if (unexpectedToken) return `Unexpected ${token.value} ${positionDescription}`
const invalidTokenAfterIdentifier = error instanceof InvalidTokenAfterIdentifierError
if (invalidTokenAfterIdentifier) return `Expected line break or comment after variable declaration ${positionDescription}`
const duplicateVariable = error instanceof DuplicateVariableError
if (duplicateVariable) {
// TODO put this var in other IFs?
const positionDescription = getPositionDescription(filePath, token)
return `Duplicate variable declaration "${token.value}" ${positionDescription}`
}
return `Unknown parse error ${positionDescription}`
}
const getMessageForInvalidArgumentError = (error: InvalidArgumentError) => {
const isInvalidNewlineTypeError = error instanceof InvalidNewlineTypeError
if (isInvalidNewlineTypeError) {
const validTypes = Object.values(NewlineType)
return `Invalid newline type. Valid types: "${validTypes.join('", "')}"`
}
const name = error.getArgumentName()
return `Invalid argument ${name}`
}
const sanitizeArgumentName = (name: string): string => name.split('').filter(char => /^[-a-zA-Z]$/.test(char)).join('')
const getMessageForMissingArgumentValueError = ({ name }: MissingArgumentValueError): string =>
`Missing value for argument ${sanitizeArgumentName(name)}`
const getPositionDescription = (filePath: string, { line, column }: FileCoordinates): string =>
`at ${filePath}:${line}:${column}`
|