Change implementation strategy of cashassembly, add scripts, comments support and tests
This commit is contained in:
@@ -1,16 +1,24 @@
|
||||
/* eslint-disable max-classes-per-file */
|
||||
import type { IdentifierResolutionType } from '@bitauth/libauth';
|
||||
|
||||
/**
|
||||
* Error thrown when a required variable is missing.
|
||||
*/
|
||||
export class CashAssemblyRequiredVariableMissingError extends Error {
|
||||
constructor(variableNames?: string[]) {
|
||||
/**
|
||||
* Variable names that were required but absent from the variables map.
|
||||
*/
|
||||
readonly variableNames: string[];
|
||||
|
||||
constructor(variableNames: string[] = []) {
|
||||
const defaultMessage = 'Missing required variable';
|
||||
if (variableNames !== undefined && variableNames.length > 0) {
|
||||
if (variableNames.length > 0) {
|
||||
super(`${defaultMessage}: variableNames [${variableNames.join(', ')}]`);
|
||||
} else {
|
||||
super(defaultMessage);
|
||||
}
|
||||
|
||||
this.variableNames = variableNames;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -24,6 +32,104 @@ export class CashAssemblyCompilationFailedError extends Error {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Error thrown when a quoted string inside `$()` never closes.
|
||||
*/
|
||||
export class CashAssemblyQuotedLiteralUnclosedError extends Error {
|
||||
/**
|
||||
* Index of the opening quote that never found a closer.
|
||||
*/
|
||||
readonly openingQuoteIndex: number;
|
||||
|
||||
/**
|
||||
* Quote character that opened the literal.
|
||||
*/
|
||||
readonly quoteCharacter: string;
|
||||
|
||||
/**
|
||||
* Text from the opening quote through the end of the scanned string.
|
||||
*/
|
||||
readonly unclosedText: string;
|
||||
|
||||
constructor(openingQuoteIndex: number, quoteCharacter: string, cashAssemblyText: string) {
|
||||
const unclosedText = cashAssemblyText.slice(openingQuoteIndex);
|
||||
const defaultMessage = 'Quoted literal in a CashAssembly evaluation is unclosed';
|
||||
const details = [
|
||||
`quoteCharacter ${JSON.stringify(quoteCharacter)}`,
|
||||
`openingQuoteIndex ${String(openingQuoteIndex)}`,
|
||||
`unclosedText ${JSON.stringify(unclosedText)}`,
|
||||
].join(', ');
|
||||
|
||||
super(`${defaultMessage}: ${details}`);
|
||||
|
||||
this.openingQuoteIndex = openingQuoteIndex;
|
||||
this.quoteCharacter = quoteCharacter;
|
||||
this.unclosedText = unclosedText;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Error thrown when a block comment inside `$()` never closes.
|
||||
*/
|
||||
export class CashAssemblyBlockCommentUnclosedError extends Error {
|
||||
/**
|
||||
* Index of the first slash of the block comment opener.
|
||||
*/
|
||||
readonly commentStartIndex: number;
|
||||
|
||||
/**
|
||||
* Text from the block comment opener through the end of the scanned string.
|
||||
*/
|
||||
readonly unclosedText: string;
|
||||
|
||||
constructor(commentStartIndex: number, cashAssemblyText: string) {
|
||||
const unclosedText = cashAssemblyText.slice(commentStartIndex);
|
||||
const defaultMessage = 'Block comment in a CashAssembly evaluation is unclosed';
|
||||
const details = [ `commentStartIndex ${String(commentStartIndex)}`, `unclosedText ${JSON.stringify(unclosedText)}` ].join(', ');
|
||||
|
||||
super(`${defaultMessage}: ${details}`);
|
||||
|
||||
this.commentStartIndex = commentStartIndex;
|
||||
this.unclosedText = unclosedText;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Error thrown when a `$()` evaluation never finds its matching closer.
|
||||
*/
|
||||
export class CashAssemblyEvaluationUnclosedError extends Error {
|
||||
/**
|
||||
* Index of the `$` that opened the evaluation.
|
||||
*/
|
||||
readonly evaluationStartIndex: number;
|
||||
|
||||
/**
|
||||
* How many `$()` remain open at the end of the string, including nested evaluations.
|
||||
*/
|
||||
readonly remainingOpenEvaluations: number;
|
||||
|
||||
/**
|
||||
* Text from the opening `$` through the end of the scanned string.
|
||||
*/
|
||||
readonly unclosedText: string;
|
||||
|
||||
constructor(evaluationStartIndex: number, remainingOpenEvaluations: number, cashAssemblyText: string) {
|
||||
const unclosedText = cashAssemblyText.slice(evaluationStartIndex);
|
||||
const defaultMessage = 'CashAssembly evaluation is unclosed';
|
||||
const details = [
|
||||
`evaluationStartIndex ${String(evaluationStartIndex)}`,
|
||||
`remainingOpenEvaluations ${String(remainingOpenEvaluations)}`,
|
||||
`unclosedText ${JSON.stringify(unclosedText)}`,
|
||||
].join(', ');
|
||||
|
||||
super(`${defaultMessage}: ${details}`);
|
||||
|
||||
this.evaluationStartIndex = evaluationStartIndex;
|
||||
this.remainingOpenEvaluations = remainingOpenEvaluations;
|
||||
this.unclosedText = unclosedText;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Error thrown when a variable's runtime type does not match the type required for compilation.
|
||||
*/
|
||||
@@ -74,6 +180,30 @@ export class CashAssemblyPrimitiveVariableMissingError extends Error {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Error thrown when one identifier would resolve as more than one {@link IdentifierResolutionType}.
|
||||
*/
|
||||
export class CashAssemblyIdentifierCollisionError extends Error {
|
||||
/**
|
||||
* Identifier that more than one resolution type would match.
|
||||
*/
|
||||
readonly identifier: string;
|
||||
|
||||
/**
|
||||
* The list of resolution types that the identifier matches.
|
||||
*/
|
||||
readonly resolutionTypes: IdentifierResolutionType[];
|
||||
|
||||
constructor(identifier: string, resolutionTypes: IdentifierResolutionType[]) {
|
||||
const defaultMessage = 'CashAssembly identifier exists for more than one resolution type';
|
||||
|
||||
super(`${defaultMessage}: identifier "${identifier}", resolutionTypes [${resolutionTypes.join(', ')}]`);
|
||||
|
||||
this.identifier = identifier;
|
||||
this.resolutionTypes = resolutionTypes;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Error thrown when compiled evaluation bytes cannot be decoded as a VM number.
|
||||
*/
|
||||
|
||||
Reference in New Issue
Block a user