Skip to content

Commit

Permalink
Merge 8b30744 into da3795d
Browse files Browse the repository at this point in the history
  • Loading branch information
idavis authored Apr 24, 2024
2 parents da3795d + 8b30744 commit bc8b5dd
Show file tree
Hide file tree
Showing 7 changed files with 193 additions and 30 deletions.
36 changes: 30 additions & 6 deletions npm/qsharp/src/compiler/compiler.ts
Original file line number Diff line number Diff line change
Expand Up @@ -31,9 +31,17 @@ type Wasm = typeof import("../../lib/web/qsc_wasm.js");
export interface ICompiler {
checkCode(code: string): Promise<VSDiagnostic[]>;

getAst(code: string, languageFeatures?: string[]): Promise<string>;
getAst(
code: string,
languageFeatures?: string[],
profile?: TargetProfile,
): Promise<string>;

getHir(code: string, languageFeatures?: string[]): Promise<string>;
getHir(
code: string,
languageFeatures?: string[],
profile?: TargetProfile,
): Promise<string>;

/** @deprecated -- switch to using `ProgramConfig`-based overload. Instead of passing
* sources and language features separately, pass an object with named properties. This change was made
Expand Down Expand Up @@ -197,12 +205,28 @@ export class Compiler implements ICompiler {
return this.wasm.get_estimates(sources, params, languageFeatures);
}

async getAst(code: string, languageFeatures: string[]): Promise<string> {
return this.wasm.get_ast(code, languageFeatures);
async getAst(
code: string,
languageFeatures?: string[],
profile?: TargetProfile,
): Promise<string> {
return this.wasm.get_ast(
code,
languageFeatures ?? [],
profile ?? "quantinuum",
);
}

async getHir(code: string, languageFeatures: string[]): Promise<string> {
return this.wasm.get_hir(code, languageFeatures);
async getHir(
code: string,
languageFeatures: string[],
profile: TargetProfile,
): Promise<string> {
return this.wasm.get_hir(
code,
languageFeatures ?? [],
profile ?? "quantinuum",
);
}

async run(
Expand Down
8 changes: 8 additions & 0 deletions npm/qsharp/ux/qsharp-ux.css
Original file line number Diff line number Diff line change
Expand Up @@ -275,6 +275,14 @@ html {
white-space: pre;
}

.qir-output {
height: 40vh;
min-height: 400px;
width: 100%;
resize: none;
white-space: pre;
}

/* Results output */

.output-header {
Expand Down
114 changes: 99 additions & 15 deletions playground/src/editor.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,8 @@ import {
QscEventTarget,
VSDiagnostic,
log,
ProgramConfig,
TargetProfile,
} from "qsharp-lang";
import { codeToCompressedBase64, lsRangeToMonacoRange } from "./utils.js";
import { ActiveTab } from "./main.js";
Expand Down Expand Up @@ -56,9 +58,27 @@ function VSDiagsToMarkers(errors: VSDiagnostic[]): monaco.editor.IMarkerData[] {
});
}

// get the language service profile from the URL
// default to unrestricted if not specified
export function getProfile(): TargetProfile {
const params = new URLSearchParams(window.location.search);
if (params.has("profile")) {
const profile = params.get("profile");
if (profile === "base") {
return "base";
}
if (profile === "quantinuum") {
return "quantinuum";
}
}

return "unrestricted";
}

export function Editor(props: {
code: string;
compiler: ICompilerWorker;
compiler_worker_factory: () => ICompilerWorker;
compilerState: CompilerState;
defaultShots: number;
evtTarget: QscEventTarget;
Expand All @@ -67,8 +87,10 @@ export function Editor(props: {
shotError?: VSDiagnostic;
showExpr: boolean;
showShots: boolean;
profile: TargetProfile;
setAst: (ast: string) => void;
setHir: (hir: string) => void;
setQir: (qir: string) => void;
activeTab: ActiveTab;
languageService: ILanguageServiceWorker;
}) {
Expand All @@ -80,7 +102,7 @@ export function Editor(props: {
const irRef = useRef(async () => {
return;
});

const [profile, setProfile] = useState(props.profile);
const [shotCount, setShotCount] = useState(props.defaultShots);
const [runExpr, setRunExpr] = useState("");
const [errors, setErrors] = useState<{ location: string; msg: string[] }[]>(
Expand Down Expand Up @@ -116,38 +138,79 @@ export function Editor(props: {
const code = editor.current?.getValue();
if (code == null) return;

const config = {
sources: [["code", code]],
languageFeatures: ["preview-qir-gen"],
profile: profile,
} as ProgramConfig;

if (props.activeTab === "ast-tab") {
props.setAst(await props.compiler.getAst(code, []));
props.setAst(
await props.compiler.getAst(
code,
config.languageFeatures ?? [],
config.profile,
),
);
}
if (props.activeTab === "hir-tab") {
props.setHir(await props.compiler.getHir(code, []));
props.setHir(
await props.compiler.getHir(
code,
config.languageFeatures ?? [],
config.profile,
),
);
}
const codeGenTimeout = 1000; // ms
if (props.activeTab === "qir-tab") {
let timedOut = false;
const compiler = props.compiler_worker_factory();
const compilerTimeout = setTimeout(() => {
log.info("Compiler timeout. Terminating worker.");
timedOut = true;
compiler.terminate();
}, codeGenTimeout);
try {
const qir = await compiler.getQir(config);
clearTimeout(compilerTimeout);
props.setQir(qir);
} catch (e: any) {
if (timedOut) {
props.setQir("timed out");
} else {
props.setQir(e.toString());
}
} finally {
compiler.terminate();
}
}
};

async function onRun() {
const code = editor.current?.getValue();
if (code == null) return;
props.evtTarget.clearResults();
const config = {
sources: [["code", code]],
languageFeatures: ["preview-qir-gen"],
profile: profile,
} as ProgramConfig;

try {
if (props.kataExercise) {
// This is for a kata exercise. Provide the sources that implement the solution verification.
const sources = await getExerciseSources(props.kataExercise);
// check uses the unrestricted profile and doesn't do code gen,
// so we just pass the sources
await props.compiler.checkExerciseSolution(
code,
sources,
props.evtTarget,
);
} else {
performance.mark("compiler-run-start");
await props.compiler.run(
{
sources: [["code", code]],
},
runExpr,
shotCount,
props.evtTarget,
);
await props.compiler.run(config, runExpr, shotCount, props.evtTarget);
const runTimer = performance.measure(
"compiler-run",
"compiler-run-start",
Expand Down Expand Up @@ -221,6 +284,7 @@ export function Editor(props: {

useEffect(() => {
props.languageService.updateConfiguration({
targetProfile: profile,
packageType: props.kataExercise ? "lib" : "exe",
});

Expand Down Expand Up @@ -259,6 +323,16 @@ export function Editor(props: {
irRef.current();
}, [props.activeTab]);

useEffect(() => {
// Whenever the selected profile changes, update the language service configuration
// and run the tabs again.
props.languageService.updateConfiguration({
targetProfile: profile,
packageType: props.kataExercise ? "lib" : "exe",
});
irRef.current();
}, [profile]);

// On reset, reload the initial code
function onReset() {
const theEditor = editor.current;
Expand All @@ -276,11 +350,8 @@ export function Editor(props: {
try {
const encodedCode = await codeToCompressedBase64(code);
const escapedCode = encodeURIComponent(encodedCode);

// Get current URL without query parameters to use as the base URL
const newUrl = `${
window.location.href.split("?")[0]
}?code=${escapedCode}`;
const newUrl = `${window.location.href.split("?")[0]}?code=${escapedCode}&profile=${profile}`;

// Copy link to clipboard and update url without reloading the page
navigator.clipboard.writeText(newUrl);
Expand Down Expand Up @@ -310,6 +381,11 @@ export function Editor(props: {
setRunExpr(target.value);
}

function profileChanged(e: Event) {
const target = e.target as HTMLInputElement;
setProfile(target.value as TargetProfile);
}

return (
<div class="editor-column">
<div style="display: flex; justify-content: space-between; align-items: center;">
Expand Down Expand Up @@ -356,6 +432,14 @@ export function Editor(props: {
</div>
<div class="code-editor" ref={editorDiv}></div>
<div class="button-row">
<>
<span>Profile</span>
<select value={profile} onChange={profileChanged}>
<option value="unrestricted">Unrestricted</option>
<option value="quantinuum">Quantinuum</option>
<option value="base">Base</option>
</select>
</>
{props.showExpr ? (
<>
<span>Start</span>
Expand Down
7 changes: 6 additions & 1 deletion playground/src/kata.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ import {
Question,
VSDiagnostic,
} from "qsharp-lang";
import { Editor } from "./editor.js";
import { Editor, getProfile } from "./editor.js";
import { OutputTabs } from "./tabs.js";

function ExplainedSolutionAsHtml(solution: ExplainedSolution): string {
Expand Down Expand Up @@ -76,6 +76,7 @@ function QuestionAsHtml(question: Question): string {
export function Kata(props: {
kata: Kata;
compiler: ICompilerWorker;
compiler_worker_factory: () => ICompilerWorker;
compilerState: CompilerState;
onRestartCompiler: () => void;
languageService: ILanguageServiceWorker;
Expand Down Expand Up @@ -166,12 +167,15 @@ export function Kata(props: {
evtTarget={itemEvtHandlers[idx]}
compiler={props.compiler}
compilerState={props.compilerState}
compiler_worker_factory={props.compiler_worker_factory}
onRestartCompiler={props.onRestartCompiler}
code={section.placeholderCode}
kataExercise={section}
key={section.id}
profile={getProfile()}
setAst={() => ({})}
setHir={() => ({})}
setQir={() => ({})}
activeTab="results-tab"
languageService={props.languageService}
></Editor>
Expand All @@ -183,6 +187,7 @@ export function Kata(props: {
onShotError={(diag?: VSDiagnostic) => setShotError(diag)}
ast=""
hir=""
qir=""
activeTab="results-tab"
setActiveTab={() => undefined}
></OutputTabs>
Expand Down
21 changes: 19 additions & 2 deletions playground/src/main.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ import {
} from "qsharp-lang";

import { Nav } from "./nav.js";
import { Editor } from "./editor.js";
import { Editor, getProfile } from "./editor.js";
import { OutputTabs } from "./tabs.js";
import { useEffect, useState } from "preact/hooks";
import { Kata as Katas } from "./kata.js";
Expand All @@ -37,7 +37,12 @@ import {
monacoPositionToLsPosition,
} from "./utils.js";

export type ActiveTab = "results-tab" | "hir-tab" | "ast-tab" | "logs-tab";
export type ActiveTab =
| "results-tab"
| "hir-tab"
| "ast-tab"
| "qir-tab"
| "logs-tab";

const basePath = (window as any).qscBasePath || "";
const monacoPath = basePath + "libs/monaco/vs";
Expand Down Expand Up @@ -70,6 +75,12 @@ function App(props: { katas: Kata[]; linkedCode?: string }) {
const [compiler, setCompiler] = useState(() =>
createCompiler(setCompilerState),
);

const [compiler_worker_factory] = useState(() => {
const compiler_worker_factory = () => getCompilerWorker(compilerWorkerPath);
return compiler_worker_factory;
});

const [evtTarget] = useState(() => new QscEventTarget(true));

const [languageService] = useState(() => {
Expand All @@ -87,6 +98,7 @@ function App(props: { katas: Kata[]; linkedCode?: string }) {

const [ast, setAst] = useState<string>("");
const [hir, setHir] = useState<string>("");
const [qir, setQir] = useState<string>("");
const [activeTab, setActiveTab] = useState<ActiveTab>("results-tab");

const onRestartCompiler = () => {
Expand Down Expand Up @@ -147,15 +159,18 @@ function App(props: { katas: Kata[]; linkedCode?: string }) {
<Editor
code={sampleCode}
compiler={compiler}
compiler_worker_factory={compiler_worker_factory}
compilerState={compilerState}
onRestartCompiler={onRestartCompiler}
evtTarget={evtTarget}
defaultShots={defaultShots}
showShots={true}
showExpr={true}
shotError={shotError}
profile={getProfile()}
setAst={setAst}
setHir={setHir}
setQir={setQir}
activeTab={activeTab}
languageService={languageService}
></Editor>
Expand All @@ -165,6 +180,7 @@ function App(props: { katas: Kata[]; linkedCode?: string }) {
onShotError={(diag?: VSDiagnostic) => setShotError(diag)}
ast={ast}
hir={hir}
qir={qir}
activeTab={activeTab}
setActiveTab={setActiveTab}
></OutputTabs>
Expand All @@ -174,6 +190,7 @@ function App(props: { katas: Kata[]; linkedCode?: string }) {
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
kata={activeKata!}
compiler={compiler}
compiler_worker_factory={compiler_worker_factory}
compilerState={compilerState}
onRestartCompiler={onRestartCompiler}
languageService={languageService}
Expand Down
Loading

0 comments on commit bc8b5dd

Please sign in to comment.