Skip to content

Commit

Permalink
make errors i18n-able #35
Browse files Browse the repository at this point in the history
  • Loading branch information
cshaa committed Sep 18, 2021
1 parent 4484d1b commit c383e46
Show file tree
Hide file tree
Showing 3 changed files with 112 additions and 14 deletions.
95 changes: 95 additions & 0 deletions src/errors.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
/**
* Runtime error – user attempted to call a function
* which is not a predefined function, nor specified
* in `options.extraFunctions`.
*
* @prop {string} functionName
* @prop {string} I18N_STRING has the value `'UNKNOWN_FUNCTION'`
*/
export class UnknownFunctionError
extends ReferenceError {
I18N_STRING = 'UNKNOWN_FUNCTION'

constructor (funcName) {
super(`Unknown function: ${funcName}()`)
this.functionName = funcName
}
}

/**
* Runtime error – user attempted to access a property which
* is not present in the `data` object, nor in the `constants`.
* If the property is meant to be empty, use `undefined` or
* `null` as its value. If you need to use optional properties
* in your `data`, define a `customProp` function.
*
* @prop {string} propertyName
* @prop {string} I18N_STRING has the value `'UNKNOWN_PROPERTY'`
*/
export class UnknownPropertyError
extends ReferenceError {
I18N_STRING = 'UNKNOWN_PROPERTY'

constructor (propName) {
super(`Property “${propName}” does not exist.`)
this.propertyName = propName
}
}

/**
* Compile time error – you specified an option which
* was not recognized by Filtrex. Double-check your
* spelling and the version of Filtrex you are using.
*
* @prop {string} keyName
* @prop {string} I18N_STRING has the value `'UNKNOWN_OPTION'`
*/
export class UnknownOptionError
extends TypeError {
I18N_STRING = 'UNKNOWN_OPTION'

constructor (key) {
super(`Unknown option: ${key}`)
this.keyName = key
}
}

/**
* Runtime error – user passed a different type than the one
* accepted by the function or operator.
*
* The possible values of `expectedType` and `recievedType`
* are: `"undefined"`, `"null"`, `"true"`, `"false"`, `"number"`,
* `"text"`, `"unknown type"`, `"list"`, `"object"`, `"text or number"`
* and `"logical value (“true” or “false”)"`
*
* @prop {string} expectedType
* @prop {string} recievedType
* @prop {string} I18N_STRING has the value `'UNEXPECTED_TYPE'`
*/
export class UnexpectedTypeError
extends TypeError {
I18N_STRING = 'UNEXPECTED_TYPE'

constructor (expected, got) {
super(`Expected a ${expected}, but got a ${got} instead.`)

this.expectedType = expected
this.recievedType = got
}
}

/**
* An internal error. This was not meant to happen, please report
* at https://github.com/m93a/filtrex/
*
* @prop {string} I18N_STRING has the value `'INTERNAL'`
*/
export class InternalError
extends Error {
I18N_STRING = 'INTERNAL'

constructor (message) {
super(message)
}
}
19 changes: 10 additions & 9 deletions src/filtrex.mjs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
// the parser is dynamically generated from generateParser.js at compile time
import { parser } from './parser.mjs'
import { hasOwnProperty, bool, num, numstr, mod, arr, str, flatten, code } from './utils.mjs'
import { UnknownFunctionError, UnknownPropertyError, UnknownOptionError, InternalError } from './errors.mjs'

// Shared utility functions
const std =
Expand All @@ -11,7 +12,7 @@ const std =
},

unknown(funcName) {
throw new ReferenceError('Unknown function: ' + funcName + '()')
throw new UnknownFunctionError(funcName)
},

coerceArray: arr,
Expand All @@ -32,22 +33,22 @@ const std =
let built = ''

if (literal[0] !== quote || literal[literal.length-1] !== quote)
throw new Error(`Unexpected internal error: String literal doesn't begin/end with the right quotation mark.`)
throw new InternalError(`Unexpected internal error: String literal doesn't begin/end with the right quotation mark.`)

for (let i = 1; i < literal.length - 1; i++)
{
if (literal[i] === "\\")
{
i++;
if (i >= literal.length - 1) throw new Error(`Unexpected internal error: Unescaped backslash at the end of string literal.`)
if (i >= literal.length - 1) throw new InternalError(`Unexpected internal error: Unescaped backslash at the end of string literal.`)

if (literal[i] === "\\") built += '\\'
else if (literal[i] === quote) built += quote
else throw new Error(`Unexpected internal error: Invalid escaped character in string literal: ${literal[i]}`)
else throw new InternalError(`Unexpected internal error: Invalid escaped character in string literal: ${literal[i]}`)
}
else if (literal[i] === quote)
{
throw new Error(`Unexpected internal error: String literal contains unescaped quotation mark.`)
throw new InternalError(`Unexpected internal error: String literal contains unescaped quotation mark.`)
}
else
{
Expand Down Expand Up @@ -103,7 +104,7 @@ export function compileExpression(expression, options) {
for (const key of Object.keys(options))
{
if (!(["extraFunctions", "customProp", "operators"].includes(key)))
throw new TypeError(`Unknown option: ${key}`)
throw new UnknownOptionError(key)
}


Expand Down Expand Up @@ -174,15 +175,15 @@ export function compileExpression(expression, options) {
if (hasOwnProperty(obj||{}, name))
return obj[name]

throw new ReferenceError(`Property “${name}” does not exist.`)
throw new UnknownPropertyError(name)
}

function safeGetter(obj) {
return function get(name) {
if (hasOwnProperty(obj||{}, name))
return obj[name]

throw new ReferenceError(`Property “${name}” does not exist.`)
throw new UnknownPropertyError(name)
}
}

Expand All @@ -195,7 +196,7 @@ export function compileExpression(expression, options) {
if (hasOwnProperty(fns, name) && typeof fns[name] === "function")
return fns[name](...args)

throw new ReferenceError(`Unknown function: ${name}()`)
throw new UnknownFunctionError(name)
}
}

Expand Down
12 changes: 7 additions & 5 deletions src/utils.mjs
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
import { UnexpectedTypeError } from './errors.mjs'

/**
* Determines whether an object has a property with the specified name.
* @param {object} obj the object to be checked
Expand Down Expand Up @@ -77,33 +79,33 @@ export function num(value) {
value = unwrap(value)
if (typeof value === 'number') return value

throw new TypeError(`Expected a number, but got a ${prettyType(value)} instead.`)
throw new UnexpectedTypeError('number', prettyType(value))
}

export function str(value) {
value = unwrap(value)
if (typeof value === 'string') return value

throw new TypeError(`Expected a text, but got a ${prettyType(value)} instead.`)
throw new UnexpectedTypeError('text', prettyType(value))
}

export function numstr(value) {
value = unwrap(value)
if (typeof value === 'string' || typeof value === 'number') return value

throw new TypeError(`Expected a text or a number, but got a ${prettyType(value)} instead.`)
throw new UnexpectedTypeError('text or number', prettyType(value))
}

export function bool(value) {
value = unwrap(value)
if (typeof value === 'boolean') return value

throw new TypeError(`Expected a logical value (“true” or “false”), but got a ${prettyType(value)} instead.`)
throw new UnexpectedTypeError('logical value (“true” or “false”)', prettyType(value))
}

export function arr(value) {
if (value === undefined || value === null) {
throw new TypeError(`Expected a list, but got ${value} instead.`)
throw new UnexpectedTypeError('list', prettyType(value))
}

if (Array.isArray(value)) {
Expand Down

0 comments on commit c383e46

Please sign in to comment.