60 lines
2.9 KiB
TypeScript
60 lines
2.9 KiB
TypeScript
import { generateBytecodeMap, IdentifierResolutionType, OpcodesBchSpec } from '@bitauth/libauth';
|
|
import { CashAssemblyIdentifierCollisionError } from './errors.ts';
|
|
import type { CompileCashAssemblyContext } from './types.ts';
|
|
|
|
/**
|
|
* Set of opcode names that the compiler recognizes.
|
|
*/
|
|
const COMPILER_OPCODE_NAMES: ReadonlySet<string> = new Set(Object.keys(generateBytecodeMap(OpcodesBchSpec)));
|
|
|
|
/**
|
|
* Parameters for {@link assertNoIdentifierCollisions} function.
|
|
*/
|
|
export type AssertNoIdentifierCollisionsParameters = Pick<CompileCashAssemblyContext, 'variables' | 'templateScripts'>;
|
|
|
|
/**
|
|
* Libauth resolves opcodes, then variables, then scripts, and the first match silently hides the
|
|
* rest, leading to unexpected compilation results and no errors. This function throws when one identifier matches more than one resolution type.
|
|
*
|
|
* @param {AssertNoIdentifierCollisionsParameters} parameters - Provided variables and optional template scripts.
|
|
* @throws {@link CashAssemblyIdentifierCollisionError} - When one identifier matches more than one resolution type.
|
|
*/
|
|
export const assertNoIdentifierCollisions = (parameters: AssertNoIdentifierCollisionsParameters): void => {
|
|
const { variables, templateScripts } = parameters;
|
|
|
|
// Create a set of the script identifiers from the template scripts map.
|
|
const scriptIdentifiers = new Set(Object.keys(templateScripts ?? {}));
|
|
|
|
const variableNames = new Set(Object.keys(variables));
|
|
|
|
// Combine both, the variableNames and the scriptIdentifiers, to get a set of all identifiers.
|
|
const declaredIdentifiers = new Set([ ...variableNames, ...scriptIdentifiers ]);
|
|
|
|
// This loop will check for more than one occurrence of an identifier in OP_CODES, variables, and scripts
|
|
for (const identifier of declaredIdentifiers) {
|
|
// Resolution types is a native construct from libauth.
|
|
const resolutionTypes: IdentifierResolutionType[] = [];
|
|
|
|
// If the identifier is an opcode, then push it to the resolution types array, this will also be
|
|
// detected by either or both of the variableNames and scriptIdentifiers checks.
|
|
if (COMPILER_OPCODE_NAMES.has(identifier) === true) {
|
|
resolutionTypes.push(IdentifierResolutionType.opcode);
|
|
}
|
|
|
|
// If the identifier is in variables, then push it to the resolution types array.
|
|
if (variableNames.has(identifier) === true) {
|
|
resolutionTypes.push(IdentifierResolutionType.variable);
|
|
}
|
|
|
|
// If the identifier is a script identifier, then push it to the resolution types array.
|
|
if (scriptIdentifiers.has(identifier) === true) {
|
|
resolutionTypes.push(IdentifierResolutionType.script);
|
|
}
|
|
|
|
// If there are more than one resolution types, then throw an error.
|
|
if (resolutionTypes.length > 1) {
|
|
throw new CashAssemblyIdentifierCollisionError(identifier, resolutionTypes);
|
|
}
|
|
}
|
|
};
|