From d9e5a76c17d110b8271658243f55c15061f1bb9b Mon Sep 17 00:00:00 2001 From: Yuki Okushi Date: Wed, 9 Jun 2021 03:18:00 +0900 Subject: [PATCH] Add support for `-Zunpretty=ast-tree` --- .../spec/features/compilation_targets_spec.rb | 12 +++++++ ui/frontend/BuildMenu.tsx | 13 ++++++++ ui/frontend/Output.tsx | 8 ++++- ui/frontend/actions.ts | 30 +++++++++++++++++ ui/frontend/reducers/output/ast.ts | 33 +++++++++++++++++++ ui/frontend/reducers/output/index.ts | 2 ++ ui/frontend/reducers/output/meta.ts | 3 ++ ui/frontend/selectors/index.ts | 3 ++ ui/frontend/types.ts | 2 ++ ui/src/main.rs | 1 + ui/src/sandbox.rs | 7 ++++ 11 files changed, 113 insertions(+), 1 deletion(-) create mode 100644 ui/frontend/reducers/output/ast.ts diff --git a/tests/spec/features/compilation_targets_spec.rb b/tests/spec/features/compilation_targets_spec.rb index e2836dfdc..e56ff8772 100644 --- a/tests/spec/features/compilation_targets_spec.rb +++ b/tests/spec/features/compilation_targets_spec.rb @@ -106,6 +106,18 @@ end end + scenario "compiling to AST" do + editor.set <<~EOF + fn demo() -> impl std::fmt::Display { 42 } + EOF + + in_build_menu { click_on("AST") } + + within(:output, :result) do + expect(page).to have_content 'ident: demo#0,' + end + end + scenario "compiling to WebAssembly" do in_build_menu { click_on("WASM") } diff --git a/ui/frontend/BuildMenu.tsx b/ui/frontend/BuildMenu.tsx index e755c0051..3effd9a01 100644 --- a/ui/frontend/BuildMenu.tsx +++ b/ui/frontend/BuildMenu.tsx @@ -28,6 +28,7 @@ const useDispatchAndClose = (action: () => void, close: () => void) => { const BuildMenu: React.SFC = props => { const isHirAvailable = useSelector(selectors.isHirAvailable); + const isAstAvailable = useSelector(selectors.isAstAvailable); const isWasmAvailable = useSelector(selectors.isWasmAvailable); const compile = useDispatchAndClose(actions.performCompile, props.close); @@ -35,6 +36,7 @@ const BuildMenu: React.SFC = props => { const compileToLLVM = useDispatchAndClose(actions.performCompileToLLVM, props.close); const compileToMir = useDispatchAndClose(actions.performCompileToMir, props.close); const compileToHir = useDispatchAndClose(actions.performCompileToNightlyHir, props.close); + const compileToAst = useDispatchAndClose(actions.performCompileToNightlyAst, props.close); const compileToWasm = useDispatchAndClose(actions.performCompileToNightlyWasm, props.close); const execute = useDispatchAndClose(actions.performExecute, props.close); const test = useDispatchAndClose(actions.performTest, props.close); @@ -66,6 +68,10 @@ const BuildMenu: React.SFC = props => { Build and show the resulting HIR, Rust’s syntax-based intermediate representation. {!isHirAvailable && } + + Build and show the resulting AST. + {!isAstAvailable && } + Build a WebAssembly module for web browsers, in the .WAT textual representation. {!isWasmAvailable && } @@ -81,6 +87,13 @@ const HirAside: React.SFC = () => ( ); +const AstAside: React.SFC = () => ( + + Note: AST currently requires using the Nightly channel, selecting this + option will switch to Nightly. + +); + const WasmAside: React.SFC = () => ( Note: WASM currently requires using the Nightly channel, selecting this diff --git a/ui/frontend/Output.tsx b/ui/frontend/Output.tsx index d7300c180..c2f541f04 100644 --- a/ui/frontend/Output.tsx +++ b/ui/frontend/Output.tsx @@ -47,7 +47,7 @@ interface PaneWithCodeProps extends SimplePaneProps { const Output: React.SFC = () => { const somethingToShow = useSelector(selectors.getSomethingToShow); - const { meta: { focus }, execute, format, clippy, miri, macroExpansion, assembly, llvmIr, mir, hir, wasm, gist } = + const { meta: { focus }, execute, format, clippy, miri, macroExpansion, assembly, llvmIr, mir, hir, ast, wasm, gist } = useSelector((state: State) => state.output); const dispatch = useDispatch(); @@ -61,6 +61,7 @@ const Output: React.SFC = () => { const focusLlvmIr = useCallback(() => dispatch(actions.changeFocus(Focus.LlvmIr)), [dispatch]); const focusMir = useCallback(() => dispatch(actions.changeFocus(Focus.Mir)), [dispatch]); const focusHir = useCallback(() => dispatch(actions.changeFocus(Focus.Hir)), [dispatch]); + const focusAst = useCallback(() => dispatch(actions.changeFocus(Focus.Ast)), [dispatch]); const focusWasm = useCallback(() => dispatch(actions.changeFocus(Focus.Wasm)), [dispatch]); const focusGist = useCallback(() => dispatch(actions.changeFocus(Focus.Gist)), [dispatch]); @@ -84,6 +85,7 @@ const Output: React.SFC = () => { {focus === Focus.LlvmIr && } {focus === Focus.Mir && } {focus === Focus.Hir && } + {focus === Focus.Ast && } {focus === Focus.Wasm && } {focus === Focus.Gist && } @@ -129,6 +131,10 @@ const Output: React.SFC = () => { label="HIR" onClick={focusHir} tabProps={hir} /> + failure: receiveCompileLlvmIrFailure, }); +const requestCompileAst = () => + createAction(ActionType.CompileAstRequest); + +const receiveCompileAstSuccess = ({ code, stdout, stderr }) => + createAction(ActionType.CompileAstSucceeded, { code, stdout, stderr }); + +const receiveCompileAstFailure = ({ error }) => + createAction(ActionType.CompileAstFailed, { error }); + +const performCompileToAstOnly = () => + performCompileShow('ast', { + request: requestCompileAst, + success: receiveCompileAstSuccess, + failure: receiveCompileAstFailure, + }); + +const performCompileToNightlyAstOnly = (): ThunkAction => dispatch => { + dispatch(changeChannel(Channel.Nightly)); + dispatch(performCompileToAstOnly()); +}; + const requestCompileHir = () => createAction(ActionType.CompileHirRequest); @@ -424,6 +448,7 @@ const PRIMARY_ACTIONS: { [index in PrimaryAction]: () => ThunkAction } = { [PrimaryActionCore.Test]: performTestOnly, [PrimaryActionAuto.Auto]: performAutoOnly, [PrimaryActionCore.LlvmIr]: performCompileToLLVMOnly, + [PrimaryActionCore.Ast]: performCompileToAstOnly, [PrimaryActionCore.Hir]: performCompileToHirOnly, [PrimaryActionCore.Mir]: performCompileToMirOnly, [PrimaryActionCore.Wasm]: performCompileToNightlyWasmOnly, @@ -454,6 +479,8 @@ export const performCompileToMir = performAndSwitchPrimaryAction(performCompileToMirOnly, PrimaryActionCore.Mir); export const performCompileToNightlyHir = performAndSwitchPrimaryAction(performCompileToNightlyHirOnly, PrimaryActionCore.Hir); + export const performCompileToNightlyAst = + performAndSwitchPrimaryAction(performCompileToNightlyAstOnly, PrimaryActionCore.Ast); export const performCompileToNightlyWasm = performAndSwitchPrimaryAction(performCompileToNightlyWasmOnly, PrimaryActionCore.Wasm); @@ -838,6 +865,9 @@ export type Action = | ReturnType | ReturnType | ReturnType + | ReturnType + | ReturnType + | ReturnType | ReturnType | ReturnType | ReturnType diff --git a/ui/frontend/reducers/output/ast.ts b/ui/frontend/reducers/output/ast.ts new file mode 100644 index 000000000..bf2ea17ea --- /dev/null +++ b/ui/frontend/reducers/output/ast.ts @@ -0,0 +1,33 @@ +import { Action, ActionType } from '../../actions'; +import { finish, start } from './sharedStateManagement'; + +const DEFAULT: State = { + requestsInProgress: 0, + code: null, + stdout: null, + stderr: null, + error: null, +}; + +interface State { + requestsInProgress: number; + code?: string; + stdout?: string; + stderr?: string; + error?: string; +} + +export default function ast(state = DEFAULT, action: Action) { + switch (action.type) { + case ActionType.CompileAstRequest: + return start(DEFAULT, state); + case ActionType.CompileAstSucceeded: { + const { code = '', stdout = '', stderr = '' } = action; + return finish(state, { code, stdout, stderr }); + } + case ActionType.CompileAstFailed: + return finish(state, { error: action.error }); + default: + return state; + } +} diff --git a/ui/frontend/reducers/output/index.ts b/ui/frontend/reducers/output/index.ts index 8942a967a..4d0b7bb6d 100644 --- a/ui/frontend/reducers/output/index.ts +++ b/ui/frontend/reducers/output/index.ts @@ -1,6 +1,7 @@ import { combineReducers } from 'redux'; import assembly from './assembly'; +import ast from './ast'; import clippy from './clippy'; import execute from './execute'; import format from './format'; @@ -23,6 +24,7 @@ const output = combineReducers({ llvmIr, mir, hir, + ast, wasm, execute, gist, diff --git a/ui/frontend/reducers/output/meta.ts b/ui/frontend/reducers/output/meta.ts index 37689c903..e9b300bdd 100644 --- a/ui/frontend/reducers/output/meta.ts +++ b/ui/frontend/reducers/output/meta.ts @@ -31,6 +31,9 @@ export default function meta(state = DEFAULT, action: Action) { case ActionType.CompileHirRequest: return { ...state, focus: Focus.Hir }; + + case ActionType.CompileAstRequest: + return { ...state, focus: Focus.Ast }; case ActionType.CompileWasmRequest: return { ...state, focus: Focus.Wasm }; diff --git a/ui/frontend/selectors/index.ts b/ui/frontend/selectors/index.ts index 80e5811be..796c0b820 100644 --- a/ui/frontend/selectors/index.ts +++ b/ui/frontend/selectors/index.ts @@ -71,6 +71,7 @@ const LABELS: { [index in PrimaryActionCore]: string } = { [PrimaryActionCore.Compile]: 'Build', [PrimaryActionCore.Execute]: 'Run', [PrimaryActionCore.LlvmIr]: 'Show LLVM IR', + [PrimaryActionCore.Ast]: 'Show AST', [PrimaryActionCore.Hir]: 'Show HIR', [PrimaryActionCore.Mir]: 'Show MIR', [PrimaryActionCore.Test]: 'Test', @@ -108,6 +109,7 @@ export const isNightlyChannel = (state: State) => ( ); export const isWasmAvailable = isNightlyChannel; export const isHirAvailable = isNightlyChannel; +export const isAstAvailable = isNightlyChannel; export const isRust2021Available = isNightlyChannel; export const getModeLabel = (state: State) => { @@ -147,6 +149,7 @@ const getOutputs = (state: State) => [ state.output.llvmIr, state.output.mir, state.output.hir, + state.output.ast, state.output.miri, state.output.macroExpansion, state.output.wasm, diff --git a/ui/frontend/types.ts b/ui/frontend/types.ts index e321b9105..4888da89d 100644 --- a/ui/frontend/types.ts +++ b/ui/frontend/types.ts @@ -70,6 +70,7 @@ export enum PrimaryActionAuto { } export enum PrimaryActionCore { + Ast = 'ast', Asm = 'asm', Compile = 'compile', Execute = 'execute', @@ -111,6 +112,7 @@ export enum Focus { LlvmIr = 'llvm-ir', Mir = 'mir', Hir = 'hir', + Ast = 'ast', Wasm = 'wasm', Asm = 'asm', Execute = 'execute', diff --git a/ui/src/main.rs b/ui/src/main.rs index 8166c572b..669af8405 100644 --- a/ui/src/main.rs +++ b/ui/src/main.rs @@ -1429,6 +1429,7 @@ fn parse_target(s: &str) -> Result { "llvm-ir" => sandbox::CompileTarget::LlvmIr, "mir" => sandbox::CompileTarget::Mir, "hir" => sandbox::CompileTarget::Hir, + "ast" => sandbox::CompileTarget::Ast, "wasm" => sandbox::CompileTarget::Wasm, value => InvalidTarget { value }.fail()?, }) diff --git a/ui/src/sandbox.rs b/ui/src/sandbox.rs index 8f8f72cd8..d36fc532e 100644 --- a/ui/src/sandbox.rs +++ b/ui/src/sandbox.rs @@ -484,6 +484,9 @@ fn build_execution_command(target: Option, channel: Channel, mode if target == Hir { // -Zunpretty=hir only emits the HIR, not the binary itself cmd.push("/playground-result/compilation.hir"); + } else if target == Ast { + // -Zunpretty=ast-tree only emits the AST, not the binary itself + cmd.push("/playground-result/compilation.ast"); } else { cmd.push("/playground-result/compilation"); } @@ -509,6 +512,7 @@ fn build_execution_command(target: Option, channel: Channel, mode LlvmIr => cmd.push("--emit=llvm-ir"), Mir => cmd.push("--emit=mir"), Hir => cmd.push("-Zunpretty=hir"), + Ast => cmd.push("-Zunpretty=ast-tree"), Wasm => { /* handled by cargo-wasm wrapper */ }, } } @@ -622,6 +626,7 @@ pub enum CompileTarget { LlvmIr, Mir, Hir, + Ast, Wasm, } @@ -632,6 +637,7 @@ impl CompileTarget { CompileTarget::LlvmIr => "ll", CompileTarget::Mir => "mir", CompileTarget::Hir => "hir", + CompileTarget::Ast => "ast", CompileTarget::Wasm => "wat", }; OsStr::new(ext) @@ -647,6 +653,7 @@ impl fmt::Display for CompileTarget { LlvmIr => "LLVM IR".fmt(f), Mir => "Rust MIR".fmt(f), Hir => "Rust HIR".fmt(f), + Ast => "Rust AST".fmt(f), Wasm => "WebAssembly".fmt(f), } }