qsharp-lang 0.1.0-dev.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (55) hide show
  1. package/LICENSE.txt +21 -0
  2. package/README.md +74 -0
  3. package/dist/browser.d.ts +26 -0
  4. package/dist/browser.js +156 -0
  5. package/dist/cancellation.d.ts +10 -0
  6. package/dist/cancellation.js +31 -0
  7. package/dist/compiler/common.d.ts +31 -0
  8. package/dist/compiler/common.js +47 -0
  9. package/dist/compiler/compiler.d.ts +28 -0
  10. package/dist/compiler/compiler.js +62 -0
  11. package/dist/compiler/events.d.ts +54 -0
  12. package/dist/compiler/events.js +92 -0
  13. package/dist/compiler/worker-browser.d.ts +1 -0
  14. package/dist/compiler/worker-browser.js +43 -0
  15. package/dist/compiler/worker-node.d.ts +1 -0
  16. package/dist/compiler/worker-node.js +41 -0
  17. package/dist/compiler/worker-proxy.d.ts +7 -0
  18. package/dist/compiler/worker-proxy.js +16 -0
  19. package/dist/debug-service/debug-service.d.ts +35 -0
  20. package/dist/debug-service/debug-service.js +136 -0
  21. package/dist/debug-service/worker-browser.d.ts +1 -0
  22. package/dist/debug-service/worker-browser.js +32 -0
  23. package/dist/debug-service/worker-node.d.ts +1 -0
  24. package/dist/debug-service/worker-node.js +30 -0
  25. package/dist/debug-service/worker-proxy.d.ts +7 -0
  26. package/dist/debug-service/worker-proxy.js +22 -0
  27. package/dist/katas-content.generated.d.ts +61 -0
  28. package/dist/katas-content.generated.js +2499 -0
  29. package/dist/katas.d.ts +55 -0
  30. package/dist/katas.js +16 -0
  31. package/dist/language-service/language-service.d.ts +48 -0
  32. package/dist/language-service/language-service.js +85 -0
  33. package/dist/language-service/worker-browser.d.ts +1 -0
  34. package/dist/language-service/worker-browser.js +32 -0
  35. package/dist/language-service/worker-node.d.ts +1 -0
  36. package/dist/language-service/worker-node.js +30 -0
  37. package/dist/language-service/worker-proxy.d.ts +6 -0
  38. package/dist/language-service/worker-proxy.js +20 -0
  39. package/dist/log.d.ts +33 -0
  40. package/dist/log.js +92 -0
  41. package/dist/main.d.ts +11 -0
  42. package/dist/main.js +82 -0
  43. package/dist/samples.generated.d.ts +6 -0
  44. package/dist/samples.generated.js +62 -0
  45. package/dist/vsdiagnostic.d.ts +27 -0
  46. package/dist/vsdiagnostic.js +117 -0
  47. package/dist/worker-proxy.d.ts +95 -0
  48. package/dist/worker-proxy.js +226 -0
  49. package/lib/node/qsc_wasm.cjs +1010 -0
  50. package/lib/node/qsc_wasm.d.cts +266 -0
  51. package/lib/node/qsc_wasm_bg.wasm +0 -0
  52. package/lib/web/qsc_wasm.d.ts +328 -0
  53. package/lib/web/qsc_wasm.js +1026 -0
  54. package/lib/web/qsc_wasm_bg.wasm +0 -0
  55. package/package.json +35 -0
package/LICENSE.txt ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) Microsoft Corporation.
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE
package/README.md ADDED
@@ -0,0 +1,74 @@
1
+ # qsharp npm module
2
+
3
+ This package contains the qsharp compiler and language service functionality shipped for consumption via npm.
4
+
5
+ The source is written in TypeScript, which is compiled to ECMAScript modules in the ./dist directory.
6
+ The wasm binaries from the Rust builds are copied to the ./lib directory.
7
+
8
+ Consuming browser projects should import from this module and use a bundler to create their
9
+ own JavaScript bundle, and also copy the wasm file to their project and provide the URL
10
+ to it when calling the `loadWasmModule` method so it may be located and loaded.
11
+
12
+ ## Node and browser support
13
+
14
+ wasm-pack generates different files for the browser and Node.js environments. The wasm is slightly
15
+ different, and the loader code is quite different. This can be seen in `./lib/web/qsc_wasm.cjs`
16
+ and `./lib/node/qsc_wasm.js` files respectively. Specifically, the web environment loads the wasm
17
+ file using async web APIs such as `fetch` with a URI, and Node.js uses `require` to load the `fs` module
18
+ and calls to `readFileSync`. Once the wasm module is loaded however, the exported APIs are used
19
+ in a similar manner.
20
+
21
+ To support using this npm package from both environments, the package uses "conditional exports"
22
+ <https://nodejs.org/dist/latest-v18.x/docs/api/packages.html#conditional-exports> to expose one
23
+ entry point for Node.js, and another for browsers. The distinct entry points uses their respective
24
+ loader to load the wasm module for the platform, and then expose functionality that uses the
25
+ loaded module via common code.
26
+
27
+ When bundling for the web, bundlers such as esbuild will automatically use the default entry point,
28
+ whereas when loaded as a Node.js module, it will use the "node" entry point.
29
+
30
+ Note that TypeScript seems to add the ['import', 'types', 'node'] conditions by default when
31
+ searching the Node.js `exports`, and so will always find the 'node' export before the 'default'
32
+ export. To resolve this, a 'browser' condition was added (which is same as 'default' but earlier
33
+ than 'node') and the tsconfig compiler option `"customConditions": ["browser"]` should be added
34
+ (requires TypeScript 5.0 or later). esbuild also adds the 'browser' condition when bundling for
35
+ the browser (see <https://esbuild.github.io/api/#how-conditions-work>).
36
+
37
+ ## Design
38
+
39
+ This package provides two services, the compiler and the language service.
40
+
41
+ The API for using these services is similar whether using a browser or Node.js,
42
+ and whether running in the main thread or a worker thread. You instantiate the service
43
+ and call operations on it which complete in the order called.
44
+
45
+ All operations return a Promise which resolves then the operation is complete. Some operations
46
+ may also emit events, such as debug messages or state dumps as they are processed. The service
47
+ itself can also emit events which can be subscribed to using `addEventListener`.
48
+
49
+ See the Q# playground code at <https://github.com/microsoft/qsharp/tree/main/playground> for
50
+ an example of code that uses this package. The unit tests at
51
+ <https://github.com/microsoft/qsharp/tree/main/npm/test> are also a good reference.
52
+
53
+ Promises, Events, and Cancellation are based on JavaScript or Web standards, or the VS Code API:
54
+
55
+ - Promises <https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Using_promises>
56
+ - EventTarget <https://developer.mozilla.org/en-US/docs/Web/API/EventTarget>
57
+ - Event <https://developer.mozilla.org/en-US/docs/Web/API/Event/Event>
58
+ - VS Code API for CancellationToken <https://code.visualstudio.com/api/references/vscode-api#CancellationToken>
59
+
60
+ The standard Web APIs for custom events were added to Node.js in v16.17. <https://nodejs.org/dist/v16.17.0/docs/api/events.html>, but behind an experimental flag. As CustomEvent is not on
61
+ the global by default until v19 or later, the code will use Event with a 'detail'
62
+ property manually set until v20 is in common use.
63
+
64
+ The VS Code implementation for cancellation tokens is viewable in their source code
65
+ at <src/vs/base/common/cancellation.ts>. This code uses a simplified version of that API.
66
+
67
+ ## Testing
68
+
69
+ Node.js tests can be run via `node --test` (see
70
+ <https://nodejs.org/dist/latest-v18.x/docs/api/test.html#test-runner-execution-model>).
71
+
72
+ The test module was also added to Node.js v16.17.0, and Electron 22 (which VS Code plans to move to
73
+ in first half of 2023) includes v16.17.1, so v16.17 should be our minimum Node.js
74
+ version supported (it shipped in Aug 2022).
@@ -0,0 +1,26 @@
1
+ import { ICompiler, ICompilerWorker } from "./compiler/compiler.js";
2
+ import { IDebugService, IDebugServiceWorker } from "./debug-service/debug-service.js";
3
+ import { ILanguageService, ILanguageServiceWorker, qsharpLibraryUriScheme } from "./language-service/language-service.js";
4
+ import { LogLevel, log } from "./log.js";
5
+ export { qsharpLibraryUriScheme };
6
+ export declare function loadWasmModule(uriOrBuffer: string | ArrayBuffer): Promise<void>;
7
+ export declare function getLibrarySourceContent(path: string): Promise<string | undefined>;
8
+ export declare function getDebugService(): Promise<IDebugService>;
9
+ export declare function getDebugServiceWorker(workerArg: string | Worker): IDebugServiceWorker;
10
+ export declare function getCompiler(): Promise<ICompiler>;
11
+ export declare function getCompilerWorker(workerArg: string | Worker): ICompilerWorker;
12
+ export declare function getLanguageService(): Promise<ILanguageService>;
13
+ export declare function getLanguageServiceWorker(workerArg: string | Worker): ILanguageServiceWorker;
14
+ export { type Dump, type ShotResult } from "./compiler/common.js";
15
+ export { type CompilerState } from "./compiler/compiler.js";
16
+ export { QscEventTarget } from "./compiler/events.js";
17
+ export { getAllKatas, getExerciseSources, getKata, type ContentItem, type Example, type Exercise, type ExplainedSolution, type ExplainedSolutionItem, type Kata, type KataSection, type Lesson, type LessonItem, type Question, } from "./katas.js";
18
+ export { default as samples } from "./samples.generated.js";
19
+ export { type VSDiagnostic } from "./vsdiagnostic.js";
20
+ export { log, type LogLevel };
21
+ export type { ICompilerWorker, ICompiler };
22
+ export type { ILanguageServiceWorker, ILanguageService };
23
+ export type { IDebugServiceWorker, IDebugService };
24
+ export type { IBreakpointSpan, IStackFrame } from "../lib/web/qsc_wasm.js";
25
+ export { type IStructStepResult, StepResultId } from "../lib/web/qsc_wasm.js";
26
+ export { type LanguageServiceEvent } from "./language-service/language-service.js";
@@ -0,0 +1,156 @@
1
+ // Copyright (c) Microsoft Corporation.
2
+ // Licensed under the MIT License.
3
+ // This module is the entry point for browser environments. For Node.js environment,
4
+ // the "./main.js" module is the entry point.
5
+ import initWasm, * as wasm from "../lib/web/qsc_wasm.js";
6
+ import { Compiler } from "./compiler/compiler.js";
7
+ import { createCompilerProxy } from "./compiler/worker-proxy.js";
8
+ import { QSharpDebugService, } from "./debug-service/debug-service.js";
9
+ import { createDebugServiceProxy } from "./debug-service/worker-proxy.js";
10
+ import { QSharpLanguageService, qsharpLibraryUriScheme, } from "./language-service/language-service.js";
11
+ import { createLanguageServiceProxy } from "./language-service/worker-proxy.js";
12
+ import { log } from "./log.js";
13
+ export { qsharpLibraryUriScheme };
14
+ // Create once. A module is stateless and can be efficiently passed to WebWorkers.
15
+ let wasmModule = null;
16
+ let wasmModulePromise = null;
17
+ // Used to track if an instance is already instantiated
18
+ let wasmInstancePromise = null;
19
+ async function wasmLoader(uriOrBuffer) {
20
+ if (typeof uriOrBuffer === "string") {
21
+ log.info("Fetching wasm module from %s", uriOrBuffer);
22
+ performance.mark("fetch-wasm-start");
23
+ const wasmRequst = await fetch(uriOrBuffer);
24
+ const wasmBuffer = await wasmRequst.arrayBuffer();
25
+ const fetchTiming = performance.measure("fetch-wasm", "fetch-wasm-start");
26
+ log.logTelemetry({
27
+ id: "fetch-wasm",
28
+ data: {
29
+ duration: fetchTiming.duration,
30
+ uri: uriOrBuffer,
31
+ },
32
+ });
33
+ wasmModule = await WebAssembly.compile(wasmBuffer);
34
+ }
35
+ else {
36
+ log.info("Compiling wasm module from provided buffer");
37
+ wasmModule = await WebAssembly.compile(uriOrBuffer);
38
+ }
39
+ }
40
+ export function loadWasmModule(uriOrBuffer) {
41
+ // Only initiate if not already in flight, to avoid race conditions
42
+ if (!wasmModulePromise) {
43
+ wasmModulePromise = wasmLoader(uriOrBuffer);
44
+ }
45
+ return wasmModulePromise;
46
+ }
47
+ async function instantiateWasm() {
48
+ // Ensure loading the module has been initiated, and wait for it.
49
+ if (!wasmModulePromise)
50
+ throw "Wasm module must be loaded first";
51
+ await wasmModulePromise;
52
+ if (!wasmModule)
53
+ throw "Wasm module failed to load";
54
+ if (wasmInstancePromise) {
55
+ // Either in flight or already complete. The prior request will do the init,
56
+ // so just wait on that.
57
+ await wasmInstancePromise;
58
+ return;
59
+ }
60
+ // Set the promise to signal this is in flight, then wait on the result.
61
+ wasmInstancePromise = initWasm(wasmModule);
62
+ await wasmInstancePromise;
63
+ // Once ready, set up logging and telemetry as soon as possible after instantiating
64
+ wasm.initLogging(log.logWithLevel, log.getLogLevel());
65
+ log.onLevelChanged = (level) => wasm.setLogLevel(level);
66
+ }
67
+ export async function getLibrarySourceContent(path) {
68
+ await instantiateWasm();
69
+ return wasm.get_library_source_content(path);
70
+ }
71
+ export async function getDebugService() {
72
+ await instantiateWasm();
73
+ return new QSharpDebugService(wasm);
74
+ }
75
+ // Create the debugger inside a WebWorker and proxy requests.
76
+ // If the Worker was already created via other means and is ready to receive
77
+ // messages, then the worker may be passed in and it will be initialized.
78
+ export function getDebugServiceWorker(workerArg) {
79
+ if (!wasmModule)
80
+ throw "Wasm module must be loaded first";
81
+ // Create or use the WebWorker
82
+ const worker = typeof workerArg === "string" ? new Worker(workerArg) : workerArg;
83
+ // Send it the Wasm module to instantiate
84
+ worker.postMessage({
85
+ type: "init",
86
+ wasmModule,
87
+ qscLogLevel: log.getLogLevel(),
88
+ });
89
+ // If you lose the 'this' binding, some environments have issues
90
+ const postMessage = worker.postMessage.bind(worker);
91
+ const onTerminate = () => worker.terminate();
92
+ // Create the proxy which will forward method calls to the worker
93
+ const proxy = createDebugServiceProxy(postMessage, onTerminate);
94
+ // Let proxy handle response and event messages from the worker
95
+ worker.onmessage = (ev) => proxy.onMsgFromWorker(ev.data);
96
+ return proxy;
97
+ }
98
+ export async function getCompiler() {
99
+ await instantiateWasm();
100
+ return new Compiler(wasm);
101
+ }
102
+ // Create the compiler inside a WebWorker and proxy requests.
103
+ // If the Worker was already created via other means and is ready to receive
104
+ // messages, then the worker may be passed in and it will be initialized.
105
+ export function getCompilerWorker(workerArg) {
106
+ if (!wasmModule)
107
+ throw "Wasm module must be loaded first";
108
+ // Create or use the WebWorker
109
+ const worker = typeof workerArg === "string" ? new Worker(workerArg) : workerArg;
110
+ // Send it the Wasm module to instantiate
111
+ worker.postMessage({
112
+ type: "init",
113
+ wasmModule,
114
+ qscLogLevel: log.getLogLevel(),
115
+ });
116
+ // If you lose the 'this' binding, some environments have issues
117
+ const postMessage = worker.postMessage.bind(worker);
118
+ const onTerminate = () => worker.terminate();
119
+ // Create the proxy which will forward method calls to the worker
120
+ const proxy = createCompilerProxy(postMessage, onTerminate);
121
+ // Let proxy handle response and event messages from the worker
122
+ worker.onmessage = (ev) => proxy.onMsgFromWorker(ev.data);
123
+ return proxy;
124
+ }
125
+ export async function getLanguageService() {
126
+ await instantiateWasm();
127
+ return new QSharpLanguageService(wasm);
128
+ }
129
+ // Create the compiler inside a WebWorker and proxy requests.
130
+ // If the Worker was already created via other means and is ready to receive
131
+ // messages, then the worker may be passed in and it will be initialized.
132
+ export function getLanguageServiceWorker(workerArg) {
133
+ if (!wasmModule)
134
+ throw "Wasm module must be loaded first";
135
+ // Create or use the WebWorker
136
+ const worker = typeof workerArg === "string" ? new Worker(workerArg) : workerArg;
137
+ // Send it the Wasm module to instantiate
138
+ worker.postMessage({
139
+ type: "init",
140
+ wasmModule,
141
+ qscLogLevel: log.getLogLevel(),
142
+ });
143
+ // If you lose the 'this' binding, some environments have issues
144
+ const postMessage = worker.postMessage.bind(worker);
145
+ const onTerminate = () => worker.terminate();
146
+ // Create the proxy which will forward method calls to the worker
147
+ const proxy = createLanguageServiceProxy(postMessage, onTerminate);
148
+ // Let proxy handle response and event messages from the worker
149
+ worker.onmessage = (ev) => proxy.onMsgFromWorker(ev.data);
150
+ return proxy;
151
+ }
152
+ export { QscEventTarget } from "./compiler/events.js";
153
+ export { getAllKatas, getExerciseSources, getKata, } from "./katas.js";
154
+ export { default as samples } from "./samples.generated.js";
155
+ export { log };
156
+ export { StepResultId } from "../lib/web/qsc_wasm.js";
@@ -0,0 +1,10 @@
1
+ export interface CancellationToken {
2
+ readonly isCancellationRequested: boolean;
3
+ readonly onCancellationRequested: (listener: (e: any) => any) => void;
4
+ }
5
+ export declare class CancellationTokenSource {
6
+ private _token;
7
+ constructor(parent?: CancellationToken);
8
+ get token(): CancellationToken;
9
+ cancel(): void;
10
+ }
@@ -0,0 +1,31 @@
1
+ // Copyright (c) Microsoft Corporation.
2
+ // Licensed under the MIT License.
3
+ class InternalToken {
4
+ constructor() {
5
+ this.isCancellationRequested = false;
6
+ this.eventTarget = new EventTarget();
7
+ }
8
+ onCancellationRequested(listener) {
9
+ this.eventTarget.addEventListener("cancelled", listener);
10
+ }
11
+ cancel() {
12
+ if (this.isCancellationRequested)
13
+ return; // Only fires once
14
+ this.isCancellationRequested = true;
15
+ this.eventTarget.dispatchEvent(new Event("cancelled"));
16
+ }
17
+ }
18
+ export class CancellationTokenSource {
19
+ constructor(parent) {
20
+ // There are some optimizations you can do here to lazily allocate, but keeping it simple for now.
21
+ this._token = new InternalToken();
22
+ if (parent)
23
+ parent.onCancellationRequested(() => this.cancel());
24
+ }
25
+ get token() {
26
+ return this._token;
27
+ }
28
+ cancel() {
29
+ this._token.cancel();
30
+ }
31
+ }
@@ -0,0 +1,31 @@
1
+ import { VSDiagnostic } from "../vsdiagnostic.js";
2
+ export type Dump = {
3
+ [index: string]: [number, number];
4
+ };
5
+ export type Result = {
6
+ success: true;
7
+ value: string;
8
+ } | {
9
+ success: false;
10
+ value: VSDiagnostic;
11
+ };
12
+ interface DumpMsg {
13
+ type: "DumpMachine";
14
+ state: Dump;
15
+ }
16
+ interface MessageMsg {
17
+ type: "Message";
18
+ message: string;
19
+ }
20
+ interface ResultMsg {
21
+ type: "Result";
22
+ result: Result;
23
+ }
24
+ type EventMsg = ResultMsg | DumpMsg | MessageMsg;
25
+ export declare function eventStringToMsg(msg: string): EventMsg | null;
26
+ export type ShotResult = {
27
+ success: boolean;
28
+ result: string | VSDiagnostic;
29
+ events: Array<MessageMsg | DumpMsg>;
30
+ };
31
+ export {};
@@ -0,0 +1,47 @@
1
+ // Copyright (c) Microsoft Corporation.
2
+ // Licensed under the MIT License.
3
+ function outputAsResult(msg) {
4
+ try {
5
+ const obj = JSON.parse(msg);
6
+ if (obj?.type == "Result" && typeof obj.success == "boolean") {
7
+ return {
8
+ type: "Result",
9
+ result: {
10
+ success: obj.success,
11
+ value: obj.result,
12
+ },
13
+ };
14
+ }
15
+ }
16
+ catch {
17
+ return null;
18
+ }
19
+ return null;
20
+ }
21
+ function outputAsMessage(msg) {
22
+ try {
23
+ const obj = JSON.parse(msg);
24
+ if (obj?.type == "Message" && typeof obj.message == "string") {
25
+ return obj;
26
+ }
27
+ }
28
+ catch {
29
+ return null;
30
+ }
31
+ return null;
32
+ }
33
+ function outputAsDump(msg) {
34
+ try {
35
+ const obj = JSON.parse(msg);
36
+ if (obj?.type == "DumpMachine" && typeof obj.state == "object") {
37
+ return obj;
38
+ }
39
+ }
40
+ catch {
41
+ return null;
42
+ }
43
+ return null;
44
+ }
45
+ export function eventStringToMsg(msg) {
46
+ return outputAsResult(msg) || outputAsMessage(msg) || outputAsDump(msg);
47
+ }
@@ -0,0 +1,28 @@
1
+ import { VSDiagnostic } from "../vsdiagnostic.js";
2
+ import { IServiceProxy, ServiceState } from "../worker-proxy.js";
3
+ import { IQscEventTarget } from "./events.js";
4
+ type Wasm = typeof import("../../lib/node/qsc_wasm.cjs");
5
+ export interface ICompiler {
6
+ /**
7
+ * @deprecated use the language service for errors and other editor features.
8
+ */
9
+ checkCode(code: string): Promise<VSDiagnostic[]>;
10
+ getHir(code: string): Promise<string>;
11
+ run(code: string, expr: string, shots: number, eventHandler: IQscEventTarget): Promise<void>;
12
+ checkExerciseSolution(user_code: string, exercise_sources: string[], eventHandler: IQscEventTarget): Promise<boolean>;
13
+ }
14
+ export type ICompilerWorker = ICompiler & IServiceProxy;
15
+ export type CompilerState = ServiceState;
16
+ export declare class Compiler implements ICompiler {
17
+ private wasm;
18
+ constructor(wasm: Wasm);
19
+ /**
20
+ * @deprecated use the language service for errors and other editor features.
21
+ */
22
+ checkCode(code: string): Promise<VSDiagnostic[]>;
23
+ getHir(code: string): Promise<string>;
24
+ run(code: string, expr: string, shots: number, eventHandler: IQscEventTarget): Promise<void>;
25
+ checkExerciseSolution(user_code: string, exercise_sources: string[], eventHandler: IQscEventTarget): Promise<boolean>;
26
+ }
27
+ export declare function onCompilerEvent(msg: string, eventTarget: IQscEventTarget): void;
28
+ export {};
@@ -0,0 +1,62 @@
1
+ // Copyright (c) Microsoft Corporation.
2
+ // Licensed under the MIT License.
3
+ import { log } from "../log.js";
4
+ import { mapDiagnostics } from "../vsdiagnostic.js";
5
+ import { eventStringToMsg } from "./common.js";
6
+ import { makeEvent } from "./events.js";
7
+ export class Compiler {
8
+ constructor(wasm) {
9
+ log.info("Constructing a Compiler instance");
10
+ this.wasm = wasm;
11
+ globalThis.qscGitHash = this.wasm.git_hash();
12
+ }
13
+ /**
14
+ * @deprecated use the language service for errors and other editor features.
15
+ */
16
+ async checkCode(code) {
17
+ let diags = [];
18
+ const languageService = new this.wasm.LanguageService((uri, version, errors) => {
19
+ diags = errors;
20
+ });
21
+ languageService.update_document("code", 1, code, true /* exe */);
22
+ return mapDiagnostics(diags, code);
23
+ }
24
+ async getHir(code) {
25
+ return this.wasm.get_hir(code);
26
+ }
27
+ async run(code, expr, shots, eventHandler) {
28
+ // All results are communicated as events, but if there is a compiler error (e.g. an invalid
29
+ // entry expression or similar), it may throw on run. The caller should expect this promise
30
+ // may reject without all shots running or events firing.
31
+ this.wasm.run(code, expr, (msg) => onCompilerEvent(msg, eventHandler), shots);
32
+ }
33
+ async checkExerciseSolution(user_code, exercise_sources, eventHandler) {
34
+ const success = this.wasm.check_exercise_solution(user_code, exercise_sources, (msg) => onCompilerEvent(msg, eventHandler));
35
+ return success;
36
+ }
37
+ }
38
+ export function onCompilerEvent(msg, eventTarget) {
39
+ const qscMsg = eventStringToMsg(msg);
40
+ if (!qscMsg) {
41
+ log.error("Unknown event message: %s", msg);
42
+ return;
43
+ }
44
+ let qscEvent;
45
+ const msgType = qscMsg.type;
46
+ switch (msgType) {
47
+ case "Message":
48
+ qscEvent = makeEvent("Message", qscMsg.message);
49
+ break;
50
+ case "DumpMachine":
51
+ qscEvent = makeEvent("DumpMachine", qscMsg.state);
52
+ break;
53
+ case "Result":
54
+ qscEvent = makeEvent("Result", qscMsg.result);
55
+ break;
56
+ default:
57
+ log.never(msgType);
58
+ throw "Unexpected message type";
59
+ }
60
+ log.debug("worker dispatching event " + JSON.stringify(qscEvent));
61
+ eventTarget.dispatchEvent(qscEvent);
62
+ }
@@ -0,0 +1,54 @@
1
+ import { ShotResult, Dump, Result } from "./common.js";
2
+ import { TelemetryEvent } from "../log.js";
3
+ import { IServiceEventTarget } from "../worker-proxy.js";
4
+ export type QscEventData = {
5
+ type: "Message";
6
+ detail: string;
7
+ } | {
8
+ type: "DumpMachine";
9
+ detail: Dump;
10
+ } | {
11
+ type: "Result";
12
+ detail: Result;
13
+ } | {
14
+ type: "telemetry-event";
15
+ detail: TelemetryEvent;
16
+ };
17
+ export type QscEvents = Event & QscEventData;
18
+ export type QscEvent<T extends QscEvents["type"]> = Extract<QscEvents, {
19
+ type: T;
20
+ }>;
21
+ export type IQscEventTarget = IServiceEventTarget<QscEventData>;
22
+ export declare function makeEvent<E extends QscEvents>(type: E["type"], detail: E["detail"]): E;
23
+ type QscUiEvents = QscEvents | (Event & {
24
+ type: "uiResultsRefresh";
25
+ detail: undefined;
26
+ });
27
+ export declare class QscEventTarget implements IQscEventTarget {
28
+ private eventTarget;
29
+ private results;
30
+ private shotActive;
31
+ private animationFrameId;
32
+ private supportsUiRefresh;
33
+ addEventListener<T extends QscUiEvents["type"]>(type: T, listener: (event: Extract<QscEvents, {
34
+ type: T;
35
+ }>) => void): void;
36
+ removeEventListener<T extends QscUiEvents["type"]>(type: T, listener: (event: Extract<QscEvents, {
37
+ type: T;
38
+ }>) => void): void;
39
+ dispatchEvent(event: QscUiEvents): boolean;
40
+ /**
41
+ * @param captureEvents Set to true if this instance should record events internally
42
+ */
43
+ constructor(captureEvents: boolean);
44
+ private onMessage;
45
+ private onDumpMachine;
46
+ private onResult;
47
+ private ensureActiveShot;
48
+ private queueUiRefresh;
49
+ private onUiRefresh;
50
+ getResults(): ShotResult[];
51
+ resultCount(): number;
52
+ clearResults(): void;
53
+ }
54
+ export {};
@@ -0,0 +1,92 @@
1
+ // Copyright (c) Microsoft Corporation.
2
+ // Licensed under the MIT License.
3
+ import { log } from "../log.js";
4
+ // Convenience method that also provides type safety
5
+ export function makeEvent(type, detail) {
6
+ const event = new Event(type);
7
+ event.detail = detail;
8
+ return event;
9
+ }
10
+ function makeResultObj() {
11
+ return { success: false, result: "", events: [] };
12
+ }
13
+ export class QscEventTarget {
14
+ // Overrides for the base EventTarget methods to limit to expected event types
15
+ addEventListener(type, listener) {
16
+ this.eventTarget.addEventListener(type, listener);
17
+ }
18
+ removeEventListener(type, listener) {
19
+ this.eventTarget.removeEventListener(type, listener);
20
+ }
21
+ dispatchEvent(event) {
22
+ if (log.getLogLevel() >= 4)
23
+ log.debug("Dispatching event: %o", event);
24
+ return this.eventTarget.dispatchEvent(event);
25
+ }
26
+ /**
27
+ * @param captureEvents Set to true if this instance should record events internally
28
+ */
29
+ constructor(captureEvents) {
30
+ this.eventTarget = new EventTarget();
31
+ this.results = [];
32
+ this.shotActive = false;
33
+ this.animationFrameId = 0;
34
+ this.supportsUiRefresh = false;
35
+ this.supportsUiRefresh =
36
+ typeof globalThis.requestAnimationFrame === "function";
37
+ if (captureEvents) {
38
+ this.addEventListener("Message", (ev) => this.onMessage(ev.detail));
39
+ this.addEventListener("DumpMachine", (ev) => this.onDumpMachine(ev.detail));
40
+ this.addEventListener("Result", (ev) => this.onResult(ev.detail));
41
+ }
42
+ }
43
+ onMessage(msg) {
44
+ this.ensureActiveShot();
45
+ const shotIdx = this.results.length - 1;
46
+ this.results[shotIdx].events.push({ type: "Message", message: msg });
47
+ this.queueUiRefresh();
48
+ }
49
+ onDumpMachine(dump) {
50
+ this.ensureActiveShot();
51
+ const shotIdx = this.results.length - 1;
52
+ this.results[shotIdx].events.push({ type: "DumpMachine", state: dump });
53
+ this.queueUiRefresh();
54
+ }
55
+ onResult(result) {
56
+ this.ensureActiveShot();
57
+ const shotIdx = this.results.length - 1;
58
+ this.results[shotIdx].success = result.success;
59
+ this.results[shotIdx].result = result.value;
60
+ this.shotActive = false;
61
+ this.queueUiRefresh();
62
+ }
63
+ ensureActiveShot() {
64
+ if (!this.shotActive) {
65
+ this.results.push(makeResultObj());
66
+ this.shotActive = true;
67
+ }
68
+ }
69
+ queueUiRefresh() {
70
+ if (this.supportsUiRefresh && !this.animationFrameId) {
71
+ this.animationFrameId = requestAnimationFrame(() => {
72
+ this.onUiRefresh();
73
+ });
74
+ }
75
+ }
76
+ onUiRefresh() {
77
+ this.animationFrameId = 0;
78
+ const uiRefreshEvent = new Event("uiResultsRefresh");
79
+ this.dispatchEvent(uiRefreshEvent);
80
+ }
81
+ getResults() {
82
+ return this.results;
83
+ }
84
+ resultCount() {
85
+ // May be one less than length if the last is still in flight
86
+ return this.shotActive ? this.results.length - 1 : this.results.length;
87
+ }
88
+ clearResults() {
89
+ this.results = [];
90
+ this.shotActive = false;
91
+ }
92
+ }
@@ -0,0 +1 @@
1
+ export declare function messageHandler(e: MessageEvent): void;