js-slang
Version:
Javascript-based implementations of Source, written in Typescript
471 lines • 20.9 kB
JavaScript
// =======================================
// Helper functions/constants for type checker and type error checker
// =======================================
Object.defineProperty(exports, "__esModule", { value: true });
exports.getTypeOverrides = exports.createTypeEnvironment = exports.source4TypeOverrides = exports.source3TypeOverrides = exports.source2TypeOverrides = exports.source1TypeOverrides = exports.temporaryStreamFuncs = exports.postS3equalityFuncs = exports.preS3equalityFuncs = exports.primitiveFuncs = exports.listFuncs = exports.arrayFuncs = exports.mutatingPairFuncs = exports.pairFuncs = exports.predeclaredNames = exports.tStream = exports.tailType = exports.headType = exports.tPred = exports.tLiteral = exports.tUnion = exports.tFunc = exports.tNull = exports.tVoid = exports.tUndef = exports.tString = exports.tNumber = exports.tBool = exports.tAny = exports.tArray = exports.tForAll = exports.tList = exports.tPair = exports.tAddable = exports.tVar = exports.tPrimitive = exports.formatTypeString = exports.pushEnv = exports.setTypeAlias = exports.setDeclKind = exports.setType = exports.lookupTypeAlias = exports.lookupDeclKind = exports.lookupType = exports.typeAnnotationKeywordToBasicTypeMap = exports.RETURN_TYPE_IDENTIFIER = exports.NEGATIVE_OP = void 0;
// Name of Unary negative builtin operator
exports.NEGATIVE_OP = '-_1';
// Special name used for saving return type in type environment
exports.RETURN_TYPE_IDENTIFIER = '//RETURN_TYPE';
exports.typeAnnotationKeywordToBasicTypeMap = {
TSAnyKeyword: 'any',
TSBigIntKeyword: 'bigint',
TSBooleanKeyword: 'boolean',
TSNeverKeyword: 'never',
TSNullKeyword: 'null',
TSNumberKeyword: 'number',
TSObjectKeyword: 'object',
TSStringKeyword: 'string',
TSSymbolKeyword: 'symbol',
TSUndefinedKeyword: 'undefined',
TSUnknownKeyword: 'unknown',
TSVoidKeyword: 'void'
};
// Helper functions for dealing with type environment
function lookupType(name, env) {
for (let i = env.length - 1; i >= 0; i--) {
if (env[i].typeMap.has(name)) {
return env[i].typeMap.get(name);
}
}
return undefined;
}
exports.lookupType = lookupType;
function lookupDeclKind(name, env) {
for (let i = env.length - 1; i >= 0; i--) {
if (env[i].declKindMap.has(name)) {
return env[i].declKindMap.get(name);
}
}
return undefined;
}
exports.lookupDeclKind = lookupDeclKind;
function lookupTypeAlias(name, env) {
for (let i = env.length - 1; i >= 0; i--) {
if (env[i].typeAliasMap.has(name)) {
return env[i].typeAliasMap.get(name);
}
}
return undefined;
}
exports.lookupTypeAlias = lookupTypeAlias;
function setType(name, type, env) {
env[env.length - 1].typeMap.set(name, type);
}
exports.setType = setType;
function setDeclKind(name, kind, env) {
env[env.length - 1].declKindMap.set(name, kind);
}
exports.setDeclKind = setDeclKind;
function setTypeAlias(name, type, env) {
env[env.length - 1].typeAliasMap.set(name, type);
}
exports.setTypeAlias = setTypeAlias;
function pushEnv(env) {
env.push({ typeMap: new Map(), declKindMap: new Map(), typeAliasMap: new Map() });
}
exports.pushEnv = pushEnv;
// Helper functions for formatting types
function formatTypeString(type, formatAsLiteral) {
switch (type.kind) {
case 'function':
const paramTypes = type.parameterTypes;
const paramTypeString = paramTypes
.map(type => formatTypeString(type, formatAsLiteral))
.join(', ');
return `(${paramTypeString}) => ${formatTypeString(type.returnType, formatAsLiteral)}`;
case 'union':
// Remove duplicates
const typeSet = new Set(type.types.map(type => formatTypeString(type, formatAsLiteral)));
return Array.from(typeSet).join(' | ');
case 'literal':
if (typeof type.value === 'string') {
return `"${type.value.toString()}"`;
}
return type.value.toString();
case 'primitive':
if (!formatAsLiteral || type.value === undefined) {
return type.name;
}
if (typeof type.value === 'string') {
return `"${type.value.toString()}"`;
}
return type.value.toString();
case 'pair':
return `Pair<${formatTypeString(type.headType, formatAsLiteral)}, ${formatTypeString(type.tailType, formatAsLiteral)}>`;
case 'list':
return `List<${formatTypeString(type.elementType, formatAsLiteral)}>`;
case 'array':
const elementTypeString = formatTypeString(type.elementType, formatAsLiteral);
return elementTypeString.includes('|') || elementTypeString.includes('=>')
? `(${elementTypeString})[]`
: `${elementTypeString}[]`;
case 'variable':
if (type.typeArgs !== undefined && type.typeArgs.length > 0) {
return `${type.name}<${type.typeArgs
.map(param => formatTypeString(param, formatAsLiteral))
.join(', ')}>`;
}
return type.name;
default:
return type;
}
}
exports.formatTypeString = formatTypeString;
// Helper functions and constants for parsing types
function tPrimitive(name, value) {
return {
kind: 'primitive',
name,
value
};
}
exports.tPrimitive = tPrimitive;
function tVar(name, typeArgs) {
return {
kind: 'variable',
name,
constraint: 'none',
typeArgs
};
}
exports.tVar = tVar;
function tAddable(name) {
return {
kind: 'variable',
name,
constraint: 'addable'
};
}
exports.tAddable = tAddable;
function tPair(headType, tailType) {
return {
kind: 'pair',
headType,
tailType
};
}
exports.tPair = tPair;
function tList(elementType, typeAsPair) {
return {
kind: 'list',
elementType,
// Used in Source Typed variants to check for type mismatches against pairs
typeAsPair
};
}
exports.tList = tList;
function tForAll(polyType, typeParams) {
return {
kind: 'forall',
polyType,
typeParams
};
}
exports.tForAll = tForAll;
function tArray(elementType) {
return {
kind: 'array',
elementType
};
}
exports.tArray = tArray;
exports.tAny = tPrimitive('any');
exports.tBool = tPrimitive('boolean');
exports.tNumber = tPrimitive('number');
exports.tString = tPrimitive('string');
exports.tUndef = tPrimitive('undefined');
exports.tVoid = tPrimitive('void');
exports.tNull = tPrimitive('null');
function tFunc(...types) {
const parameterTypes = types.slice(0, -1);
const returnType = types.slice(-1)[0];
return {
kind: 'function',
parameterTypes,
returnType
};
}
exports.tFunc = tFunc;
function tUnion(...types) {
return {
kind: 'union',
types
};
}
exports.tUnion = tUnion;
function tLiteral(value) {
return {
kind: 'literal',
value
};
}
exports.tLiteral = tLiteral;
function tPred(ifTrueType) {
return {
kind: 'predicate',
ifTrueType
};
}
exports.tPred = tPred;
exports.headType = tVar('headType');
exports.tailType = tVar('tailType');
// Stream type used in Source Typed
function tStream(elementType) {
return tFunc(tPair(elementType, tVar('Stream', [elementType])));
}
exports.tStream = tStream;
// Types for preludes
exports.predeclaredNames = [
// constants
['Infinity', tPrimitive('number', Infinity)],
['NaN', tPrimitive('number', NaN)],
['undefined', exports.tUndef],
['math_E', tPrimitive('number', Math.E)],
['math_LN2', tPrimitive('number', Math.LN2)],
['math_LN10', tPrimitive('number', Math.LN10)],
['math_LOG2E', tPrimitive('number', Math.LOG2E)],
['math_LOG10E', tPrimitive('number', Math.LOG10E)],
['math_PI', tPrimitive('number', Math.PI)],
['math_SQRT1_2', tPrimitive('number', Math.SQRT1_2)],
['math_SQRT2', tPrimitive('number', Math.SQRT2)],
// predicate functions
['is_boolean', tPred(exports.tBool)],
['is_number', tPred(exports.tNumber)],
['is_string', tPred(exports.tString)],
['is_undefined', tPred(exports.tUndef)],
['is_function', tPred(tForAll(tFunc(tVar('T'), tVar('U'))))],
// math functions
['math_abs', tFunc(exports.tNumber, exports.tNumber)],
['math_acos', tFunc(exports.tNumber, exports.tNumber)],
['math_acosh', tFunc(exports.tNumber, exports.tNumber)],
['math_asin', tFunc(exports.tNumber, exports.tNumber)],
['math_asinh', tFunc(exports.tNumber, exports.tNumber)],
['math_atan', tFunc(exports.tNumber, exports.tNumber)],
['math_atan2', tFunc(exports.tNumber, exports.tNumber, exports.tNumber)],
['math_atanh', tFunc(exports.tNumber, exports.tNumber)],
['math_cbrt', tFunc(exports.tNumber, exports.tNumber)],
['math_ceil', tFunc(exports.tNumber, exports.tNumber)],
['math_clz32', tFunc(exports.tNumber, exports.tNumber)],
['math_cos', tFunc(exports.tNumber, exports.tNumber)],
['math_cosh', tFunc(exports.tNumber, exports.tNumber)],
['math_exp', tFunc(exports.tNumber, exports.tNumber)],
['math_expm1', tFunc(exports.tNumber, exports.tNumber)],
['math_floor', tFunc(exports.tNumber, exports.tNumber)],
['math_fround', tFunc(exports.tNumber, exports.tNumber)],
['math_hypot', tForAll(tVar('T'))],
['math_imul', tFunc(exports.tNumber, exports.tNumber, exports.tNumber)],
['math_log', tFunc(exports.tNumber, exports.tNumber)],
['math_log1p', tFunc(exports.tNumber, exports.tNumber)],
['math_log2', tFunc(exports.tNumber, exports.tNumber)],
['math_log10', tFunc(exports.tNumber, exports.tNumber)],
['math_max', tForAll(tVar('T'))],
['math_min', tForAll(tVar('T'))],
['math_pow', tFunc(exports.tNumber, exports.tNumber, exports.tNumber)],
['math_random', tFunc(exports.tNumber)],
['math_round', tFunc(exports.tNumber, exports.tNumber)],
['math_sign', tFunc(exports.tNumber, exports.tNumber)],
['math_sin', tFunc(exports.tNumber, exports.tNumber)],
['math_sinh', tFunc(exports.tNumber, exports.tNumber)],
['math_sqrt', tFunc(exports.tNumber, exports.tNumber)],
['math_tan', tFunc(exports.tNumber, exports.tNumber)],
['math_tanh', tFunc(exports.tNumber, exports.tNumber)],
['math_trunc', tFunc(exports.tNumber, exports.tNumber)],
// misc functions
['parse_int', tFunc(exports.tString, exports.tNumber, exports.tNumber)],
['prompt', tFunc(exports.tString, exports.tString)],
['get_time', tFunc(exports.tNumber)],
['stringify', tForAll(tFunc(tVar('T'), exports.tString))],
['display', tForAll(tVar('T'))],
['error', tForAll(tVar('T'))]
];
exports.pairFuncs = [
['pair', tForAll(tFunc(exports.headType, exports.tailType, tPair(exports.headType, exports.tailType)))],
['head', tForAll(tFunc(tPair(exports.headType, exports.tailType), exports.headType))],
['tail', tForAll(tFunc(tPair(exports.headType, exports.tailType), exports.tailType))],
['is_pair', tPred(tForAll(tPair(exports.headType, exports.tailType)))],
['is_null', tPred(tForAll(tList(tVar('T'))))],
['is_list', tPred(tForAll(tList(tVar('T'))))]
];
exports.mutatingPairFuncs = [
['set_head', tForAll(tFunc(tPair(exports.headType, exports.tailType), exports.headType, exports.tUndef))],
['set_tail', tForAll(tFunc(tPair(exports.headType, exports.tailType), exports.tailType, exports.tUndef))]
];
exports.arrayFuncs = [
['is_array', tPred(tForAll(tArray(tVar('T'))))],
['array_length', tForAll(tFunc(tArray(tVar('T')), exports.tNumber))]
];
exports.listFuncs = [['list', tForAll(tVar('T1'))]];
exports.primitiveFuncs = [
[exports.NEGATIVE_OP, tFunc(exports.tNumber, exports.tNumber)],
['!', tFunc(exports.tBool, exports.tBool)],
['&&', tForAll(tFunc(exports.tBool, tVar('T'), tVar('T')))],
['||', tForAll(tFunc(exports.tBool, tVar('T'), tVar('T')))],
['<', tForAll(tFunc(tAddable('A'), tAddable('A'), exports.tBool))],
['<=', tForAll(tFunc(tAddable('A'), tAddable('A'), exports.tBool))],
['>', tForAll(tFunc(tAddable('A'), tAddable('A'), exports.tBool))],
['>=', tForAll(tFunc(tAddable('A'), tAddable('A'), exports.tBool))],
['+', tForAll(tFunc(tAddable('A'), tAddable('A'), tAddable('A')))],
['%', tFunc(exports.tNumber, exports.tNumber, exports.tNumber)],
['-', tFunc(exports.tNumber, exports.tNumber, exports.tNumber)],
['*', tFunc(exports.tNumber, exports.tNumber, exports.tNumber)],
['/', tFunc(exports.tNumber, exports.tNumber, exports.tNumber)]
];
// Source 2 and below restricts === to addables
exports.preS3equalityFuncs = [
['===', tForAll(tFunc(tAddable('A'), tAddable('A'), exports.tBool))],
['!==', tForAll(tFunc(tAddable('A'), tAddable('A'), exports.tBool))]
];
// Source 3 and above allows any values as arguments for ===
exports.postS3equalityFuncs = [
['===', tForAll(tFunc(tVar('T1'), tVar('T2'), exports.tBool))],
['!==', tForAll(tFunc(tVar('T1'), tVar('T2'), exports.tBool))]
];
exports.temporaryStreamFuncs = [
['is_stream', tForAll(tFunc(tVar('T1'), exports.tBool))],
['list_to_stream', tForAll(tFunc(tList(tVar('T1')), tVar('T2')))],
['stream_to_list', tForAll(tFunc(tVar('T1'), tList(tVar('T2'))))],
['stream_length', tForAll(tFunc(tVar('T1'), exports.tNumber))],
['stream_map', tForAll(tFunc(tVar('T1'), tVar('T2')))],
['build_stream', tForAll(tFunc(exports.tNumber, tFunc(exports.tNumber, tVar('T1')), tVar('T2')))],
['stream_for_each', tForAll(tFunc(tFunc(tVar('T1'), tVar('T2')), exports.tBool))],
['stream_reverse', tForAll(tFunc(tVar('T1'), tVar('T1')))],
['stream_append', tForAll(tFunc(tVar('T1'), tVar('T1'), tVar('T1')))],
['stream_member', tForAll(tFunc(tVar('T1'), tVar('T2'), tVar('T2')))],
['stream_remove', tForAll(tFunc(tVar('T1'), tVar('T2'), tVar('T2')))],
['stream_remove_all', tForAll(tFunc(tVar('T1'), tVar('T2'), tVar('T2')))],
['stream_filter', tForAll(tFunc(tFunc(tVar('T1'), exports.tBool), tVar('T2'), tVar('T2')))],
['enum_stream', tForAll(tFunc(exports.tNumber, exports.tNumber, tVar('T1')))],
['integers_from', tForAll(tFunc(exports.tNumber, tVar('T1')))],
['eval_stream', tForAll(tFunc(tVar('T1'), exports.tNumber, tList(tVar('T2'))))],
['stream_ref', tForAll(tFunc(tVar('T1'), exports.tNumber, tVar('T2')))]
];
// Prelude function type overrides for Source Typed variant
// No need to override predicate functions as they are automatically handled by type checker
exports.source1TypeOverrides = [
// math functions
// TODO: Add support for type checking of functions with variable no. of args
['math_hypot', tForAll(exports.tNumber)],
['math_max', tForAll(exports.tNumber)],
['math_min', tForAll(exports.tNumber)],
// misc functions
['stringify', tFunc(exports.tAny, exports.tString)],
['arity', tFunc(exports.tAny, exports.tNumber)],
['char_at', tFunc(exports.tString, exports.tNumber, tUnion(exports.tString, exports.tUndef))],
// TODO: Add support for type checking of functions with variable no. of args
['display', tForAll(exports.tAny)],
['error', tForAll(exports.tAny)]
];
exports.source2TypeOverrides = [
// list library functions
[
'accumulate',
tForAll(tFunc(tFunc(tVar('T'), tVar('U'), tVar('U')), tVar('U'), tList(tVar('T')), tVar('U')))
],
[
'append',
tForAll(tFunc(tList(tVar('T')), tList(tVar('U')), tList(tUnion(tVar('T'), tVar('U')))))
],
['build_list', tForAll(tFunc(tFunc(exports.tNumber, tVar('T')), exports.tNumber, tList(tVar('T'))))],
['enum_list', tFunc(exports.tNumber, exports.tNumber, tList(exports.tNumber))],
['filter', tForAll(tFunc(tFunc(tVar('T'), exports.tBool), tList(tVar('T')), tList(tVar('T'))))],
['for_each', tForAll(tFunc(tFunc(tVar('T'), exports.tAny), tList(tVar('T')), tLiteral(true)))],
['length', tFunc(tList(exports.tAny), exports.tNumber)],
['list_ref', tForAll(tFunc(tList(tVar('T')), exports.tNumber, tVar('T')))],
['list_to_string', tFunc(tList(exports.tAny), exports.tString)],
['map', tForAll(tFunc(tFunc(tVar('T'), tVar('U')), tList(tVar('T')), tList(tVar('U'))))],
['member', tForAll(tFunc(tVar('T'), tList(tVar('T')), tList(tVar('T'))))],
['remove', tForAll(tFunc(tVar('T'), tList(tVar('T')), tList(tVar('T'))))],
['remove_all', tForAll(tFunc(tVar('T'), tList(tVar('T')), tList(tVar('T'))))],
['reverse', tForAll(tFunc(tList(tVar('T')), tList(tVar('T'))))],
// misc functions
// TODO: Add support for type checking of functions with variable no. of args
['display_list', tForAll(exports.tAny)],
['draw_data', tForAll(exports.tAny)],
['equal', tFunc(exports.tAny, exports.tAny, exports.tBool)]
];
exports.source3TypeOverrides = [
// array functions
['array_length', tFunc(tArray(exports.tAny), exports.tNumber)],
// stream library functions
['build_stream', tForAll(tFunc(tFunc(exports.tNumber, tVar('T')), exports.tNumber, tStream(tVar('T'))))],
['enum_stream', tFunc(exports.tNumber, exports.tNumber, tStream(exports.tNumber))],
['eval_stream', tForAll(tFunc(tStream(tVar('T')), exports.tNumber, tList(tVar('T'))))],
['integers_from', tFunc(exports.tNumber, tStream(exports.tNumber))],
['is_stream', tFunc(exports.tAny, exports.tBool)],
['list_to_stream', tForAll(tFunc(tList(tVar('T')), tStream(tVar('T'))))],
[
'stream_append',
tForAll(tFunc(tStream(tVar('T')), tStream(tVar('U')), tStream(tUnion(tVar('T'), tVar('U')))))
],
[
'stream_filter',
tForAll(tFunc(tFunc(tVar('T'), exports.tBool), tStream(tVar('T')), tStream(tVar('T'))))
],
['stream_for_each', tForAll(tFunc(tFunc(tVar('T'), exports.tAny), tStream(tVar('T')), tLiteral(true)))],
['stream_length', tFunc(tStream(exports.tAny), exports.tNumber)],
[
'stream_map',
tForAll(tFunc(tFunc(tVar('T'), tVar('U')), tStream(tVar('T')), tStream(tVar('U'))))
],
['stream_member', tForAll(tFunc(tVar('T'), tStream(tVar('T')), tStream(tVar('T'))))],
['stream_ref', tForAll(tFunc(tStream(tVar('T')), exports.tNumber, tVar('T')))],
['stream_remove', tForAll(tFunc(tVar('T'), tStream(tVar('T')), tStream(tVar('T'))))],
['stream_remove_all', tForAll(tFunc(tVar('T'), tStream(tVar('T')), tStream(tVar('T'))))],
['stream_reverse', tForAll(tFunc(tStream(tVar('T')), tStream(tVar('T'))))],
['stream_tail', tForAll(tFunc(tStream(tVar('T')), tStream(tVar('T'))))],
['stream_to_list', tForAll(tFunc(tStream(tVar('T')), tList(tVar('T'))))]
];
exports.source4TypeOverrides = [
['apply_in_underlying_javascript', tFunc(exports.tAny, tList(exports.tAny), exports.tAny)],
['tokenize', tFunc(exports.tString, tList(exports.tString))],
// For parse tree types, see parseTreeTypes.prelude.ts
['parse', tFunc(exports.tString, tUnion(tVar('Program', []), tVar('Statement', [])))]
];
const predeclaredConstTypes = [];
const pairTypeAlias = [
'Pair',
tForAll(tPair(exports.headType, exports.tailType), [exports.headType, exports.tailType])
];
const listTypeAlias = ['List', tForAll(tList(tVar('T')), [tVar('T')])];
const streamTypeAlias = ['Stream', tForAll(tStream(tVar('T')), [tVar('T')])];
// Creates type environment for the appropriate Source chapter
function createTypeEnvironment(chapter) {
const initialTypeMappings = [...exports.predeclaredNames, ...exports.primitiveFuncs];
const initialTypeAliasMappings = [...predeclaredConstTypes];
if (chapter >= 2) {
initialTypeMappings.push(...exports.pairFuncs, ...exports.listFuncs);
initialTypeAliasMappings.push(pairTypeAlias, listTypeAlias);
}
if (chapter >= 3) {
initialTypeMappings.push(...exports.postS3equalityFuncs, ...exports.mutatingPairFuncs, ...exports.arrayFuncs);
initialTypeAliasMappings.push(streamTypeAlias);
}
else {
initialTypeMappings.push(...exports.preS3equalityFuncs);
}
return [
{
typeMap: new Map(initialTypeMappings),
declKindMap: new Map(initialTypeMappings.map(val => [val[0], 'const'])),
typeAliasMap: new Map(initialTypeAliasMappings)
}
];
}
exports.createTypeEnvironment = createTypeEnvironment;
function getTypeOverrides(chapter) {
const typeOverrides = [...exports.source1TypeOverrides];
if (chapter >= 2) {
typeOverrides.push(...exports.source2TypeOverrides);
}
if (chapter >= 3) {
typeOverrides.push(...exports.source3TypeOverrides);
}
if (chapter >= 4) {
typeOverrides.push(...exports.source4TypeOverrides);
}
return typeOverrides;
}
exports.getTypeOverrides = getTypeOverrides;
//# sourceMappingURL=utils.js.map
;