├── .gitignore
├── .prettierrc
├── .vscode
└── launch.json
├── README.md
├── favicon.ico
├── index.html
├── package-lock.json
├── package.json
├── public
└── default.js
├── src
├── index.ts
├── scripts
│ ├── ApplicationState.ts
│ ├── animation
│ │ └── animation.ts
│ ├── editor
│ │ ├── Editor.ts
│ │ ├── EditorThemes.ts
│ │ └── Separators.ts
│ ├── environment
│ │ ├── EnvironmentState.ts
│ │ ├── data.ts
│ │ └── environment.ts
│ ├── execution
│ │ ├── execution.ts
│ │ ├── graph
│ │ │ └── ExecutionGraph.ts
│ │ └── primitive
│ │ │ ├── Binary
│ │ │ ├── BinaryExpressionEvaluate.ts
│ │ │ └── LogicalExpressionEvaluate.ts
│ │ │ ├── Binding
│ │ │ ├── BindAnimation.ts
│ │ │ └── BindFunctionAnimation.ts
│ │ │ ├── Container
│ │ │ ├── ArrayStartAnimation.ts
│ │ │ └── ObjectStartAnimation.ts
│ │ │ ├── Data
│ │ │ ├── ConsumeDataAnimation.ts
│ │ │ ├── CopyDataAnimation.ts
│ │ │ ├── CopyReferenceAnimation.ts
│ │ │ ├── CreateLiteralAnimation.ts
│ │ │ ├── FindMember.ts
│ │ │ ├── FindVariableAnimation.ts
│ │ │ ├── FloatAnimation.ts
│ │ │ ├── GetMember.ts
│ │ │ ├── MoveAndPlaceAnimation.ts
│ │ │ └── UpdateAnimation.ts
│ │ │ ├── ExecutionNode.ts
│ │ │ ├── Functions
│ │ │ ├── Native
│ │ │ │ ├── Array
│ │ │ │ │ ├── ArrayConcatAnimation.ts
│ │ │ │ │ └── ArrayPushAnimation.ts
│ │ │ │ └── Math
│ │ │ │ │ ├── FloorAnimation.ts
│ │ │ │ │ └── SqrtAnimation.ts
│ │ │ └── ReturnStatementAnimation.ts
│ │ │ ├── Scope
│ │ │ ├── CreateScopeAnimation.ts
│ │ │ └── PopScopeAnimation.ts
│ │ │ └── Unary
│ │ │ └── UnaryExpressionEvaluate.ts
│ ├── renderer
│ │ ├── Action
│ │ │ ├── Abyss.ts
│ │ │ ├── Action.ts
│ │ │ ├── Dynamic
│ │ │ │ ├── AssignmentExpressionRepresentation.ts
│ │ │ │ ├── BinaryExpressionRepresentation.ts
│ │ │ │ ├── BlockStatementRepresentation.ts
│ │ │ │ ├── CallExpressionRepresentation.ts
│ │ │ │ ├── ExpressionStatementRepresentation.ts
│ │ │ │ ├── ForStatementRepresentation.ts
│ │ │ │ ├── FunctionCallRepresentation.ts
│ │ │ │ ├── IfStatementRepresentation.ts
│ │ │ │ ├── ProgramRepresentation.ts
│ │ │ │ ├── Representation.ts
│ │ │ │ ├── ReturnStatementRepresentation.ts
│ │ │ │ ├── UpdateExpressionRepresentation.ts
│ │ │ │ └── VariableDeclarationRepresentation.ts
│ │ │ └── Mapping
│ │ │ │ ├── ActionMapping.ts
│ │ │ │ ├── ActionMappingCursor.ts
│ │ │ │ ├── ActionProxy.ts
│ │ │ │ ├── ControlFlowCursor.ts
│ │ │ │ ├── ControlFlowState.ts
│ │ │ │ └── index.d.ts
│ │ ├── Trail
│ │ │ ├── Renderers
│ │ │ │ ├── CreateTrailRenderer.ts
│ │ │ │ ├── MoveTrailRenderer.ts
│ │ │ │ ├── PartialCreateTrailRenderer.ts
│ │ │ │ ├── PartialMoveTrailRenderer.ts
│ │ │ │ └── TrailRenderer.ts
│ │ │ ├── ReturnAnimation.ts
│ │ │ ├── Trail.ts
│ │ │ ├── TrailGroup.ts
│ │ │ └── TrailState.ts
│ │ └── View
│ │ │ ├── Environment
│ │ │ ├── EnvironmentRenderer.ts
│ │ │ ├── HardScopeRenderer.ts
│ │ │ ├── data
│ │ │ │ ├── DataRenderer.ts
│ │ │ │ ├── DataState.ts
│ │ │ │ ├── array
│ │ │ │ │ ├── ArrayRenderer.ts
│ │ │ │ │ └── IndexRenderer.ts
│ │ │ │ ├── literal
│ │ │ │ │ └── LiteralRenderer.ts
│ │ │ │ ├── object
│ │ │ │ │ ├── ObjectItemRenderer.ts
│ │ │ │ │ └── ObjectRenderer.ts
│ │ │ │ └── reference
│ │ │ │ │ ├── FunctionRenderer.ts
│ │ │ │ │ └── ReferenceRenderer.ts
│ │ │ └── identifier
│ │ │ │ └── IdentifierRenderer.ts
│ │ │ └── View.ts
│ ├── transpiler
│ │ ├── Compiler.ts
│ │ ├── Expressions
│ │ │ ├── Array
│ │ │ │ └── ArrayExpression.ts
│ │ │ ├── BinaryOperations
│ │ │ │ ├── AssigmentExpression.ts
│ │ │ │ ├── BinaryExpression
│ │ │ │ │ └── BinaryExpression.ts
│ │ │ │ ├── LogicalExpression.ts
│ │ │ │ └── MemberExpression.ts
│ │ │ ├── CallExpression.ts
│ │ │ ├── ObjectExpression.ts
│ │ │ └── UnaryOperations
│ │ │ │ ├── UnaryExpression.ts
│ │ │ │ └── UpdateExpression.ts
│ │ ├── Functions
│ │ │ ├── FunctionCall.ts
│ │ │ ├── FunctionDeclaration.ts
│ │ │ └── ReturnStatement.ts
│ │ ├── Identifier.ts
│ │ ├── Literal.ts
│ │ └── Statements
│ │ │ ├── BlockStatement.ts
│ │ │ ├── Choice
│ │ │ └── IfStatement.ts
│ │ │ ├── ExpressionStatement.ts
│ │ │ ├── Loops
│ │ │ ├── ForStatement.ts
│ │ │ └── WhileStatement.ts
│ │ │ ├── Program.ts
│ │ │ ├── VariableDeclaration.ts
│ │ │ └── VariableDeclarator.ts
│ ├── utilities
│ │ ├── Keyboard.ts
│ │ ├── Mouse.ts
│ │ ├── Ticker.ts
│ │ ├── action.ts
│ │ ├── dom.ts
│ │ ├── executor.ts
│ │ ├── generic.ts
│ │ ├── math.ts
│ │ ├── objects.ts
│ │ ├── rfdc.js
│ │ └── string.ts
│ └── visualization
│ │ ├── Focus.ts
│ │ ├── Selection.ts
│ │ ├── Visualization.ts
│ │ └── query
│ │ └── CodeSelectionQuery.ts
├── styles
│ ├── editor.scss
│ ├── generic
│ │ ├── button.scss
│ │ └── checkbox.scss
│ ├── main.scss
│ ├── renderer
│ │ ├── action.scss
│ │ ├── data
│ │ │ ├── array.scss
│ │ │ ├── data.scss
│ │ │ ├── identifier.scss
│ │ │ └── literal.scss
│ │ ├── trail.scss
│ │ └── view.scss
│ └── themes
│ │ ├── coffee.scss
│ │ └── dark.scss
└── vite-env.d.ts
├── test.js
├── tsconfig.json
├── tutorial
├── 01_basic.js
├── 02_if.js
├── 03_for_func.js
└── 03_rec.js
└── vite.config.ts
/.gitignore:
--------------------------------------------------------------------------------
1 | # Logs
2 | logs
3 | *.log
4 | npm-debug.log*
5 | yarn-debug.log*
6 | yarn-error.log*
7 | pnpm-debug.log*
8 | lerna-debug.log*
9 |
10 | node_modules
11 | dist
12 | dist-ssr
13 | *.local
14 |
15 | # Editor directories and files
16 | .vscode/*
17 | !.vscode/extensions.json
18 | .idea
19 | .DS_Store
20 | *.suo
21 | *.ntvs*
22 | *.njsproj
23 | *.sln
24 | *.sw?
25 |
--------------------------------------------------------------------------------
/.prettierrc:
--------------------------------------------------------------------------------
1 | {
2 | "tabWidth": 4,
3 | "semi": false,
4 | "singleQuote": true,
5 | "bracketSameLine": true,
6 | "printWidth": 120
7 | }
8 |
--------------------------------------------------------------------------------
/.vscode/launch.json:
--------------------------------------------------------------------------------
1 | {
2 | // Use IntelliSense to learn about possible attributes.
3 | // Hover to view descriptions of existing attributes.
4 | // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
5 | "version": "0.2.0",
6 | "configurations": [
7 | {
8 | "type": "pwa-chrome",
9 | "request": "launch",
10 | "name": "Launch Chrome against localhost",
11 | "url": "http://localhost:8080",
12 | "webRoot": "${workspaceFolder}"
13 | }
14 | ]
15 | }
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # CrossCode
2 |
3 | Note: this is an experimental research prototype that is being actively developed. It may not work as expected. It supports a limited subset of JavaScript with constants, function calls and definitions, binary operators, let-bindings, if-statements, and for-loops.
4 |
5 | ## Setup instructions
6 | 1. `npm install`
7 | 2. `npm run dev`
8 |
9 | ## Using the tool
10 | 1. The default source code file that is loaded is under `./public/default.js`.
11 | 2. Sample tutorial code snippets are under the `./tutorials` folder.
12 |
--------------------------------------------------------------------------------
/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/hayatpur/crosscode/8a73693812fc5afd02cfa41107128f149770ec7a/favicon.ico
--------------------------------------------------------------------------------
/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | CrossCode
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
79 |
80 |
81 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "cross-code",
3 | "scripts": {
4 | "dev": "vite",
5 | "build": "vite build",
6 | "preview": "vite preview"
7 | },
8 | "dependencies": {
9 | "@tweakpane/plugin-essentials": "^0.1.4",
10 | "@types/acorn": "^4.0.6",
11 | "@types/estree": "0.0.51",
12 | "acorn": "^8.7.1",
13 | "acorn-walk": "^8.2.0",
14 | "astring": "^1.8.3",
15 | "csstype": "^3.1.0",
16 | "monaco-editor": "^0.33.0",
17 | "perfect-arrows": "^0.3.7",
18 | "tweakpane": "^3.1.0",
19 | "vite-plugin-monaco-editor": "^1.1.0"
20 | },
21 | "devDependencies": {
22 | "@tweakpane/core": "^1.1.0",
23 | "@types/svg-intersections": "^0.4.1",
24 | "@typescript-eslint/eslint-plugin": "^5.29.0",
25 | "@typescript-eslint/parser": "^5.29.0",
26 | "css-loader": "^6.7.1",
27 | "file-loader": "^6.2.0",
28 | "ignore-loader": "^0.1.2",
29 | "sass": "^1.53.0",
30 | "sass-loader": "^13.0.1",
31 | "style-loader": "^3.3.1",
32 | "ts-loader": "^9.3.1",
33 | "typescript": "^4.7.4",
34 | "url-loader": "^4.1.1",
35 | "vite": "^2.9.12"
36 | }
37 | }
38 |
--------------------------------------------------------------------------------
/public/default.js:
--------------------------------------------------------------------------------
1 | // Controls:
2 | // - Left key to move one step back
3 | // - Right key to move one step forward
4 | // - Up key to move animation backward
5 | // - Down key to move animation forward
6 | // - Grab control flow cursor to move
7 | // - Control/Cmd click to get break down a step
8 |
9 | let list = [1, 2, 3, 4, 5, 6, 7, 8, 9]
10 | let n = list.length
11 |
12 | if (list[0] < list[n - 1]) {
13 | let temp = list[0]
14 | list[0] = list[n - 1]
15 | list[n - 1] = temp
16 | }
17 |
--------------------------------------------------------------------------------
/src/index.ts:
--------------------------------------------------------------------------------
1 | import { ApplicationState } from './scripts/ApplicationState'
2 | import { Editor } from './scripts/editor/Editor'
3 | import { Ticker } from './scripts/utilities/Ticker'
4 | import { createCodeQuerySelector } from './scripts/visualization/query/CodeSelectionQuery'
5 | import {
6 | compileVisualization,
7 | createVisualization,
8 | setupTweakpane,
9 | tickVisualization,
10 | } from './scripts/visualization/Visualization'
11 | import './styles/main.scss'
12 | // import './styles/themes/dark.scss'
13 |
14 | function main() {
15 | ApplicationState.editor = new Editor()
16 |
17 | ApplicationState.visualization = createVisualization()
18 |
19 | setupTweakpane(ApplicationState.visualization)
20 |
21 | /* --- Live programming: update after 0.5s of activity -- */
22 | let typingTimer: any
23 | let firstCompilation = true
24 | ApplicationState.editor.onChangeContent.add(() => {
25 | clearTimeout(typingTimer)
26 |
27 | const delay = firstCompilation ? 500 : 50 // Stagger on first execution to allow time for monaco to load
28 | typingTimer = setTimeout(() => {
29 | compileVisualization(ApplicationState.visualization, ApplicationState.editor.getValue())
30 | }, delay)
31 |
32 | firstCompilation = false
33 | })
34 |
35 | createCodeQuerySelector()
36 |
37 | /* ------------------------ Ticks ----------------------- */
38 | Ticker.instance.registerTick(tick)
39 | }
40 |
41 | function tick(dt: number) {
42 | tickVisualization(ApplicationState.visualization, dt)
43 | }
44 |
45 | main()
46 |
--------------------------------------------------------------------------------
/src/scripts/ApplicationState.ts:
--------------------------------------------------------------------------------
1 | import { Editor } from './editor/Editor'
2 | import { AbyssState } from './renderer/Action/Abyss'
3 | import { ActionState } from './renderer/Action/Action'
4 | import { VisualizationState } from './visualization/Visualization'
5 |
6 | export class ApplicationState {
7 | // Visualization
8 | static visualization: VisualizationState
9 | static editor: Editor
10 |
11 | // Interaction
12 | // static currentQuery: CodeQueryState
13 |
14 | // Constants
15 | static proxyHeightMultiplier = 0.4
16 | static proxyWidthMultiplier = 0.25
17 |
18 | // Collections
19 | static actions: { [id: string]: ActionState } = {}
20 | static abysses: { [id: string]: AbyssState } = {}
21 | static traceIndicators: { trace: HTMLElement; source: HTMLElement }[] = []
22 |
23 | static Epsilon = 0.001
24 | }
25 |
--------------------------------------------------------------------------------
/src/scripts/editor/Separators.ts:
--------------------------------------------------------------------------------
1 | // import { Ticker } from '../utilities/Ticker';
2 | // import { View } from '../view/ViewRenderer';
3 |
4 | // export class Separators {
5 | // static instance: Separators;
6 | // separatorElements: {
7 | // [viewID: string]: {
8 | // top: SVGPathElement;
9 | // bottom: SVGPathElement;
10 | // separator: SVGPathElement;
11 | // label: HTMLDivElement;
12 | // };
13 | // } = {};
14 |
15 | // constructor() {
16 | // Separators.instance = this;
17 | // Ticker.instance.registerTick(this.tick.bind(this));
18 | // }
19 |
20 | // addSeparator(view: View) {
21 | // // The movement paths
22 | // const top = document.createElementNS('http://www.w3.org/2000/svg', 'path');
23 | // top.classList.add('separator-path');
24 |
25 | // const bottom = document.createElementNS('http://www.w3.org/2000/svg', 'path');
26 | // bottom.classList.add('separator-path');
27 |
28 | // // const separator = document.createElementNS('http://www.w3.org/2000/svg', 'path');
29 | // // separator.classList.add('separator-path');
30 |
31 | // // Add them to global svg canvas
32 | // const svg = document.getElementById('svg-canvas');
33 |
34 | // svg.append(top);
35 | // svg.append(bottom);
36 | // // svg.append(separator);
37 |
38 | // // Store them in the separatorElements object
39 | // // this.separatorElements[view.animation.id] = { top: top, bottom: bottom, separator: separator, label: null };
40 | // }
41 |
42 | // tick() {
43 | // for (const id of Object.keys(this.separatorElements)) {
44 | // this.updatePath(id);
45 | // }
46 | // }
47 |
48 | // updatePath(id: string) {
49 | // const view = View.views[id];
50 | // if (view.codeSection == null || (!view.animation.playing && !view.animation.hasPlayed)) return;
51 |
52 | // const code_bbox = view.codeSection.getBoundingClientRect();
53 | // const bbox = view.element.getBoundingClientRect();
54 |
55 | // const top = this.separatorElements[id].top;
56 | // const bottom = this.separatorElements[id].bottom;
57 | // const separator = this.separatorElements[id].separator;
58 |
59 | // const x1 = code_bbox.right + 10;
60 | // const x2 = x1 + 50;
61 |
62 | // const y2 = bbox.top - 10;
63 |
64 | // // Update top points
65 | // // this.updatePoints(top, x1, code_bbox.top, x2, y2);
66 | // // this.updatePoints(bottom, x1, code_bbox.bottom, x2, y2);
67 |
68 | // separator.setAttribute('d', `M ${x2} ${y2} Q ${x2} ${y2} ${x2 + 1000} ${y2}`);
69 |
70 | // // if (this.separatorElements[id].label == null) {
71 | // // // Set label
72 | // // const labelEl = document.createElement('div');
73 | // // labelEl.classList.add('view-label');
74 | // // labelEl.innerHTML = `[${view.animation.id}] ${camelCaseToSentence(
75 | // // view.animation.getName()
76 | // // )}`;
77 | // // createViewControls(labelEl);
78 |
79 | // // document.body.append(labelEl);
80 | // // this.separatorElements[id].label = labelEl;
81 | // // } else {
82 | // // const label = this.separatorElements[id].label;
83 | // // const label_bbox = label.getBoundingClientRect();
84 | // // label.style.left = `${x2 + bbox.width / 2 - label_bbox.width / 2}px`;
85 | // // label.style.top = `${y2 - label_bbox.height / 2}px`;
86 | // // }
87 |
88 | // // let x1 = code_bbox.right;
89 | // // let y1 = code_bbox.top;
90 | // // let x2 = bbox.left;
91 | // // let y2 = bbox.top + bbox.height / 2;
92 | // // let mx = (x1 + x2) / 2;
93 | // // let my = (y1 + y2) / 2;
94 | // // my += (y1 < y2 ? 20 : -20);
95 | // }
96 |
97 | // updatePoints(path: SVGPathElement, x1: number, y1: number, x2: number, y2: number) {
98 | // let mx = (x1 + x2) / 2;
99 | // let my = (y1 + y2) / 2;
100 | // // my += y1 < y2 ? 20 : -20;
101 | // path.setAttribute('d', `M ${x1} ${y1} Q ${mx} ${y1} ${mx} ${my} Q ${mx} ${y2} ${x2} ${y2}`);
102 | // }
103 | // }
104 |
--------------------------------------------------------------------------------
/src/scripts/environment/EnvironmentState.ts:
--------------------------------------------------------------------------------
1 | import { ExecutionGraph } from '../execution/graph/ExecutionGraph'
2 | import { ExecutionNode } from '../execution/primitive/ExecutionNode'
3 | import { DataState, Transform } from '../renderer/View/Environment/data/DataState'
4 | import { ScopeType } from '../transpiler/Statements/BlockStatement'
5 | import { stringHashCode } from '../utilities/string'
6 |
7 | export enum AccessorType {
8 | ID = 'ID',
9 | Symbol = 'Symbol',
10 | Index = 'Index',
11 | Register = 'Register',
12 | }
13 |
14 | export type Accessor = {
15 | type: AccessorType
16 | value: string
17 | }
18 |
19 | export function accessorToString(accessor: Accessor): string {
20 | if (accessor.type === AccessorType.Register) {
21 | return `${accessor.type}(0x${stringHashCode(accessor.value as string)
22 | .toString()
23 | .substring(0, 4)})`
24 | }
25 | return `${accessor.type}(${accessor.value})`
26 | }
27 |
28 | export function accessorsToString(accessors: Accessor[]): string {
29 | return `${accessors.map((acc) => accessorToString(acc)).join(' > ')}`
30 | }
31 |
32 | export type IdentifierState = {
33 | name: string
34 | location: Accessor[]
35 | }
36 |
37 | export type Scope = {
38 | bindings: { [name: string]: IdentifierState }
39 | type: ScopeType
40 | }
41 |
42 | export type EnvironmentTransform = Transform & {}
43 |
44 | export type Residual = {
45 | data: DataState
46 | location: Accessor[]
47 | }
48 |
49 | export type EnvironmentState = {
50 | _type: 'EnvironmentState'
51 |
52 | // Variable name bindings
53 | scope: Scope[]
54 |
55 | // Storage data
56 | memory: { [id: string]: DataState }
57 |
58 | // Temporary data
59 | registers: { [name: string]: DataState }
60 |
61 | // Residual data (indexed with time)
62 | residuals: Residual[][]
63 |
64 | // ID map of timestamp of each data
65 | timestamps: { [id: string]: number }
66 |
67 | id: string
68 | }
69 |
70 | export type FrameInfo = {
71 | environment: EnvironmentState
72 | actionId: string
73 | overrideExecution?: ExecutionGraph | ExecutionNode
74 | }
75 |
76 | export function instanceOfEnvironment(environment: any): environment is EnvironmentState {
77 | return environment['_type'] === 'EnvironmentState'
78 | }
79 |
--------------------------------------------------------------------------------
/src/scripts/environment/data.ts:
--------------------------------------------------------------------------------
1 | import * as ESTree from 'estree'
2 | import {
3 | DataState,
4 | DataType,
5 | instanceOfObjectData,
6 | ObjectDataState,
7 | PrimitiveDataState,
8 | Transform,
9 | } from '../renderer/View/Environment/data/DataState'
10 | import { clone } from '../utilities/objects'
11 | import { Accessor } from './EnvironmentState'
12 |
13 | export function createTransform(): Transform {
14 | return {
15 | styles: {},
16 | renderOnlyStyles: {},
17 | rendered: {
18 | x: 0,
19 | y: 0,
20 | width: 0,
21 | height: 0,
22 | },
23 | classList: [],
24 | }
25 | }
26 |
27 | export function createPrimitiveData(
28 | type: DataType,
29 | value: string | boolean | number | Accessor[] | Function | null,
30 | id: string,
31 | frame: number = -1,
32 | builtin: boolean = false
33 | ): PrimitiveDataState {
34 | return {
35 | _type: 'PrimitiveDataState',
36 | type: type,
37 | value: value,
38 | id: id,
39 | frame: frame,
40 | builtin,
41 | }
42 | }
43 |
44 | export function createObjectData(
45 | value: object,
46 | id: string,
47 | frame: number = -1,
48 | builtin: boolean = false
49 | ): ObjectDataState {
50 | return {
51 | _type: 'ObjectDataState',
52 | value: value,
53 | id: id,
54 | frame: frame,
55 | builtin,
56 | }
57 | }
58 |
59 | export function getDataClassNames(type: DataType): string[] {
60 | const mapping = {
61 | [DataType.Literal]: ['data-literal-i'],
62 | [DataType.Array]: ['data-array-i'],
63 | }
64 |
65 | return mapping[type] ?? []
66 | }
67 |
68 | export function cloneData(
69 | data: DataState,
70 | copyID: boolean = true,
71 | srcID: string = null
72 | ): DataState {
73 | const copy = clone(data)
74 | copy.id = copyID ? data.id : srcID
75 | copy.builtin = false
76 | return copy
77 | }
78 |
79 | export function replaceDataWith(
80 | original: DataState,
81 | data: DataState,
82 | mask: { id?: boolean; frame?: boolean } = { id: false, frame: false }
83 | ) {
84 | const originalCopy = cloneData(original)
85 |
86 | Object.assign(original, clone(data))
87 |
88 | if (mask.id) original.id = originalCopy.id
89 |
90 | if (mask.frame) {
91 | original.frame = originalCopy.frame
92 |
93 | if (instanceOfObjectData(original)) {
94 | if (Array.isArray(original.value)) {
95 | original.value.forEach((el) => (el.frame = original.frame))
96 | } else {
97 | Object.values(original.value).forEach(
98 | (el) => (el.frame = original.frame)
99 | )
100 | }
101 | }
102 | }
103 |
104 | original.builtin = false
105 | }
106 |
107 | export function convertIdentifierToLiteral(
108 | data: ESTree.Identifier
109 | ): ESTree.Literal {
110 | return {
111 | ...data,
112 | type: 'Literal',
113 | value: data.name,
114 | }
115 | }
116 |
--------------------------------------------------------------------------------
/src/scripts/execution/graph/ExecutionGraph.ts:
--------------------------------------------------------------------------------
1 | import { Accessor, EnvironmentState } from '../../environment/EnvironmentState'
2 | import { getEmptyNodeData } from '../execution'
3 | import { ExecutionNode, NodeData } from '../primitive/ExecutionNode'
4 |
5 | export type ExecutionRuntimeOptions = {
6 | indent?: number
7 | }
8 |
9 | export type DataInfo = {
10 | location: Accessor[]
11 | id: string
12 | }
13 |
14 | export type GlobalDataInfo = {
15 | location: { viewID: string; localLocation: Accessor[] }
16 | id: string
17 | }
18 |
19 | export type ExecutionGraph = {
20 | // Meta info
21 | _type: 'ExecutionGraph'
22 | id: string
23 | nodeData: NodeData
24 |
25 | // General info
26 | precondition: EnvironmentState | null
27 | postcondition: EnvironmentState | null
28 | isGroup: boolean
29 |
30 | // Animation info
31 | vertices: (ExecutionGraph | ExecutionNode)[]
32 | isParallel: boolean
33 | parallelStarts: number[]
34 | }
35 |
36 | export function instanceOfExecutionGraph(
37 | animation: any
38 | ): animation is ExecutionGraph {
39 | return animation._type == 'ExecutionGraph'
40 | }
41 |
42 | let __EXECUTION_GRAPH_ID = 0
43 | export function createExecutionGraph(
44 | nodeData: NodeData,
45 | options: { isGroup?: boolean } = {}
46 | ): ExecutionGraph {
47 | return {
48 | // Meta info
49 | _type: 'ExecutionGraph',
50 | id: `AG(${++__EXECUTION_GRAPH_ID})`,
51 | nodeData: options.isGroup ? getEmptyNodeData(nodeData) : nodeData,
52 |
53 | // Invariant to abstraction info
54 | precondition: null,
55 | postcondition: null,
56 | isGroup: options.isGroup || false,
57 |
58 | vertices: [],
59 | isParallel: false,
60 | parallelStarts: [],
61 | }
62 | }
63 |
--------------------------------------------------------------------------------
/src/scripts/execution/primitive/Binary/LogicalExpressionEvaluate.ts:
--------------------------------------------------------------------------------
1 | import * as ESTree from 'estree'
2 | import { createPrimitiveData, replaceDataWith } from '../../../environment/data'
3 | import {
4 | addDataAt,
5 | getMemoryLocation,
6 | removeAt,
7 | resolvePath,
8 | } from '../../../environment/environment'
9 | import {
10 | Accessor,
11 | accessorsToString,
12 | EnvironmentState,
13 | } from '../../../environment/EnvironmentState'
14 | import {
15 | DataState,
16 | DataType,
17 | PrimitiveDataState,
18 | } from '../../../renderer/View/Environment/data/DataState'
19 | import { DataInfo } from '../../graph/ExecutionGraph'
20 | import { createExecutionNode, ExecutionNode } from '../ExecutionNode'
21 |
22 | export type LogicalExpressionEvaluate = ExecutionNode & {
23 | leftSpecifier: Accessor[]
24 | rightSpecifier: Accessor[]
25 | shortCircuit: boolean
26 | operator: ESTree.LogicalOperator
27 | outputRegister: Accessor[]
28 | }
29 |
30 | function apply(
31 | animation: LogicalExpressionEvaluate,
32 | environment: EnvironmentState
33 | ) {
34 | // Find left data
35 | let left = resolvePath(
36 | environment,
37 | animation.leftSpecifier,
38 | `${animation.id}_Left`
39 | ) as DataState
40 |
41 | let evaluated: DataState, right: DataState
42 |
43 | if (animation.shortCircuit) {
44 | evaluated = createPrimitiveData(
45 | DataType.Literal,
46 | (left as PrimitiveDataState).value,
47 | `${animation.id}_EvaluatedData`
48 | )
49 | addDataAt(environment, evaluated, [], null)
50 | } else {
51 | // Find right data
52 | right = resolvePath(
53 | environment,
54 | animation.rightSpecifier,
55 | `${animation.id}_Right`
56 | ) as DataState
57 |
58 | evaluated = createPrimitiveData(
59 | DataType.Literal,
60 | eval(`${left.value}${animation.operator}${right.value}`),
61 | `${animation.id}_EvaluatedData`
62 | )
63 | addDataAt(environment, evaluated, [], null)
64 | }
65 |
66 | // Reads and writes
67 |
68 | const leftData = {
69 | location: getMemoryLocation(environment, left).foundLocation,
70 | id: left.id,
71 | }
72 | const rightData =
73 | right == null
74 | ? null
75 | : {
76 | location: getMemoryLocation(environment, right).foundLocation,
77 | id: right.id,
78 | }
79 | const evaluatedData = {
80 | location: getMemoryLocation(environment, evaluated).foundLocation,
81 | id: evaluated.id,
82 | }
83 |
84 | computeReadAndWrites(animation, leftData, rightData, evaluatedData)
85 |
86 | // Point output to evaluated
87 | if (animation.outputRegister.length > 0) {
88 | const output = resolvePath(
89 | environment,
90 | animation.outputRegister,
91 | `${animation.id}_Floating`
92 | ) as DataState
93 | replaceDataWith(
94 | output,
95 | createPrimitiveData(
96 | DataType.ID,
97 | evaluated.id,
98 | `${animation.id}_Placed`
99 | )
100 | )
101 | } else {
102 | removeAt(
103 | environment,
104 | getMemoryLocation(environment, evaluated).foundLocation
105 | )
106 | }
107 |
108 | // Clean up
109 | removeAt(environment, getMemoryLocation(environment, left).foundLocation)
110 | if (!animation.shortCircuit) {
111 | removeAt(
112 | environment,
113 | getMemoryLocation(environment, right).foundLocation
114 | )
115 | }
116 | }
117 |
118 | function computeReadAndWrites(
119 | animation: LogicalExpressionEvaluate,
120 | leftData: DataInfo,
121 | rightData: DataInfo,
122 | evaluatedData: DataInfo
123 | ) {
124 | animation._reads = rightData == null ? [leftData] : [leftData, rightData]
125 | animation._writes = [evaluatedData]
126 | }
127 |
128 | export function logicalExpressionEvaluate(
129 | leftSpecifier: Accessor[],
130 | rightSpecifier: Accessor[],
131 | shortCircuit: boolean,
132 | operator: ESTree.LogicalOperator,
133 | outputRegister: Accessor[]
134 | ): LogicalExpressionEvaluate {
135 | return {
136 | ...createExecutionNode(),
137 | _name: 'LogicalExpressionEvaluate',
138 |
139 | name: `Logical Evaluate ${accessorsToString(
140 | leftSpecifier
141 | )} ${operator} ${accessorsToString(
142 | rightSpecifier
143 | )} onto ${accessorsToString(outputRegister)}`,
144 |
145 | // Attributes
146 | leftSpecifier,
147 | rightSpecifier,
148 | shortCircuit,
149 | operator,
150 | outputRegister,
151 |
152 | // Callbacks
153 | apply,
154 | }
155 | }
156 |
--------------------------------------------------------------------------------
/src/scripts/execution/primitive/Binding/BindAnimation.ts:
--------------------------------------------------------------------------------
1 | import { createPrimitiveData } from '../../../environment/data'
2 | import {
3 | addDataAt,
4 | declareVariable,
5 | getMemoryLocation,
6 | resolvePath,
7 | } from '../../../environment/environment'
8 | import {
9 | Accessor,
10 | EnvironmentState,
11 | } from '../../../environment/EnvironmentState'
12 | import {
13 | DataState,
14 | DataType,
15 | } from '../../../renderer/View/Environment/data/DataState'
16 | import { DataInfo } from '../../graph/ExecutionGraph'
17 | import { createExecutionNode, ExecutionNode } from '../ExecutionNode'
18 |
19 | export type BindAnimation = ExecutionNode & {
20 | identifier: string
21 | existingMemorySpecifier: Accessor[]
22 | }
23 |
24 | function apply(animation: BindAnimation, environment: EnvironmentState) {
25 | let data = null
26 | let location = null
27 |
28 | // Create a reference for variable
29 | const reference = createPrimitiveData(
30 | DataType.Reference,
31 | [],
32 | `${animation.id}_Reference`
33 | )
34 | const loc = addDataAt(environment, reference, [], `${animation.id}_Add`)
35 |
36 | if (animation.existingMemorySpecifier != null) {
37 | data = resolvePath(
38 | environment,
39 | animation.existingMemorySpecifier,
40 | `${animation.id}_Existing`
41 | ) as DataState
42 | location = getMemoryLocation(environment, data).foundLocation
43 | } else {
44 | data = createPrimitiveData(
45 | DataType.Literal,
46 | undefined,
47 | `${animation.id}_BindNew`
48 | )
49 | location = addDataAt(environment, data, [], null)
50 | }
51 |
52 | reference.value = location
53 |
54 | declareVariable(environment, animation.identifier, loc)
55 |
56 | computeReadAndWrites(
57 | animation,
58 | { location, id: data.id },
59 | { location: loc, id: reference.id },
60 | { location: loc, id: animation.identifier },
61 | animation.existingMemorySpecifier == null
62 | )
63 | }
64 |
65 | // TODO also add variable identifier as write
66 | function computeReadAndWrites(
67 | animation: BindAnimation,
68 | data: DataInfo,
69 | reference: DataInfo,
70 | variable: DataInfo,
71 | dataCreated: boolean
72 | ) {
73 | animation._reads = [data]
74 | animation._writes = dataCreated
75 | ? [variable, reference, data]
76 | : [variable, reference]
77 | }
78 |
79 | export function bindAnimation(
80 | identifier: string,
81 | existingMemorySpecifier: Accessor[] = null
82 | ): BindAnimation {
83 | return {
84 | ...createExecutionNode(null),
85 | _name: 'BindAnimation',
86 |
87 | // name: `Bind Variable (${identifier}), with data at ${accessorsToString(
88 | // existingMemorySpecifier ?? []
89 | // )}`,
90 | name: `Bind Variable (${identifier})`,
91 |
92 | // Attributes
93 | identifier,
94 | existingMemorySpecifier,
95 |
96 | // Callbacks
97 | apply,
98 | }
99 | }
100 |
--------------------------------------------------------------------------------
/src/scripts/execution/primitive/Binding/BindFunctionAnimation.ts:
--------------------------------------------------------------------------------
1 | import * as ESTree from 'estree'
2 | import { createPrimitiveData } from '../../../environment/data'
3 | import { addDataAt, declareVariable } from '../../../environment/environment'
4 | import { EnvironmentState } from '../../../environment/EnvironmentState'
5 | import { DataType } from '../../../renderer/View/Environment/data/DataState'
6 | import { DataInfo } from '../../graph/ExecutionGraph'
7 | import { createExecutionNode, ExecutionNode } from '../ExecutionNode'
8 |
9 | export type BindFunctionAnimation = ExecutionNode & {
10 | identifier: string
11 | ast: ESTree.FunctionDeclaration
12 | }
13 |
14 | function apply(
15 | animation: BindFunctionAnimation,
16 | environment: EnvironmentState
17 | ) {
18 | // Create a reference for variable
19 | const reference = createPrimitiveData(
20 | DataType.Reference,
21 | [],
22 | `${animation.id}_ReferenceFunction`
23 | )
24 | const referenceLocation = addDataAt(
25 | environment,
26 | reference,
27 | [],
28 | `${animation.id}_AddFunction`
29 | )
30 |
31 | const data = createPrimitiveData(
32 | DataType.Function,
33 | JSON.stringify(animation.ast),
34 | `${animation.id}_BindFunctionNew`
35 | )
36 | const location = addDataAt(environment, data, [], null)
37 |
38 | reference.value = location
39 |
40 | declareVariable(environment, animation.identifier, referenceLocation)
41 | computeReadAndWrites(
42 | animation,
43 | { location, id: data.id },
44 | { location, id: animation.identifier }
45 | )
46 | }
47 |
48 | function computeReadAndWrites(
49 | animation: BindFunctionAnimation,
50 | funcData: DataInfo,
51 | funcIdentifier: DataInfo
52 | ) {
53 | animation._reads = []
54 | animation._writes = [funcData, funcIdentifier]
55 | }
56 |
57 | export function bindFunctionAnimation(
58 | identifier: string,
59 | ast: ESTree.FunctionDeclaration = null
60 | ): BindFunctionAnimation {
61 | return {
62 | ...createExecutionNode(null),
63 | _name: 'BindFunctionAnimation',
64 | name: `Bind Function (${identifier})`,
65 |
66 | // Attributes
67 | identifier,
68 | ast,
69 |
70 | // Callbacks
71 | apply,
72 | }
73 | }
74 |
--------------------------------------------------------------------------------
/src/scripts/execution/primitive/Container/ArrayStartAnimation.ts:
--------------------------------------------------------------------------------
1 | import {
2 | createObjectData,
3 | createPrimitiveData,
4 | replaceDataWith,
5 | } from '../../../environment/data'
6 | import { addDataAt, resolvePath } from '../../../environment/environment'
7 | import {
8 | Accessor,
9 | accessorsToString,
10 | EnvironmentState,
11 | } from '../../../environment/EnvironmentState'
12 | import {
13 | DataState,
14 | DataType,
15 | } from '../../../renderer/View/Environment/data/DataState'
16 | import { DataInfo } from '../../graph/ExecutionGraph'
17 | import { createExecutionNode, ExecutionNode } from '../ExecutionNode'
18 |
19 | export type ArrayStartAnimation = ExecutionNode & {
20 | dataSpecifier: Accessor[]
21 | }
22 |
23 | function apply(animation: ArrayStartAnimation, environment: EnvironmentState) {
24 | // Create a new array somewhere in memory
25 | const data = createObjectData([], `${animation.id}_CreateArray`)
26 |
27 | const loc = addDataAt(environment, data, [], null)
28 |
29 | computeReadAndWrites(animation, {
30 | location: loc,
31 | id: data.id,
32 | })
33 |
34 | // Point the output register to the newly created data
35 | const outputRegister = resolvePath(
36 | environment,
37 | animation.dataSpecifier,
38 | `${animation.id}_Floating`
39 | ) as DataState
40 | replaceDataWith(
41 | outputRegister,
42 | createPrimitiveData(
43 | DataType.ID,
44 | data.id,
45 | `${animation.id}_OutputRegister`
46 | )
47 | )
48 | }
49 |
50 | function computeReadAndWrites(animation: ArrayStartAnimation, data: DataInfo) {
51 | animation._reads = [data]
52 | animation._writes = [data]
53 | }
54 |
55 | export function arrayStartAnimation(
56 | dataSpecifier: Accessor[]
57 | ): ArrayStartAnimation {
58 | return {
59 | ...createExecutionNode(null),
60 |
61 | _name: 'ArrayStartAnimation',
62 |
63 | name: `Initialize array at ${accessorsToString(dataSpecifier)}`,
64 |
65 | // Attributes
66 | dataSpecifier,
67 |
68 | // Callbacks
69 | apply,
70 | }
71 | }
72 |
--------------------------------------------------------------------------------
/src/scripts/execution/primitive/Container/ObjectStartAnimation.ts:
--------------------------------------------------------------------------------
1 | import {
2 | createObjectData,
3 | createPrimitiveData,
4 | replaceDataWith,
5 | } from '../../../environment/data'
6 | import { addDataAt, resolvePath } from '../../../environment/environment'
7 | import {
8 | Accessor,
9 | accessorsToString,
10 | EnvironmentState,
11 | } from '../../../environment/EnvironmentState'
12 | import {
13 | DataState,
14 | DataType,
15 | } from '../../../renderer/View/Environment/data/DataState'
16 | import { DataInfo } from '../../graph/ExecutionGraph'
17 | import { createExecutionNode, ExecutionNode } from '../ExecutionNode'
18 |
19 | export type ObjectStartAnimation = ExecutionNode & {
20 | dataSpecifier: Accessor[]
21 | }
22 |
23 | function apply(animation: ObjectStartAnimation, environment: EnvironmentState) {
24 | // Create a new array somewhere in memory
25 | const data = createObjectData({}, `${animation.id}_CreateObject`)
26 |
27 | const loc = addDataAt(environment, data, [], null)
28 |
29 | computeReadAndWrites(animation, {
30 | location: loc,
31 | id: data.id,
32 | })
33 |
34 | // Point the output register to the newly created data
35 | const outputRegister = resolvePath(
36 | environment,
37 | animation.dataSpecifier,
38 | `${animation.id}_Floating`
39 | ) as DataState
40 | replaceDataWith(
41 | outputRegister,
42 | createPrimitiveData(
43 | DataType.ID,
44 | data.id,
45 | `${animation.id}_OutputRegister`
46 | )
47 | )
48 | }
49 |
50 | function computeReadAndWrites(animation: ObjectStartAnimation, data: DataInfo) {
51 | animation._reads = [data]
52 | animation._writes = [data]
53 | }
54 |
55 | export function objectStartAnimation(
56 | dataSpecifier: Accessor[]
57 | ): ObjectStartAnimation {
58 | return {
59 | ...createExecutionNode(null),
60 |
61 | _name: 'ObjectStartAnimation',
62 |
63 | name: `Initialize object at ${accessorsToString(dataSpecifier)}`,
64 |
65 | // Attributes
66 | dataSpecifier,
67 |
68 | // Callbacks
69 | apply,
70 | }
71 | }
72 |
--------------------------------------------------------------------------------
/src/scripts/execution/primitive/Data/ConsumeDataAnimation.ts:
--------------------------------------------------------------------------------
1 | import {
2 | getMemoryLocation,
3 | removeAt,
4 | resolvePath,
5 | } from '../../../environment/environment'
6 | import {
7 | Accessor,
8 | EnvironmentState,
9 | } from '../../../environment/EnvironmentState'
10 | import { DataState } from '../../../renderer/View/Environment/data/DataState'
11 | import { DataInfo } from '../../graph/ExecutionGraph'
12 | import { createExecutionNode, ExecutionNode } from '../ExecutionNode'
13 |
14 | export type ConsumeDataAnimation = ExecutionNode & {
15 | register: Accessor[]
16 | }
17 |
18 | function apply(animation: ConsumeDataAnimation, environment: EnvironmentState) {
19 | // Get data
20 | const data = resolvePath(
21 | environment,
22 | animation.register,
23 | `${animation.id}_Property`,
24 | null,
25 | {
26 | noResolvingReference: true,
27 | }
28 | ) as DataState
29 |
30 | const dataLocation = getMemoryLocation(environment, data).foundLocation
31 |
32 | // Consume data
33 | removeAt(environment, dataLocation)
34 |
35 | computeReadAndWrites(animation, {
36 | location: dataLocation,
37 | id: data.id,
38 | })
39 | }
40 |
41 | function computeReadAndWrites(animation: ConsumeDataAnimation, data: DataInfo) {
42 | animation._reads = []
43 | animation._writes = [data]
44 | }
45 |
46 | export function consumeDataAnimation(
47 | register: Accessor[]
48 | ): ConsumeDataAnimation {
49 | return {
50 | ...createExecutionNode(null),
51 | _name: 'ConsumeDataAnimation',
52 |
53 | name: 'ConsumeDataAnimation',
54 |
55 | // Attributes
56 | register,
57 |
58 | // Callbacks
59 | apply,
60 | }
61 | }
62 |
--------------------------------------------------------------------------------
/src/scripts/execution/primitive/Data/CopyDataAnimation.ts:
--------------------------------------------------------------------------------
1 | import { cloneData, createPrimitiveData, replaceDataWith } from '../../../environment/data'
2 | import { addDataAt, getMemoryLocation, resolvePath } from '../../../environment/environment'
3 | import { Accessor, accessorsToString, AccessorType, EnvironmentState } from '../../../environment/EnvironmentState'
4 | import { DataState, DataType } from '../../../renderer/View/Environment/data/DataState'
5 | import { DataInfo } from '../../graph/ExecutionGraph'
6 | import { createExecutionNode, ExecutionNode } from '../ExecutionNode'
7 |
8 | export type CopyDataAnimation = ExecutionNode & {
9 | dataSpecifier: Accessor[]
10 | outputRegister: Accessor[]
11 | hardCopy: boolean
12 | }
13 |
14 | function apply(animation: CopyDataAnimation, environment: EnvironmentState) {
15 | const data = resolvePath(environment, animation.dataSpecifier, `${animation.id}_Data`) as DataState
16 | const copy = cloneData(data, false, `${animation.id}_Copy`)
17 | const location = addDataAt(environment, copy, [], null)
18 |
19 | // Put it in the floating stack
20 | const register = resolvePath(environment, animation.outputRegister, `${animation.id}_Floating`) as DataState
21 | replaceDataWith(register, createPrimitiveData(DataType.ID, copy.id, `${animation.id}_Floating`))
22 |
23 | if (animation.hardCopy) {
24 | data.value = undefined
25 | }
26 |
27 | computeReadAndWrites(
28 | animation,
29 | {
30 | location: getMemoryLocation(environment, data).foundLocation,
31 | id: data.id,
32 | },
33 | { location, id: copy.id }
34 | )
35 |
36 | if (animation.dataSpecifier[0].type == AccessorType.Symbol) {
37 | animation._reads.push({
38 | id: animation.dataSpecifier[0].value,
39 | location: animation.dataSpecifier,
40 | })
41 | }
42 | }
43 |
44 | function computeReadAndWrites(animation: CopyDataAnimation, original: DataInfo, copy: DataInfo) {
45 | animation._reads = [original]
46 | animation._writes = [copy]
47 | }
48 |
49 | export function copyDataAnimation(
50 | dataSpecifier: Accessor[],
51 | outputRegister: Accessor[],
52 | hardCopy: boolean = false
53 | ): CopyDataAnimation {
54 | return {
55 | ...createExecutionNode(null),
56 | _name: 'CopyDataAnimation',
57 |
58 | // name: `Copy ${accessorsToString(dataSpecifier)} to ${accessorsToString(outputRegister)}`,
59 | name: `Copy ${accessorsToString(dataSpecifier)}`,
60 |
61 | // Attributes
62 | dataSpecifier,
63 | outputRegister,
64 | hardCopy,
65 |
66 | // Callbacks
67 | apply,
68 | }
69 | }
70 |
--------------------------------------------------------------------------------
/src/scripts/execution/primitive/Data/CopyReferenceAnimation.ts:
--------------------------------------------------------------------------------
1 | import { createPrimitiveData, replaceDataWith } from '../../../environment/data'
2 | import {
3 | addDataAt,
4 | getMemoryLocation,
5 | resolvePath,
6 | } from '../../../environment/environment'
7 | import {
8 | Accessor,
9 | accessorsToString,
10 | EnvironmentState,
11 | } from '../../../environment/EnvironmentState'
12 | import {
13 | DataState,
14 | DataType,
15 | } from '../../../renderer/View/Environment/data/DataState'
16 | import { DataInfo } from '../../graph/ExecutionGraph'
17 | import { createExecutionNode, ExecutionNode } from '../ExecutionNode'
18 |
19 | export type CopyReferenceAnimation = ExecutionNode & {
20 | dataSpecifier: Accessor[]
21 | outputRegister: Accessor[]
22 | }
23 |
24 | function apply(
25 | animation: CopyReferenceAnimation,
26 | environment: EnvironmentState
27 | ) {
28 | const data = resolvePath(
29 | environment,
30 | animation.dataSpecifier,
31 | `${animation.id}_Data`
32 | ) as DataState
33 | const reference = createPrimitiveData(
34 | DataType.Reference,
35 | getMemoryLocation(environment, data).foundLocation,
36 | `${animation.id}_Reference`
37 | )
38 | const loc = addDataAt(environment, reference, [], null)
39 |
40 | computeReadAndWrites(
41 | animation,
42 | {
43 | location: getMemoryLocation(environment, data).foundLocation,
44 | id: data.id,
45 | },
46 | { location: loc, id: reference.id }
47 | )
48 |
49 | // Put it in the floating stack
50 | const register = resolvePath(
51 | environment,
52 | animation.outputRegister,
53 | `${animation.id}_FloatingRegister`
54 | ) as DataState
55 | replaceDataWith(
56 | register,
57 | createPrimitiveData(
58 | DataType.ID,
59 | reference.id,
60 | `${animation.id}_Floating`
61 | )
62 | )
63 | }
64 |
65 | function computeReadAndWrites(
66 | animation: CopyReferenceAnimation,
67 | original: DataInfo,
68 | copy: DataInfo
69 | ) {
70 | animation._reads = [original]
71 | animation._writes = [copy]
72 | }
73 |
74 | export function copyReferenceAnimation(
75 | dataSpecifier: Accessor[],
76 | outputRegister: Accessor[]
77 | ): CopyReferenceAnimation {
78 | return {
79 | ...createExecutionNode(null),
80 | _name: 'CopyReferenceAnimation',
81 |
82 | name: `CopyReference ${accessorsToString(
83 | dataSpecifier
84 | )} to ${accessorsToString(outputRegister)}`,
85 |
86 | // Attributes
87 | dataSpecifier,
88 | outputRegister,
89 |
90 | // Callbacks
91 | apply,
92 | }
93 | }
94 |
--------------------------------------------------------------------------------
/src/scripts/execution/primitive/Data/CreateLiteralAnimation.ts:
--------------------------------------------------------------------------------
1 | import { createPrimitiveData, replaceDataWith } from '../../../environment/data'
2 | import { addDataAt, resolvePath } from '../../../environment/environment'
3 | import {
4 | Accessor,
5 | accessorsToString,
6 | EnvironmentState,
7 | } from '../../../environment/EnvironmentState'
8 | import {
9 | DataState,
10 | DataType,
11 | } from '../../../renderer/View/Environment/data/DataState'
12 | import { DataInfo } from '../../graph/ExecutionGraph'
13 | import { createExecutionNode, ExecutionNode } from '../ExecutionNode'
14 |
15 | export type CreateLiteralAnimation = ExecutionNode & {
16 | value: string | number | bigint | boolean | RegExp
17 | locationHint: Accessor[]
18 | outputRegister: Accessor[]
19 | }
20 |
21 | /**
22 | * Puts data into context.output register.
23 | * @param animation
24 | * @param view
25 | * @param options
26 | */
27 | function apply(
28 | animation: CreateLiteralAnimation,
29 | environment: EnvironmentState
30 | ) {
31 | const data = createPrimitiveData(
32 | DataType.Literal,
33 | animation.value as number | string | boolean,
34 | `${animation.id}_Create`
35 | )
36 |
37 | // Add the newly created data to environment
38 | const loc = addDataAt(environment, data, [], null)
39 |
40 | computeReadAndWrites(animation, {
41 | location: loc,
42 | id: data.id,
43 | })
44 |
45 | // Point the output register to the newly created data
46 | const outputRegister = resolvePath(
47 | environment,
48 | animation.outputRegister,
49 | `${animation.id}_Floating`
50 | ) as DataState
51 | replaceDataWith(
52 | outputRegister,
53 | createPrimitiveData(
54 | DataType.ID,
55 | data.id,
56 | `${animation.id}_OutputRegister`
57 | )
58 | )
59 | }
60 |
61 | function computeReadAndWrites(
62 | animation: CreateLiteralAnimation,
63 | data: DataInfo
64 | ) {
65 | animation._reads = []
66 | animation._writes = [data]
67 | }
68 |
69 | export function createLiteralAnimation(
70 | value: string | number | bigint | boolean | RegExp,
71 | outputRegister: Accessor[],
72 | locationHint: Accessor[]
73 | ): CreateLiteralAnimation {
74 | return {
75 | ...createExecutionNode(null),
76 | _name: 'CreateLiteralAnimation',
77 | name: `Create Literal ${value} at ${accessorsToString(outputRegister)}`,
78 |
79 | // Attributes
80 | value,
81 | outputRegister,
82 | locationHint,
83 |
84 | // Callbacks
85 | apply,
86 | }
87 | }
88 |
--------------------------------------------------------------------------------
/src/scripts/execution/primitive/Data/FindVariableAnimation.ts:
--------------------------------------------------------------------------------
1 | import { createPrimitiveData, replaceDataWith } from '../../../environment/data'
2 | import { getMemoryLocation, resolvePath } from '../../../environment/environment'
3 | import { Accessor, AccessorType, EnvironmentState } from '../../../environment/EnvironmentState'
4 | import { DataState, DataType } from '../../../renderer/View/Environment/data/DataState'
5 | import { createExecutionNode, ExecutionNode } from '../ExecutionNode'
6 |
7 | export type FindVariableAnimation = ExecutionNode & {
8 | identifier: string
9 | outputRegister: Accessor[]
10 | }
11 |
12 | function apply(animation: FindVariableAnimation, environment: EnvironmentState) {
13 | const reference = resolvePath(
14 | environment,
15 | [{ type: AccessorType.Symbol, value: animation.identifier }],
16 | null,
17 | null,
18 | { noResolvingReference: true }
19 | )
20 |
21 | // Put it in the floating stack
22 | const register = resolvePath(environment, animation.outputRegister, `${animation.id}_FloatingRegister`) as DataState
23 |
24 | replaceDataWith(register, createPrimitiveData(DataType.ID, reference.id, `${animation.id}_Floating`))
25 |
26 | computeReadAndWrites(animation, {
27 | location: getMemoryLocation(environment, reference as DataState).foundLocation!,
28 | id: reference.id,
29 | })
30 | }
31 |
32 | /**
33 | * TODO
34 | */
35 | function computeReadAndWrites(animation: FindVariableAnimation, ref: DataInfo) {
36 | animation._reads = [ref]
37 | animation._writes = []
38 | }
39 |
40 | export function findVariableAnimation(identifier: string, outputRegister: Accessor[]): FindVariableAnimation {
41 | return {
42 | ...createExecutionNode(null),
43 | _name: 'FindVariableAnimation',
44 |
45 | name: `Locating ${identifier}`,
46 |
47 | // Attributes
48 | identifier,
49 | outputRegister,
50 |
51 | // Callbacks
52 | apply,
53 | }
54 | }
55 |
--------------------------------------------------------------------------------
/src/scripts/execution/primitive/Data/FloatAnimation.ts:
--------------------------------------------------------------------------------
1 | import {
2 | getMemoryLocation,
3 | resolvePath,
4 | } from '../../../environment/environment'
5 | import {
6 | Accessor,
7 | EnvironmentState,
8 | } from '../../../environment/EnvironmentState'
9 | import { DataState } from '../../../renderer/View/Environment/data/DataState'
10 | import { DataInfo } from '../../graph/ExecutionGraph'
11 | import { createExecutionNode, ExecutionNode } from '../ExecutionNode'
12 |
13 | export type FloatAnimation = ExecutionNode & {
14 | dataSpecifier: Accessor[]
15 | }
16 |
17 | function apply(animation: FloatAnimation, environment: EnvironmentState) {
18 | const data = resolvePath(
19 | environment,
20 | animation.dataSpecifier,
21 | null
22 | ) as DataState
23 |
24 | computeReadAndWrites(animation, {
25 | location: getMemoryLocation(environment, data).foundLocation,
26 | id: data.id,
27 | })
28 | }
29 |
30 | function computeReadAndWrites(animation: FloatAnimation, data: DataInfo) {
31 | animation._reads = [data]
32 | animation._writes = []
33 | }
34 |
35 | export function floatAnimation(dataSpecifier: Accessor[]): FloatAnimation {
36 | return {
37 | ...createExecutionNode(null),
38 | _name: 'FloatAnimation',
39 |
40 | name: 'FloatAnimation',
41 |
42 | // Attributes
43 | dataSpecifier,
44 |
45 | // Callbacks
46 | apply,
47 | }
48 | }
49 |
--------------------------------------------------------------------------------
/src/scripts/execution/primitive/Data/GetMember.ts:
--------------------------------------------------------------------------------
1 | import {
2 | cloneData,
3 | createPrimitiveData,
4 | replaceDataWith,
5 | } from '../../../environment/data'
6 | import {
7 | addDataAt,
8 | getMemoryLocation,
9 | removeAt,
10 | resolvePath,
11 | } from '../../../environment/environment'
12 | import {
13 | Accessor,
14 | EnvironmentState,
15 | } from '../../../environment/EnvironmentState'
16 | import {
17 | DataState,
18 | DataType,
19 | instanceOfData,
20 | } from '../../../renderer/View/Environment/data/DataState'
21 | import { DataInfo } from '../../graph/ExecutionGraph'
22 | import { createExecutionNode, ExecutionNode } from '../ExecutionNode'
23 |
24 | export type GetMember = ExecutionNode & {
25 | objectRegister: Accessor[]
26 | propertyRegister?: Accessor[] // Only if computed
27 | outputRegister: Accessor[]
28 | }
29 |
30 | function apply(animation: GetMember, environment: EnvironmentState) {
31 | // Get object
32 | const object = resolvePath(
33 | environment,
34 | animation.objectRegister,
35 | `${animation.id}_Object`
36 | ) as DataState
37 |
38 | // Get property
39 | const property = resolvePath(
40 | environment,
41 | animation.propertyRegister,
42 | `${animation.id}_Property`
43 | ) as DataState
44 | const propertyLocation = getMemoryLocation(
45 | environment,
46 | property
47 | ).foundLocation
48 |
49 | // Index into it
50 | const original = (object.value as DataState[])[
51 | property.value as number | string
52 | ]
53 |
54 | let copy: DataState
55 |
56 | if (instanceOfData(original)) {
57 | // Create a copy of data
58 | copy = cloneData(original, false, `${animation.id}_Copy`)
59 | } else {
60 | // Create a new data
61 | copy = createPrimitiveData(
62 | DataType.Literal,
63 | original,
64 | `${animation.id}_Copy`
65 | )
66 | }
67 |
68 | const location = addDataAt(environment, copy, [], null)
69 |
70 | // Consume property
71 | removeAt(
72 | environment,
73 | getMemoryLocation(environment, property).foundLocation
74 | )
75 |
76 | // Remove object (reference)
77 | const objectReference = resolvePath(
78 | environment,
79 | animation.objectRegister,
80 | `${animation.id}_Object`,
81 | null,
82 | {
83 | noResolvingReference: true,
84 | }
85 | ) as DataState
86 | const objectLocation = getMemoryLocation(
87 | environment,
88 | objectReference
89 | ).foundLocation
90 |
91 | removeAt(
92 | environment,
93 | getMemoryLocation(environment, objectReference).foundLocation,
94 | {
95 | noResolvingReference: true,
96 | }
97 | )
98 |
99 | const propertyData = {
100 | location: propertyLocation,
101 | id: property.id,
102 | }
103 |
104 | const objectData = {
105 | location: objectLocation,
106 | id: objectReference.id,
107 | }
108 |
109 | const copyData = { location, id: copy.id }
110 |
111 | if (instanceOfData(original)) {
112 | computeReadAndWrites(animation, objectData, propertyData, copyData, {
113 | location: getMemoryLocation(environment, original).foundLocation,
114 | id: original.id,
115 | })
116 | } else {
117 | computeReadAndWrites(animation, objectData, propertyData, copyData)
118 | }
119 |
120 | // Point the output register to the newly created data
121 | const outputRegister = resolvePath(
122 | environment,
123 | animation.outputRegister,
124 | `${animation.id}_Floating`
125 | ) as DataState
126 | replaceDataWith(
127 | outputRegister,
128 | createPrimitiveData(
129 | DataType.ID,
130 | copy.id,
131 | `${animation.id}_OutputRegister`
132 | )
133 | )
134 | }
135 |
136 | function computeReadAndWrites(
137 | animation: GetMember,
138 | obj: DataInfo,
139 | property: DataInfo,
140 | copy: DataInfo,
141 | original: DataInfo = null
142 | ) {
143 | animation._reads =
144 | original != null ? [original, obj, property] : [obj, property]
145 | animation._writes = [copy, obj]
146 | }
147 |
148 | export function getMember(
149 | objectRegister: Accessor[],
150 | propertyRegister: Accessor[],
151 | outputRegister: Accessor[]
152 | ): GetMember {
153 | return {
154 | ...createExecutionNode(null),
155 | _name: 'GetMember',
156 |
157 | name: 'GetMember',
158 |
159 | // Attributes
160 | objectRegister,
161 | propertyRegister,
162 | outputRegister,
163 |
164 | // Callbacks
165 | apply,
166 | }
167 | }
168 |
--------------------------------------------------------------------------------
/src/scripts/execution/primitive/Data/MoveAndPlaceAnimation.ts:
--------------------------------------------------------------------------------
1 | import { replaceDataWith } from '../../../environment/data'
2 | import {
3 | getMemoryLocation,
4 | removeAt,
5 | resolvePath,
6 | } from '../../../environment/environment'
7 | import {
8 | Accessor,
9 | accessorsToString,
10 | EnvironmentState,
11 | instanceOfEnvironment,
12 | } from '../../../environment/EnvironmentState'
13 | import {
14 | DataState,
15 | instanceOfData,
16 | } from '../../../renderer/View/Environment/data/DataState'
17 | import { DataInfo } from '../../graph/ExecutionGraph'
18 | import { createExecutionNode, ExecutionNode } from '../ExecutionNode'
19 |
20 | /**
21 | * Move and place animation or just place if the from
22 | * is not defined.
23 | */
24 | export type MoveAndPlaceAnimation = Omit & {
25 | inputSpecifier: Accessor[]
26 | outputSpecifier: Accessor[]
27 |
28 | apply: (
29 | animation: MoveAndPlaceAnimation,
30 | environment: EnvironmentState
31 | ) => void
32 | }
33 |
34 | function apply(
35 | animation: MoveAndPlaceAnimation,
36 | environment: EnvironmentState
37 | ) {
38 | const from = resolvePath(
39 | environment,
40 | animation.inputSpecifier,
41 | null,
42 | null,
43 | {
44 | noResolvingReference: true,
45 | }
46 | ) as DataState
47 |
48 | const to = resolvePath(
49 | environment,
50 | animation.outputSpecifier,
51 | `${animation.id}_To(${accessorsToString(animation.outputSpecifier)})`
52 | )
53 |
54 | const fromData = {
55 | location: getMemoryLocation(environment, from).foundLocation,
56 | id: from.id,
57 | }
58 |
59 | if (instanceOfEnvironment(to)) {
60 | computeReadAndWrites(animation, fromData, null)
61 | } else {
62 | computeReadAndWrites(animation, fromData, {
63 | location: getMemoryLocation(environment, to).foundLocation,
64 | id: to.id,
65 | })
66 | }
67 |
68 | if (instanceOfData(to)) {
69 | removeAt(
70 | environment,
71 | getMemoryLocation(environment, from).foundLocation
72 | )
73 | replaceDataWith(to, from, { frame: true, id: true })
74 | }
75 | }
76 |
77 | function computeReadAndWrites(
78 | animation: MoveAndPlaceAnimation,
79 | inputData: DataInfo,
80 | outputData: DataInfo | null
81 | ) {
82 | animation._reads = [inputData]
83 | animation._writes = outputData ? [outputData, inputData] : [inputData]
84 | }
85 |
86 | export function moveAndPlaceAnimation(
87 | inputSpecifier: Accessor[],
88 | outputSpecifier: Accessor[]
89 | ): MoveAndPlaceAnimation {
90 | return {
91 | ...createExecutionNode(),
92 | _name: 'MoveAndPlaceAnimation',
93 |
94 | name: `Move data at ${accessorsToString(
95 | inputSpecifier
96 | )} onto ${accessorsToString(outputSpecifier)}`,
97 |
98 | // Attributes
99 | inputSpecifier,
100 | outputSpecifier,
101 |
102 | // Callbacks
103 | apply,
104 | }
105 | }
106 |
--------------------------------------------------------------------------------
/src/scripts/execution/primitive/Data/UpdateAnimation.ts:
--------------------------------------------------------------------------------
1 | import * as ESTree from 'estree'
2 | import {
3 | getMemoryLocation,
4 | resolvePath,
5 | } from '../../../environment/environment'
6 | import {
7 | Accessor,
8 | EnvironmentState,
9 | } from '../../../environment/EnvironmentState'
10 | import { DataState } from '../../../renderer/View/Environment/data/DataState'
11 | import { DataInfo } from '../../graph/ExecutionGraph'
12 | import { createExecutionNode, ExecutionNode } from '../ExecutionNode'
13 |
14 | export type UpdateAnimation = ExecutionNode & {
15 | dataSpecifier: Accessor[]
16 | operator: ESTree.UpdateOperator
17 | }
18 |
19 | function apply(animation: UpdateAnimation, environment: EnvironmentState) {
20 | const data = resolvePath(
21 | environment,
22 | animation.dataSpecifier,
23 | `${animation.id}_Data`
24 | ) as DataState
25 |
26 | switch (animation.operator) {
27 | case '++':
28 | data.value = (data.value as number) + 1
29 | break
30 | case '--':
31 | data.value = (data.value as number) - 1
32 | break
33 | default:
34 | console.warn('Unrecognized update operator', animation.operator)
35 | }
36 |
37 | computeReadAndWrites(animation, {
38 | id: data.id,
39 | location: getMemoryLocation(environment, data).foundLocation,
40 | })
41 | }
42 |
43 | function computeReadAndWrites(animation: UpdateAnimation, data: DataInfo) {
44 | animation._reads = [data]
45 | animation._writes = [data]
46 | }
47 |
48 | export function updateAnimation(
49 | dataSpecifier: Accessor[],
50 | operator: ESTree.UpdateOperator
51 | ): UpdateAnimation {
52 | return {
53 | ...createExecutionNode(null),
54 | _name: 'UpdateAnimation',
55 |
56 | name: 'UpdateAnimation',
57 |
58 | // Attributes
59 | dataSpecifier,
60 | operator,
61 |
62 | // Callbacks
63 | apply,
64 | }
65 | }
66 |
--------------------------------------------------------------------------------
/src/scripts/execution/primitive/ExecutionNode.ts:
--------------------------------------------------------------------------------
1 | import * as ESTree from 'estree'
2 | import { Accessor, EnvironmentState } from '../../environment/EnvironmentState'
3 | import { DataInfo } from '../graph/ExecutionGraph'
4 |
5 | export type NodeData = {
6 | location?: ESTree.SourceLocation
7 | type?: string
8 | preLabel?: string
9 | hasLineBreak?: boolean
10 | }
11 |
12 | export type ChunkNodeData = NodeData & {}
13 |
14 | export enum ControlOutput {
15 | None = 'None',
16 | Break = 'Break',
17 | Continue = 'Continue',
18 | Return = 'Return',
19 | }
20 |
21 | export type ControlOutputData = {
22 | output: ControlOutput
23 | }
24 |
25 | export type ReturnData = {
26 | frame: number
27 | register: Accessor[]
28 | environmentID: string
29 | }
30 |
31 | export type ExecutionContext = {
32 | locationHint?: Accessor[] // Hint of where to place data (for aesthetic reasons)
33 | outputRegister?: Accessor[] // Register to place data at
34 | feed?: boolean // Puts the location of data instead of data itself - used for feeding (assignment / declaration)'
35 | args?: Accessor[][] // Arguments to pass to the function
36 | object?: Accessor[] // Object to pass to the function
37 | controlOutput?: ControlOutputData
38 | returnData?: ReturnData // Register to place the return value at
39 | }
40 |
41 | export type ExecutionNode = {
42 | _type: 'ExecutionNode'
43 | _name: string // Name of the node
44 |
45 | name: string // Name of operation
46 | id: string
47 |
48 | precondition: EnvironmentState | null
49 | postcondition: EnvironmentState | null
50 |
51 | _reads: DataInfo[] | null
52 | _writes: DataInfo[] | null
53 | nodeData: NodeData
54 |
55 | apply: (animation: ExecutionNode, environment: EnvironmentState) => void
56 | }
57 |
58 | export function instanceOfExecutionNode(animation: any): animation is ExecutionNode {
59 | return animation._type == 'ExecutionNode'
60 | }
61 |
62 | let __EXECUTION_NODE_ID = 0
63 | export function createExecutionNode(nodeData: NodeData = {}): ExecutionNode {
64 | return {
65 | _type: 'ExecutionNode',
66 |
67 | _name: 'ExecutionNode',
68 | name: 'Animation Node',
69 |
70 | id: `AN(${++__EXECUTION_NODE_ID})`,
71 | precondition: null,
72 | postcondition: null,
73 |
74 | _reads: null,
75 | _writes: null,
76 |
77 | nodeData,
78 |
79 | apply: () => console.warn('[ExecutionNode] Non-implemented apply callback'),
80 | }
81 | }
82 |
--------------------------------------------------------------------------------
/src/scripts/execution/primitive/Functions/Native/Array/ArrayPushAnimation.ts:
--------------------------------------------------------------------------------
1 | import { cloneData } from '../../../../../environment/data'
2 | import {
3 | getMemoryLocation,
4 | resolvePath,
5 | } from '../../../../../environment/environment'
6 | import {
7 | Accessor,
8 | EnvironmentState,
9 | } from '../../../../../environment/EnvironmentState'
10 | import { DataState } from '../../../../../renderer/View/Environment/data/DataState'
11 | import { DataInfo } from '../../../../graph/ExecutionGraph'
12 | import { createExecutionNode, ExecutionNode } from '../../../ExecutionNode'
13 |
14 | export type ArrayPushAnimation = ExecutionNode & {
15 | object: Accessor[]
16 | args: Accessor[][]
17 | }
18 |
19 | function apply(animation: ArrayPushAnimation, environment: EnvironmentState) {
20 | const object = resolvePath(
21 | environment,
22 | animation.object,
23 | `${animation.id}_Data`
24 | ) as DataState
25 |
26 | const args = animation.args.map(
27 | (arg) =>
28 | resolvePath(environment, arg, `${animation.id}_Data`) as DataState
29 | )
30 |
31 | const objectValue = object.value as DataState[]
32 |
33 | // TODO: Copy data
34 | let originalLength = objectValue.length
35 | for (let i = 0; i < args.length; i++) {
36 | objectValue[objectValue.length] = cloneData(
37 | args[i],
38 | false,
39 | `${animation.id}_Arg${i}`
40 | )
41 | }
42 |
43 | computeReadAndWrites(
44 | animation,
45 | {
46 | location: getMemoryLocation(environment, object).foundLocation,
47 | id: object.id,
48 | },
49 | args.map((arg) => {
50 | return {
51 | location: getMemoryLocation(environment, arg).foundLocation,
52 | id: arg.id,
53 | }
54 | }),
55 | args.map((arg, i) => {
56 | const element = objectValue[originalLength + i]
57 | return {
58 | location: getMemoryLocation(environment, element).foundLocation,
59 | id: element.id,
60 | }
61 | })
62 | )
63 | }
64 |
65 | function computeReadAndWrites(
66 | animation: ArrayPushAnimation,
67 | object: DataInfo,
68 | args: DataInfo[],
69 | elements: DataInfo[]
70 | ) {
71 | animation._reads = [...args]
72 | animation._writes = [object, ...elements]
73 | }
74 |
75 | export function ArrayPushAnimation(
76 | object: Accessor[],
77 | args: Accessor[][]
78 | ): ArrayPushAnimation {
79 | return {
80 | ...createExecutionNode(null),
81 | _name: 'ArrayPushAnimation',
82 |
83 | name: 'ArrayPushAnimation',
84 | object,
85 | args,
86 |
87 | // Callbacks
88 | apply,
89 | }
90 | }
91 |
--------------------------------------------------------------------------------
/src/scripts/execution/primitive/Functions/Native/Math/FloorAnimation.ts:
--------------------------------------------------------------------------------
1 | import {
2 | cloneData,
3 | createPrimitiveData,
4 | replaceDataWith,
5 | } from '../../../../../environment/data'
6 | import {
7 | addDataAt,
8 | getMemoryLocation,
9 | resolvePath,
10 | } from '../../../../../environment/environment'
11 | import {
12 | Accessor,
13 | EnvironmentState,
14 | } from '../../../../../environment/EnvironmentState'
15 | import {
16 | DataState,
17 | DataType,
18 | } from '../../../../../renderer/View/Environment/data/DataState'
19 | import { DataInfo } from '../../../../graph/ExecutionGraph'
20 | import { createExecutionNode, ExecutionNode } from '../../../ExecutionNode'
21 |
22 | export type FloorAnimation = ExecutionNode & {
23 | argument: Accessor[]
24 | outputRegister: Accessor[]
25 | }
26 |
27 | function apply(animation: FloorAnimation, environment: EnvironmentState) {
28 | const arg = resolvePath(
29 | environment,
30 | animation.argument,
31 | `${animation.id}_Data`
32 | ) as DataState
33 | const copy = cloneData(arg, false, `${animation.id}_Arg`)
34 | const location = addDataAt(environment, copy, [], null)
35 | copy.value = Math.floor(arg.value as number)
36 |
37 | // Put it in the floating stack
38 | const register = resolvePath(
39 | environment,
40 | animation.outputRegister,
41 | `${animation.id}_Floating`
42 | ) as DataState
43 | replaceDataWith(
44 | register,
45 | createPrimitiveData(DataType.ID, copy.id, `${animation.id}_Floating`)
46 | )
47 |
48 | computeReadAndWrites(
49 | animation,
50 | {
51 | location: getMemoryLocation(environment, arg).foundLocation,
52 | id: arg.id,
53 | },
54 | {
55 | location: location,
56 | id: copy.id,
57 | }
58 | )
59 | }
60 |
61 | function computeReadAndWrites(
62 | animation: FloorAnimation,
63 | argument: DataInfo,
64 | data: DataInfo
65 | ) {
66 | animation._reads = [argument]
67 | animation._writes = [data]
68 | }
69 |
70 | export function FloorAnimation(
71 | object: Accessor[],
72 | args: Accessor[][],
73 | outputRegister: Accessor[]
74 | ): FloorAnimation {
75 | return {
76 | ...createExecutionNode(null),
77 | _name: 'FloorAnimation',
78 |
79 | name: 'FloorAnimation',
80 | argument: args[0],
81 | outputRegister: outputRegister,
82 |
83 | // Callbacks
84 | apply,
85 | }
86 | }
87 |
--------------------------------------------------------------------------------
/src/scripts/execution/primitive/Functions/Native/Math/SqrtAnimation.ts:
--------------------------------------------------------------------------------
1 | import {
2 | cloneData,
3 | createPrimitiveData,
4 | replaceDataWith,
5 | } from '../../../../../environment/data'
6 | import {
7 | addDataAt,
8 | getMemoryLocation,
9 | resolvePath,
10 | } from '../../../../../environment/environment'
11 | import {
12 | Accessor,
13 | EnvironmentState,
14 | } from '../../../../../environment/EnvironmentState'
15 | import {
16 | DataState,
17 | DataType,
18 | } from '../../../../../renderer/View/Environment/data/DataState'
19 | import { DataInfo } from '../../../../graph/ExecutionGraph'
20 | import { createExecutionNode, ExecutionNode } from '../../../ExecutionNode'
21 |
22 | export type SqrtAnimation = ExecutionNode & {
23 | argument: Accessor[]
24 | outputRegister: Accessor[]
25 | }
26 |
27 | function apply(animation: SqrtAnimation, environment: EnvironmentState) {
28 | const arg = resolvePath(
29 | environment,
30 | animation.argument,
31 | `${animation.id}_Data`
32 | ) as DataState
33 | const copy = cloneData(arg, false, `${animation.id}_Arg`)
34 | const location = addDataAt(environment, copy, [], null)
35 | copy.value = Math.sqrt(arg.value as number)
36 |
37 | // Put it in the floating stack
38 | const register = resolvePath(
39 | environment,
40 | animation.outputRegister,
41 | `${animation.id}_Floating`
42 | ) as DataState
43 | replaceDataWith(
44 | register,
45 | createPrimitiveData(DataType.ID, copy.id, `${animation.id}_Floating`)
46 | )
47 |
48 | computeReadAndWrites(
49 | animation,
50 | {
51 | location: getMemoryLocation(environment, arg).foundLocation,
52 | id: arg.id,
53 | },
54 | {
55 | location: location,
56 | id: copy.id,
57 | }
58 | )
59 | }
60 |
61 | function computeReadAndWrites(
62 | animation: SqrtAnimation,
63 | argument: DataInfo,
64 | data: DataInfo
65 | ) {
66 | animation._reads = [argument]
67 | animation._writes = [data]
68 | }
69 |
70 | export function SqrtAnimation(
71 | object: Accessor[],
72 | args: Accessor[][],
73 | outputRegister: Accessor[]
74 | ): SqrtAnimation {
75 | return {
76 | ...createExecutionNode(null),
77 | _name: 'SqrtAnimation',
78 |
79 | name: 'SqrtAnimation',
80 | argument: args[0],
81 | outputRegister: outputRegister,
82 |
83 | // Callbacks
84 | apply,
85 | }
86 | }
87 |
--------------------------------------------------------------------------------
/src/scripts/execution/primitive/Functions/ReturnStatementAnimation.ts:
--------------------------------------------------------------------------------
1 | import { getMemoryLocation, resolvePath } from '../../../environment/environment'
2 | import { EnvironmentState } from '../../../environment/EnvironmentState'
3 | import { DataState } from '../../../renderer/View/Environment/data/DataState'
4 | import { DataInfo } from '../../graph/ExecutionGraph'
5 | import { createExecutionNode, ExecutionNode, ReturnData } from '../ExecutionNode'
6 |
7 | export type ReturnStatementAnimation = ExecutionNode & {
8 | returnData: ReturnData
9 | }
10 |
11 | function apply(animation: ReturnStatementAnimation, environment: EnvironmentState) {
12 | const data = resolvePath(environment, animation.returnData.register, `${animation.id}_Data`) as DataState
13 | data.frame = animation.returnData.frame
14 |
15 | const ref = resolvePath(environment, animation.returnData.register, `${animation.id}_Data`, null, {
16 | noResolvingReference: true,
17 | }) as DataState
18 | ref.frame = animation.returnData.frame
19 |
20 | // Any reference that points to data should also be uplifted
21 | // for (const id of Object.keys(environment.memory)) {
22 | // const ref = environment.memory[id]
23 | // if (instanceOfPrimitiveData(ref) && ref.type == DataType.Reference) {
24 | // const resolves = resolvePath(
25 | // environment,
26 | // ref.value as Accessor[],
27 | // `${animation.id}_RefData`
28 | // ) as DataState
29 | // if (resolves.value == data.value) {
30 | // ref.frame = animation.returnData.frame
31 | // }
32 | // }
33 | // }
34 |
35 | computeReadAndWrites(animation, {
36 | location: getMemoryLocation(environment, data).foundLocation,
37 | id: data.id,
38 | })
39 | }
40 |
41 | function computeReadAndWrites(animation: ReturnStatementAnimation, data: DataInfo) {
42 | animation._reads = [data]
43 | animation._writes = []
44 | }
45 |
46 | export function returnStatementAnimation(returnData: ReturnData): ReturnStatementAnimation {
47 | return {
48 | ...createExecutionNode(null),
49 | _name: 'ReturnStatementAnimation',
50 |
51 | name: 'Return Statement',
52 |
53 | returnData,
54 |
55 | // Callbacks
56 | apply,
57 | }
58 | }
59 |
--------------------------------------------------------------------------------
/src/scripts/execution/primitive/Scope/CreateScopeAnimation.ts:
--------------------------------------------------------------------------------
1 | import { createScope } from '../../../environment/environment'
2 | import { EnvironmentState } from '../../../environment/EnvironmentState'
3 | import { ScopeType } from '../../../transpiler/Statements/BlockStatement'
4 | import { createExecutionNode, ExecutionNode } from '../ExecutionNode'
5 |
6 | export type CreateScopeAnimation = ExecutionNode & {
7 | type: ScopeType
8 | }
9 |
10 | function apply(animation: CreateScopeAnimation, environment: EnvironmentState) {
11 | createScope(environment, animation.type)
12 |
13 | computeReadAndWrites(animation)
14 | }
15 |
16 | function computeReadAndWrites(animation: CreateScopeAnimation) {
17 | animation._reads = []
18 | animation._writes = []
19 | }
20 |
21 | export function createScopeAnimation(type: ScopeType = ScopeType.Default): CreateScopeAnimation {
22 | return {
23 | ...createExecutionNode(null),
24 | _name: 'CreateScopeAnimation',
25 |
26 | name: 'CreateScopeAnimation',
27 |
28 | type,
29 |
30 | // Callbacks
31 | apply,
32 | }
33 | }
34 |
--------------------------------------------------------------------------------
/src/scripts/execution/primitive/Scope/PopScopeAnimation.ts:
--------------------------------------------------------------------------------
1 | import { popScope } from '../../../environment/environment'
2 | import { EnvironmentState } from '../../../environment/EnvironmentState'
3 | import { createExecutionNode, ExecutionNode } from '../ExecutionNode'
4 |
5 | export type PopScopeAnimation = ExecutionNode & {}
6 |
7 | function apply(animation: PopScopeAnimation, environment: EnvironmentState) {
8 | popScope(environment)
9 | computeReadAndWrites(animation)
10 | }
11 |
12 | // TODO: Reads and writes
13 | function computeReadAndWrites(animation: PopScopeAnimation) {
14 | animation._reads = []
15 | animation._writes = []
16 | }
17 |
18 | export function popScopeAnimation(): PopScopeAnimation {
19 | return {
20 | ...createExecutionNode(null),
21 | _name: 'PopScopeAnimation',
22 |
23 | name: 'Pop Scope',
24 |
25 | // Callbacks
26 | apply,
27 | }
28 | }
29 |
--------------------------------------------------------------------------------
/src/scripts/execution/primitive/Unary/UnaryExpressionEvaluate.ts:
--------------------------------------------------------------------------------
1 | import * as ESTree from 'estree'
2 | import { createPrimitiveData, replaceDataWith } from '../../../environment/data'
3 | import {
4 | addDataAt,
5 | getMemoryLocation,
6 | removeAt,
7 | resolvePath,
8 | } from '../../../environment/environment'
9 | import {
10 | Accessor,
11 | EnvironmentState,
12 | } from '../../../environment/EnvironmentState'
13 | import {
14 | DataState,
15 | DataType,
16 | } from '../../../renderer/View/Environment/data/DataState'
17 | import { DataInfo } from '../../graph/ExecutionGraph'
18 | import { createExecutionNode, ExecutionNode } from '../ExecutionNode'
19 |
20 | export type UnaryExpressionEvaluate = ExecutionNode & {
21 | specifier: Accessor[]
22 | operator: ESTree.UnaryOperator
23 | outputRegister: Accessor[]
24 | }
25 |
26 | function apply(
27 | animation: UnaryExpressionEvaluate,
28 | environment: EnvironmentState
29 | ) {
30 | // Find arg
31 | let arg = resolvePath(
32 | environment,
33 | animation.specifier,
34 | `${animation.id}_Left`
35 | ) as DataState
36 |
37 | // Evaluated
38 | const evaluated = createPrimitiveData(
39 | DataType.Literal,
40 | computeUnaryExpression(arg.value, animation.operator),
41 | `${animation.id}_EvaluatedData`
42 | )
43 | addDataAt(environment, evaluated, [], null)
44 |
45 | computeReadAndWrites(
46 | animation,
47 | {
48 | location: getMemoryLocation(environment, arg).foundLocation,
49 | id: arg.id,
50 | },
51 | {
52 | location: getMemoryLocation(environment, evaluated).foundLocation,
53 | id: evaluated.id,
54 | }
55 | )
56 |
57 | // Point output to evaluated
58 | if (animation.outputRegister.length > 0) {
59 | const output = resolvePath(
60 | environment,
61 | animation.outputRegister,
62 | `${animation.id}_Floating`
63 | ) as DataState
64 | replaceDataWith(
65 | output,
66 | createPrimitiveData(
67 | DataType.ID,
68 | evaluated.id,
69 | `${animation.id}_Placed`
70 | )
71 | )
72 | } else {
73 | removeAt(
74 | environment,
75 | getMemoryLocation(environment, evaluated).foundLocation
76 | )
77 | }
78 |
79 | removeAt(environment, getMemoryLocation(environment, arg).foundLocation)
80 | }
81 |
82 | function computeReadAndWrites(
83 | animation: UnaryExpressionEvaluate,
84 | original: DataInfo,
85 | evaluatedData: DataInfo
86 | ) {
87 | animation._reads = [original]
88 | animation._writes = [evaluatedData]
89 | }
90 |
91 | export function unaryExpressionEvaluate(
92 | specifier: Accessor[],
93 | operator: ESTree.UnaryOperator,
94 | outputRegister: Accessor[]
95 | ): UnaryExpressionEvaluate {
96 | return {
97 | ...createExecutionNode(null),
98 | _name: 'UnaryExpressionEvaluate',
99 |
100 | // name: `Binary Evaluate ${accessorsToString(leftSpecifier)} ${operator} ${accessorsToString(
101 | // rightSpecifier
102 | // )} onto ${accessorsToString(outputRegister)}`,
103 |
104 | name: `Binary ${operator}`,
105 |
106 | // Attributes
107 | specifier,
108 | operator,
109 | outputRegister,
110 |
111 | // Callbacks
112 | apply,
113 | }
114 | }
115 |
116 | function computeUnaryExpression(data: any, operator: ESTree.UnaryOperator) {
117 | switch (operator) {
118 | case '-':
119 | return -data
120 | case '+':
121 | return +data
122 | case '!':
123 | return !data
124 | case '~':
125 | return ~data
126 | case 'typeof':
127 | return typeof data
128 | case 'void':
129 | return void data
130 | default:
131 | throw new Error(`Unknown unary operator: ${operator}`)
132 | }
133 | }
134 |
--------------------------------------------------------------------------------
/src/scripts/renderer/Action/Dynamic/AssignmentExpressionRepresentation.ts:
--------------------------------------------------------------------------------
1 | import { ApplicationState } from '../../../ApplicationState'
2 | import { getConsumedAbyss } from '../Abyss'
3 | import { ActionState } from '../Action'
4 | import { Representation } from './Representation'
5 |
6 | export class AssignmentExpressionRepresentation extends Representation {
7 | constructor(action: ActionState) {
8 | super(action)
9 | this.shouldHover = true
10 | }
11 |
12 | getControlFlowPoints(
13 | usePlaceholder: boolean = true,
14 | referencePoint: { x: number; y: number } = { x: 0, y: 0 }
15 | ): [number, number][] | null {
16 | const action = ApplicationState.actions[this.actionId]
17 | const abyssInfo = getConsumedAbyss(action.id)
18 |
19 | if (abyssInfo != null && !action.isSpatial) {
20 | // Find the abyss that it's in
21 | return super.getControlFlowPoints(usePlaceholder)
22 | }
23 |
24 | const parent = ApplicationState.actions[action.parentID!]
25 |
26 | if (
27 | action.execution.nodeData.preLabel == 'Update' &&
28 | parent.execution.nodeData.type == 'ForStatement' &&
29 | !action.isShowingSteps
30 | ) {
31 | let bbox = action.proxy.element.getBoundingClientRect()
32 |
33 | const offset = Math.min(2, bbox.height * 0.1)
34 |
35 | return [
36 | [bbox.x + bbox.width, bbox.y + bbox.height / 2],
37 | [bbox.x, bbox.y + bbox.height / 2],
38 | ]
39 | } else {
40 | return super.getControlFlowPoints(usePlaceholder)
41 | }
42 | }
43 |
44 | createSteps(): void {
45 | super.createSteps()
46 |
47 | const action = ApplicationState.actions[this.actionId]
48 | const proxy = action.proxy
49 |
50 | // Swap second and third child in the proxy
51 | const secondChild = proxy.element.children[1]
52 | const thirdChild = proxy.element.children[2]
53 | proxy.element.insertBefore(thirdChild, secondChild)
54 |
55 | console.log('Runs...')
56 | }
57 | }
58 |
--------------------------------------------------------------------------------
/src/scripts/renderer/Action/Dynamic/BinaryExpressionRepresentation.ts:
--------------------------------------------------------------------------------
1 | import { ApplicationState } from '../../../ApplicationState'
2 | import { getConsumedAbyss } from '../Abyss'
3 | import { ActionState } from '../Action'
4 | import { Representation } from './Representation'
5 |
6 | export class BinaryStatementRepresentation extends Representation {
7 | // operatorLabelElement: HTMLElement
8 |
9 | constructor(action: ActionState) {
10 | super(action)
11 |
12 | // this.operatorLabelElement = createElement('div', ['action-proxy-code-label', 'action-proxy-binary-label'])
13 | // const evaluation = (action.execution as ExecutionGraph).vertices[2]
14 |
15 | // if (evaluation.nodeData.location == null) {
16 | // throw new Error('Binary expression evaluation has no associated location.')
17 | // }
18 |
19 | // const evaluationText = ApplicationState.editor.getValueAt(evaluation.nodeData.location)?.trim()
20 |
21 | // if (evaluationText == null) {
22 | // throw new Error('Binary expression evaluation has no text.')
23 | // }
24 |
25 | // this.operatorLabelElement.innerText = evaluationText
26 | // monaco.editor.colorize(this.operatorLabelElement.innerHTML, 'javascript', {}).then((html) => {
27 | // this.operatorLabelElement.innerHTML = html
28 | // })
29 |
30 | this.shouldHover = true
31 | }
32 |
33 | createSteps(): void {
34 | super.createSteps()
35 |
36 | if (!this.isBreakable) return
37 |
38 | const action = ApplicationState.actions[this.actionId]
39 | const proxy = action.proxy
40 |
41 | if (!action.isShowingSteps) return
42 |
43 | const [leftElement, rightElement, operatorElement] = Array.from(proxy.element.children).filter((child) =>
44 | child.classList.contains('action-proxy-container')
45 | )
46 |
47 | // Remove all action-proxy children
48 | leftElement.remove()
49 | rightElement.remove()
50 | operatorElement.remove()
51 |
52 | // Add left element
53 | proxy.element.appendChild(leftElement)
54 |
55 | // Add operator label
56 | // proxy.element.appendChild(this.operatorLabelElement)
57 |
58 | // Add operator element
59 | // operatorElement.classList.add('skip-')
60 | proxy.element.appendChild(operatorElement)
61 |
62 | // Add right element
63 | proxy.element.appendChild(rightElement)
64 | }
65 |
66 | destroySteps(): void {
67 | super.destroySteps()
68 | // this.operatorLabelElement.remove()
69 | }
70 |
71 | getControlFlowPoints(
72 | usePlaceholder: boolean = true,
73 | referencePoint: { x: number; y: number } = { x: 0, y: 0 }
74 | ): [number, number][] | null {
75 | const action = ApplicationState.actions[this.actionId]
76 | const abyssInfo = getConsumedAbyss(action.id)
77 |
78 | if (abyssInfo != null && !action.isSpatial) {
79 | // Find the abyss that it's in
80 | return super.getControlFlowPoints(usePlaceholder)
81 | }
82 |
83 | const parent = ApplicationState.actions[action.parentID!]
84 |
85 | if (
86 | action.execution.nodeData.preLabel == 'Test' &&
87 | parent.execution.nodeData.type == 'ForStatement' &&
88 | !action.isShowingSteps
89 | ) {
90 | let bbox = action.proxy.element.getBoundingClientRect()
91 |
92 | const offset = Math.min(2, bbox.height * 0.1)
93 |
94 | return [
95 | [bbox.x, bbox.y + bbox.height / 2],
96 | [bbox.x + bbox.width / 2, bbox.y + bbox.height / 2],
97 | [bbox.x + bbox.width / 2, bbox.y + bbox.height],
98 | ]
99 | } else {
100 | return super.getControlFlowPoints(usePlaceholder)
101 | }
102 | }
103 | }
104 |
--------------------------------------------------------------------------------
/src/scripts/renderer/Action/Dynamic/BlockStatementRepresentation.ts:
--------------------------------------------------------------------------------
1 | import { ApplicationState } from '../../../ApplicationState'
2 | import { getExecutionSteps } from '../../../utilities/action'
3 | import { getConsumedAbyss } from '../Abyss'
4 | import { ActionState } from '../Action'
5 | import { Representation } from './Representation'
6 |
7 | export class BlockStatementRepresentation extends Representation {
8 | constructor(action: ActionState) {
9 | super(action)
10 |
11 | this.shouldHover = true
12 |
13 | const executionSteps = getExecutionSteps(action.execution)
14 | this.isSelectableGroup = !(executionSteps.length == 1 && action.execution.nodeData.preLabel != 'Body')
15 | }
16 |
17 | postCreate(): void {
18 | const action = ApplicationState.actions[this.actionId]
19 |
20 | // If there is only one step, then expand it
21 | const executionSteps = getExecutionSteps(action.execution)
22 | if (executionSteps.length == 1 && action.execution.nodeData.preLabel != 'Body') {
23 | this.createSteps()
24 |
25 | if (executionSteps[0].nodeData.preLabel != 'IfStatement') {
26 | action.proxy.container.classList.add('is-singular')
27 | }
28 | }
29 | }
30 |
31 | getControlFlowPoints(
32 | usePlaceholder: boolean = true,
33 | referencePoint: { x: number; y: number } = { x: 0, y: 0 }
34 | ): [number, number][] | null {
35 | if (this.isTrimmed) {
36 | return null
37 | }
38 |
39 | const action = ApplicationState.actions[this.actionId]
40 | const abyssInfo = getConsumedAbyss(action.id)
41 |
42 | if (abyssInfo != null && !action.isSpatial) {
43 | // Find the abyss that it's in
44 | return super.getControlFlowPoints(usePlaceholder)
45 | }
46 |
47 | const parent = ApplicationState.actions[action.parentID!]
48 |
49 | if (
50 | action.execution.nodeData.preLabel == 'Body' &&
51 | parent.execution.nodeData.type == 'ForStatement' &&
52 | !action.isShowingSteps
53 | ) {
54 | let bbox = action.proxy.element.getBoundingClientRect()
55 |
56 | const offset = Math.min(2, bbox.height * 0.1)
57 |
58 | let mid = referencePoint!.x
59 |
60 | return [
61 | [mid, bbox.y],
62 | [mid, bbox.y + bbox.height / 2],
63 | [bbox.x + bbox.width, bbox.y + bbox.height / 2],
64 | ]
65 | } else {
66 | return super.getControlFlowPoints(usePlaceholder)
67 | }
68 | }
69 | }
70 |
--------------------------------------------------------------------------------
/src/scripts/renderer/Action/Dynamic/CallExpressionRepresentation.ts:
--------------------------------------------------------------------------------
1 | import * as monaco from 'monaco-editor'
2 | import { ApplicationState } from '../../../ApplicationState'
3 | import { createElement } from '../../../utilities/dom'
4 | import { collapseActionIntoAbyss } from '../Abyss'
5 | import { ActionState } from '../Action'
6 | import { getTotalDuration } from '../Mapping/ControlFlowCursor'
7 | import { Representation } from './Representation'
8 |
9 | export class CallExpressionRepresentation extends Representation {
10 | callExpressionLabelElement: HTMLElement
11 |
12 | constructor(action: ActionState) {
13 | super(action)
14 |
15 | this.callExpressionLabelElement = createElement('div', ['action-proxy-code-label'])
16 |
17 | if (action.execution.nodeData.location == undefined) {
18 | throw new Error('Call expression has no nodeData location.')
19 | }
20 |
21 | this.callExpressionLabelElement.innerText =
22 | ApplicationState.editor.getValueAt(action.execution.nodeData.location) ?? 'Unknown'
23 |
24 | monaco.editor.colorize(this.callExpressionLabelElement.innerHTML, 'javascript', {}).then((html) => {
25 | this.callExpressionLabelElement.innerHTML = html
26 | })
27 | }
28 |
29 | createSteps(): void {
30 | const action = ApplicationState.actions[this.actionId]
31 |
32 | const selection = ApplicationState.visualization.selections['main']
33 | const globalTime = selection.targetGlobalTime
34 | const timeDelta = {
35 | start: globalTime - action.globalTimeOffset,
36 | end: globalTime - action.globalTimeOffset - getTotalDuration(action.id),
37 | }
38 |
39 | super.createSteps()
40 |
41 | // If was before the global time, then seek to the start of the function call
42 | if (timeDelta.start < 0) {
43 | setTimeout(() => {
44 | let firstChild = ApplicationState.actions[action.vertices[1]]
45 | while (firstChild.vertices.length > 0) {
46 | firstChild = ApplicationState.actions[firstChild.vertices[0]]
47 | }
48 | selection.targetGlobalTime = firstChild.globalTimeOffset + getTotalDuration(firstChild.id)
49 | }, 0)
50 | } else if (timeDelta.end >= 0) {
51 | setTimeout(() => {
52 | let lastChild = ApplicationState.actions[action.vertices.at(-1)!]
53 | while (lastChild.vertices.length > 0) {
54 | lastChild = ApplicationState.actions[lastChild.vertices.at(-1)!]
55 | }
56 |
57 | // const parent = ApplicationState.actions[action.parentID!]
58 | selection.targetGlobalTime = lastChild.globalTimeOffset + getTotalDuration(lastChild.id)
59 | selection._globalTime = lastChild.globalTimeOffset + getTotalDuration(lastChild.id)
60 |
61 | setTimeout(() => {
62 | const lastChildParent = ApplicationState.actions[lastChild.parentID!]
63 | lastChildParent.representation.focus()
64 | }, 0)
65 | }, 100)
66 | }
67 |
68 | const proxy = action.proxy
69 |
70 | // const [argElement, bodyElement] = Array.from(proxy.element.children).filter(
71 | // (child) =>
72 | // child.classList.contains('action-proxy-container') ||
73 | // child.classList.contains('action-proxy-placeholder')
74 | // )
75 |
76 | if (ApplicationState.visualization.PARAMS.Closure) {
77 | const spatialParent = ApplicationState.actions[action.spatialParentID!]
78 | spatialParent.representation.minimize()
79 |
80 | if (spatialParent.parentID != null) {
81 | const spatialParentParent = ApplicationState.actions[spatialParent.parentID!]
82 | const spatialParentParentSpatialParent = ApplicationState.actions[spatialParentParent.spatialParentID!]
83 |
84 | if (
85 | spatialParentParentSpatialParent.minimized &&
86 | spatialParentParentSpatialParent.execution.nodeData.type != 'Program' &&
87 | !spatialParentParentSpatialParent.proxy.container.classList.contains('consumed')
88 | ) {
89 | collapseActionIntoAbyss(spatialParentParentSpatialParent.id)
90 | }
91 | }
92 | }
93 |
94 | // Remove all action-proxy children
95 | // argElement.remove()
96 | // bodyElement.remove()
97 |
98 | // Add call expression label to placeholder
99 | // TODO
100 |
101 | // action.proxy.element.append(bodyElement)
102 | }
103 | }
104 |
--------------------------------------------------------------------------------
/src/scripts/renderer/Action/Dynamic/ExpressionStatementRepresentation.ts:
--------------------------------------------------------------------------------
1 | import { ApplicationState } from '../../../ApplicationState'
2 | import { ExecutionGraph } from '../../../execution/graph/ExecutionGraph'
3 | import { ActionState } from '../Action'
4 | import { Representation } from './Representation'
5 |
6 | export class ExpressionStatementRepresentation extends Representation {
7 | constructor(action: ActionState) {
8 | super(action)
9 | this.shouldHover = true
10 | }
11 |
12 | createSteps(): void {
13 | super.createSteps()
14 |
15 | const action = ApplicationState.actions[this.actionId]
16 | const execution = action.execution as ExecutionGraph
17 |
18 | // let isAssignment = false
19 | // if (execution.vertices.length > 0 && execution.vertices[0].nodeData.preLabel == 'AssignmentIdentifier') {
20 | // isAssignment = true
21 | // }
22 |
23 | // const proxy = action.proxy
24 |
25 | // if (isAssignment) {
26 | // TODO
27 | // Swap second and third child in the proxy
28 | // const secondChild = proxy.element.children[1]
29 | // const thirdChild = proxy.element.children[2]
30 | // proxy.element.insertBefore(thirdChild, secondChild)
31 |
32 | // this.isBreakable = false
33 | // }
34 | }
35 | }
36 |
--------------------------------------------------------------------------------
/src/scripts/renderer/Action/Dynamic/ProgramRepresentation.ts:
--------------------------------------------------------------------------------
1 | import { ApplicationState } from '../../../ApplicationState'
2 | import { getAccumulatedBoundingBox } from '../../../utilities/action'
3 | import { ActionState } from '../Action'
4 | import { Representation } from './Representation'
5 |
6 | export class ProgramRepresentation extends Representation {
7 | constructor(action: ActionState) {
8 | super(action)
9 |
10 | setTimeout(() => {
11 | this.createSteps()
12 | })
13 | }
14 |
15 | updateSpatialActionProxyPositionChildren(offset: { x: number; y: number }): string[] {
16 | const action = ApplicationState.actions[this.actionId]
17 |
18 | const rOffset = { x: 50, y: 0 }
19 | const spatialIDs: string[] = []
20 |
21 | action.vertices.forEach((id) => {
22 | const vertex = ApplicationState.actions[id]
23 | spatialIDs.push(...vertex.representation.updateSpatialActionProxyPosition({ ...rOffset }))
24 |
25 | // If hit something
26 | if (spatialIDs.length > 0) {
27 | const bbox = getAccumulatedBoundingBox(spatialIDs)
28 | rOffset.y += bbox.height + 20
29 | }
30 | })
31 |
32 | return spatialIDs
33 | }
34 |
35 | clicked() {
36 | return false
37 | }
38 | }
39 |
--------------------------------------------------------------------------------
/src/scripts/renderer/Action/Dynamic/ReturnStatementRepresentation.ts:
--------------------------------------------------------------------------------
1 | import * as monaco from 'monaco-editor'
2 | import { ApplicationState } from '../../../ApplicationState'
3 | import { createElement } from '../../../utilities/dom'
4 | import { assert } from '../../../utilities/generic'
5 | import { ActionState } from '../Action'
6 | import { getTotalDuration } from '../Mapping/ControlFlowCursor'
7 | import { Representation } from './Representation'
8 |
9 | export class ReturnStatementRepresentation extends Representation {
10 | returnLabelElement: HTMLElement
11 |
12 | constructor(action: ActionState) {
13 | super(action)
14 |
15 | this.returnLabelElement = createElement('div', ['action-proxy-code-label', 'action-proxy-return-label'])
16 | this.returnLabelElement.innerText = 'return'
17 | monaco.editor.colorize(this.returnLabelElement.innerHTML, 'javascript', {}).then((html) => {
18 | this.returnLabelElement.innerHTML = html
19 | })
20 |
21 | this.shouldHover = true
22 | }
23 |
24 | focus() {
25 | const action = ApplicationState.actions[this.actionId]
26 |
27 | // Update time
28 | const spatialId = action.spatialParentID
29 | assert(spatialId != null, 'Spatial parent ID is null')
30 |
31 | const spatial = ApplicationState.actions[spatialId]
32 | assert(spatial.isSpatial, 'Action is not spatial')
33 | const controlFlow = spatial.controlFlow
34 | assert(controlFlow != null, 'Control flow is null')
35 |
36 | const selection = ApplicationState.visualization.selections[controlFlow.cursor.selectionId]
37 |
38 | const child = ApplicationState.actions[action.vertices[0]]
39 | selection.targetGlobalTime = child.globalTimeOffset + getTotalDuration(child.id)
40 | }
41 |
42 | postCreate() {
43 | this.createSteps()
44 | }
45 |
46 | createSteps(): void {
47 | super.createSteps()
48 |
49 | const action = ApplicationState.actions[this.actionId]
50 | const proxy = action.proxy
51 | if (!action.isShowingSteps) return
52 |
53 | const children = Array.from(proxy.element.children).filter((child) =>
54 | child.classList.contains('action-proxy-container')
55 | )
56 |
57 | // Remove all action-proxy children
58 | for (const child of children) {
59 | child.remove()
60 | }
61 |
62 | // Add return label
63 | proxy.element.appendChild(this.returnLabelElement)
64 |
65 | // Add children
66 | for (const child of children) {
67 | proxy.element.appendChild(child)
68 | }
69 | }
70 |
71 | destroySteps(): void {
72 | super.destroySteps()
73 |
74 | this.returnLabelElement.remove()
75 | }
76 | }
77 |
--------------------------------------------------------------------------------
/src/scripts/renderer/Action/Dynamic/UpdateExpressionRepresentation.ts:
--------------------------------------------------------------------------------
1 | import { ApplicationState } from '../../../ApplicationState'
2 | import { getConsumedAbyss } from '../Abyss'
3 | import { ActionState } from '../Action'
4 | import { Representation } from './Representation'
5 |
6 | export class UpdateExpressionRepresentation extends Representation {
7 | constructor(action: ActionState) {
8 | super(action)
9 | this.shouldHover = true
10 | }
11 |
12 | getControlFlowPoints(
13 | usePlaceholder: boolean = true,
14 | referencePoint: { x: number; y: number } = { x: 0, y: 0 }
15 | ): [number, number][] | null {
16 | const action = ApplicationState.actions[this.actionId]
17 | const abyssInfo = getConsumedAbyss(action.id)
18 |
19 | if (abyssInfo != null && !action.isSpatial) {
20 | // Find the abyss that it's in
21 | return super.getControlFlowPoints(usePlaceholder)
22 | }
23 |
24 | const parent = ApplicationState.actions[action.parentID!]
25 |
26 | if (
27 | action.execution.nodeData.preLabel == 'Update' &&
28 | parent.execution.nodeData.type == 'ForStatement' &&
29 | !action.isShowingSteps
30 | ) {
31 | let bbox = action.proxy.element.getBoundingClientRect()
32 |
33 | const offset = Math.min(2, bbox.height * 0.1)
34 |
35 | return [
36 | [bbox.x + bbox.width, bbox.y + bbox.height / 2],
37 | [bbox.x, bbox.y + bbox.height / 2],
38 | ]
39 | } else {
40 | return super.getControlFlowPoints(usePlaceholder)
41 | }
42 | }
43 | }
44 |
--------------------------------------------------------------------------------
/src/scripts/renderer/Action/Mapping/ActionProxy.ts:
--------------------------------------------------------------------------------
1 | import { ApplicationState } from '../../../ApplicationState'
2 | import { ExecutionGraph } from '../../../execution/graph/ExecutionGraph'
3 | import { ExecutionNode } from '../../../execution/primitive/ExecutionNode'
4 | import { getAccumulatedBoundingBox } from '../../../utilities/action'
5 | import { createElement } from '../../../utilities/dom'
6 | import { getTitleFromExecution } from '../Dynamic/Representation'
7 |
8 | export type ActionProxyState = {
9 | // Container of this things indicator and its steps
10 | container: HTMLElement
11 | header: HTMLElement
12 | controls: HTMLElement | null
13 | element: HTMLElement
14 |
15 | // Corresponding action
16 | actionID: string
17 |
18 | isHovering: boolean
19 | }
20 |
21 | /**
22 | * Creates an executor.
23 | * @param overrides
24 | * @returns
25 | */
26 | export function createActionProxy(overrides: Partial = {}): ActionProxyState {
27 | if (overrides.actionID === undefined) {
28 | throw new Error('ActionProxy requires an action')
29 | }
30 |
31 | const action = ApplicationState.actions[overrides.actionID]
32 |
33 | // Container for the action and its label
34 | const container = createElement('div', 'action-proxy-container')
35 |
36 | // Header for the action
37 | const header = createElement('div', 'action-proxy-header', container)
38 |
39 | // Label
40 | const label = createElement('div', 'action-proxy-label', header)
41 | label.innerText = `${getTitleFromExecution(action.execution)}`
42 |
43 | const element = createElement(
44 | 'div',
45 | [
46 | 'action-proxy',
47 | `type-${action.execution.nodeData.type?.replace(' ', '-')}`,
48 | `pre-label-${action.execution.nodeData.preLabel?.replace(' ', '-')}`,
49 | ],
50 | container
51 | )
52 |
53 | element.addEventListener('mouseenter', () => {
54 | action?.element?.classList.add('hover')
55 | })
56 |
57 | element.addEventListener('mouseleave', () => {
58 | action?.element?.classList.remove('hover')
59 | })
60 |
61 | // autoAnimate(element)
62 |
63 | const base: ActionProxyState = {
64 | element: element,
65 | header: header,
66 | controls: null,
67 | container: container,
68 | actionID: overrides.actionID,
69 | timeOffset: 0,
70 | isHovering: false,
71 | }
72 |
73 | return { ...base, ...overrides }
74 | }
75 |
76 | export function destroyActionProxy(proxy: ActionProxyState) {
77 | proxy.container.remove()
78 | }
79 |
80 | export function isSpatialByDefault(execution: ExecutionGraph | ExecutionNode) {
81 | return execution.nodeData.type == 'FunctionCall' || execution.nodeData.type == 'Program'
82 | }
83 |
84 | export function clipActionProxy(actionID: string) {
85 | const action = ApplicationState.actions[actionID]
86 |
87 | if (action.spatialVertices.size > 0) {
88 | const bbox = getAccumulatedBoundingBox(
89 | [...action.spatialVertices].map((v) => ApplicationState.actions[v].parentID as string)
90 | )
91 | const actionBbox = action.proxy.element.getBoundingClientRect()
92 | let { y, height } = bbox
93 |
94 | y = y - actionBbox.y
95 |
96 | // action.proxy.element.style.margin
97 | const firstChild = action.proxy.element.children[0] as HTMLElement
98 |
99 | firstChild.style.marginTop = `${-y}px`
100 | action.proxy.element.style.maxHeight = `${height}px`
101 |
102 | action.proxy.element.classList.add('clipped')
103 | } else {
104 | // Get what things are not going to be clipped by default
105 | throw new Error('No implementation for clipping')
106 | }
107 | }
108 |
109 | export function unClipActionProxy(actionID: string) {
110 | // TODO
111 | }
112 |
--------------------------------------------------------------------------------
/src/scripts/renderer/Action/Mapping/index.d.ts:
--------------------------------------------------------------------------------
1 | declare module 'kld-intersections' {
2 | const Intersection: any
3 | const ShapeInfo: any
4 | }
5 |
--------------------------------------------------------------------------------
/src/scripts/renderer/Trail/Renderers/CreateTrailRenderer.ts:
--------------------------------------------------------------------------------
1 | import { getMemoryLocation, resolvePath } from '../../../environment/environment'
2 | import { AccessorType, EnvironmentState, instanceOfEnvironment, Residual } from '../../../environment/EnvironmentState'
3 | import { remap } from '../../../utilities/math'
4 | import { DataRenderer } from '../../View/Environment/data/DataRenderer'
5 | import { EnvironmentRenderer } from '../../View/Environment/EnvironmentRenderer'
6 | import { Trail } from '../Trail'
7 | import { resetResidual, TrailRenderer, updateResidual } from './TrailRenderer'
8 |
9 | export class CreateTrailRenderer extends TrailRenderer {
10 | /* ----------------------- Create ----------------------- */
11 | constructor(trail: Trail) {
12 | super(trail)
13 | }
14 |
15 | /* ----------------------- Animate ---------------------- */
16 | update(amount: number, environment: EnvironmentRenderer, totalTime: number) {
17 | /* ---------------------- Old data ---------------------- */
18 | const prev = environment.getResidualOf(this.trail.state.toDataID, this.trail.time)
19 | if (prev != null) {
20 | updateResidual(amount, prev)
21 | } else {
22 | // Reset the residual
23 | const prev = environment.findRendererById(this.trail.state.toDataID)
24 | if (prev != null) {
25 | resetResidual(prev.data)
26 | }
27 | }
28 |
29 | /* ---------------------- New data ---------------------- */
30 | const t = remap(amount, 0, 1, 5, 0)
31 | const data = environment.getResidualOf(this.trail.state.toDataID, this.trail.time + 1) as DataRenderer
32 |
33 | if ((amount == 1 && data == null) || (amount == 0 && data == null)) {
34 | // Has been destroyed / not yet initialized
35 | return
36 | } else if (data == null) {
37 | console.warn('Data is null in CreateTrailRenderer')
38 | return
39 | }
40 |
41 | // data.element.style.transform = `translate(${t}px, ${-t}px)`
42 | data.element.style.opacity = `${Math.min(1, amount * 2)}`
43 | data.element.style.left = '0px'
44 | data.element.style.top = '0px'
45 | }
46 |
47 | /**
48 | * @param environment Prev environment
49 | * @returns
50 | */
51 | computeResidual(environment: EnvironmentState): Residual | null {
52 | const prev = resolvePath(environment, [{ type: AccessorType.ID, value: this.trail.state.toDataID }], null)
53 |
54 | if (instanceOfEnvironment(prev)) {
55 | return null
56 | }
57 |
58 | const location = getMemoryLocation(environment, prev).foundLocation
59 |
60 | if (location == null) {
61 | throw new Error('Location not found')
62 | }
63 |
64 | return {
65 | data: prev,
66 | location: location,
67 | }
68 | }
69 |
70 | applyTimestamps(environment: EnvironmentState) {
71 | environment.timestamps[this.trail.state.toDataID] = this.trail.time
72 | }
73 |
74 | /* ----------------------- Destroy ---------------------- */
75 | destroy() {
76 | super.destroy()
77 | }
78 | }
79 |
--------------------------------------------------------------------------------
/src/scripts/renderer/Trail/Renderers/PartialCreateTrailRenderer.ts:
--------------------------------------------------------------------------------
1 | import { getMemoryLocation, resolvePath } from '../../../environment/environment'
2 | import { AccessorType, EnvironmentState, instanceOfEnvironment, Residual } from '../../../environment/EnvironmentState'
3 | import { remap } from '../../../utilities/math'
4 | import { DataRenderer } from '../../View/Environment/data/DataRenderer'
5 | import { EnvironmentRenderer } from '../../View/Environment/EnvironmentRenderer'
6 | import { Trail } from '../Trail'
7 | import { resetResidual, TrailRenderer, updateResidual } from './TrailRenderer'
8 |
9 | export class PartialCreateTrailRenderer extends TrailRenderer {
10 | /* ----------------------- Create ----------------------- */
11 | constructor(trail: Trail) {
12 | super(trail)
13 | }
14 |
15 | /* ----------------------- Animate ---------------------- */
16 | update(amount: number, environment: EnvironmentRenderer, totalTime: number) {
17 | /* ---------------------- Old data ---------------------- */
18 | const prev = environment.getResidualOf(this.trail.state.toDataID, this.trail.time)
19 |
20 | if (prev != null) {
21 | updateResidual(amount, prev)
22 | } else {
23 | // Reset the residual
24 | const prev = environment.findRendererById(this.trail.state.toDataID)
25 | if (prev != null) {
26 | resetResidual(prev.data)
27 | }
28 | }
29 |
30 | /* ---------------------- New data ---------------------- */
31 | const t = remap(amount, 0, 1, 5, 0)
32 |
33 | const data = environment.getResidualOf(this.trail.state.toDataID, this.trail.time + 1) as DataRenderer
34 |
35 | if ((amount == 1 && data == null) || (amount == 0 && data == null)) {
36 | // Has been destroyed / not yet initialized
37 | return
38 | } else if (data == null) {
39 | console.warn('Data is null in PartialCreateTrailRenderer')
40 | return
41 | }
42 |
43 | // data.element.style.transform = `translate(${t}px, ${-t}px)`
44 | data.element.style.opacity = `${Math.min(1, amount * 2)}`
45 | data.element.style.left = '0px'
46 | data.element.style.top = '0px'
47 | }
48 |
49 | /**
50 | * @param environment Prev environment
51 | * @returns
52 | */
53 | computeResidual(environment: EnvironmentState): Residual | null {
54 | const prev = resolvePath(environment, [{ type: AccessorType.ID, value: this.trail.state.toDataID }], null)
55 |
56 | if (instanceOfEnvironment(prev)) {
57 | return null
58 | }
59 |
60 | const location = getMemoryLocation(environment, prev).foundLocation
61 |
62 | if (location == null) {
63 | throw new Error('Location not found')
64 | }
65 |
66 | return {
67 | data: prev,
68 | location: location,
69 | }
70 | }
71 |
72 | applyTimestamps(environment: EnvironmentState) {
73 | environment.timestamps[this.trail.state.toDataID] = this.trail.time
74 | }
75 |
76 | /* ----------------------- Destroy ---------------------- */
77 | destroy() {
78 | super.destroy()
79 | }
80 | }
81 |
--------------------------------------------------------------------------------
/src/scripts/renderer/Trail/Renderers/TrailRenderer.ts:
--------------------------------------------------------------------------------
1 | import { EnvironmentState, Residual } from '../../../environment/EnvironmentState'
2 | import { remap } from '../../../utilities/math'
3 | import { DataRenderer } from '../../View/Environment/data/DataRenderer'
4 | import { EnvironmentRenderer } from '../../View/Environment/EnvironmentRenderer'
5 | import { IdentifierRenderer } from '../../View/Environment/identifier/IdentifierRenderer'
6 | import { Trail } from '../Trail'
7 |
8 | export class TrailRenderer {
9 | trail: Trail
10 |
11 | /* ----------------------- Create ----------------------- */
12 | constructor(trail: Trail) {
13 | this.trail = trail
14 | }
15 |
16 | /* ----------------------- Animate ---------------------- */
17 | update(amount: number, environment: EnvironmentRenderer, totalTime: number) {
18 | console.warn('Update not implemented for', this)
19 | }
20 |
21 | postUpdate(amount: number, environment: EnvironmentRenderer, totalTime: number) {}
22 |
23 | alwaysUpdate(environment: EnvironmentRenderer) {}
24 |
25 | computeResidual(environment: EnvironmentState): Residual | null {
26 | console.warn('Compute residual not implemented for', this)
27 | return null
28 | }
29 |
30 | /**
31 | * @param environment Current environment
32 | */
33 | applyTimestamps(environment: EnvironmentState) {
34 | console.warn('Apply timestamps not implemented for', this)
35 | }
36 |
37 | /* ----------------------- Destroy ---------------------- */
38 | destroy() {}
39 | }
40 |
41 | export function updateResidual(amount: number, prev: DataRenderer | IdentifierRenderer) {
42 | const pt = remap(amount, 0, 1, 0, 5)
43 | let offset = 0
44 | let hit = false
45 |
46 | if (prev.element.parentElement == null) {
47 | throw new Error('Element has no parent')
48 | }
49 |
50 | for (let i = 0; i < prev.element.parentElement.childElementCount; i++) {
51 | const child = prev.element.parentElement.children[i]
52 | if (hit) {
53 | offset++
54 | }
55 |
56 | if (child == prev.element) {
57 | hit = true
58 | }
59 | }
60 | offset *= 5
61 |
62 | prev.element.style.transform = `translate(${-pt - offset}px, ${pt + offset}px) scale(${1})`
63 |
64 | let opacity
65 |
66 | if (offset / 5 >= 2) {
67 | opacity = 0
68 | } else if (offset / 5 >= 1) {
69 | opacity = Math.max(0, 1 - 0.8 * amount - offset / 20)
70 | } else {
71 | opacity = Math.max(0.1, 1 - 0.8 * amount)
72 | }
73 |
74 | prev.element.style.color = `rgba(138, 138, 138, ${opacity})`
75 | prev.element.style.filter = `saturate(${Math.max(0, 1 - 2 * amount)})`
76 | }
77 |
78 | export function resetResidual(prev: DataRenderer | IdentifierRenderer) {
79 | prev.element.style.transform = `translate(0px, 0px) scale(1)`
80 | prev.element.style.filter = `saturate(1)`
81 | prev.element.style.color = ``
82 | }
83 |
--------------------------------------------------------------------------------
/src/scripts/renderer/Trail/Trail.ts:
--------------------------------------------------------------------------------
1 | import { ExecutionGraph } from '../../execution/graph/ExecutionGraph'
2 | import { ExecutionNode } from '../../execution/primitive/ExecutionNode'
3 | import { EnvironmentRenderer } from '../View/Environment/EnvironmentRenderer'
4 | import { CreateTrailRenderer } from './Renderers/CreateTrailRenderer'
5 | import { MoveTrailRenderer } from './Renderers/MoveTrailRenderer'
6 | import { PartialCreateTrailRenderer } from './Renderers/PartialCreateTrailRenderer'
7 | import { PartialMoveTrailRenderer } from './Renderers/PartialMoveTrailRenderer'
8 | import { TrailRenderer } from './Renderers/TrailRenderer'
9 | import { TrailState, TrailType } from './TrailState'
10 |
11 | /* ------------------------------------------------------ */
12 | /* Trails visualize change via path or animation */
13 | /* ------------------------------------------------------ */
14 | export class Trail {
15 | state: TrailState
16 | renderer: TrailRenderer
17 | execution: ExecutionGraph | ExecutionNode
18 |
19 | time: number
20 |
21 | constructor(state: TrailState, execution: ExecutionGraph | ExecutionNode, time: number) {
22 | this.state = state
23 | this.execution = execution
24 | this.renderer = createTrailRenderer(this)
25 | this.time = time
26 | }
27 |
28 | /* ----------------------- Update ----------------------- */
29 | update(amount: number, environment: EnvironmentRenderer, totalTime: number) {
30 | this.renderer.update(amount, environment, totalTime)
31 | }
32 |
33 | /* ----------------------- Destroy ---------------------- */
34 | destroy() {
35 | this.renderer.destroy()
36 | }
37 | }
38 |
39 | export function createTrailRenderer(trail: Trail) {
40 | switch (trail.state.type) {
41 | case TrailType.Create:
42 | return new CreateTrailRenderer(trail)
43 | case TrailType.PartialCreate:
44 | return new PartialCreateTrailRenderer(trail)
45 | case TrailType.Move:
46 | return new MoveTrailRenderer(trail)
47 | case TrailType.PartialMove:
48 | return new PartialMoveTrailRenderer(trail)
49 | default:
50 | throw new Error(`Unsupported trail type ${trail.state.type}`)
51 | }
52 | }
53 |
--------------------------------------------------------------------------------
/src/scripts/renderer/Trail/TrailState.ts:
--------------------------------------------------------------------------------
1 | /* ------------------------------------------------------ */
2 | /* Trail State */
3 | /* ------------------------------------------------------ */
4 |
5 | import { AnimationTraceOperator } from '../../execution/execution'
6 |
7 | /* --------------------- Definition --------------------- */
8 |
9 | export enum TrailType {
10 | Create = 'Create',
11 | Move = 'Move',
12 | Delete = 'Delete',
13 |
14 | PartialMove = 'PartialMove',
15 | PartialCreate = 'PartialCreate',
16 | }
17 |
18 | export type TrailState = {
19 | fromDataIDs?: string[]
20 | toDataID: string
21 | type: TrailType
22 | operations: AnimationTraceOperator[]
23 | }
24 |
--------------------------------------------------------------------------------
/src/scripts/renderer/View/Environment/HardScopeRenderer.ts:
--------------------------------------------------------------------------------
1 | // export class HardScopeRenderer {
2 | // isGlobal: boolean
3 | // element: HTMLElement
4 |
5 | // constructor(isGlobal: boolean) {
6 | // this.isGlobal = isGlobal
7 |
8 | // this.element = document.createElement('div')
9 | // }
10 | // }
11 |
--------------------------------------------------------------------------------
/src/scripts/renderer/View/Environment/data/DataRenderer.ts:
--------------------------------------------------------------------------------
1 | import { FrameInfo } from '../../../../environment/EnvironmentState'
2 | import { DataState } from './DataState'
3 |
4 | export class DataRenderer {
5 | element: HTMLDivElement
6 | array: DataRenderer[]
7 |
8 | _cachedState: DataState | null = null
9 |
10 | constructor() {
11 | this.element = document.createElement('div')
12 | this.element.classList.add('data')
13 | }
14 |
15 | setState(data: DataState, frame: FrameInfo) {
16 | console.warn('No fallback for', data)
17 | }
18 |
19 | destroy() {
20 | this.element.remove()
21 | }
22 |
23 | getAllChildRenderers(): { [id: string]: DataRenderer } {
24 | return {}
25 | }
26 |
27 | /* ------------------------ Focus ----------------------- */
28 | secondaryFocus() {
29 | this.element.classList.add('secondary-focused')
30 | }
31 |
32 | focus() {
33 | this.element.classList.remove('secondary-focused')
34 | }
35 |
36 | clearFocus() {
37 | this.element.classList.remove('secondary-focused')
38 | }
39 | }
40 |
--------------------------------------------------------------------------------
/src/scripts/renderer/View/Environment/data/DataState.ts:
--------------------------------------------------------------------------------
1 | import * as CSS from 'csstype'
2 | import { Accessor } from '../../../../environment/EnvironmentState'
3 |
4 | export enum DataType {
5 | Literal = 'Literal',
6 | Array = 'Array',
7 | ID = 'ID',
8 | Reference = 'Reference',
9 | Register = 'Register',
10 | Function = 'Function',
11 | }
12 |
13 | export type TransformStyles = CSS.Properties & {
14 | elevation?: number
15 | xoffset?: number
16 | yoffset?: number
17 | }
18 |
19 | export type Transform = {
20 | rendered: {
21 | x: number
22 | y: number
23 | width: number
24 | height: number
25 | }
26 | styles: TransformStyles
27 | renderOnlyStyles: TransformStyles
28 | classList: string[]
29 | }
30 |
31 | export type DataTransform = {
32 | classList: string[]
33 | }
34 |
35 | export type PrimitiveDataState = {
36 | _type: 'PrimitiveDataState'
37 | type: DataType
38 |
39 | value: string | number | boolean | null | Accessor[] | Function
40 |
41 | // Binding frame
42 | frame: number
43 |
44 | id: string
45 |
46 | builtin?: boolean
47 | }
48 |
49 | export type ObjectDataState = {
50 | _type: 'ObjectDataState'
51 | value: object
52 |
53 | // Binding frame
54 | frame: number
55 |
56 | id: string
57 |
58 | builtin?: boolean
59 | }
60 |
61 | export type DataState = PrimitiveDataState | ObjectDataState
62 |
63 | export function instanceOfData(data: any): data is DataState {
64 | return (
65 | data != null &&
66 | (data['_type'] === 'PrimitiveDataState' ||
67 | data['_type'] === 'ObjectDataState')
68 | )
69 | }
70 |
71 | export function instanceOfPrimitiveData(data: any): data is PrimitiveDataState {
72 | return data != null && data['_type'] === 'PrimitiveDataState'
73 | }
74 |
75 | export function instanceOfObjectData(data: any): data is ObjectDataState {
76 | return data != null && data['_type'] === 'ObjectDataState'
77 | }
78 |
--------------------------------------------------------------------------------
/src/scripts/renderer/View/Environment/data/array/IndexRenderer.ts:
--------------------------------------------------------------------------------
1 | export class IndexRenderer {
2 | element: HTMLDivElement
3 |
4 | constructor() {
5 | this.element = document.createElement('div')
6 | this.element.classList.add('data-array-index')
7 | }
8 |
9 | setState(index: number, dataElement: HTMLElement, environmentElement: HTMLElement) {
10 | this.element.innerText = `${index}`
11 |
12 | const dataBbox = dataElement.getBoundingClientRect()
13 | const environmentBbox = environmentElement.getBoundingClientRect()
14 | const delta = dataBbox.x - environmentBbox.x
15 |
16 | let ratio = 1
17 | this.element.style.top = `${18}px`
18 | this.element.style.left = `${(1 / ratio) * delta}px`
19 | }
20 |
21 | destroy() {
22 | this.element.remove()
23 | }
24 | }
25 |
--------------------------------------------------------------------------------
/src/scripts/renderer/View/Environment/data/literal/LiteralRenderer.ts:
--------------------------------------------------------------------------------
1 | import { FrameInfo } from '../../../../../environment/EnvironmentState'
2 | import { clone } from '../../../../../utilities/objects'
3 | import { DataRenderer } from '../DataRenderer'
4 | import { DataState, TransformStyles } from '../DataState'
5 |
6 | export class LiteralRenderer extends DataRenderer {
7 | prevRenderStyles!: TransformStyles
8 |
9 | constructor() {
10 | super()
11 | this.element.classList.add('data-literal')
12 | }
13 |
14 | setState(data: DataState, frame: FrameInfo) {
15 | this.element.innerText = data.value?.toString() ?? 'null'
16 |
17 | if (typeof data.value == 'string') {
18 | this.element.classList.add('data-literal-string')
19 | this.element.classList.remove('data-literal-boolean')
20 | this.element.classList.remove('true')
21 | this.element.classList.remove('false')
22 |
23 | this.element.innerText = `'${data.value.toString()}'`
24 | } else if (typeof data.value == 'boolean') {
25 | this.element.classList.add('data-literal-boolean')
26 | this.element.classList.remove('data-literal-string')
27 |
28 | if (data.value) {
29 | this.element.classList.add('true')
30 | this.element.classList.remove('false')
31 | } else {
32 | this.element.classList.add('false')
33 | this.element.classList.remove('true')
34 | }
35 | } else {
36 | this.element.classList.remove('data-literal-string')
37 | this.element.classList.remove('data-literal-boolean')
38 |
39 | this.element.classList.remove('true')
40 | this.element.classList.remove('false')
41 | }
42 |
43 | this._cachedState = clone(data)
44 | }
45 | }
46 |
--------------------------------------------------------------------------------
/src/scripts/renderer/View/Environment/data/object/ObjectItemRenderer.ts:
--------------------------------------------------------------------------------
1 | import { FrameInfo } from '../../../../../environment/EnvironmentState'
2 | import { createDataRenderer } from '../../EnvironmentRenderer'
3 | import { DataRenderer } from '../DataRenderer'
4 | import { DataState } from '../DataState'
5 |
6 | export class ObjectItemRenderer {
7 | keyRenderer: HTMLElement
8 | dataRenderer: DataRenderer
9 |
10 | element: HTMLElement
11 |
12 | constructor() {
13 | this.element = document.createElement('div')
14 | this.element.classList.add('data-object-item')
15 | this.keyRenderer = document.createElement('div')
16 | this.keyRenderer.classList.add('data-object-item-key')
17 | }
18 |
19 | setState(key: string, value: DataState, frame: FrameInfo) {
20 | this.dataRenderer = createDataRenderer(value)
21 | this.dataRenderer.setState(value, frame)
22 |
23 | this.keyRenderer.innerText = key
24 |
25 | this.element.innerHTML = ''
26 | this.element.append(this.keyRenderer)
27 | this.element.append(this.dataRenderer.element)
28 | }
29 |
30 | destroy() {
31 | this.dataRenderer.destroy()
32 | this.element.remove()
33 | }
34 | }
35 |
--------------------------------------------------------------------------------
/src/scripts/renderer/View/Environment/data/object/ObjectRenderer.ts:
--------------------------------------------------------------------------------
1 | import { FrameInfo } from '../../../../../environment/EnvironmentState'
2 | import { DataRenderer } from '../DataRenderer'
3 | import { DataState } from '../DataState'
4 | import { ObjectItemRenderer } from './ObjectItemRenderer'
5 |
6 | export class ObjectRenderer extends DataRenderer {
7 | dataRenderers: {
8 | [id: string]: ObjectItemRenderer
9 | } = {}
10 |
11 | constructor() {
12 | super()
13 |
14 | this.element.classList.add('object-renderer')
15 | }
16 |
17 | findRendererById(id: string): {
18 | data: DataRenderer
19 | column: HTMLElement
20 | } | null {
21 | console.log(id, Object.keys(this.dataRenderers))
22 |
23 | for (const [key, renderer] of Object.entries(this.dataRenderers)) {
24 | if (key.split('@')[0] == id) {
25 | return { data: renderer.dataRenderer, column: null! }
26 | }
27 | }
28 |
29 | for (const [containerId, containerRenderer] of Object.entries(this.dataRenderers)) {
30 | if (containerRenderer.dataRenderer instanceof ObjectRenderer) {
31 | const item = containerRenderer.dataRenderer.findRendererById(id)
32 | if (item != null) {
33 | return item
34 | }
35 | }
36 | }
37 |
38 | return null
39 | }
40 |
41 | setState(data: DataState, frame: FrameInfo) {
42 | const { actionId } = frame
43 | const obj = data.value as { [id: string]: DataState }
44 | const hits = new Set()
45 |
46 | // Need to do both data *and* key as separate things
47 | for (const [key, value] of Object.entries(obj)) {
48 | const storeKey = `${value.id}@${key}`
49 |
50 | // Create renderer if not there
51 | if (!(storeKey in this.dataRenderers)) {
52 | const renderer = new ObjectItemRenderer()
53 | this.dataRenderers[storeKey] = renderer
54 | }
55 |
56 | hits.add(storeKey)
57 | this.element.append(this.dataRenderers[storeKey].element)
58 | this.dataRenderers[storeKey].setState(key, value, actionId)
59 | }
60 |
61 | // Remove data that are no longer in the view
62 | for (const id of Object.keys(this.dataRenderers)) {
63 | if (!hits.has(id)) {
64 | const renderer = this.dataRenderers[id]
65 | renderer.destroy()
66 | delete this.dataRenderers[id]
67 | }
68 | }
69 | }
70 |
71 | destroy(): void {
72 | super.destroy()
73 | for (const id of Object.keys(this.dataRenderers)) {
74 | const renderer = this.dataRenderers[id]
75 | renderer.destroy()
76 | }
77 | this.dataRenderers = {}
78 | }
79 |
80 | getAllChildRenderers() {
81 | let renderers: { [id: string]: DataRenderer } = {}
82 | for (const [id, item] of Object.entries(this.dataRenderers)) {
83 | renderers[id.split('@')[0]] = item.dataRenderer
84 | renderers = {
85 | ...renderers,
86 | ...item.dataRenderer.getAllChildRenderers(),
87 | }
88 | }
89 |
90 | // console.log('The whole thing:', renderers, this.dataRenderers)
91 | return renderers
92 | }
93 |
94 | select(selection: Set) {
95 | // this.selection = new Set([...selection, ...this.selection])
96 | // for (const id of Object.keys(this.dataRenderers)) {
97 | // const renderer = this.dataRenderers[id].renderer
98 | // if (this.selection.has(id)) {
99 | // renderer.select(this.selection)
100 | // }
101 | // }
102 | }
103 |
104 | deselect() {
105 | // for (const id of Object.keys(this.dataRenderers)) {
106 | // const renderer = this.dataRenderers[id].renderer
107 | // renderer.deselect()
108 | // }
109 | }
110 | }
111 |
--------------------------------------------------------------------------------
/src/scripts/renderer/View/Environment/data/reference/FunctionRenderer.ts:
--------------------------------------------------------------------------------
1 | import { FrameInfo } from '../../../../../environment/EnvironmentState'
2 | import { DataRenderer } from '../DataRenderer'
3 | import { DataState, TransformStyles } from '../DataState'
4 |
5 | export class FunctionRenderer extends DataRenderer {
6 | prevRenderStyles: TransformStyles
7 |
8 | constructor() {
9 | super()
10 | this.element.classList.add('data-function')
11 | }
12 |
13 | setState(data: DataState, frame: FrameInfo) {
14 | this.element.innerText = 'f'
15 | }
16 | }
17 |
--------------------------------------------------------------------------------
/src/scripts/renderer/View/Environment/data/reference/ReferenceRenderer.ts:
--------------------------------------------------------------------------------
1 | import { FrameInfo } from '../../../../../environment/EnvironmentState'
2 | import { clone } from '../../../../../utilities/objects'
3 | import { DataRenderer } from '../DataRenderer'
4 | import { DataState, TransformStyles } from '../DataState'
5 |
6 | export class ReferenceRenderer extends DataRenderer {
7 | prevRenderStyles!: TransformStyles
8 |
9 | constructor() {
10 | super()
11 | this.element.classList.add('data-function')
12 | }
13 |
14 | setState(data: DataState, frame: FrameInfo) {
15 | this.element.innerText = 'Ref'
16 | this._cachedState = clone(data)
17 | }
18 | }
19 |
--------------------------------------------------------------------------------
/src/scripts/renderer/View/Environment/identifier/IdentifierRenderer.ts:
--------------------------------------------------------------------------------
1 | import { IdentifierState } from '../../../../environment/EnvironmentState'
2 | import { clone } from '../../../../utilities/objects'
3 | import { Ticker } from '../../../../utilities/Ticker'
4 |
5 | export class IdentifierRenderer {
6 | element: HTMLElement
7 | environmentReference: HTMLElement
8 |
9 | private _tickerID: string
10 | _cachedState: IdentifierState | null = null
11 |
12 | constructor() {
13 | this.element = document.createElement('div')
14 | this.element.classList.add('identifier')
15 |
16 | this._tickerID = Ticker.instance.registerTick(this.tick.bind(this))
17 | }
18 |
19 | setState(state: IdentifierState) {
20 | this.element.innerHTML = `${state.name}`
21 |
22 | this._cachedState = clone(state)
23 | }
24 |
25 | tick(dt: number) {}
26 |
27 | destroy() {
28 | Ticker.instance.removeTickFrom(this._tickerID)
29 | this.element.remove()
30 | }
31 |
32 | /* ------------------------ Focus ----------------------- */
33 |
34 | secondaryFocus() {
35 | this.element.classList.add('secondary-focused')
36 | }
37 |
38 | focus() {
39 | this.element.classList.remove('secondary-focused')
40 | }
41 |
42 | clearFocus() {
43 | this.element.classList.remove('secondary-focused')
44 | }
45 | }
46 |
--------------------------------------------------------------------------------
/src/scripts/transpiler/Compiler.ts:
--------------------------------------------------------------------------------
1 | //@ts-check
2 |
3 | import * as ESTree from 'estree'
4 | import { EnvironmentState } from '../environment/EnvironmentState'
5 | import { ExecutionGraph } from '../execution/graph/ExecutionGraph'
6 | import { ExecutionContext, NodeData } from '../execution/primitive/ExecutionNode'
7 | import { ArrayExpression } from './Expressions/Array/ArrayExpression'
8 | import { AssignmentExpression } from './Expressions/BinaryOperations/AssigmentExpression'
9 | import { BinaryExpression } from './Expressions/BinaryOperations/BinaryExpression/BinaryExpression'
10 | import { LogicalExpression } from './Expressions/BinaryOperations/LogicalExpression'
11 | import { MemberExpression } from './Expressions/BinaryOperations/MemberExpression'
12 | import { CallExpression } from './Expressions/CallExpression'
13 | import { ObjectExpression } from './Expressions/ObjectExpression'
14 | import { UnaryExpression } from './Expressions/UnaryOperations/UnaryExpression'
15 | import { UpdateExpression } from './Expressions/UnaryOperations/UpdateExpression'
16 | import { FunctionCall } from './Functions/FunctionCall'
17 | import { FunctionDeclaration } from './Functions/FunctionDeclaration'
18 | import { ReturnStatement } from './Functions/ReturnStatement'
19 | import { Identifier } from './Identifier'
20 | import { Literal } from './Literal'
21 | import { BlockStatement } from './Statements/BlockStatement'
22 | import { IfStatement } from './Statements/Choice/IfStatement'
23 | import { ExpressionStatement } from './Statements/ExpressionStatement'
24 | import { ForStatement } from './Statements/Loops/ForStatement'
25 | import { WhileStatement } from './Statements/Loops/WhileStatement'
26 | import { Program } from './Statements/Program'
27 | import { VariableDeclaration } from './Statements/VariableDeclaration'
28 | import { VariableDeclarator } from './Statements/VariableDeclarator'
29 |
30 | /* ------------------------------------------------------ */
31 | /* Compiles an execution graph from an ESTree AST. */
32 | /* ------------------------------------------------------ */
33 | export class Compiler {
34 | static compile(ast: ESTree.Node, environment: EnvironmentState, context: ExecutionContext): ExecutionGraph {
35 | const mapping = {
36 | VariableDeclarator,
37 |
38 | BinaryExpression,
39 | ArrayExpression,
40 | ObjectExpression,
41 | MemberExpression,
42 | ExpressionStatement,
43 | VariableDeclaration,
44 | AssignmentExpression,
45 | Identifier,
46 | Literal,
47 | Program,
48 | BlockStatement,
49 | ForStatement,
50 | ReturnStatement,
51 |
52 | IfStatement,
53 | FunctionCall,
54 | CallExpression,
55 | LogicalExpression,
56 | UpdateExpression,
57 |
58 | WhileStatement,
59 | FunctionDeclaration,
60 |
61 | UnaryExpression,
62 | }
63 |
64 | let type = `${ast.type}`
65 | if (!(type in mapping)) {
66 | throw new Error(`Unknown type ${ast.type}`)
67 | }
68 |
69 | const node = mapping[type](ast, environment, context)
70 | return node
71 | }
72 | }
73 |
74 | /* ------------------------------------------------------ */
75 | /* Helpers */
76 | /* ------------------------------------------------------ */
77 |
78 | export function getNodeData(node: ESTree.Node | ESTree.Node[], preLabel?: string, hasLineBreak?: boolean): NodeData {
79 | if (Array.isArray(node)) {
80 | return {
81 | location: getUnionOfLocations(node.map((n) => n.loc as ESTree.SourceLocation)),
82 | preLabel: preLabel,
83 | type: preLabel,
84 | hasLineBreak: hasLineBreak ?? false,
85 | }
86 | } else {
87 | return {
88 | location: node.loc as ESTree.SourceLocation | undefined,
89 | type: node.type,
90 | preLabel,
91 | hasLineBreak: hasLineBreak ?? false,
92 | }
93 | }
94 | }
95 |
96 | export function getUnionOfLocations(locs: ESTree.SourceLocation[]): ESTree.SourceLocation {
97 | let start = locs[0].start
98 | let end = locs[0].end
99 |
100 | for (const loc of locs) {
101 | if (loc.start.line < start.line) {
102 | start = loc.start
103 | }
104 |
105 | if (loc.end.line > end.line) {
106 | end = loc.end
107 | }
108 |
109 | if (loc.start.column < start.column) {
110 | start = loc.start
111 | }
112 |
113 | if (loc.end.column > end.column) {
114 | end = loc.end
115 | }
116 | }
117 |
118 | return {
119 | start,
120 | end,
121 | }
122 | }
123 |
--------------------------------------------------------------------------------
/src/scripts/transpiler/Expressions/Array/ArrayExpression.ts:
--------------------------------------------------------------------------------
1 | import * as ESTree from 'estree'
2 | import { cleanUpRegister } from '../../../environment/environment'
3 | import { AccessorType, EnvironmentState } from '../../../environment/EnvironmentState'
4 | import { addVertex, applyExecutionNode } from '../../../execution/execution'
5 | import { createExecutionGraph, ExecutionGraph } from '../../../execution/graph/ExecutionGraph'
6 | import { arrayStartAnimation } from '../../../execution/primitive/Container/ArrayStartAnimation'
7 | import { moveAndPlaceAnimation } from '../../../execution/primitive/Data/MoveAndPlaceAnimation'
8 | import { ExecutionContext } from '../../../execution/primitive/ExecutionNode'
9 | import { clone } from '../../../utilities/objects'
10 | import { Compiler, getNodeData } from '../../Compiler'
11 |
12 | export function ArrayExpression(ast: ESTree.ArrayExpression, environment: EnvironmentState, context: ExecutionContext) {
13 | const graph: ExecutionGraph = createExecutionGraph(getNodeData(ast))
14 | graph.precondition = clone(environment)
15 |
16 | const start = arrayStartAnimation(context.outputRegister)
17 | addVertex(graph, start, { nodeData: getNodeData(ast, 'start') })
18 | applyExecutionNode(start, environment)
19 |
20 | for (let i = 0; i < ast.elements.length; i++) {
21 | // Create a register that'll point to the RHS
22 | const register = [
23 | {
24 | type: AccessorType.Register,
25 | value: `${graph.id}_ArrayExpression_${i}`,
26 | },
27 | ]
28 |
29 | // Array element
30 | const animation = Compiler.compile(ast.elements[i], environment, {
31 | ...context,
32 | outputRegister: register,
33 | })
34 | addVertex(graph, animation, {
35 | nodeData: getNodeData(ast.elements[i], `element ${i}`),
36 | })
37 |
38 | const place = moveAndPlaceAnimation(register, [
39 | ...context.outputRegister,
40 | { type: AccessorType.Index, value: i.toString() },
41 | ])
42 | addVertex(graph, place, { nodeData: getNodeData(ast.elements[i]) })
43 | applyExecutionNode(place, environment)
44 |
45 | cleanUpRegister(environment, register[0].value)
46 | }
47 |
48 | // const end = arrayEndAnimation(context.outputRegister);
49 | // addVertex(graph, end, getNodeData(ast));
50 | // applyExecution(end, view);
51 |
52 | graph.postcondition = clone(environment)
53 | return graph
54 | }
55 |
--------------------------------------------------------------------------------
/src/scripts/transpiler/Expressions/BinaryOperations/AssigmentExpression.ts:
--------------------------------------------------------------------------------
1 | import * as ESTree from 'estree'
2 | import { cleanUpRegister } from '../../../environment/environment'
3 | import { AccessorType, EnvironmentState } from '../../../environment/EnvironmentState'
4 | import { addVertex, applyExecutionNode } from '../../../execution/execution'
5 | import { createExecutionGraph, ExecutionGraph } from '../../../execution/graph/ExecutionGraph'
6 | import { moveAndPlaceAnimation } from '../../../execution/primitive/Data/MoveAndPlaceAnimation'
7 | import { ExecutionContext } from '../../../execution/primitive/ExecutionNode'
8 | import { clone } from '../../../utilities/objects'
9 | import { Compiler, getNodeData } from '../../Compiler'
10 |
11 | export function AssignmentExpression(
12 | ast: ESTree.AssignmentExpression,
13 | environment: EnvironmentState,
14 | context: ExecutionContext
15 | ) {
16 | const graph: ExecutionGraph = createExecutionGraph(getNodeData(ast))
17 | graph.precondition = clone(environment)
18 |
19 | const leftRegister = [{ type: AccessorType.Register, value: `${graph.id}_Assignment_Left` }]
20 | const rightRegister = [{ type: AccessorType.Register, value: `${graph.id}_Assignment_Right` }]
21 |
22 | // Put the *location* of the LHS in the left register
23 | const left = Compiler.compile(ast.left, environment, {
24 | ...context,
25 | outputRegister: leftRegister,
26 | feed: true,
27 | })
28 | addVertex(graph, left, { nodeData: getNodeData(ast.left, 'AssignmentIdentifier') })
29 |
30 | // RHS should be in the stack
31 | const right = Compiler.compile(ast.right, environment, {
32 | ...context,
33 | outputRegister: rightRegister,
34 | })
35 | addVertex(graph, right, { nodeData: getNodeData(ast.right) })
36 |
37 | const place = moveAndPlaceAnimation(rightRegister, leftRegister)
38 | addVertex(graph, place, { nodeData: getNodeData(ast.left, 'AssignmentEquals') })
39 | applyExecutionNode(place, environment)
40 |
41 | cleanUpRegister(environment, leftRegister[0].value)
42 | cleanUpRegister(environment, rightRegister[0].value)
43 |
44 | graph.postcondition = clone(environment)
45 | return graph
46 | }
47 |
--------------------------------------------------------------------------------
/src/scripts/transpiler/Expressions/BinaryOperations/BinaryExpression/BinaryExpression.ts:
--------------------------------------------------------------------------------
1 | import * as ESTree from 'estree'
2 | import { cleanUpRegister } from '../../../../environment/environment'
3 | import { AccessorType, EnvironmentState } from '../../../../environment/EnvironmentState'
4 | import { addVertex, applyExecutionNode } from '../../../../execution/execution'
5 | import { createExecutionGraph } from '../../../../execution/graph/ExecutionGraph'
6 | import { binaryExpressionEvaluate } from '../../../../execution/primitive/Binary/BinaryExpressionEvaluate'
7 | import { ExecutionContext } from '../../../../execution/primitive/ExecutionNode'
8 | import { clone } from '../../../../utilities/objects'
9 | import { Compiler, getNodeData } from '../../../Compiler'
10 |
11 | export function BinaryExpression(
12 | ast: ESTree.BinaryExpression,
13 | environment: EnvironmentState,
14 | context: ExecutionContext
15 | ) {
16 | const graph = createExecutionGraph(getNodeData(ast))
17 | graph.precondition = clone(environment)
18 |
19 | const leftRegister = [
20 | {
21 | type: AccessorType.Register,
22 | value: `${graph.id}_BinaryExpressionLeft`,
23 | },
24 | ]
25 | const rightRegister = [
26 | {
27 | type: AccessorType.Register,
28 | value: `${graph.id}_BinaryExpressionRight`,
29 | },
30 | ]
31 |
32 | const left = Compiler.compile(ast.left, environment, {
33 | ...context,
34 | outputRegister: leftRegister,
35 | })
36 |
37 | addVertex(graph, left, { nodeData: getNodeData(ast.left, 'Left') })
38 |
39 | // Fix left position
40 | // if (left.nodeData.location != null && ast.loc != null) {
41 | // // Put it at the start of the binary expression
42 | // left.nodeData.location.start.column = ast.loc.start.column
43 |
44 | // // Find how long the left is
45 |
46 | // }
47 |
48 | const right = Compiler.compile(ast.right, environment, {
49 | ...context,
50 | outputRegister: rightRegister,
51 | })
52 | addVertex(graph, right, { nodeData: getNodeData(ast.right, 'Right') })
53 |
54 | const evaluate = binaryExpressionEvaluate(leftRegister, rightRegister, ast.operator, context.outputRegister)
55 |
56 | const location = clone(ast.left.loc) as ESTree.SourceLocation
57 | location.end = (clone(ast.right.loc) as ESTree.SourceLocation).start
58 | location.start = (clone(ast.left.loc) as ESTree.SourceLocation).end
59 |
60 | addVertex(graph, evaluate, {
61 | nodeData: {
62 | ...getNodeData(ast, 'Evaluate'),
63 | type: 'BinaryExpressionEvaluate',
64 | location,
65 | },
66 | })
67 | applyExecutionNode(evaluate, environment)
68 |
69 | cleanUpRegister(environment, leftRegister[0].value)
70 | cleanUpRegister(environment, rightRegister[0].value)
71 |
72 | graph.postcondition = clone(environment)
73 | return graph
74 | }
75 |
--------------------------------------------------------------------------------
/src/scripts/transpiler/Expressions/BinaryOperations/LogicalExpression.ts:
--------------------------------------------------------------------------------
1 | import * as ESTree from 'estree'
2 | import { cleanUpRegister, resolvePath } from '../../../environment/environment'
3 | import { AccessorType, EnvironmentState } from '../../../environment/EnvironmentState'
4 | import { addVertex, applyExecutionNode } from '../../../execution/execution'
5 | import { createExecutionGraph } from '../../../execution/graph/ExecutionGraph'
6 | import { logicalExpressionEvaluate } from '../../../execution/primitive/Binary/LogicalExpressionEvaluate'
7 | import { ExecutionContext } from '../../../execution/primitive/ExecutionNode'
8 | import { DataState } from '../../../renderer/View/Environment/data/DataState'
9 | import { clone } from '../../../utilities/objects'
10 | import { Compiler, getNodeData } from '../../Compiler'
11 |
12 | export function LogicalExpression(
13 | ast: ESTree.LogicalExpression,
14 | environment: EnvironmentState,
15 | context: ExecutionContext
16 | ) {
17 | const graph = createExecutionGraph(getNodeData(ast))
18 | graph.precondition = clone(environment)
19 |
20 | const leftRegister = [
21 | {
22 | type: AccessorType.Register,
23 | value: `${graph.id}_BinaryExpressionLeft`,
24 | },
25 | ]
26 |
27 | const left = Compiler.compile(ast.left, environment, {
28 | ...context,
29 | outputRegister: leftRegister,
30 | })
31 | addVertex(graph, left, { nodeData: getNodeData(ast) })
32 |
33 | const leftValue = resolvePath(environment, leftRegister, null) as DataState
34 |
35 | let shortCircuit = false
36 |
37 | // Short circuit
38 | if (ast.operator == '&&' && leftValue.value == false) {
39 | shortCircuit = true
40 | } else if (ast.operator == '||' && leftValue.value == true) {
41 | shortCircuit = true
42 | }
43 |
44 | const rightRegister = [
45 | {
46 | type: AccessorType.Register,
47 | value: `${graph.id}_BinaryExpressionRight`,
48 | },
49 | ]
50 |
51 | if (!shortCircuit) {
52 | const right = Compiler.compile(ast.right, environment, {
53 | ...context,
54 | outputRegister: rightRegister,
55 | })
56 | addVertex(graph, right, { nodeData: getNodeData(ast) })
57 | }
58 |
59 | const evaluate = logicalExpressionEvaluate(
60 | leftRegister,
61 | rightRegister,
62 | shortCircuit,
63 | ast.operator,
64 | context.outputRegister
65 | )
66 |
67 | // addVertex(graph, evaluate, { nodeData: getNodeData(ast) })
68 | applyExecutionNode(evaluate, environment)
69 |
70 | cleanUpRegister(environment, leftRegister[0].value)
71 | cleanUpRegister(environment, rightRegister[0].value)
72 |
73 | graph.postcondition = clone(environment)
74 | return graph
75 | }
76 |
--------------------------------------------------------------------------------
/src/scripts/transpiler/Expressions/BinaryOperations/MemberExpression.ts:
--------------------------------------------------------------------------------
1 | import * as ESTree from 'estree'
2 | import { convertIdentifierToLiteral } from '../../../environment/data'
3 | import { cleanUpRegister } from '../../../environment/environment'
4 | import { AccessorType, EnvironmentState } from '../../../environment/EnvironmentState'
5 | import { addVertex, applyExecutionNode } from '../../../execution/execution'
6 | import { createExecutionGraph, ExecutionGraph } from '../../../execution/graph/ExecutionGraph'
7 | import { findMember } from '../../../execution/primitive/Data/FindMember'
8 | import { getMember } from '../../../execution/primitive/Data/GetMember'
9 | import { ExecutionContext } from '../../../execution/primitive/ExecutionNode'
10 | import { clone } from '../../../utilities/objects'
11 | import { Compiler, getNodeData } from '../../Compiler'
12 |
13 | /**
14 | *
15 | * @param ast
16 | * @param view
17 | * @param context
18 | * @returns
19 | */
20 | export function MemberExpression(
21 | ast: ESTree.MemberExpression,
22 | environment: EnvironmentState,
23 | context: ExecutionContext
24 | ) {
25 | const graph: ExecutionGraph = createExecutionGraph(getNodeData(ast))
26 | graph.precondition = clone(environment)
27 |
28 | // Create a register which'll *point* to the location of object
29 | const objectRegister = [{ type: AccessorType.Register, value: `${graph.id}_Object` }]
30 | const object = Compiler.compile(ast.object, environment, {
31 | ...context,
32 | feed: false,
33 | outputRegister: objectRegister,
34 | })
35 | addVertex(graph, object, { nodeData: getNodeData(ast.object, 'Object') })
36 |
37 | // Create a register that'll point to the location of computed property
38 | const propertyRegister = [{ type: AccessorType.Register, value: `${graph.id}_Property` }]
39 |
40 | // Something like obj[i], or obj['x']
41 | const property = Compiler.compile(
42 | ast.computed ? ast.property : convertIdentifierToLiteral(ast.property as ESTree.Identifier),
43 | environment,
44 | {
45 | ...context,
46 | outputRegister: propertyRegister,
47 | feed: false,
48 | }
49 | )
50 | addVertex(graph, property, {
51 | nodeData: getNodeData(ast.property, 'Property'),
52 | })
53 |
54 | // Compute the result
55 | if (context.feed) {
56 | const find = findMember(objectRegister, propertyRegister, context.outputRegister)
57 | addVertex(graph, find, { nodeData: getNodeData(ast, 'Find Member') })
58 | applyExecutionNode(find, environment)
59 | } else {
60 | const member = getMember(objectRegister, propertyRegister, context.outputRegister)
61 | addVertex(graph, member, { nodeData: getNodeData(ast, 'Get Member') })
62 | applyExecutionNode(member, environment)
63 | }
64 |
65 | cleanUpRegister(environment, objectRegister[0].value)
66 | cleanUpRegister(environment, propertyRegister[0].value)
67 |
68 | graph.postcondition = clone(environment)
69 | return graph
70 | }
71 |
--------------------------------------------------------------------------------
/src/scripts/transpiler/Expressions/ObjectExpression.ts:
--------------------------------------------------------------------------------
1 | import { generate } from 'astring'
2 | import * as ESTree from 'estree'
3 | import { cleanUpRegister } from '../../environment/environment'
4 | import { AccessorType, EnvironmentState } from '../../environment/EnvironmentState'
5 | import { addVertex, applyExecutionNode } from '../../execution/execution'
6 | import { createExecutionGraph, ExecutionGraph } from '../../execution/graph/ExecutionGraph'
7 | import { objectStartAnimation } from '../../execution/primitive/Container/ObjectStartAnimation'
8 | import { moveAndPlaceAnimation } from '../../execution/primitive/Data/MoveAndPlaceAnimation'
9 | import { ExecutionContext } from '../../execution/primitive/ExecutionNode'
10 | import { clone } from '../../utilities/objects'
11 | import { Compiler, getNodeData } from '../Compiler'
12 |
13 | export function ObjectExpression(
14 | ast: ESTree.ObjectExpression,
15 | environment: EnvironmentState,
16 | context: ExecutionContext
17 | ) {
18 | const graph: ExecutionGraph = createExecutionGraph(getNodeData(ast))
19 | graph.precondition = clone(environment)
20 |
21 | const start = objectStartAnimation(context.outputRegister)
22 | addVertex(graph, start, { nodeData: getNodeData(ast, 'start') })
23 | applyExecutionNode(start, environment)
24 |
25 | for (let i = 0; i < ast.properties.length; i++) {
26 | const property = ast.properties[i]
27 | if (property.type === 'SpreadElement') {
28 | throw new Error("Spread elements aren't supported yet")
29 | }
30 |
31 | const register = [
32 | {
33 | type: AccessorType.Register,
34 | value: `${graph.id}_ArrayExpression_${i}`,
35 | },
36 | ]
37 |
38 | // Object element
39 | const animation = Compiler.compile(property.value, environment, {
40 | ...context,
41 | outputRegister: register,
42 | })
43 | addVertex(graph, animation, {
44 | nodeData: getNodeData(property.value, `${property.key}`),
45 | })
46 |
47 | const place = moveAndPlaceAnimation(register, [
48 | ...context.outputRegister,
49 | { type: AccessorType.Index, value: generate(property.key) },
50 | ])
51 | addVertex(graph, place, { nodeData: getNodeData(property) })
52 | applyExecutionNode(place, environment)
53 |
54 | cleanUpRegister(environment, register[0].value)
55 | }
56 |
57 | graph.postcondition = clone(environment)
58 | return graph
59 | }
60 |
--------------------------------------------------------------------------------
/src/scripts/transpiler/Expressions/UnaryOperations/UnaryExpression.ts:
--------------------------------------------------------------------------------
1 | import * as ESTree from 'estree'
2 | import { cleanUpRegister } from '../../../environment/environment'
3 | import { AccessorType, EnvironmentState } from '../../../environment/EnvironmentState'
4 | import { addVertex, applyExecutionNode } from '../../../execution/execution'
5 | import { createExecutionGraph, ExecutionGraph } from '../../../execution/graph/ExecutionGraph'
6 | import { ExecutionContext } from '../../../execution/primitive/ExecutionNode'
7 | import { unaryExpressionEvaluate } from '../../../execution/primitive/Unary/UnaryExpressionEvaluate'
8 | import { clone } from '../../../utilities/objects'
9 | import { Compiler, getNodeData } from '../../Compiler'
10 |
11 | export function UnaryExpression(
12 | ast: ESTree.UnaryExpression,
13 | environment: EnvironmentState,
14 | context: ExecutionContext
15 | ) {
16 | const graph: ExecutionGraph = createExecutionGraph(getNodeData(ast))
17 | graph.precondition = clone(environment)
18 |
19 | const argRegister = [{ type: AccessorType.Register, value: `${graph.id}_UpdateExpr` }]
20 |
21 | const argument = Compiler.compile(ast.argument, environment, {
22 | ...context,
23 | outputRegister: argRegister,
24 | })
25 | addVertex(graph, argument, { nodeData: getNodeData(ast.argument) })
26 |
27 | const evaluate = unaryExpressionEvaluate(argRegister, ast.operator, context.outputRegister)
28 | addVertex(graph, evaluate, { nodeData: getNodeData(ast, 'Evaluate') })
29 | applyExecutionNode(evaluate, environment)
30 |
31 | cleanUpRegister(environment, argRegister[0].value)
32 |
33 | graph.postcondition = clone(environment)
34 | return graph
35 | }
36 |
--------------------------------------------------------------------------------
/src/scripts/transpiler/Expressions/UnaryOperations/UpdateExpression.ts:
--------------------------------------------------------------------------------
1 | import * as ESTree from 'estree'
2 | import { cleanUpRegister } from '../../../environment/environment'
3 | import { AccessorType, EnvironmentState } from '../../../environment/EnvironmentState'
4 | import { addVertex, applyExecutionNode } from '../../../execution/execution'
5 | import { createExecutionGraph, ExecutionGraph } from '../../../execution/graph/ExecutionGraph'
6 | import { updateAnimation } from '../../../execution/primitive/Data/UpdateAnimation'
7 | import { ExecutionContext } from '../../../execution/primitive/ExecutionNode'
8 | import { clone } from '../../../utilities/objects'
9 | import { Compiler, getNodeData } from '../../Compiler'
10 |
11 | export function UpdateExpression(
12 | ast: ESTree.UpdateExpression,
13 | environment: EnvironmentState,
14 | context: ExecutionContext
15 | ) {
16 | const graph: ExecutionGraph = createExecutionGraph(getNodeData(ast))
17 | graph.precondition = clone(environment)
18 |
19 | const argRegister = [{ type: AccessorType.Register, value: `${graph.id}_UpdateExpr` }]
20 |
21 | // Put the *location* of argument in a register
22 | const argument = Compiler.compile(ast.argument, environment, {
23 | ...context,
24 | outputRegister: argRegister,
25 | feed: true,
26 | })
27 | addVertex(graph, argument, { nodeData: getNodeData(ast.argument) })
28 |
29 | if (context.outputRegister != null) {
30 | // Return a copy of it before applying update
31 | const output = Compiler.compile(ast.argument, environment, {
32 | ...context,
33 | outputRegister: context.outputRegister,
34 | feed: false,
35 | })
36 | addVertex(graph, output, { nodeData: getNodeData(ast.argument) })
37 | }
38 |
39 | // Apply the operation
40 | const update = updateAnimation(argRegister, ast.operator)
41 | addVertex(graph, update, { nodeData: getNodeData(ast.argument) })
42 | applyExecutionNode(update, environment)
43 |
44 | cleanUpRegister(environment, argRegister[0].value)
45 |
46 | graph.postcondition = clone(environment)
47 | return graph
48 | }
49 |
--------------------------------------------------------------------------------
/src/scripts/transpiler/Functions/FunctionCall.ts:
--------------------------------------------------------------------------------
1 | import * as ESTree from 'estree'
2 | import { EnvironmentState } from '../../environment/EnvironmentState'
3 | import { addVertex, applyExecutionNode } from '../../execution/execution'
4 | import { createExecutionGraph, ExecutionGraph } from '../../execution/graph/ExecutionGraph'
5 | import { bindAnimation } from '../../execution/primitive/Binding/BindAnimation'
6 | import { ExecutionContext } from '../../execution/primitive/ExecutionNode'
7 | import { createScopeAnimation } from '../../execution/primitive/Scope/CreateScopeAnimation'
8 | import { popScopeAnimation } from '../../execution/primitive/Scope/PopScopeAnimation'
9 | import { clone } from '../../utilities/objects'
10 | import { getNodeData } from '../Compiler'
11 | import { BlockStatement, ScopeType } from '../Statements/BlockStatement'
12 |
13 | export function FunctionCall(
14 | ast: ESTree.FunctionDeclaration,
15 | environment: EnvironmentState,
16 | context: ExecutionContext
17 | ) {
18 | const graph: ExecutionGraph = createExecutionGraph(getNodeData(ast))
19 | graph.precondition = clone(environment)
20 |
21 | // Create a scope @TODO: HARD SCOPE
22 | const createScope = createScopeAnimation(ScopeType.Hard)
23 | // addVertex(graph, createScope, { nodeData: getNodeData(ast) })
24 | applyExecutionNode(createScope, environment)
25 |
26 | // Bind arguments
27 | const args: ExecutionGraph = createExecutionGraph(getNodeData(ast))
28 | args.precondition = clone(environment)
29 |
30 | for (let i = 0; i < ast.params.length; i++) {
31 | const param = ast.params[i] as ESTree.Identifier
32 | const argRegister = context.args[i]
33 |
34 | const bind = bindAnimation(param.name, argRegister)
35 | addVertex(args, bind, { nodeData: getNodeData(ast.params[i]) })
36 | applyExecutionNode(bind, environment)
37 | }
38 | args.postcondition = clone(environment)
39 |
40 | // addVertex(graph, args, { nodeData: getNodeData(ast.params, 'Arguments') })
41 |
42 | // Call function
43 | const body = BlockStatement(ast.body, environment, { ...context, args: null }, ScopeType.None)
44 | addVertex(graph, body, { nodeData: getNodeData(ast.body, 'Body') })
45 |
46 | // Pop scope
47 | const popScope = popScopeAnimation()
48 | // addVertex(body, popScope, { nodeData: getNodeData(ast) })
49 | applyExecutionNode(popScope, environment)
50 |
51 | graph.postcondition = clone(environment)
52 | body.postcondition = clone(environment)
53 | return body
54 | }
55 |
--------------------------------------------------------------------------------
/src/scripts/transpiler/Functions/FunctionDeclaration.ts:
--------------------------------------------------------------------------------
1 | import * as ESTree from 'estree'
2 | import { EnvironmentState } from '../../environment/EnvironmentState'
3 | import { applyExecutionNode } from '../../execution/execution'
4 | import { bindFunctionAnimation } from '../../execution/primitive/Binding/BindFunctionAnimation'
5 | import { ExecutionContext } from '../../execution/primitive/ExecutionNode'
6 |
7 | export function FunctionDeclaration(
8 | ast: ESTree.FunctionDeclaration,
9 | environment: EnvironmentState,
10 | context: ExecutionContext
11 | ) {
12 | // const graph: ExecutionGraph = createExecutionGraph(getNodeData(ast))
13 | // graph.precondition = clone(environment)
14 |
15 | // Allocate a place for variable that *points* to the register @TODO: support other initializations that identifier
16 | const bind = bindFunctionAnimation((ast.id as ESTree.Identifier).name, ast)
17 | // addVertex(graph, bind, { nodeData: getNodeData(ast.id) })
18 | applyExecutionNode(bind, environment)
19 |
20 | // const FunctionCallInstance = (ast: ESTree.FunctionDeclaration, environment: EnvironmentState, context: ExecutionContext) => {
21 | // const subScope = createScopeAnimation();
22 |
23 | // for (let i = 0; i < params.length; i++) {
24 | // const param = params[i] as ESTree.Identifier;
25 | //
26 | // const bind = bindAnimation(param.name, context.args[i], subScope);
27 | // }
28 | // };
29 |
30 | // graph.postcondition = clone(environment)
31 | return null
32 | }
33 |
--------------------------------------------------------------------------------
/src/scripts/transpiler/Functions/ReturnStatement.ts:
--------------------------------------------------------------------------------
1 | import * as ESTree from 'estree'
2 | import { cleanUpRegister } from '../../environment/environment'
3 | import { Accessor, AccessorType, EnvironmentState } from '../../environment/EnvironmentState'
4 | import { addVertex, applyExecutionNode } from '../../execution/execution'
5 | import { createExecutionGraph, ExecutionGraph } from '../../execution/graph/ExecutionGraph'
6 | import { consumeDataAnimation } from '../../execution/primitive/Data/ConsumeDataAnimation'
7 | import { ControlOutput, ExecutionContext } from '../../execution/primitive/ExecutionNode'
8 | import { returnStatementAnimation } from '../../execution/primitive/Functions/ReturnStatementAnimation'
9 | import { clone } from '../../utilities/objects'
10 | import { Compiler, getNodeData } from '../Compiler'
11 |
12 | export function ReturnStatement(ast: ESTree.ReturnStatement, environment: EnvironmentState, context: ExecutionContext) {
13 | const graph: ExecutionGraph = createExecutionGraph(getNodeData(ast))
14 | graph.precondition = clone(environment)
15 |
16 | // Evaluate the result of argument into register
17 |
18 | let usingTemp = false
19 | let returnRegister: Accessor[] = context.returnData.register
20 |
21 | if (context.returnData.register == null) {
22 | // Temp return register
23 | returnRegister = [
24 | {
25 | type: AccessorType.Register,
26 | value: `${graph.id}_TempReturnRegister`,
27 | },
28 | ]
29 |
30 | usingTemp = true
31 | }
32 |
33 | const argument = Compiler.compile(ast.argument, environment, {
34 | ...context,
35 | outputRegister: returnRegister,
36 | })
37 | addVertex(graph, argument, { nodeData: getNodeData(ast.argument) })
38 |
39 | // Make sure the memory this register is pointing at doesn't get destroyed
40 | // when popping scopes
41 | if (usingTemp) {
42 | context.returnData.register = returnRegister
43 | }
44 | const ret = returnStatementAnimation(context.returnData)
45 | addVertex(graph, ret, { nodeData: { ...getNodeData(ast), type: 'ReturnStatementAnimation' } })
46 | applyExecutionNode(ret, environment)
47 |
48 | // Cleanup temp return
49 | if (usingTemp) {
50 | const consume = consumeDataAnimation(returnRegister)
51 | // addVertex(graph, consume, { nodeData: getNodeData(ast) })
52 | applyExecutionNode(consume, environment)
53 | cleanUpRegister(environment, returnRegister[0].value)
54 |
55 | context.returnData.register = null
56 | }
57 |
58 | // const special = createExecutionNode({
59 | // ...getNodeData(ast),
60 | // type: 'ReturnSpecial',
61 | // preLabel: 'ReturnSpecial',
62 | // })
63 | // special._reads = [
64 | // {
65 | // location: context.returnData.register,
66 | // value: 'return',
67 | // },
68 | // ]
69 | // special._writes = []
70 | // special.precondition = clone(environment)
71 | // special.postcondition = clone(environment)
72 |
73 | // addVertex(graph, special, { nodeData: getNodeData(ast) })
74 |
75 | // TODO: Does this need a move and place?
76 | // const place = moveAndPlaceAnimation(register, context.returnData.register);
77 | // addVertex(graph, place, getNodeData(ast));
78 | //applyExecution(place, view);
79 |
80 | context.controlOutput.output = ControlOutput.Return
81 |
82 | graph.postcondition = clone(environment)
83 | return graph
84 | }
85 |
--------------------------------------------------------------------------------
/src/scripts/transpiler/Identifier.ts:
--------------------------------------------------------------------------------
1 | import * as ESTree from 'estree'
2 | import { resolvePath } from '../environment/environment'
3 | import { AccessorType, EnvironmentState } from '../environment/EnvironmentState'
4 | import { addVertex, applyExecutionNode } from '../execution/execution'
5 | import { createExecutionGraph } from '../execution/graph/ExecutionGraph'
6 | import { copyDataAnimation } from '../execution/primitive/Data/CopyDataAnimation'
7 | import { copyReferenceAnimation } from '../execution/primitive/Data/CopyReferenceAnimation'
8 | import { findVariableAnimation } from '../execution/primitive/Data/FindVariableAnimation'
9 | import { ExecutionContext } from '../execution/primitive/ExecutionNode'
10 | import { DataState, instanceOfObjectData } from '../renderer/View/Environment/data/DataState'
11 | import { clone } from '../utilities/objects'
12 | import { getNodeData } from './Compiler'
13 |
14 | export function Identifier(
15 | ast: ESTree.Identifier,
16 | environment: EnvironmentState,
17 | context: ExecutionContext
18 | ) {
19 | const graph = createExecutionGraph(getNodeData(ast))
20 | graph.precondition = clone(environment)
21 |
22 | if (context.feed) {
23 | // Feeding
24 | const reference = findVariableAnimation(ast.name, context.outputRegister)
25 | addVertex(graph, reference, { nodeData: getNodeData(ast) })
26 | applyExecutionNode(reference, environment)
27 | } else {
28 | const data = resolvePath(
29 | environment,
30 | [{ type: AccessorType.Symbol, value: ast.name }],
31 | null
32 | ) as DataState
33 |
34 | if (instanceOfObjectData(data)) {
35 | // Create a reference to it
36 | const copyReference = copyReferenceAnimation(
37 | [{ type: AccessorType.Symbol, value: ast.name }],
38 | context.outputRegister
39 | )
40 | addVertex(graph, copyReference, { nodeData: getNodeData(ast) })
41 | applyExecutionNode(copyReference, environment)
42 | } else {
43 | // Create a copy of it
44 | const copy = copyDataAnimation(
45 | [{ type: AccessorType.Symbol, value: ast.name }],
46 | context.outputRegister
47 | )
48 | addVertex(graph, copy, { nodeData: getNodeData(ast) })
49 | applyExecutionNode(copy, environment)
50 | }
51 | }
52 |
53 | graph.postcondition = clone(environment)
54 | return graph
55 | }
56 |
--------------------------------------------------------------------------------
/src/scripts/transpiler/Literal.ts:
--------------------------------------------------------------------------------
1 | import * as ESTree from 'estree'
2 | import { EnvironmentState } from '../environment/EnvironmentState'
3 | import { addVertex, applyExecutionNode } from '../execution/execution'
4 | import { createExecutionGraph, ExecutionGraph } from '../execution/graph/ExecutionGraph'
5 | import { createLiteralAnimation } from '../execution/primitive/Data/CreateLiteralAnimation'
6 | import { ExecutionContext } from '../execution/primitive/ExecutionNode'
7 | import { clone } from '../utilities/objects'
8 | import { getNodeData } from './Compiler'
9 |
10 | export function Literal(
11 | ast: ESTree.Literal,
12 | environment: EnvironmentState,
13 | context: ExecutionContext
14 | ) {
15 | const graph: ExecutionGraph = createExecutionGraph(getNodeData(ast))
16 | graph.precondition = clone(environment)
17 |
18 | const create = createLiteralAnimation(ast.value, context.outputRegister, context.locationHint)
19 | addVertex(graph, create, { nodeData: getNodeData(ast) })
20 | applyExecutionNode(create, environment)
21 |
22 | graph.postcondition = clone(environment)
23 | return graph
24 | }
25 |
--------------------------------------------------------------------------------
/src/scripts/transpiler/Statements/BlockStatement.ts:
--------------------------------------------------------------------------------
1 | import * as ESTree from 'estree'
2 | import { EnvironmentState } from '../../environment/EnvironmentState'
3 | import { addVertex, applyExecutionNode } from '../../execution/execution'
4 | import { createExecutionGraph, ExecutionGraph } from '../../execution/graph/ExecutionGraph'
5 | import { ControlOutput, ControlOutputData, ExecutionContext } from '../../execution/primitive/ExecutionNode'
6 | import { createScopeAnimation } from '../../execution/primitive/Scope/CreateScopeAnimation'
7 | import { popScopeAnimation } from '../../execution/primitive/Scope/PopScopeAnimation'
8 | import { clone } from '../../utilities/objects'
9 | import { Compiler, getNodeData } from '../Compiler'
10 |
11 | export enum ScopeType {
12 | None = 'None',
13 | Default = 'Default',
14 | Global = 'Global',
15 | Hard = 'Hard',
16 | }
17 |
18 | /**
19 | * A block contains a sequence of one or more statements.
20 | * @param ast Block Statement AST
21 | * @param view ViewState state leading up to block statement
22 | * @param context
23 | * @returns {ExecutionGraph} animation
24 | */
25 | export function BlockStatement(
26 | ast: ESTree.BlockStatement,
27 | environment: EnvironmentState,
28 | context: ExecutionContext,
29 | scopeType: ScopeType = ScopeType.Default
30 | ): ExecutionGraph {
31 | const graph = createExecutionGraph(getNodeData(ast))
32 | graph.precondition = clone(environment)
33 |
34 | context.locationHint = []
35 |
36 | // Create a scope
37 | if (scopeType == ScopeType.Default) {
38 | const createScope = createScopeAnimation()
39 | // addVertex(graph, createScope, { nodeData: getNodeData(ast, 'Create Scope') })
40 | applyExecutionNode(createScope, environment)
41 | }
42 |
43 | let currLine = -1
44 |
45 | // Add statements
46 | for (const statement of ast.body) {
47 | const controlOutput: ControlOutputData = { output: ControlOutput.None }
48 |
49 | const animation = Compiler.compile(statement, environment, {
50 | ...context,
51 | controlOutput,
52 | })
53 |
54 | let line = animation.nodeData.location?.start.line as number
55 | let hasLineBreak = false
56 | if (line > currLine + 1 && currLine >= 0) {
57 | // Add a line break
58 | hasLineBreak = true
59 | }
60 | currLine = animation.nodeData.location?.end.line as number
61 |
62 | addVertex(graph, animation, { nodeData: getNodeData(statement, 'Statement', hasLineBreak) })
63 |
64 | if (controlOutput.output == ControlOutput.Break) {
65 | // Keep propagating 'break' until reaching a ForStatement or WhileStatement
66 | context.controlOutput.output = ControlOutput.Break
67 | break
68 | } else if (controlOutput.output == ControlOutput.Continue) {
69 | // Keep propagating 'continue' until reaching a ForStatement or WhileStatement
70 | context.controlOutput.output = ControlOutput.Continue
71 | break
72 | } else if (controlOutput.output == ControlOutput.Return) {
73 | // Keep propagating 'return' until reaching a ForStatement or WhileStatement
74 | context.controlOutput.output = ControlOutput.Return
75 | break
76 | }
77 | }
78 |
79 | // Pop scope
80 | if (scopeType == ScopeType.Default) {
81 | const popScope = popScopeAnimation()
82 | // addVertex(graph, popScope, { nodeData: getNodeData(ast, 'Pop Scope') })
83 | applyExecutionNode(popScope, environment)
84 | }
85 |
86 | graph.postcondition = clone(environment)
87 | return graph
88 | }
89 |
--------------------------------------------------------------------------------
/src/scripts/transpiler/Statements/Choice/IfStatement.ts:
--------------------------------------------------------------------------------
1 | import * as ESTree from 'estree'
2 | import { cleanUpRegister, resolvePath } from '../../../environment/environment'
3 | import { AccessorType, EnvironmentState } from '../../../environment/EnvironmentState'
4 | import { addVertex, applyExecutionNode } from '../../../execution/execution'
5 | import { createExecutionGraph, ExecutionGraph } from '../../../execution/graph/ExecutionGraph'
6 | import { consumeDataAnimation } from '../../../execution/primitive/Data/ConsumeDataAnimation'
7 | import {
8 | ControlOutput,
9 | ControlOutputData,
10 | ExecutionContext,
11 | } from '../../../execution/primitive/ExecutionNode'
12 | import { DataState } from '../../../renderer/View/Environment/data/DataState'
13 | import { clone } from '../../../utilities/objects'
14 | import { Compiler, getNodeData } from '../../Compiler'
15 |
16 | export function IfStatement(
17 | ast: ESTree.IfStatement,
18 | environment: EnvironmentState,
19 | context: ExecutionContext
20 | ) {
21 | const graph: ExecutionGraph = createExecutionGraph(getNodeData(ast))
22 | graph.precondition = clone(environment)
23 |
24 | // Points to the result of the test
25 | const testRegister = [{ type: AccessorType.Register, value: `${graph.id}_TestIf` }]
26 |
27 | const test = Compiler.compile(ast.test, environment, {
28 | ...context,
29 | outputRegister: testRegister,
30 | })
31 | addVertex(graph, test, { nodeData: getNodeData(ast.test, 'Test') })
32 |
33 | // @TODO: Add a probe test animation
34 | const testData = resolvePath(environment, testRegister, null) as DataState
35 | const testValue = testData.value as boolean
36 |
37 | // Consume testData
38 | const consume = consumeDataAnimation(testRegister)
39 | // addVertex(graph, consume, { nodeData: getNodeData(ast) })
40 | applyExecutionNode(consume, environment)
41 | cleanUpRegister(environment, testRegister[0].value)
42 |
43 | const controlOutput: ControlOutputData = { output: ControlOutput.None }
44 |
45 | if (testValue) {
46 | // Execute the body
47 | const body = Compiler.compile(ast.consequent, environment, {
48 | ...context,
49 | controlOutput,
50 | })
51 | addVertex(graph, body, { nodeData: getNodeData(ast.consequent, 'Consequent') })
52 | } else if (ast.alternate != null) {
53 | // Execute the alternate (if any)
54 | const alternate = Compiler.compile(ast.alternate, environment, {
55 | ...context,
56 | controlOutput,
57 | })
58 | addVertex(graph, alternate, { nodeData: getNodeData(ast.alternate, 'Alternate') })
59 | }
60 |
61 | context.controlOutput.output = controlOutput.output
62 |
63 | graph.postcondition = clone(environment)
64 | return graph
65 | }
66 |
--------------------------------------------------------------------------------
/src/scripts/transpiler/Statements/ExpressionStatement.ts:
--------------------------------------------------------------------------------
1 | import * as ESTree from 'estree'
2 | import { EnvironmentState } from '../../environment/EnvironmentState'
3 | import { ExecutionContext } from '../../execution/primitive/ExecutionNode'
4 | import { Compiler } from '../Compiler'
5 |
6 | export function ExpressionStatement(
7 | ast: ESTree.ExpressionStatement,
8 | environment: EnvironmentState,
9 | context: ExecutionContext
10 | ) {
11 | return Compiler.compile(ast.expression, environment, context)
12 | }
13 |
--------------------------------------------------------------------------------
/src/scripts/transpiler/Statements/Loops/ForStatement.ts:
--------------------------------------------------------------------------------
1 | import * as ESTree from 'estree'
2 | import { cleanUpRegister, resolvePath } from '../../../environment/environment'
3 | import { AccessorType, EnvironmentState } from '../../../environment/EnvironmentState'
4 | import { addVertex, applyExecutionNode } from '../../../execution/execution'
5 | import { createExecutionGraph, ExecutionGraph } from '../../../execution/graph/ExecutionGraph'
6 | import { consumeDataAnimation } from '../../../execution/primitive/Data/ConsumeDataAnimation'
7 | import { ControlOutput, ControlOutputData, ExecutionContext } from '../../../execution/primitive/ExecutionNode'
8 | import { createScopeAnimation } from '../../../execution/primitive/Scope/CreateScopeAnimation'
9 | import { popScopeAnimation } from '../../../execution/primitive/Scope/PopScopeAnimation'
10 | import { DataState } from '../../../renderer/View/Environment/data/DataState'
11 | import { clone } from '../../../utilities/objects'
12 | import { Compiler, getNodeData } from '../../Compiler'
13 |
14 | export function ForStatement(ast: ESTree.ForStatement, environment: EnvironmentState, context: ExecutionContext) {
15 | const graph: ExecutionGraph = createExecutionGraph(getNodeData(ast))
16 | graph.precondition = clone(environment)
17 |
18 | // Create a scope
19 | const createScope = createScopeAnimation()
20 | // addVertex(graph, createScope, { nodeData: getNodeData(ast, 'Create Scope') })
21 | applyExecutionNode(createScope, environment)
22 |
23 | // let iteration = createExecutionGraph({
24 | // ...getNodeData(ast),
25 | // type: 'ForStatementIteration',
26 | // })
27 | // iteration.precondition = clone(environment)
28 |
29 | // Init
30 | const init = Compiler.compile(ast.init, environment, context)
31 | addVertex(graph, init, { nodeData: getNodeData(ast.init, 'Initial') })
32 |
33 | // Points to the result of test
34 |
35 | let _i = 0
36 | // Loop
37 | while (true) {
38 | // Test
39 | const testRegister = [{ type: AccessorType.Register, value: `${graph.id}_TestIf${_i}` }]
40 | const test = Compiler.compile(ast.test, environment, {
41 | ...context,
42 | outputRegister: testRegister,
43 | })
44 | addVertex(graph, test, { nodeData: getNodeData(ast.test, 'Test') })
45 |
46 | const testData = resolvePath(environment, testRegister, null) as DataState
47 | const testValue = testData.value as boolean
48 |
49 | // Consume testData
50 | const consume = consumeDataAnimation(testRegister)
51 | applyExecutionNode(consume, environment)
52 | cleanUpRegister(environment, testRegister[0].value)
53 |
54 | if (!testValue) {
55 | graph.postcondition = clone(environment)
56 | break
57 | }
58 |
59 | // Body
60 | const controlOutput: ControlOutputData = { output: ControlOutput.None }
61 | const body = Compiler.compile(ast.body, environment, {
62 | ...context,
63 | controlOutput: controlOutput,
64 | })
65 |
66 | addVertex(graph, body, { nodeData: getNodeData(ast.body, 'Body') })
67 |
68 | if (controlOutput.output == ControlOutput.Break) {
69 | context.controlOutput.output = ControlOutput.None
70 | break
71 | } else if (controlOutput.output == ControlOutput.Continue) {
72 | context.controlOutput.output = ControlOutput.None
73 | } else if (controlOutput.output == ControlOutput.Return) {
74 | context.controlOutput.output = ControlOutput.Return
75 | break
76 | }
77 |
78 | // Update
79 | const update = Compiler.compile(ast.update, environment, {
80 | ...context,
81 | outputRegister: null,
82 | })
83 | addVertex(graph, update, { nodeData: getNodeData(ast.update, 'Update') })
84 |
85 | // Add iteration to the graph
86 | // iteration.postcondition = clone(environment)
87 | // addVertex(graph, iteration, {
88 | // nodeData: {
89 | // ...getNodeData(ast, `Iteration ${_i}`),
90 | // type: 'ForStatementIteration',
91 | // },
92 | // })
93 |
94 | // iteration = createExecutionGraph({
95 | // ...getNodeData(ast),
96 | // type: 'ForStatementIteration',
97 | // })
98 | // iteration.precondition = clone(environment)
99 |
100 | _i++
101 | }
102 |
103 | if (context.controlOutput.output != ControlOutput.Return) {
104 | context.controlOutput.output = ControlOutput.None
105 | }
106 |
107 | // Pop scope
108 | const popScope = popScopeAnimation()
109 | // addVertex(graph, popScope, { nodeData: getNodeData(ast, 'Pop Scope') })
110 | applyExecutionNode(popScope, environment)
111 |
112 | graph.postcondition = clone(environment)
113 | return graph
114 | }
115 |
--------------------------------------------------------------------------------
/src/scripts/transpiler/Statements/Loops/WhileStatement.ts:
--------------------------------------------------------------------------------
1 | import * as ESTree from 'estree'
2 | import { cleanUpRegister, resolvePath } from '../../../environment/environment'
3 | import { AccessorType, EnvironmentState } from '../../../environment/EnvironmentState'
4 | import { addVertex, applyExecutionNode } from '../../../execution/execution'
5 | import { createExecutionGraph, ExecutionGraph } from '../../../execution/graph/ExecutionGraph'
6 | import {
7 | ControlOutput,
8 | ControlOutputData,
9 | ExecutionContext,
10 | } from '../../../execution/primitive/ExecutionNode'
11 | import { createScopeAnimation } from '../../../execution/primitive/Scope/CreateScopeAnimation'
12 | import { popScopeAnimation } from '../../../execution/primitive/Scope/PopScopeAnimation'
13 | import { DataState } from '../../../renderer/View/Environment/data/DataState'
14 | import { clone } from '../../../utilities/objects'
15 | import { Compiler, getNodeData } from '../../Compiler'
16 |
17 | export function WhileStatement(
18 | ast: ESTree.WhileStatement,
19 | environment: EnvironmentState,
20 | context: ExecutionContext
21 | ) {
22 | const graph: ExecutionGraph = createExecutionGraph(getNodeData(ast))
23 | graph.precondition = clone(environment)
24 |
25 | // Create a scope
26 | const createScope = createScopeAnimation()
27 | addVertex(graph, createScope, { nodeData: getNodeData(ast) })
28 | applyExecutionNode(createScope, environment)
29 |
30 | let _i = 0
31 |
32 | // Loop
33 | while (true) {
34 | // Test
35 | const testRegister = [{ type: AccessorType.Register, value: `${graph.id}_TestIf${_i}` }]
36 | const test = Compiler.compile(ast.test, environment, {
37 | ...context,
38 | outputRegister: testRegister,
39 | })
40 | addVertex(graph, test, { nodeData: getNodeData(ast.test) })
41 | const testData = resolvePath(environment, testRegister, null) as DataState // @TODO: Add a probe test animation
42 | const testValue = testData.value as boolean
43 | cleanUpRegister(environment, testRegister[0].value)
44 | if (!testValue) break
45 |
46 | // Body
47 | const controlOutput: ControlOutputData = { output: ControlOutput.None }
48 | const body = Compiler.compile(ast.body, environment, {
49 | ...context,
50 | controlOutput: controlOutput,
51 | })
52 | addVertex(graph, body, { nodeData: getNodeData(ast.body) })
53 |
54 | if (controlOutput.output == ControlOutput.Break) {
55 | context.controlOutput.output = ControlOutput.None
56 | break
57 | } else if (controlOutput.output == ControlOutput.Continue) {
58 | context.controlOutput.output = ControlOutput.None
59 | } else if (controlOutput.output == ControlOutput.Return) {
60 | context.controlOutput.output = ControlOutput.Return
61 | break
62 | }
63 |
64 | // Update
65 | _i++
66 | }
67 |
68 | if (context.controlOutput.output != ControlOutput.Return) {
69 | context.controlOutput.output = ControlOutput.None
70 | }
71 |
72 | // Pop scope
73 | const popScope = popScopeAnimation()
74 | addVertex(graph, popScope, { nodeData: getNodeData(ast) })
75 | applyExecutionNode(popScope, environment)
76 |
77 | graph.postcondition = clone(environment)
78 | return graph
79 | }
80 |
--------------------------------------------------------------------------------
/src/scripts/transpiler/Statements/Program.ts:
--------------------------------------------------------------------------------
1 | import * as ESTree from 'estree'
2 | import { EnvironmentState } from '../../environment/EnvironmentState'
3 | import { addVertex } from '../../execution/execution'
4 | import { createExecutionGraph } from '../../execution/graph/ExecutionGraph'
5 | import { ControlOutput, ExecutionContext } from '../../execution/primitive/ExecutionNode'
6 | import { clone } from '../../utilities/objects'
7 | import { Compiler, getNodeData } from '../Compiler'
8 |
9 | export function Program(ast: ESTree.Program, environment: EnvironmentState, context: ExecutionContext) {
10 | const graph = createExecutionGraph(getNodeData(ast, 'Program'))
11 | graph.precondition = clone(environment)
12 |
13 | const controlOutput = { output: ControlOutput.None }
14 |
15 | let currLine = -1
16 |
17 | // Add blocks
18 | for (const statement of ast.body) {
19 | const animation = Compiler.compile(statement, environment, {
20 | ...context,
21 | controlOutput,
22 | })
23 |
24 | if (animation != null) {
25 | let line = animation.nodeData.location?.start.line as number
26 | let hasLineBreak = false
27 | if (line > currLine + 1 && currLine >= 0) {
28 | // Add a line break
29 | hasLineBreak = true
30 | }
31 | currLine = animation.nodeData.location?.end.line as number
32 |
33 | addVertex(graph, animation, { nodeData: getNodeData(statement, 'Statement', hasLineBreak) })
34 | }
35 | }
36 |
37 | graph.postcondition = clone(environment)
38 | return graph
39 | }
40 |
--------------------------------------------------------------------------------
/src/scripts/transpiler/Statements/VariableDeclaration.ts:
--------------------------------------------------------------------------------
1 | import * as ESTree from 'estree'
2 | import { EnvironmentState } from '../../environment/EnvironmentState'
3 | import { addVertex } from '../../execution/execution'
4 | import { createExecutionGraph, ExecutionGraph } from '../../execution/graph/ExecutionGraph'
5 | import { ExecutionContext } from '../../execution/primitive/ExecutionNode'
6 | import { clone } from '../../utilities/objects'
7 | import { getNodeData } from '../Compiler'
8 | import { VariableDeclarator } from './VariableDeclarator'
9 |
10 | export function VariableDeclaration(
11 | ast: ESTree.VariableDeclaration,
12 | environment: EnvironmentState,
13 | context: ExecutionContext
14 | ) {
15 | if (ast.declarations.length == 1) {
16 | return VariableDeclarator(ast.declarations[0], environment, context)
17 | }
18 |
19 | const graph: ExecutionGraph = createExecutionGraph(getNodeData(ast))
20 | graph.precondition = clone(environment)
21 |
22 | for (const declaration of ast.declarations) {
23 | const animation = VariableDeclarator(declaration, environment, context)
24 | addVertex(graph, animation, { nodeData: getNodeData(declaration, 'declaration') })
25 | }
26 |
27 | graph.postcondition = clone(environment)
28 | return graph
29 | }
30 |
--------------------------------------------------------------------------------
/src/scripts/transpiler/Statements/VariableDeclarator.ts:
--------------------------------------------------------------------------------
1 | import * as ESTree from 'estree'
2 | import { cleanUpRegister } from '../../environment/environment'
3 | import { AccessorType, EnvironmentState } from '../../environment/EnvironmentState'
4 | import { addVertex, applyExecutionNode } from '../../execution/execution'
5 | import { createExecutionGraph, ExecutionGraph } from '../../execution/graph/ExecutionGraph'
6 | import { bindAnimation } from '../../execution/primitive/Binding/BindAnimation'
7 | import { ExecutionContext } from '../../execution/primitive/ExecutionNode'
8 | import { clone } from '../../utilities/objects'
9 | import { Compiler, getNodeData } from '../Compiler'
10 |
11 | export function VariableDeclarator(
12 | ast: ESTree.VariableDeclarator,
13 | environment: EnvironmentState,
14 | context: ExecutionContext
15 | ) {
16 | const graph: ExecutionGraph = createExecutionGraph(getNodeData(ast))
17 | graph.precondition = clone(environment)
18 |
19 | // Create a register that'll point to the RHS
20 | const register = [
21 | {
22 | type: AccessorType.Register,
23 | value: `${graph.id}__VariableDeclaration`,
24 | },
25 | ]
26 |
27 | // Copy / create and float up RHS
28 | const init = Compiler.compile(ast.init, environment, {
29 | ...context,
30 | outputRegister: register,
31 | })
32 | addVertex(graph, init, { nodeData: getNodeData(ast.init, 'Value') })
33 |
34 | // Allocate a place for variable that *points* to the RHS
35 | // @TODO: support initializations other than identifier
36 | const bind = bindAnimation((ast.id as ESTree.Identifier).name, register)
37 | const bindNodeData = getNodeData(ast.id, 'Name')
38 | bindNodeData.location.start.column -= 4
39 |
40 | addVertex(graph, bind, { nodeData: bindNodeData })
41 | applyExecutionNode(bind, environment)
42 | cleanUpRegister(environment, register[0].value)
43 |
44 | graph.postcondition = clone(environment)
45 | return graph
46 | }
47 |
--------------------------------------------------------------------------------
/src/scripts/utilities/Keyboard.ts:
--------------------------------------------------------------------------------
1 | export class Keyboard {
2 | // Singleton
3 | static instance: Keyboard
4 |
5 | // List of tick callbacks
6 | private pressed: { [key: string]: boolean } = {}
7 |
8 | // List of tick callbacks
9 |
10 | static _initialize() {
11 | if (Keyboard.instance) return
12 | Keyboard.instance = new Keyboard()
13 | }
14 |
15 | constructor() {
16 | const keyboard = this
17 |
18 | document.addEventListener('keydown', (e) => {
19 | keyboard.pressed[e.key] = true
20 | })
21 |
22 | document.addEventListener('keyup', (e) => {
23 | keyboard.pressed[e.key] = false
24 | })
25 | }
26 |
27 | isPressed(key: string) {
28 | return key in this.pressed && this.pressed[key]
29 | }
30 | }
31 |
32 | Keyboard._initialize()
33 |
--------------------------------------------------------------------------------
/src/scripts/utilities/Mouse.ts:
--------------------------------------------------------------------------------
1 | export class Mouse {
2 | // Singleton
3 | static instance: Mouse
4 |
5 | // List of tick callbacks
6 | position: { x: number; y: number } = { x: 0, y: 0 }
7 |
8 | held: boolean = false
9 |
10 | element: HTMLElement
11 |
12 | static _initialize() {
13 | if (Mouse.instance) return
14 | Mouse.instance = new Mouse()
15 | }
16 |
17 | constructor() {
18 | document.addEventListener('mousemove', (e) => {
19 | this.position = { x: e.clientX, y: e.clientY }
20 |
21 | this.element.style.left = `${this.position.x}px`
22 | this.element.style.top = `${this.position.y}px`
23 | })
24 |
25 | document.addEventListener('mousedown', (e) => {
26 | this.held = true
27 | })
28 |
29 | document.addEventListener('mouseup', (e) => {
30 | this.held = false
31 | })
32 |
33 | this.element = document.createElement('div')
34 | this.element.classList.add('mouse-indicator')
35 | document.body.appendChild(this.element)
36 | }
37 | }
38 |
39 | Mouse._initialize()
40 |
--------------------------------------------------------------------------------
/src/scripts/utilities/Ticker.ts:
--------------------------------------------------------------------------------
1 | import { ApplicationState } from '../ApplicationState'
2 |
3 | export class Ticker {
4 | // Singleton
5 | static instance: Ticker
6 |
7 | // List of tick callbacks
8 | private callbacks: { [id: string]: (dt: number) => void } = {}
9 | private time = 0
10 | private currID = 0
11 |
12 | static _initialize() {
13 | if (Ticker.instance) return
14 | Ticker.instance = new Ticker()
15 | }
16 |
17 | constructor() {
18 | const timer = this
19 |
20 | function tick(time: number) {
21 | ApplicationState.visualization.fpsGraph.begin()
22 | const dt = time - timer.time
23 | Object.values(timer.callbacks).forEach((callback) => callback(dt))
24 | timer.time = time
25 | ApplicationState.visualization.fpsGraph.end()
26 | requestAnimationFrame(tick)
27 | }
28 |
29 | requestAnimationFrame(tick)
30 | }
31 |
32 | registerTick(callback: (dt: number) => void) {
33 | this.currID++
34 | return this.registerTickFrom(callback, `Ticker_${this.currID}`)
35 | }
36 |
37 | registerTickFrom(callback: (dt: number) => void, from: string) {
38 | this.callbacks[from] = callback
39 | return from
40 | }
41 |
42 | removeTickFrom(from: string) {
43 | delete this.callbacks[from]
44 | }
45 | }
46 |
47 | Ticker._initialize()
48 |
--------------------------------------------------------------------------------
/src/scripts/utilities/executor.ts:
--------------------------------------------------------------------------------
1 | import * as acorn from 'acorn'
2 | import * as ESTree from 'estree'
3 |
4 | export function getAST(code: string): {
5 | ast: ESTree.Node | null
6 | errors: string[]
7 | } {
8 | // Compile AST
9 | let ast: ESTree.Node
10 |
11 | try {
12 | ast = acorn.parse(code, {
13 | locations: true,
14 | ecmaVersion: 2017,
15 | }) as ESTree.Node
16 | } catch (e: any) {
17 | return { ast: null, errors: [e.toString()] }
18 | }
19 |
20 | return { ast, errors: [] }
21 | // Check for runtime errors
22 | }
23 |
--------------------------------------------------------------------------------
/src/scripts/utilities/generic.ts:
--------------------------------------------------------------------------------
1 | export function assert(condition: any, msg?: string): asserts condition {
2 | if (!condition) {
3 | throw new Error(msg)
4 | }
5 | }
6 |
--------------------------------------------------------------------------------
/src/scripts/utilities/objects.ts:
--------------------------------------------------------------------------------
1 | import * as RFDC from './rfdc.js'
2 |
3 | export function clone(obj: T, d = 0): T {
4 | return RFDC.RFDCclone(obj)
5 | }
6 |
--------------------------------------------------------------------------------
/src/scripts/utilities/rfdc.js:
--------------------------------------------------------------------------------
1 | function RFDCcopyBuffer(cur) {
2 | if (cur instanceof Buffer) {
3 | return Buffer.from(cur)
4 | }
5 |
6 | return new cur.constructor(cur.buffer.slice(), cur.byteOffset, cur.length)
7 | }
8 |
9 | export function RFDCcloneArray(a, fn) {
10 | var keys = Object.keys(a)
11 | var a2 = new Array(keys.length)
12 | for (var i = 0; i < keys.length; i++) {
13 | var k = keys[i]
14 | var cur = a[k]
15 | if (typeof cur !== 'object' || cur === null) {
16 | a2[k] = cur
17 | } else if (cur instanceof Date) {
18 | a2[k] = new Date(cur)
19 | } else if (ArrayBuffer.isView(cur)) {
20 | a2[k] = RFDCcopyBuffer(cur)
21 | } else {
22 | a2[k] = fn(cur)
23 | }
24 | }
25 | return a2
26 | }
27 |
28 | export function RFDCclone(o) {
29 | if (typeof o !== 'object' || o === null) return o
30 | if (o instanceof Date) return new Date(o)
31 | if (Array.isArray(o)) return RFDCcloneArray(o, RFDCclone)
32 | if (o instanceof Map) return new Map(RFDCcloneArray(Array.from(o), RFDCclone))
33 | if (o instanceof Set) return new Set(RFDCcloneArray(Array.from(o), clone))
34 | var o2 = {}
35 | for (var k in o) {
36 | if (Object.hasOwnProperty.call(o, k) === false) continue
37 | var cur = o[k]
38 | if (typeof cur !== 'object' || cur === null) {
39 | o2[k] = cur
40 | } else if (cur instanceof Date) {
41 | o2[k] = new Date(cur)
42 | } else if (cur instanceof Map) {
43 | o2[k] = new Map(RFDCcloneArray(Array.from(cur), RFDCclone))
44 | } else if (cur instanceof Set) {
45 | o2[k] = new Set(RFDCcloneArray(Array.from(cur), RFDCclone))
46 | } else if (ArrayBuffer.isView(cur)) {
47 | o2[k] = RFDCcopyBuffer(cur)
48 | } else {
49 | o2[k] = RFDCclone(cur)
50 | }
51 | }
52 | return o2
53 | }
54 |
55 | export function RFDCcloneProto(o) {
56 | if (typeof o !== 'object' || o === null) return o
57 | if (o instanceof Date) return new Date(o)
58 | if (Array.isArray(o)) return RFDCcloneArray(o, RFDCcloneProto)
59 | if (o instanceof Map) return new Map(RFDCcloneArray(Array.from(o), RFDCcloneProto))
60 | if (o instanceof Set) return new Set(RFDCcloneArray(Array.from(o), RFDCcloneProto))
61 | var o2 = {}
62 | for (var k in o) {
63 | var cur = o[k]
64 | if (typeof cur !== 'object' || cur === null) {
65 | o2[k] = cur
66 | } else if (cur instanceof Date) {
67 | o2[k] = new Date(cur)
68 | } else if (cur instanceof Map) {
69 | o2[k] = new Map(RFDCcloneArray(Array.from(cur), RFDCcloneProto))
70 | } else if (cur instanceof Set) {
71 | o2[k] = new Set(RFDCcloneArray(Array.from(cur), RFDCcloneProto))
72 | } else if (ArrayBuffer.isView(cur)) {
73 | o2[k] = RFDCcopyBuffer(cur)
74 | } else {
75 | o2[k] = RFDCcloneProto(cur)
76 | }
77 | }
78 | return o2
79 | }
80 |
--------------------------------------------------------------------------------
/src/scripts/utilities/string.ts:
--------------------------------------------------------------------------------
1 | export function camelCaseToSentence(string: string) {
2 | const result = string.replace(/([A-Z])/g, ' $1')
3 | return result.charAt(0).toUpperCase() + result.slice(1)
4 | }
5 |
6 | export function stringHashCode(str: string) {
7 | let hash = 0,
8 | i: number,
9 | chr: number
10 | if (str.length === 0) return hash
11 | for (i = 0; i < str.length; i++) {
12 | chr = str.charCodeAt(i)
13 | hash = (hash << 5) - hash + chr
14 | hash |= 0 // Convert to 32bit integer
15 | }
16 | return Math.abs(hash)
17 | }
18 |
--------------------------------------------------------------------------------
/src/scripts/visualization/Focus.ts:
--------------------------------------------------------------------------------
1 | import { Ticker } from '../utilities/Ticker'
2 |
3 | export type FocusState = {
4 | focusedActions: (string | string[])[]
5 |
6 | // Tick id
7 | tickID: string
8 | }
9 |
10 | export function createFocus(overrides: Partial = {}): FocusState {
11 | // const element = createElement('div', 'focus', document.body)
12 |
13 | const base: FocusState = {
14 | focusedActions: [],
15 | tickID: Ticker.instance.registerTick(() => {
16 | updateFocus()
17 | }),
18 | }
19 |
20 | return { ...base, ...overrides }
21 | }
22 |
23 | export function destroyFocus(focus: FocusState) {
24 | // focus.element.remove()
25 | }
26 |
27 | // export function clearExistingFocus() {
28 | // const focus = ApplicationState.visualization.focus
29 | // assert(focus != undefined, 'Focus is undefined')
30 |
31 | // if (Keyboard.instance.isPressed('Shift')) {
32 | // return
33 | // }
34 |
35 | // for (let i = focus.focusedActions.length - 1; i >= 0; i--) {
36 | // const otherId = focus.focusedActions[i]
37 |
38 | // if (Array.isArray(otherId)) {
39 | // const forLoopId = ApplicationState.actions[otherId[0]].parentID as string
40 | // const forLoop = ApplicationState.actions[forLoopId]
41 |
42 | // const iterationIndex = Math.floor(Math.max(0, forLoop.vertices.indexOf(otherId[0]) - 1) / 3)
43 | // const representation = forLoop.representation as ForStatementRepresentation
44 |
45 | // if (!representation.pinnedIterations.has(iterationIndex)) {
46 | // // Remove iteration from focus
47 | // focus.focusedActions.splice(focus.focusedActions.indexOf(otherId), 1)
48 | // representation.pinnedIterations.delete(iterationIndex)
49 | // representation.iterationElements[iterationIndex].classList.remove('is-focused')
50 | // }
51 | // } else {
52 | // const other = ApplicationState.actions[otherId]
53 | // if (!other.isPinned) {
54 | // other.proxy.container.classList.remove('is-focused')
55 | // other.proxy.element.classList.remove('is-focused')
56 |
57 | // focus.focusedActions.splice(i, 1)
58 | // }
59 | // }
60 | // }
61 | // }
62 |
63 | export function updateFocus() {
64 | // const focus = ApplicationState.visualization.focus
65 | // assert(focus != undefined, 'Focus is undefined.')
66 | // if (focus.focusedActions.length == 0) {
67 | // focus.element.classList.add('is-hidden')
68 | // } else {
69 | // focus.element.classList.remove('is-hidden')
70 | // const element = ApplicationState.actions[focus.focusedActions[0]].proxy.element
71 | // if (element == undefined) {
72 | // throw new Error('Focus element is undefined.')
73 | // }
74 | // // Set position
75 | // const bbox = element.getBoundingClientRect()
76 | // focus.element.style.left = `${bbox.left}px`
77 | // focus.element.style.top = `${bbox.top}px`
78 | // focus.element.style.width = `${bbox.width}px`
79 | // focus.element.style.height = `${bbox.height}px`
80 | // }
81 | // Scale down all elements that are not focused
82 | // const spatialActionProxies = Object.values(ApplicationState.actions).filter(
83 | // (action) =>
84 | // action.isSpatial || action.execution.nodeData.type == 'Program'
85 | // )
86 | // for (const action of spatialActionProxies) {
87 | // if (
88 | // action.id == focus.currentFocus ||
89 | // focus.currentFocus == undefined
90 | // ) {
91 | // if (action.proxy.element.classList.contains('out-of-focus')) {
92 | // action.proxy.element.classList.remove('out-of-focus')
93 | // }
94 | // } else {
95 | // if (!action.proxy.element.classList.contains('out-of-focus')) {
96 | // action.proxy.element.classList.add('out-of-focus')
97 | // }
98 | // }
99 | // }
100 | }
101 |
--------------------------------------------------------------------------------
/src/styles/editor.scss:
--------------------------------------------------------------------------------
1 | /* ------------------------------------------------------ */
2 | /* Defines styles for source code editor */
3 | /* ------------------------------------------------------ */
4 |
5 | /* ---------------------- Container --------------------- */
6 |
7 | #editor {
8 | width: 100%;
9 | height: 100%;
10 | padding: 2rem;
11 | padding-top: 2rem;
12 | padding-left: 0px;
13 | }
14 |
15 | /* ----------------------- Monaco ----------------------- */
16 |
17 | .monaco-editor,
18 | .monaco-editor-background,
19 | .monaco-editor .inputarea.ime-input {
20 | background-color: rgba(0, 0, 0, 0) !important;
21 | }
22 |
23 | .monaco-editor-background {
24 | /* background-color: var(--main-bg-color) !important; */
25 | background-color: rgba(0, 0, 0, 0) !important;
26 | }
27 |
28 | .monaco-editor .margin {
29 | /* background-color: var(--main-bg-color) !important; */
30 | background-color: rgba(0, 0, 0, 0) !important;
31 | }
32 |
33 | .monaco-editor .view-overlays .current-line {
34 | display: none;
35 | }
36 |
37 | // // .execution-decoration {
38 | // // left: 45px !important;
39 |
40 | // // width: 8px !important;
41 | // // height: 8px !important;
42 |
43 | // // border-radius: 50%;
44 | // // border: 1px solid #bfbebe;
45 | // // background-color: #00000008 !important;
46 |
47 | // // margin-top: 10%;
48 |
49 | // // display: none;
50 | // // }
51 |
52 | .monaco-editor .line-numbers {
53 | opacity: 0.4;
54 | font-size: 12px !important;
55 | margin-top: 2px !important;
56 | transition-property: opacity;
57 | color: black !important;
58 | }
59 |
60 | .monaco-editor .line-numbers:hover {
61 | opacity: 1 !important;
62 | }
63 |
64 | .line-selected {
65 | font-weight: 800 !important;
66 | opacity: 1 !important;
67 | }
68 |
69 | .core-guide {
70 | box-shadow: none !important;
71 | }
72 |
73 | .cigr {
74 | box-shadow: 0.5px 0 0 0 rgba(0, 0, 0, 0) inset !important;
75 | }
76 |
77 | .cigra {
78 | box-shadow: 0.5px 0 0 0 rgba(0, 0, 0, 0) inset !important;
79 | }
80 |
81 | .monaco-editor .margin-view-overlays .codicon-chevron-down {
82 | font-size: 80%;
83 | }
84 |
85 | .monaco-editor .margin-view-overlays .codicon-folding-collapsed,
86 | .monaco-editor .margin-view-overlays .codicon-folding-expanded {
87 | font-size: 80% !important;
88 | }
89 |
90 | .cdr.bracket-match {
91 | opacity: 0.5;
92 | border: none !important;
93 | outline: none !important;
94 | border-radius: 3px;
95 | padding: 2px;
96 | background: none !important;
97 | }
98 |
99 | .monaco-editor .cursors-layer .cursor {
100 | background-color: var(--color-6) !important;
101 | // transition-property: top, left;
102 | // transition: 0.1s;
103 | opacity: 0.8;
104 | height: 15px !important;
105 | margin-top: 4px !important;
106 | margin-left: 0px !important;
107 | }
108 |
109 | .monaco-editor .scroll-decoration {
110 | box-shadow: none !important;
111 | }
112 |
113 | .lines-content {
114 | contain: none !important;
115 | overflow: visible !important;
116 | }
117 |
118 | .monaco-scrollable-element {
119 | overflow: visible !important;
120 | }
121 |
122 | .monaco-editor .codelens-decoration {
123 | color: #999999;
124 | margin-left: -10px;
125 | margin-top: -2px;
126 | opacity: 1;
127 | }
128 |
129 | .monaco-editor .codelens-decoration > span,
130 | .monaco-editor .codelens-decoration > a {
131 | font-size: 14px;
132 | }
133 |
134 | .monaco-editor .editor-widget {
135 | display: none !important;
136 | visibility: hidden !important;
137 | }
138 |
139 | .cdr.folded-background {
140 | opacity: 0;
141 | }
142 |
143 | /* -------------------- Syntax colors ------------------- */
144 |
145 | // Generic
146 | span.mtk1 {
147 | color: black !important;
148 | }
149 |
150 | // Literals
151 | span.mtk7 {
152 | color: var(--s-color-0) !important;
153 | }
154 |
155 | // Keywords
156 | span.mtk22 {
157 | color: var(--s-color-5) !important;
158 | font-weight: normal !important;
159 | }
160 |
161 | // In between keywords
162 | span.mtk14 {
163 | color: #535359 !important;
164 | }
165 |
166 | // Strings
167 | span.mtk32 {
168 | color: var(--s-color-2) !important;
169 | }
170 |
171 | // Comments
172 | span.mtk8 {
173 | color: var(--color-4);
174 | font-style: italic;
175 | }
176 |
177 | /* ------------------- Error feedback ------------------- */
178 |
179 | #errors {
180 | font-family: var(--code-font-family);
181 | border-radius: 10px;
182 | padding: 10px;
183 | position: absolute;
184 | top: 50px;
185 | left: 177px;
186 | display: none;
187 | color: #d86f64;
188 | }
189 |
190 | #errors.active {
191 | display: inherit;
192 | }
193 |
194 | .monaco-hover {
195 | display: none !important;
196 | }
197 |
198 | /* ------------------------ Focus ----------------------- */
199 | .view-line > span > span.is-focused {
200 | background-color: #161d23;
201 | }
202 |
--------------------------------------------------------------------------------
/src/styles/generic/button.scss:
--------------------------------------------------------------------------------
1 | .button {
2 | color: var(--color-5);
3 | cursor: pointer;
4 | border-radius: 7px;
5 | display: flex;
6 | align-items: center;
7 | justify-content: center;
8 | height: fit-content;
9 | height: -moz-fit-content;
10 | font-weight: 400;
11 | font-size: 14px;
12 | padding: 5px;
13 | opacity: 0.8;
14 |
15 | box-shadow: -1px 1.5px 0px 0.5px var(--color-2);
16 | background: var(--color-1);
17 | transform: translate(0px, 0px);
18 | }
19 |
20 | .button > ion-icon {
21 | color: var(--color-5);
22 | }
23 |
24 | .button:hover {
25 | opacity: 1;
26 | box-shadow: -1.5px 2.5px 0px 1px var(--color-2);
27 | transform: translate(1px, -1px);
28 | }
29 |
30 | .button.active,
31 | .button:active {
32 | background-color: var(--s-color-0);
33 | opacity: 1;
34 | box-shadow: -0.5px 0.5px 0px 0.5px var(--s-color-1);
35 | transform: translate(-1px, 1px);
36 | }
37 |
--------------------------------------------------------------------------------
/src/styles/generic/checkbox.scss:
--------------------------------------------------------------------------------
1 | * {
2 | box-sizing: border-box;
3 | }
4 |
5 | .cbx {
6 | -webkit-user-select: none;
7 | user-select: none;
8 | cursor: pointer;
9 | padding: 6px 8px;
10 | border-radius: 6px;
11 | overflow: hidden;
12 | }
13 |
14 | .cbx:not(:last-child) {
15 | margin-right: 6px;
16 | }
17 |
18 | .cbx:hover {
19 | background: rgba(139, 139, 139, 0.06);
20 | }
21 |
22 | .cbx span {
23 | float: left;
24 | vertical-align: middle;
25 | transform: translate3d(0, 0, 0);
26 | }
27 |
28 | .cbx span:first-child {
29 | position: relative;
30 | width: 18px;
31 | height: 18px;
32 | border-radius: 4px;
33 | transform: scale(1);
34 | border: 1px solid #cccfdb;
35 | box-shadow: 0 1px 1px rgba(56, 56, 56, 0.05);
36 | }
37 |
38 | .cbx span:first-child svg {
39 | position: absolute;
40 | top: 3px;
41 | left: 2px;
42 | fill: none;
43 | stroke: #fff;
44 | stroke-width: 2;
45 | stroke-linecap: round;
46 | stroke-linejoin: round;
47 | stroke-dasharray: 16px;
48 | stroke-dashoffset: 16px;
49 | transition-delay: 0.1s;
50 | transform: translate3d(0, 0, 0);
51 | }
52 |
53 | .cbx span:last-child {
54 | padding-left: 8px;
55 | line-height: 18px;
56 | }
57 |
58 | .cbx:hover span:first-child {
59 | border-color: rgb(73, 84, 86);
60 | }
61 |
62 | .inp-cbx {
63 | position: absolute;
64 | visibility: hidden;
65 | }
66 |
67 | .inp-cbx:checked + .cbx span:first-child {
68 | background: rgb(73, 84, 86);
69 | border-color: rgb(73, 84, 86);
70 | animation: wave 0.4s ease;
71 | }
72 |
73 | .inp-cbx:checked + .cbx span:first-child svg {
74 | stroke-dashoffset: 0;
75 | }
76 |
77 | .inline-svg {
78 | position: absolute;
79 | width: 0;
80 | height: 0;
81 | pointer-events: none;
82 | user-select: none;
83 | }
84 |
85 | @-moz-keyframes wave {
86 | 50% {
87 | transform: scale(0.9);
88 | }
89 | }
90 |
91 | @-webkit-keyframes wave {
92 | 50% {
93 | transform: scale(0.9);
94 | }
95 | }
96 |
97 | @-o-keyframes wave {
98 | 50% {
99 | transform: scale(0.9);
100 | }
101 | }
102 |
103 | @keyframes wave {
104 | 50% {
105 | transform: scale(0.9);
106 | }
107 | }
108 |
--------------------------------------------------------------------------------
/src/styles/renderer/data/array.scss:
--------------------------------------------------------------------------------
1 | /** Array style used for computing layout **/
2 | .data-array {
3 | display: flex;
4 | min-height: 20px;
5 | }
6 |
7 | // .data.data-literal.array-item:not(.last-array-item):after {
8 | // content: ',';
9 | // color: var(--color-4);
10 | // }
11 |
12 | .data-array-brace {
13 | color: var(--color-4);
14 | font-weight: 400;
15 | font-family: var(--code-font-family);
16 | font-size: 12px;
17 | }
18 |
19 | .data-array-opening-brace {
20 | }
21 |
22 | .data-array-closing-brace {
23 | }
24 |
25 | .data.array-item:not(.first-array-item):not(.last-array-item) {
26 | // border-radius: 0px;
27 | // border-left: none;
28 | }
29 |
30 | .data.array-item {
31 | // margin-left: -1px;
32 | }
33 |
34 | .data.first-array-item {
35 | // border-radius: 7px 0px 0px 7px;
36 | }
37 |
38 | .data.last-array-item {
39 | // border-radius: 0px 7px 7px 0px;
40 | // border-left: none;
41 | }
42 |
43 | .data-array-index {
44 | background: none;
45 | position: absolute;
46 | font-size: 12px;
47 | opacity: 0.5;
48 | margin-left: 8px;
49 | margin-top: 6px;
50 | }
51 |
52 | .data-array-comma {
53 | color: var(--color-4);
54 | font-weight: 400;
55 | font-family: var(--code-font-family);
56 | font-size: 12px;
57 | }
58 |
59 | // .data.data-array.environment-write > .data-array-brace {
60 | // color: var(--s-color-0-3);
61 | // }
62 |
--------------------------------------------------------------------------------
/src/styles/renderer/data/data.scss:
--------------------------------------------------------------------------------
1 | .data {
2 | display: flex;
3 | justify-content: center;
4 | align-items: center;
5 | height: fit-content;
6 | position: relative;
7 | }
8 |
9 | .environment > .data:not(:first-child) {
10 | margin-left: 25px;
11 | }
12 |
13 | .data.hidden {
14 | opacity: 0 !important;
15 | }
16 |
17 | .data-row {
18 | position: relative;
19 | }
20 |
21 | /* ------------------------ Focus ----------------------- */
22 | .data.secondary-focused {
23 | color: var(--s-color-3);
24 | }
25 |
26 | .data.is-free {
27 | position: absolute;
28 | }
29 |
30 | /* ---------------------- Residual ---------------------- */
31 | .data.is-residual {
32 | position: absolute;
33 | filter: saturate(0);
34 | opacity: 0.2;
35 | top: 0px;
36 | left: 0px;
37 | }
38 |
39 | .hole {
40 | position: relative;
41 | }
42 |
43 | .data.temp-move {
44 | position: absolute;
45 | }
46 |
47 | .temp-return-animation-node {
48 | position: absolute !important;
49 | }
50 |
51 | .trail-return {
52 | opacity: 0 !important;
53 | }
54 |
55 | .temp-return-environment .data.data-literal {
56 | opacity: 1 !important;
57 | color: #000000a1 !important;
58 | }
59 |
60 | .temp-return-environment {
61 | opacity: 0.4;
62 |
63 | border-top: 1px solid #000;
64 | border-left: 1px solid #000;
65 | }
66 |
67 | .action-proxy.type-BinaryExpressionEvaluate {
68 | background: none !important;
69 | box-shadow: none !important;
70 | }
71 |
--------------------------------------------------------------------------------
/src/styles/renderer/data/identifier.scss:
--------------------------------------------------------------------------------
1 | .identifier {
2 | font-size: 12px;
3 | border-radius: var(--border-radius);
4 | font-family: var(--code-font-family);
5 | color: black;
6 | padding: 1px 2px 1px 2px;
7 | height: fit-content;
8 | }
9 |
10 | .identifier.selected {
11 | color: var(--s-color-4);
12 | font-weight: 500;
13 | }
14 |
15 | .identifier.ret {
16 | color: var(--s-color-3);
17 | font-weight: 500;
18 | }
19 |
20 | .identifier.trail-create {
21 | color: var(--s-color-2);
22 | }
23 |
24 | .identifier.trail-move {
25 | color: var(--s-color-3);
26 | }
27 |
28 | /* ------------------------ Focus ----------------------- */
29 | .identifier.secondary-focused {
30 | color: var(--s-color-3);
31 | opacity: 0.7;
32 | }
33 |
34 | .identifier.identifier-write {
35 | color: var(--s-color-0-3);
36 | }
37 |
38 | .identifier.identifier-read {
39 | color: #ff9431;
40 | }
41 |
--------------------------------------------------------------------------------
/src/styles/renderer/data/literal.scss:
--------------------------------------------------------------------------------
1 | /** Literal style used for computing layout **/
2 |
3 | .data-literal,
4 | .data-function {
5 | font-size: var(--code-font-size);
6 | font-family: var(--code-font-family);
7 | color: var(--color-4-1);
8 | padding: 0px 4px;
9 | min-height: 20px;
10 | font-weight: 400;
11 | position: relative;
12 | z-index: 10;
13 | width: fit-content;
14 | font-size: 14px;
15 | transition-property: color;
16 | transition-duration: 0s; // Flashes if given a transition
17 | }
18 |
19 | .data-array > .hole > .data-literal {
20 | // padding: 0px 0px 0px 6px;
21 | }
22 |
23 | .data-literal.environment-write {
24 | color: var(--s-color-0-3);
25 | }
26 |
27 | .data-literal.environment-read {
28 | color: #ff9431;
29 | }
30 |
31 | .data-array > .hole:nth-child(2) > .data-literal {
32 | // padding: 0px 0px 0px 0px;
33 | }
34 |
35 | .data-function {
36 | color: var(--s-color-2);
37 | }
38 |
39 | .data-literal-string {
40 | color: var(--s-color-2) !important;
41 | }
42 |
43 | .data-literal.trail-create {
44 | box-shadow: 0px 0px 0px 0.8px var(--s-color-2) inset;
45 | }
46 |
47 | .data-literal.trail-move {
48 | box-shadow: 0px 0px 0px 0.8px var(--s-color-3) inset;
49 | }
50 |
51 | .data-literal.data-literal-boolean.true {
52 | color: var(--s-color-2);
53 | }
54 |
55 | .data-literal.data-literal-boolean.false {
56 | color: var(--s-color-5);
57 | }
58 |
--------------------------------------------------------------------------------
/src/styles/renderer/trail.scss:
--------------------------------------------------------------------------------
1 | /* ------------------------------------------------------ */
2 | /* Defines styles for trails */
3 | /* ------------------------------------------------------ */
4 |
5 | /* --------------------- Animations --------------------- */
6 | @keyframes showStroke {
7 | from {
8 | stroke-dashoffset: 200;
9 | }
10 | to {
11 | stroke-dashoffset: 0;
12 | }
13 | }
14 |
15 | /* --------------------- Trail path --------------------- */
16 | .trail-path {
17 | fill: #0000;
18 | stroke: url(#grad1);
19 | stroke-width: 1px;
20 | z-index: -1;
21 | // animation: showStroke 10s;
22 | }
23 |
24 | .trail-move {
25 | stroke: url(#grad3) !important;
26 | stroke-dasharray: 6 2;
27 | stroke-dashoffset: 0;
28 | }
29 |
30 | .trail-move.is-expired {
31 | filter: saturate(0);
32 | }
33 |
34 | .trail-create {
35 | stroke: url(#grad2);
36 | stroke-width: 1px;
37 | }
38 |
39 | .trail-path.hidden {
40 | display: none;
41 | opacity: 0;
42 | }
43 |
44 | .reference-movement-path {
45 | fill: #0000;
46 | stroke: #0000;
47 | }
48 |
--------------------------------------------------------------------------------
/src/styles/themes/coffee.scss:
--------------------------------------------------------------------------------
1 | /* ------------------------------------------------------ */
2 | /* Coffee-like light brown theme */
3 | /* ------------------------------------------------------ */
4 | :root {
5 | --color-0: #f7f4f3;
6 | --color-1: #eae2de;
7 | --color-2: #d2c8c6;
8 | --color-3: #ae9a98;
9 | --color-4: #482528;
10 | --color-5: #2f2625;
11 | --color-6: #171212;
12 |
13 | --s-color-0: #cfe2ea;
14 | --s-color-1: #bdc3c5;
15 | --s-color-2: #a0c4d0;
16 | }
17 |
--------------------------------------------------------------------------------
/src/vite-env.d.ts:
--------------------------------------------------------------------------------
1 | ///
2 |
--------------------------------------------------------------------------------
/test.js:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/hayatpur/crosscode/8a73693812fc5afd02cfa41107128f149770ec7a/test.js
--------------------------------------------------------------------------------
/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "target": "ESNext",
4 | "useDefineForClassFields": true,
5 | "module": "ESNext",
6 | "lib": ["ESNext", "DOM"],
7 | "moduleResolution": "Node",
8 | "strict": true,
9 | "sourceMap": true,
10 | "resolveJsonModule": true,
11 | "isolatedModules": true,
12 | "esModuleInterop": true,
13 | "noEmit": true,
14 | "noUnusedLocals": true,
15 | "noUnusedParameters": true,
16 | "noImplicitReturns": true,
17 | "skipLibCheck": true
18 | },
19 | "include": ["src"]
20 | }
21 |
--------------------------------------------------------------------------------
/tutorial/01_basic.js:
--------------------------------------------------------------------------------
1 | // Tutorial A
2 | //
3 | // Controls:
4 | // - Left key to move one step back
5 | // - Right key to move one step forward
6 | // - Up key to move animation backward
7 | // - Down key to move animation forward
8 | // - Grab control flow cursor to move
9 | // - Set animation speed
10 | // Concepts
11 | // - Control flow panel
12 | // - Data panel
13 | // - Source code panel
14 | // - Color encoding for read and writes
15 | // - Trace and trace information
16 | // - Movement and effect animations
17 |
18 | // Variable declaration
19 | let l = [1, 2, 3]
20 |
21 | // Trace, and partial trace
22 | let y = l[0]
23 | let z = l[2] + y
24 |
25 | // Residual
26 | y = z
27 |
--------------------------------------------------------------------------------
/tutorial/02_if.js:
--------------------------------------------------------------------------------
1 | // Tutorial B
2 | //
3 | // Controls:
4 | // - Shift click to get break down a step
5 | // Concepts
6 | // - Multiple levels of operational detail
7 | // - Specialized representations for if statement
8 |
9 | let list = [1, 2, 3, 4, 5, 6, 7, 8, 9]
10 | let n = list.length
11 |
12 | if (list[0] < list[n - 1]) {
13 | let temp = list[0]
14 | list[0] = list[n - 1]
15 | list[n - 1] = temp
16 | }
17 |
--------------------------------------------------------------------------------
/tutorial/03_for_func.js:
--------------------------------------------------------------------------------
1 | // Tutorial C
2 | //
3 | // Controls:
4 | // - Shift click into a function call
5 | // - For expanding / collapsing
6 | // - Minimize / maximize
7 | // - Progressive closure enable
8 | // - Expanding / collapsing abbreviations
9 | // Concepts
10 | // - Aggregation over breadth
11 | // - Abbreviations
12 | // - Specialized representations for For Statement
13 | // - Data state scope
14 | // - Detailed / less detailed representations
15 | // - Progressive closure
16 |
17 | function avg(list) {
18 | let sum = 0
19 | let n = list.length
20 |
21 | for (let i = 0; i < n; i = i + 1) {
22 | sum = sum + list[i]
23 | }
24 |
25 | return sum / n
26 | }
27 |
28 | let l = [1, 5, 6, 10, 2, 5, 1, 2]
29 | let k = avg(l)
30 |
--------------------------------------------------------------------------------
/tutorial/03_rec.js:
--------------------------------------------------------------------------------
1 | // Tutorial D
2 | //
3 | // Controls:
4 | // - Progressive closure
5 | // - Progressive disclosure
6 | // - Selection from source code
7 | // Concepts
8 | // - Aggregation over depth
9 |
10 | function fact(x) {
11 | if (x > 1) {
12 | return x * fact(x - 1)
13 | }
14 |
15 | return 1
16 | }
17 |
18 | function fact2(x) {
19 | let r = 5
20 | let z = r + 1
21 | let y = z - r
22 | y = y - r
23 | r = y * y
24 |
25 | if (x > 1) {
26 | return x * fact(x - 1)
27 | }
28 |
29 | return 1
30 | }
31 |
32 | let n = 5
33 | let y = fact(n)
34 |
--------------------------------------------------------------------------------
/vite.config.ts:
--------------------------------------------------------------------------------
1 | import { defineConfig } from 'vite'
2 | import monacoEditorPlugin from 'vite-plugin-monaco-editor'
3 |
4 | export default defineConfig({
5 | plugins: [monacoEditorPlugin({})],
6 | })
7 |
--------------------------------------------------------------------------------