js-slang
Version:
Javascript-based implementations of Source, written in Typescript
359 lines (358 loc) • 11.1 kB
TypeScript
import { SourceLocation } from 'acorn';
import * as es from 'estree';
import { EnvTree } from './createContext';
import Heap from './cse-machine/heap';
import { Control, Stash, Transformers } from './cse-machine/interpreter';
import type { ModuleFunctions } from './modules/moduleTypes';
import { Representation } from './alt-langs/mapper';
/**
* Defines functions that act as built-ins, but might rely on
* different implementations. e.g display() in a web application.
*/
export interface CustomBuiltIns {
rawDisplay: (value: Value, str: string, externalContext: any) => Value;
prompt: (value: Value, str: string, externalContext: any) => string | null;
alert: (value: Value, str: string, externalContext: any) => void;
visualiseList: (list: any, externalContext: any) => void;
}
export declare enum ErrorType {
IMPORT = "Import",
RUNTIME = "Runtime",
SYNTAX = "Syntax",
TYPE = "Type"
}
export declare enum ErrorSeverity {
WARNING = "Warning",
ERROR = "Error"
}
export interface SourceError {
type: ErrorType;
severity: ErrorSeverity;
location: es.SourceLocation;
explain(): string;
elaborate(): string;
}
export interface Comment {
type: 'Line' | 'Block';
value: string;
start: number;
end: number;
loc: SourceLocation | undefined;
}
export type ExecutionMethod = 'native' | 'auto' | 'cse-machine';
export declare enum Chapter {
SOURCE_1 = 1,
SOURCE_2 = 2,
SOURCE_3 = 3,
SOURCE_4 = 4,
FULL_JS = -1,
HTML = -2,
FULL_TS = -3,
PYTHON_1 = -4,
PYTHON_2 = -5,
PYTHON_3 = -6,
PYTHON_4 = -7,
FULL_PYTHON = -8,
SCHEME_1 = -9,
SCHEME_2 = -10,
SCHEME_3 = -11,
SCHEME_4 = -12,
FULL_SCHEME = -13,
FULL_C = -14,
FULL_JAVA = -15,
LIBRARY_PARSER = 100
}
export declare enum Variant {
DEFAULT = "default",
TYPED = "typed",
NATIVE = "native",
WASM = "wasm",
EXPLICIT_CONTROL = "explicit-control"
}
export type LanguageOptions = Record<string, string>;
export interface Language {
chapter: Chapter;
variant: Variant;
languageOptions?: LanguageOptions;
}
export type ValueWrapper = LetWrapper | ConstWrapper;
export interface LetWrapper {
kind: 'let';
getValue: () => Value;
assignNewValue: <T>(newValue: T) => T;
}
export interface ConstWrapper {
kind: 'const';
getValue: () => Value;
}
export interface NativeStorage {
builtins: Map<string, Value>;
previousProgramsIdentifiers: Set<string>;
operators: Map<string, (...operands: Value[]) => Value>;
maxExecTime: number;
evaller: null | ((program: string) => Value);
loadedModules: Record<string, ModuleFunctions>;
loadedModuleTypes: Record<string, Record<string, string>>;
}
export interface Context<T = any> {
/** The source version used */
chapter: Chapter;
/** The external symbols that exist in the Context. */
externalSymbols: string[];
/** All the errors gathered */
errors: SourceError[];
/** Runtime Specific state */
runtime: {
transformers?: Transformers;
break: boolean;
debuggerOn: boolean;
isRunning: boolean;
environmentTree: EnvTree;
environments: Environment[];
nodes: Node[];
control: Control | null;
stash: Stash | null;
objectCount: number;
envStepsTotal: number;
breakpointSteps: number[];
changepointSteps: number[];
};
numberOfOuterEnvironments: number;
prelude: string | null;
/** the state of the debugger */
debugger: {
/** External observers watching this context */
status: boolean;
state: {
it: IterableIterator<T>;
};
};
/**
* Used for storing external properties.
* For e.g, this can be used to store some application-related
* context for use in your own built-in functions (like `display(a)`)
*/
externalContext?: T;
/**
* Used for storing the native context and other values
*/
nativeStorage: NativeStorage;
/**
* Describes the language processor to be used for evaluation
*/
executionMethod: ExecutionMethod;
/**
* Describes the strategy / paradigm to be used for evaluation
* Examples: concurrent
*/
variant: Variant;
/**
* Describes the custom language option to be used for evaluation
*/
languageOptions: LanguageOptions;
/**
* Contains the evaluated code that has not yet been typechecked.
*/
unTypecheckedCode: string[];
typeEnvironment: TypeEnvironment;
/**
* Storage container for module specific information and state
*/
moduleContexts: {
[name: string]: ModuleContext;
};
/**
* Programs previously executed in this context
*/
previousPrograms: es.Program[];
/**
* Whether the evaluation timeout should be increased
*/
shouldIncreaseEvaluationTimeout: boolean;
}
export type ModuleContext = {
state: null | any;
tabs: null | any[];
};
export interface BlockFrame {
type: string;
loc?: es.SourceLocation | null;
enclosingLoc?: es.SourceLocation | null;
children: (DefinitionNode | BlockFrame)[];
}
export interface DefinitionNode {
name: string;
type: string;
loc?: es.SourceLocation | null;
}
export interface Frame {
[name: string]: any;
}
export type Value = any;
export type AllowedDeclarations = 'const' | 'let';
export interface Environment {
readonly id: string;
name: string;
tail: Environment | null;
callExpression?: es.CallExpression;
head: Frame;
heap: Heap;
thisContext?: Value;
}
export interface Error {
status: 'error';
}
export interface Finished {
status: 'finished';
context: Context;
value: Value;
representation?: Representation;
}
export interface SuspendedCseEval {
status: 'suspended-cse-eval';
context: Context;
}
export type Result = Finished | Error | SuspendedCseEval;
/**
* StatementSequence : A sequence of statements not surrounded by braces.
* It is *not* a block, and thus does not trigger environment creation when evaluated.
*
* The current ESTree specification does not have this node type, so we define it here.
*/
export interface StatementSequence extends es.BaseStatement {
type: 'StatementSequence';
body: Array<es.Statement>;
innerComments?: Array<Comment> | undefined;
}
/**
* js-slang's custom Node type - this should be used wherever es.Node is used.
*/
export type Node = {
isEnvDependent?: boolean;
} & (es.Node | StatementSequence | es.MaybeNamedClassDeclaration | es.MaybeNamedFunctionDeclaration);
export interface Directive extends es.ExpressionStatement {
type: 'ExpressionStatement';
expression: es.Literal;
directive: string;
}
/** For use in the substituter, to differentiate between a function declaration in the expression position,
* which has an id, as opposed to function expressions.
*/
export interface FunctionDeclarationExpression extends es.FunctionExpression {
id: es.Identifier;
body: es.BlockStatement;
}
/**
* For use in the substituter: call expressions can be reduced into an expression if the block
* only contains a single return statement; or a block, but has to be in the expression position.
* This is NOT compliant with the ES specifications, just as an intermediate step during substitutions.
*/
export interface BlockExpression extends es.BaseExpression {
type: 'BlockExpression';
body: es.Statement[];
}
export type substituterNodes = Node | BlockExpression;
export { Instruction as SVMInstruction, Program as SVMProgram, Address as SVMAddress, Argument as SVMArgument, Offset as SVMOffset, SVMFunction } from './vm/svml-compiler';
export type ContiguousArrayElementExpression = Exclude<es.ArrayExpression['elements'][0], null>;
export type ContiguousArrayElements = ContiguousArrayElementExpression[];
export type PrimitiveType = 'boolean' | 'null' | 'number' | 'string' | 'undefined';
export type TSAllowedTypes = 'any' | 'void';
export declare const disallowedTypes: readonly ["bigint", "never", "object", "symbol", "unknown"];
export type TSDisallowedTypes = (typeof disallowedTypes)[number];
export type TSBasicType = PrimitiveType | TSAllowedTypes | TSDisallowedTypes;
export type NodeWithInferredType<T extends Node> = InferredType & T;
export type FuncDeclWithInferredTypeAnnotation = NodeWithInferredType<es.FunctionDeclaration> & TypedFuncDecl;
export type InferredType = Untypable | Typed | NotYetTyped;
export interface TypedFuncDecl {
functionInferredType?: Type;
}
export interface Untypable {
typability?: 'Untypable';
inferredType?: Type;
}
export interface NotYetTyped {
typability?: 'NotYetTyped';
inferredType?: Type;
}
export interface Typed {
typability?: 'Typed';
inferredType?: Type;
}
export type Constraint = 'none' | 'addable';
export type Type = Primitive | Variable | FunctionType | List | Pair | SArray | UnionType | LiteralType;
export interface Primitive {
kind: 'primitive';
name: PrimitiveType | TSAllowedTypes;
value?: string | number | boolean;
}
export interface Variable {
kind: 'variable';
name: string;
constraint: Constraint;
typeArgs?: Type[];
}
export interface FunctionType {
kind: 'function';
parameterTypes: Type[];
returnType: Type;
}
export interface List {
kind: 'list';
elementType: Type;
typeAsPair?: Pair;
}
export interface Pair {
kind: 'pair';
headType: Type;
tailType: Type;
}
export interface SArray {
kind: 'array';
elementType: Type;
}
export interface UnionType {
kind: 'union';
types: Type[];
}
export interface LiteralType {
kind: 'literal';
value: string | number | boolean;
}
export type BindableType = Type | ForAll | PredicateType;
export interface ForAll {
kind: 'forall';
polyType: Type;
typeParams?: Variable[];
}
export interface PredicateType {
kind: 'predicate';
ifTrueType: Type | ForAll;
}
export type PredicateTest = {
node: NodeWithInferredType<es.CallExpression>;
ifTrueType: Type | ForAll;
argVarName: string;
};
/**
* Each element in the TypeEnvironment array represents a different scope
* (e.g. first element is the global scope, last element is the closest).
* Within each scope, variable types/declaration kinds, as well as type aliases, are stored.
*/
export type TypeEnvironment = {
typeMap: Map<string, BindableType>;
declKindMap: Map<string, AllowedDeclarations>;
typeAliasMap: Map<string, Type | ForAll>;
}[];
/**
* Helper type to recursively make properties that are also objects
* partial
*
* By default, `Partial<Array<T>>` is equivalent to `Array<T | undefined>`. For this type, `Array<T>` will be
* transformed to Array<Partial<T>> instead
*/
export type RecursivePartial<T> = T extends Array<any> ? Array<RecursivePartial<T[number]>> : T extends Record<any, any> ? Partial<{
[K in keyof T]: RecursivePartial<T[K]>;
}> : T;
export type NodeTypeToNode<T extends Node['type']> = Extract<Node, {
type: T;
}>;