Fixes
https://github.com/compiler-explorer/compiler-explorer/issues/5634.

Adds the Lean 4 language and compiler. 

> Lean is an open-source programming language and proof assistant that
enables correct, maintainable, and formally verified code

Since it was first requested in
https://github.com/compiler-explorer/compiler-explorer/issues/5634, it
has become increasingly relevant due to interest in AI-assisted theorem
proving, formalized mathematics, and verified software.

---

Lean compiles in two steps: first to C source code using the `lean`
executable, and then to assembly using the `leanc` compiler distributed
with Lean (I believe it's Clang).

The PR also includes a pane to visualize the emitted C source code that
was modelled after the panes for Rust and Haskell IRs and after the C
preprocessor pane for clang-format integration. Lean outputs mostly
unindented C code, so I enabled the formatter by default.

See companion infrastructure PR at
https://github.com/compiler-explorer/infra/pull/2130.



<img width="3024" height="1720" alt="image"
src="https://github.com/user-attachments/assets/5e7a1e91-e748-4599-8190-d3cb09fca503"
/>

---------

Co-authored-by: Matt Godbolt <matt@godbolt.org>
This commit is contained in:
Francisco Giordano
2026-06-02 14:19:29 -03:00
committed by GitHub
parent cc515ed658
commit a6cdc6a66d
27 changed files with 937 additions and 0 deletions

7
.github/labeler.yml vendored
View File

@@ -246,6 +246,13 @@
- 'lib/compilers/kotlin.ts'
- 'etc/config/kotlin.*.properties'
'lang-lean':
- changed-files:
- any-glob-to-any-file:
- 'lib/compilers/lean.ts'
- 'etc/config/lean.*.properties'
- 'static/modes/lean-mode.ts'
'lang-llvm':
- changed-files:
- any-glob-to-any-file:

View File

@@ -0,0 +1,12 @@
compilers=&lean
defaultCompiler=lean_4_29_1
compilerType=lean
supportsBinary=false
versionRe=^Lean \(version [^\n]+
group.lean.compilers=lean_4_29_1
group.lean.isSemVer=true
group.lean.baseName=Lean
compiler.lean_4_29_1.exe=/opt/compiler-explorer/lean-4.29.1/bin/lean
compiler.lean_4_29_1.semver=4.29.1

View File

@@ -0,0 +1,7 @@
compilers=lean
compilerType=lean
supportsBinary=false
versionRe=^Lean \(version [^\n]+
compiler.lean.exe=lean
compiler.lean.name=Lean

View File

@@ -0,0 +1,4 @@
def add (x y : Nat) : Nat := x + y
def main : IO Unit := do
IO.println (add 1 2)

View File

@@ -1655,6 +1655,10 @@ export class BaseCompiler {
return utils.changeExtension(inputFilename, '.dump-cmm');
}
getLeanCOutputFilename(outputFilename: string) {
return utils.changeExtension(outputFilename, '.c');
}
getYulOutputFilename(defaultOutputFilename: string) {
return utils.changeExtension(defaultOutputFilename, '.yul');
}
@@ -1737,6 +1741,25 @@ export class BaseCompiler {
return [{text: 'Internal error; unable to open output path'}];
}
async processLeanCOutput(
outputFilename: string,
output: CompilationResult,
options?: {['clang-format']: boolean} | null,
): Promise<ResultLine[]> {
if (output.code !== 0) {
return [{text: 'Failed to run compiler to get Lean C output'}];
}
const outpath = this.getLeanCOutputFilename(outputFilename);
if (await utils.fileExists(outpath)) {
let content = await fs.readFile(outpath, 'utf8');
if (options?.['clang-format']) {
content = await this.applyClangFormat(content);
}
return content.split('\n').map(line => ({text: line}));
}
return [{text: 'Internal error; unable to open output path'}];
}
async processYulOutput(
defaultOutputFilename: string,
result: CompilationResult,
@@ -2507,6 +2530,7 @@ export class BaseCompiler {
const makeHaskellCore = backendOptions.produceHaskellCore && this.compiler.supportsHaskellCoreView;
const makeHaskellStg = backendOptions.produceHaskellStg && this.compiler.supportsHaskellStgView;
const makeHaskellCmm = backendOptions.produceHaskellCmm && this.compiler.supportsHaskellCmmView;
const makeLeanC = !!backendOptions.produceLeanC && this.compiler.supportsLeanCView;
const makeGccDump = backendOptions.produceGccDump?.opened && this.compiler.supportsGccDump;
const makeYul = backendOptions.produceYul && this.compiler.supportsYulView;
@@ -2575,6 +2599,10 @@ export class BaseCompiler {
? await this.processHaskellExtraOutput(this.getHaskellCmmOutputFilename(inputFilename), asmResult)
: undefined;
const leanCResult = makeLeanC
? await this.processLeanCOutput(outputFilename, asmResult, backendOptions.produceLeanC)
: undefined;
const yulResult = makeYul
? await this.processYulOutput(outputFilename, asmResult, backendOptions.produceYul)
: undefined;
@@ -2628,6 +2656,7 @@ export class BaseCompiler {
asmResult.haskellCoreOutput = haskellCoreResult;
asmResult.haskellStgOutput = haskellStgResult;
asmResult.haskellCmmOutput = haskellCmmResult;
asmResult.leanCOutput = leanCResult;
asmResult.clojureMacroExpOutput = clojureMacroExpResult;

View File

@@ -103,6 +103,7 @@ export {JavaCompiler} from './java.js';
export {JuliaCompiler} from './julia.js';
export {KotlinCompiler} from './kotlin.js';
export {LDCCompiler} from './ldc.js';
export {LeanCompiler} from './lean.js';
export {LFortranCompiler} from './lfortran.js';
export {LLCCompiler} from './llc.js';
export {LLVMmcaTool} from './llvm-mca.js';

167
lib/compilers/lean.ts Normal file
View File

@@ -0,0 +1,167 @@
// Copyright (c) 2026, Compiler Explorer Authors
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are met:
//
// * Redistributions of source code must retain the above copyright notice,
// this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above copyright
// notice, this list of conditions and the following disclaimer in the
// documentation and/or other materials provided with the distribution.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE
// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
// POSSIBILITY OF SUCH DAMAGE.
import path from 'node:path';
import {CompilationResult, ExecutionOptionsWithEnv} from '../../types/compilation/compilation.interfaces.js';
import type {PreliminaryCompilerInfo} from '../../types/compiler.interfaces.js';
import type {ParseFiltersAndOutputOptions} from '../../types/features/filters.interfaces.js';
import {BaseCompiler} from '../base-compiler.js';
import {CompilationEnvironment} from '../compilation-env.js';
export class LeanCompiler extends BaseCompiler {
// Flags that will be forwarded to `lean` instead of `leanc`.
private readonly leanFlags = new Set(['--profile', '--stats']);
static get key() {
return 'lean';
}
constructor(info: PreliminaryCompilerInfo, env: CompilationEnvironment) {
super(info, env);
this.compiler.supportsLeanCView = true;
}
override optionsForFilter(filters: ParseFiltersAndOutputOptions, outputFilename: string) {
return [`--c=${this.getLeanCOutputFilename(outputFilename)}`];
}
override getSharedLibraryPathsAsArguments() {
return [];
}
private async getLeanPrefix(compiler: string, execOptions: ExecutionOptionsWithEnv): Promise<string | null> {
const prefixResult = await this.exec(compiler, ['--print-prefix'], execOptions);
const prefix = prefixResult.stdout.trim();
if (prefixResult.code === 0 && prefix) {
return prefix;
}
return null;
}
private async getLeancPath(compiler: string, execOptions: ExecutionOptionsWithEnv): Promise<string> {
const prefix = await this.getLeanPrefix(compiler, execOptions);
if (prefix) return path.join(prefix, 'bin', 'leanc');
return path.join(path.dirname(compiler), 'leanc');
}
private async doLeanBuildstep(
command: string,
args: string[],
execOptions: ExecutionOptionsWithEnv,
inputFilename: string,
) {
const result = await this.exec(command, args, execOptions);
const processedResult = this.processExecutionResult(result, result.filenameTransform(inputFilename));
for (const output of [processedResult.stdout, processedResult.stderr]) {
for (const line of output) {
if (line.tag?.column !== undefined) {
// Lean reports zero-based columns, Monaco diagnostics use one-based columns
line.tag.column++;
}
}
}
return processedResult;
}
private partitionOptionsForSteps(
options: string[],
inputFilename: string,
): {
leanOptions: string[];
leancOptions: string[];
} {
const leanOptions: string[] = [];
const leancOptions: string[] = [];
for (let i = 0; i < options.length; i++) {
const option = options[i];
if (option.startsWith('--c=')) {
leanOptions.push(option);
} else if (option === inputFilename) {
leanOptions.push(option);
} else if (this.leanFlags.has(option)) {
leanOptions.push(option);
} else {
leancOptions.push(option);
}
}
return {leanOptions, leancOptions};
}
override async runCompiler(
compiler: string,
options: string[],
inputFilename: string,
execOptions: ExecutionOptionsWithEnv,
): Promise<CompilationResult> {
if (!execOptions) {
execOptions = this.getDefaultExecOptions();
}
const tmpDir = path.dirname(inputFilename);
if (!execOptions.customCwd) {
execOptions.customCwd = tmpDir;
}
const outputFilename = this.getOutputFilename(tmpDir, this.outputFilebase);
const cOutputOption = options.find(option => option.startsWith('--c='));
const cOutputFilename = cOutputOption?.substring('--c='.length) || this.getLeanCOutputFilename(outputFilename);
const {leanOptions, leancOptions} = this.partitionOptionsForSteps(options, inputFilename);
const leanResult = await this.doLeanBuildstep(compiler, leanOptions, execOptions, inputFilename);
if (leanResult.code !== 0) {
return {
...leanResult,
inputFilename,
languageId: 'asm',
};
}
const leanc = await this.getLeancPath(compiler, execOptions);
const leancResult = await this.doBuildstep(
leanc,
[...leancOptions, '-S', cOutputFilename, '-o', outputFilename],
execOptions,
);
return {
...leancResult,
okToCache: leanResult.okToCache && leancResult.okToCache,
stdout: leanResult.stdout.concat(leancResult.stdout),
stderr: leanResult.stderr.concat(leancResult.stderr),
inputFilename,
languageId: 'asm',
};
}
}

View File

@@ -584,6 +584,18 @@ const definitions: Record<LanguageKey, LanguageDefinition> = {
monacoDisassembly: null,
digitSeparator: '_',
},
lean: {
name: 'Lean',
monaco: 'lean',
extensions: ['.lean'],
alias: [],
logoFilename: 'lean.png',
logoFilenameDark: null,
formatter: null,
previewFilter: null,
monacoDisassembly: null,
digitSeparator: '_',
},
llvm: {
name: 'LLVM IR',
monaco: 'llvm-ir',

BIN
public/logos/lean.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.1 KiB

View File

@@ -27,6 +27,7 @@ import GoldenLayout from 'golden-layout';
import {ConfiguredOverrides} from '../types/compilation/compiler-overrides.interfaces.js';
import {ConfiguredRuntimeTools} from '../types/execution/execution.interfaces.js';
import {CompilerOutputOptions} from '../types/features/filters.interfaces.js';
import {ResultLine} from '../types/resultline/resultline.interfaces.js';
import {CfgState} from './panes/cfg-view.interfaces.js';
import {ClangirState} from './panes/clangir-view.interfaces.js';
import {GccDumpViewState} from './panes/gccdump-view.interfaces.js';
@@ -71,6 +72,7 @@ export const RUST_MIR_VIEW_COMPONENT_NAME = 'rustmir' as const;
export const HASKELL_CORE_VIEW_COMPONENT_NAME = 'haskellCore' as const;
export const HASKELL_STG_VIEW_COMPONENT_NAME = 'haskellStg' as const;
export const HASKELL_CMM_VIEW_COMPONENT_NAME = 'haskellCmm' as const;
export const LEAN_C_VIEW_COMPONENT_NAME = 'leanC' as const;
export const GNAT_DEBUG_TREE_VIEW_COMPONENT_NAME = 'gnatdebugtree' as const;
export const GNAT_DEBUG_VIEW_COMPONENT_NAME = 'gnatdebug' as const;
export const RUST_MACRO_EXP_VIEW_COMPONENT_NAME = 'rustmacroexp' as const;
@@ -297,6 +299,15 @@ export type PopulatedHaskellCmmViewState = StateWithId & {
treeid: number;
};
export type EmptyLeanCViewState = EmptyState;
export type PopulatedLeanCViewState = StateWithId & {
source: string;
leanCOutput: ResultLine[];
compilerName: string;
editorid: number;
treeid: number;
};
export type EmptyGnatDebugTreeViewState = EmptyState;
export type PopulatedGnatDebugTreeViewState = StateWithId & {
source: string;
@@ -396,6 +407,7 @@ export interface ComponentStateMap {
[HASKELL_CORE_VIEW_COMPONENT_NAME]: EmptyHaskellCoreViewState | PopulatedHaskellCoreViewState;
[HASKELL_STG_VIEW_COMPONENT_NAME]: EmptyHaskellStgViewState | PopulatedHaskellStgViewState;
[HASKELL_CMM_VIEW_COMPONENT_NAME]: EmptyHaskellCmmViewState | PopulatedHaskellCmmViewState;
[LEAN_C_VIEW_COMPONENT_NAME]: EmptyLeanCViewState | PopulatedLeanCViewState;
[GNAT_DEBUG_TREE_VIEW_COMPONENT_NAME]: EmptyGnatDebugTreeViewState | PopulatedGnatDebugTreeViewState;
[GNAT_DEBUG_VIEW_COMPONENT_NAME]: EmptyGnatDebugViewState | PopulatedGnatDebugViewState;
[RUST_MACRO_EXP_VIEW_COMPONENT_NAME]: EmptyRustMacroExpViewState | PopulatedRustMacroExpViewState;

View File

@@ -28,6 +28,7 @@ import {ConfiguredOverrides} from '../types/compilation/compiler-overrides.inter
import {ConfiguredRuntimeTools} from '../types/execution/execution.interfaces.js';
import {ParseFiltersAndOutputOptions} from '../types/features/filters.interfaces.js';
import {LanguageKey} from '../types/languages.interfaces.js';
import {ResultLine} from '../types/resultline/resultline.interfaces.js';
import {
AnyComponentConfig,
AST_VIEW_COMPONENT_NAME,
@@ -55,6 +56,7 @@ import {
IR_VIEW_COMPONENT_NAME,
ItemConfig,
LayoutItem,
LEAN_C_VIEW_COMPONENT_NAME,
LLVM_OPT_PIPELINE_VIEW_COMPONENT_NAME,
OPT_PIPELINE_VIEW_COMPONENT_NAME,
OPT_VIEW_COMPONENT_NAME,
@@ -816,6 +818,38 @@ export function getYulViewWith(
};
}
/** Get an empty Lean C view component. */
export function getLeanCView(): ComponentConfig<typeof LEAN_C_VIEW_COMPONENT_NAME> {
return {
type: 'component',
componentName: LEAN_C_VIEW_COMPONENT_NAME,
componentState: {},
};
}
/** Get a Lean C view with the given configuration. */
export function getLeanCViewWith(
id: number,
source: string,
leanCOutput: ResultLine[] | undefined,
compilerName: string,
editorid: number,
treeid: number,
): ComponentConfig<typeof LEAN_C_VIEW_COMPONENT_NAME> {
return {
type: 'component',
componentName: LEAN_C_VIEW_COMPONENT_NAME,
componentState: {
id,
source,
leanCOutput,
compilerName,
editorid,
treeid,
},
};
}
/** Get an empty gnat debug tree view component. */
export function getGnatDebugTreeView(): ComponentConfig<typeof GNAT_DEBUG_TREE_VIEW_COMPONENT_NAME> {
return {

View File

@@ -35,6 +35,7 @@ import {MessageWithLocation} from '../types/resultline/resultline.interfaces.js'
import {NewToolSettings, ToolState} from './components.interfaces.js';
import {Motd} from './motd.interfaces.js';
import {GccDumpFiltersState, GccDumpViewSelectedPass} from './panes/gccdump-view.interfaces.js';
import {LeanCOptions} from './panes/leanc-view.interfaces.js';
import {PPOptions} from './panes/pp-view.interfaces.js';
import {SiteSettings} from './settings.js';
import {Theme} from './themes.js';
@@ -114,6 +115,9 @@ export type EventMap = {
haskellCoreViewOpened: (compilerId: number) => void;
haskellStgViewClosed: (compilerId: number) => void;
haskellStgViewOpened: (compilerId: number) => void;
leanCViewClosed: (compilerId: number) => void;
leanCViewOpened: (compilerId: number) => void;
leanCViewOptionsUpdated: (compilerId: number, options: LeanCOptions, recompile: boolean) => void;
initialised: () => void;
irViewClosed: (compilerId: number) => void;
irViewOpened: (compilerId: number) => void;

View File

@@ -48,6 +48,7 @@ import {
HASKELL_STG_VIEW_COMPONENT_NAME,
InferComponentState,
IR_VIEW_COMPONENT_NAME,
LEAN_C_VIEW_COMPONENT_NAME,
LLVM_OPT_PIPELINE_VIEW_COMPONENT_NAME,
OPT_PIPELINE_VIEW_COMPONENT_NAME,
OPT_VIEW_COMPONENT_NAME,
@@ -84,6 +85,7 @@ import {HaskellCmm as HaskellCmmView} from './panes/haskellcmm-view.js';
import {HaskellCore as HaskellCoreView} from './panes/haskellcore-view.js';
import {HaskellStg as HaskellStgView} from './panes/haskellstg-view.js';
import {Ir as IrView} from './panes/ir-view.js';
import {LeanC as LeanCView} from './panes/leanc-view.js';
import {OptPipeline} from './panes/opt-pipeline.js';
import {Opt as OptView} from './panes/opt-view.js';
import {Output} from './panes/output.js';
@@ -159,6 +161,7 @@ export class Hub {
);
layout.registerComponent(HASKELL_STG_VIEW_COMPONENT_NAME, (c: GLC, s: any) => this.haskellStgViewFactory(c, s));
layout.registerComponent(HASKELL_CMM_VIEW_COMPONENT_NAME, (c: GLC, s: any) => this.haskellCmmViewFactory(c, s));
layout.registerComponent(LEAN_C_VIEW_COMPONENT_NAME, (c: GLC, s: any) => this.leanCViewFactory(c, s));
layout.registerComponent(GNAT_DEBUG_TREE_VIEW_COMPONENT_NAME, (c: GLC, s: any) =>
this.gnatDebugTreeViewFactory(c, s),
);
@@ -583,6 +586,10 @@ export class Hub {
return new HaskellCmmView(this, container, state);
}
public leanCViewFactory(container: GoldenLayout.Container, state: InferComponentState<LeanCView>): LeanCView {
return new LeanCView(this, container, state);
}
public clojureMacroExpViewFactory(
container: GoldenLayout.Container,
state: InferComponentState<ClojureMacroExpView>,

View File

@@ -52,6 +52,7 @@ import './hook-mode';
import './hylo-mode';
import './ispc-mode';
import './jakt-mode';
import './lean-mode';
import './llvm-ir-mode';
import './mlir-mode';
import './modula2-mode';

347
static/modes/lean-mode.ts Normal file
View File

@@ -0,0 +1,347 @@
// Copyright (c) 2026, Compiler Explorer Authors
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are met:
//
// * Redistributions of source code must retain the above copyright notice,
// this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above copyright
// notice, this list of conditions and the following disclaimer in the
// documentation and/or other materials provided with the distribution.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
// POSSIBILITY OF SUCH DAMAGE.
import * as monaco from 'monaco-editor';
// Based on https://github.com/leanprover/vscode-lean4/blob/bbe3ff12/vscode-lean4/syntaxes/lean4.json.
function definition(): monaco.languages.IMonarchLanguage {
return {
escapes: /\\(?:[\\'"ntr]|x[0-9A-Fa-f]{2}|u[0-9A-Fa-f]{4})/,
keywords: [
'local',
'scoped',
'partial',
'unsafe',
'nonrec',
'public',
'private',
'protected',
'noncomputable',
'meta',
'deriving',
'instance',
'inductive',
'coinductive',
'structure',
'theorem',
'axiom',
'abbrev',
'lemma',
'def',
'instance',
'class',
'with',
'extends',
'where',
'theorem',
'show',
'have',
'using',
'haveI',
'from',
'suffices',
'nomatch',
'nofun',
'no_index',
'def',
'class',
'structure',
'instance',
'elab',
'set_option',
'initialize',
'builtin_initialize',
'example',
'inductive_fixpoint',
'inductive',
'coinductive_fixpoint',
'coinductive',
'termination_by?',
'termination_by',
'decreasing_by',
'partial_fixpoint',
'axiom',
'universe',
'variable',
'module',
'import all',
'import',
'open',
'export',
'prelude',
'renaming',
'hiding',
'do',
'by?',
'by',
'let',
'letI',
'let_expr',
'extends',
'mutual',
'mut',
'where',
'rec',
'declare_syntax_cat',
'syntax',
'macro_rules',
'macro',
'max_prec',
'leading_parser',
'elab_rules',
'deriving',
'fun',
'section',
'namespace',
'end',
'prefix',
'postfix',
'infixl',
'infixr',
'infix',
'notation',
'abbrev',
'if',
'bif',
'then',
'else',
'calc',
'matches',
'match_expr',
'match',
'with',
'forall',
'for',
'while',
'repeat',
'unless',
'until',
'panic!',
'unreachable!',
'assert!',
'try',
'catch',
'finally',
'return',
'continue',
'break',
'exists',
'mod_cast',
'include_str',
'include',
'in',
'trailing_parser',
'tactic_tag',
'tactic_alt',
'tactic_extension',
'register_tactic_tag',
'binder_predicate',
'grind_propagator',
'builtin_grind_propagator',
'grind_pattern',
'simproc',
'builtin_simproc',
'simproc_decl',
'builtin_simproc_decl',
'dsimproc',
'builtin_dsimproc',
'dsimproc_decl',
'builtin_dsimproc_decl',
'show_panel_widgets',
'show_term',
'seal',
'unseal',
'nat_lit',
'norm_cast_add_elim',
'println!',
'declare_config_elab',
'register_error_explanation',
'register_builtin_option',
'register_option',
'register_parser_alias',
'register_simp_attr',
'register_linter_set',
'register_label_attr',
'recommended_spelling',
'reportIssue!',
'reprove',
'run_elab',
'run_cmd',
'run_meta',
'add_decl_doc',
'omit',
'opaque',
'dbg_trace',
'trace_goal',
'trace',
'throwErrorAt',
'throwError',
'throwNamedErrorAt',
'throwNamedError',
'logNamedWarningAt',
'logNamedWarning',
'logNamedErrorAt',
'logNamedError',
],
types: ['Prop', 'Type', 'Sort'],
invalidKeywords: ['sorry', 'admit'],
commands: [
'#print',
'#eval',
'#eval!',
'#reduce',
'#synth',
'#widget',
'#where',
'#version',
'#with_exporting',
'#check',
'#check_tactic',
'#check_tactic_failure',
'#check_failure',
'#check_simp',
'#discr_tree_key',
'#discr_tree_simp_key',
'#guard',
'#guard_expr',
'#guard_msgs',
],
invalidCommands: ['#exit'],
tokenizer: {
root: [
[/r(#*)"/, {token: 'string.quote', bracket: '@open', next: '@stringraw.$1'}],
{include: '@whitespace'},
[/"/, 'string', '@string'],
[/(?<![\]\w])'[^\\']'/, 'string'],
[/(?<![\]\w])(')(@escapes)(')/, ['string', 'string.escape', 'string']],
[/\b(0[xX][0-9a-fA-F]+|[0-9]+|-?(0|[1-9][0-9]*)(\.[0-9]+)?([eE][+-]?[0-9]+)?)\b/, 'number'],
[/«/, 'identifier', '@escapedIdentifier'],
[/\battribute\b\s*\[[^\]\s]*\]/, 'keyword.modifier'],
[/@\[[^\]\s]*\]/, 'keyword.modifier'],
[
/#[a-zA-Z_][a-zA-Z0-9_!]*/,
{
cases: {
'@invalidCommands': 'invalid',
'@commands': 'keyword',
'@default': '',
},
},
],
[/[{}()[\]]/, '@brackets'],
[
/[a-zA-Z_][a-zA-Z0-9_'?!]*/,
{
cases: {
'@invalidKeywords': 'invalid',
'@keywords': 'keyword',
'@types': 'keyword.type',
'@default': 'identifier',
},
},
],
],
whitespace: [
[/[ \t\r\n]+/, 'white'],
[/--.*$/, 'comment'],
[/\/--/, 'comment.doc', '@docComment'],
[/\/-!/, 'comment.doc', '@docComment'],
[/\/-/, 'comment', '@blockComment'],
],
docComment: [
[/[^/-]+/, 'comment.doc'],
[/\/-/, 'comment.doc', '@blockComment'],
[/-\//, 'comment.doc', '@pop'],
[/[/-]/, 'comment.doc'],
],
blockComment: [
[/[^/-]+/, 'comment'],
[/\/-/, 'comment', '@push'],
[/-\//, 'comment', '@pop'],
[/[/-]/, 'comment'],
],
string: [
[/[^\\"]+/, 'string'],
[/@escapes/, 'string.escape'],
[/\\./, 'string.escape.invalid'],
[/"/, 'string', '@pop'],
],
escapedIdentifier: [
[/[^»]+/, 'identifier'],
[/»/, 'identifier', '@pop'],
],
stringraw: [
[/[^"#]+/, {token: 'string'}],
[
/"(#*)/,
{
cases: {
'$1==$S2': {token: 'string.quote', bracket: '@close', next: '@pop'},
'@default': {token: 'string'},
},
},
],
[/["#]/, {token: 'string'}],
],
},
};
}
monaco.languages.register({id: 'lean'});
monaco.languages.setMonarchTokensProvider('lean', definition());
monaco.languages.setLanguageConfiguration('lean', {
comments: {
lineComment: '--',
blockComment: ['/-', '-/'],
},
brackets: [
['(', ')'],
['[', ']'],
['{', '}'],
],
autoClosingPairs: [
{open: '(', close: ')'},
{open: '[', close: ']'},
{open: '{', close: '}'},
{open: '"', close: '"', notIn: ['string', 'comment']},
],
surroundingPairs: [
{open: '(', close: ')'},
{open: '[', close: ']'},
{open: '{', close: '}'},
{open: '"', close: '"'},
],
});

View File

@@ -66,6 +66,7 @@ import * as TimingWidget from '../widgets/timing-info-widget.js';
import {Toggles} from '../widgets/toggles.js';
import {CompilerCurrentState, CompilerState} from './compiler.interfaces.js';
import {GccDumpFiltersState, GccDumpViewSelectedPass} from './gccdump-view.interfaces.js';
import {LeanCOptions} from './leanc-view.interfaces.js';
import {MonacoPaneState} from './pane.interfaces.js';
import {MonacoPane} from './pane.js';
import {PPOptions} from './pp-view.interfaces.js';
@@ -197,6 +198,7 @@ export class Compiler extends MonacoPane<monaco.editor.IStandaloneCodeEditor, Co
private haskellCoreButton: JQuery<HTMLButtonElement>;
private haskellStgButton: JQuery<HTMLButtonElement>;
private haskellCmmButton: JQuery<HTMLButtonElement>;
private leanCButton: JQuery<HTMLButtonElement>;
private clojureMacroExpButton: JQuery<HTMLButtonElement>;
private yulButton: JQuery<HTMLButtonElement>;
private gccDumpButton: JQuery<HTMLButtonElement>;
@@ -276,6 +278,8 @@ export class Compiler extends MonacoPane<monaco.editor.IStandaloneCodeEditor, Co
private haskellCoreViewOpen: boolean;
private haskellStgViewOpen: boolean;
private haskellCmmViewOpen: boolean;
private leanCViewOpen: boolean;
private leanCOptions: LeanCOptions;
private clojureMacroExpViewOpen: boolean;
private yulViewOpen: boolean;
private ppOptions: PPOptions;
@@ -633,6 +637,17 @@ export class Compiler extends MonacoPane<monaco.editor.IStandaloneCodeEditor, Co
);
};
const createLeanCView = () => {
return Components.getLeanCViewWith(
this.id,
this.source,
this.lastResult?.leanCOutput,
this.getCompilerName(),
this.sourceEditorId ?? 0,
this.sourceTreeId ?? 0,
);
};
const createClojureMacroExpView = () => {
return Components.getClojureMacroExpViewWith(
this.id,
@@ -904,6 +919,18 @@ export class Compiler extends MonacoPane<monaco.editor.IStandaloneCodeEditor, Co
insertPoint.addChild(createHaskellCmmView());
});
createDragSource(this.container.layoutManager, this.leanCButton, () => createLeanCView()).on(
'dragStart',
hidePaneAdder,
);
this.leanCButton.on('click', () => {
const insertPoint =
this.hub.findParentRowOrColumn(this.container.parent) ||
this.container.layoutManager.root.contentItems[0];
insertPoint.addChild(createLeanCView());
});
createDragSource(this.container.layoutManager, this.rustMacroExpButton, () => createRustMacroExpView()).on(
'dragStart',
hidePaneAdder,
@@ -1346,6 +1373,7 @@ export class Compiler extends MonacoPane<monaco.editor.IStandaloneCodeEditor, Co
produceHaskellCore: this.haskellCoreViewOpen,
produceHaskellStg: this.haskellStgViewOpen,
produceHaskellCmm: this.haskellCmmViewOpen,
produceLeanC: this.leanCViewOpen ? this.leanCOptions : null,
produceClojureMacroExp: this.clojureMacroExpViewOpen,
produceYul: this.yulViewOpen ? this.yulOptions : null,
overrides: this.getCurrentState().overrides,
@@ -2216,6 +2244,29 @@ export class Compiler extends MonacoPane<monaco.editor.IStandaloneCodeEditor, Co
}
}
onLeanCViewOpened(id: number): void {
if (this.id === id) {
this.leanCButton.prop('disabled', true);
this.leanCViewOpen = true;
}
}
onLeanCViewClosed(id: number): void {
if (this.id === id) {
this.leanCButton.prop('disabled', false);
this.leanCViewOpen = false;
}
}
onLeanCViewOptionsUpdated(id: number, options: LeanCOptions, recompile: boolean): void {
if (this.id === id) {
this.leanCOptions = options;
if (recompile) {
this.compile();
}
}
}
onGnatDebugTreeViewOpened(id: number): void {
if (this.id === id) {
this.gnatDebugTreeButton.prop('disabled', true);
@@ -2503,6 +2554,7 @@ export class Compiler extends MonacoPane<monaco.editor.IStandaloneCodeEditor, Co
this.haskellCoreButton = this.domRoot.find('.btn.view-haskellCore');
this.haskellStgButton = this.domRoot.find('.btn.view-haskellStg');
this.haskellCmmButton = this.domRoot.find('.btn.view-haskellCmm');
this.leanCButton = this.domRoot.find('.btn.view-leanC');
this.clojureMacroExpButton = this.domRoot.find('.btn.view-clojuremacroexp');
this.yulButton = this.domRoot.find('.btn.view-yul');
this.gccDumpButton = this.domRoot.find('.btn.view-gccdump');
@@ -2794,6 +2846,7 @@ export class Compiler extends MonacoPane<monaco.editor.IStandaloneCodeEditor, Co
this.haskellCoreButton.prop('disabled', this.haskellCoreViewOpen);
this.haskellStgButton.prop('disabled', this.haskellStgViewOpen);
this.haskellCmmButton.prop('disabled', this.haskellCmmViewOpen);
this.leanCButton.prop('disabled', this.leanCViewOpen);
this.rustMacroExpButton.prop('disabled', this.rustMacroExpViewOpen);
this.rustHirButton.prop('disabled', this.rustHirViewOpen);
this.clojureMacroExpButton.prop('disabled', this.clojureMacroExpViewOpen);
@@ -2818,6 +2871,7 @@ export class Compiler extends MonacoPane<monaco.editor.IStandaloneCodeEditor, Co
this.haskellCoreButton.toggle(!!this.compiler.supportsHaskellCoreView);
this.haskellStgButton.toggle(!!this.compiler.supportsHaskellStgView);
this.haskellCmmButton.toggle(!!this.compiler.supportsHaskellCmmView);
this.leanCButton.toggle(!!this.compiler.supportsLeanCView);
this.clojureMacroExpButton.toggle(!!this.compiler.supportsClojureMacroExpView);
this.yulButton.toggle(!!this.compiler.supportsYulView);
// TODO(jeremy-rifkin): Disable cfg button when binary mode is set?
@@ -3000,6 +3054,9 @@ export class Compiler extends MonacoPane<monaco.editor.IStandaloneCodeEditor, Co
this.eventHub.on('haskellStgViewClosed', this.onHaskellStgViewClosed, this);
this.eventHub.on('haskellCmmViewOpened', this.onHaskellCmmViewOpened, this);
this.eventHub.on('haskellCmmViewClosed', this.onHaskellCmmViewClosed, this);
this.eventHub.on('leanCViewOpened', this.onLeanCViewOpened, this);
this.eventHub.on('leanCViewClosed', this.onLeanCViewClosed, this);
this.eventHub.on('leanCViewOptionsUpdated', this.onLeanCViewOptionsUpdated, this);
this.eventHub.on('clojureMacroExpViewOpened', this.onClojureMacroExpViewOpened, this);
this.eventHub.on('clojureMacroExpViewClosed', this.onClojureMacroExpViewClosed, this);
this.eventHub.on('yulViewOpened', this.onYulViewOpened, this);

View File

@@ -39,6 +39,7 @@ export enum DiffType {
RustHirOutput = 12,
ClojureMacroExpOutput = 13,
YulOutput = 14,
LeanCOutput = 15,
}
export type DiffState = {

View File

@@ -181,6 +181,11 @@ class DiffStateObject {
{text: "<select 'Add new...' → 'Yul (Solidity IR)' in this compiler's pane>"},
];
break;
case DiffType.LeanCOutput:
output = this.result.leanCOutput || [
{text: "<select 'Add new...' → 'Lean C Output' in this compiler's pane>"},
];
break;
}
}
this.model.setValue(output.map(x => x.text).join('\n'));
@@ -490,6 +495,9 @@ export class Diff extends MonacoPane<monaco.editor.IStandaloneDiffEditor, DiffSt
if (compiler.supportsYulView) {
options.push({id: DiffType.YulOutput.toString(), name: 'Yul (Solidity IR)'});
}
if (compiler.supportsLeanCView) {
options.push({id: DiffType.LeanCOutput.toString(), name: 'Lean C Output'});
}
}
const lhsoptions = this.getDiffableOptions(this.selectize.lhs, lhsextraoptions);

View File

@@ -0,0 +1,31 @@
// Copyright (c) 2026, Compiler Explorer Authors
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are met:
//
// * Redistributions of source code must retain the above copyright notice,
// this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above copyright
// notice, this list of conditions and the following disclaimer in the
// documentation and/or other materials provided with the distribution.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE
// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
// POSSIBILITY OF SUCH DAMAGE.
import {ResultLine} from '../../types/resultline/resultline.interfaces.js';
export interface LeanCState {
leanCOutput: ResultLine[];
}
export type {LeanCOptions} from '../../types/compilation/lean-c.interfaces.js';

151
static/panes/leanc-view.ts Normal file
View File

@@ -0,0 +1,151 @@
// Copyright (c) 2026, Compiler Explorer Authors
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are met:
//
// * Redistributions of source code must retain the above copyright notice,
// this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above copyright
// notice, this list of conditions and the following disclaimer in the
// documentation and/or other materials provided with the distribution.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE
// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
// POSSIBILITY OF SUCH DAMAGE.
import {Container} from 'golden-layout';
import $ from 'jquery';
import * as monaco from 'monaco-editor';
import _ from 'underscore';
import {CompilationResult} from '../../types/compilation/compilation.interfaces.js';
import {CompilerInfo} from '../../types/compiler.interfaces.js';
import {Hub} from '../hub.js';
import {extendConfig} from '../monaco-config.js';
import {Toggles} from '../widgets/toggles.js';
import {LeanCOptions, LeanCState} from './leanc-view.interfaces.js';
import {MonacoPaneState} from './pane.interfaces.js';
import {MonacoPane} from './pane.js';
export class LeanC extends MonacoPane<monaco.editor.IStandaloneCodeEditor, LeanCState> {
private options: Toggles;
constructor(hub: Hub, container: Container, state: LeanCState & MonacoPaneState) {
super(hub, container, state);
if (state.leanCOutput) {
this.showLeanCResults(state.leanCOutput);
}
this.onOptionsChange();
}
override getInitialHTML(): string {
return $('#leanC').html();
}
override createEditor(editorRoot: HTMLElement): void {
this.editor = monaco.editor.create(
editorRoot,
extendConfig({
language: 'c',
readOnly: true,
glyphMargin: true,
lineNumbersMinChars: 3,
}),
);
}
override getPrintName() {
return 'Lean C Output';
}
override getDefaultPaneName(): string {
return 'Lean C Output';
}
override registerButtons(state: LeanCState & MonacoPaneState): void {
super.registerButtons(state);
this.options = new Toggles(this.domRoot.find('.options'), state as unknown as Record<string, boolean>);
this.options.on('change', this.onOptionsChange.bind(this));
}
override registerCallbacks(): void {
const throttleFunction = _.throttle(
(event: monaco.editor.ICursorSelectionChangedEvent) => this.onDidChangeCursorSelection(event),
500,
);
this.editor.onDidChangeCursorSelection(event => throttleFunction(event));
this.eventHub.emit('leanCViewOpened', this.compilerInfo.compilerId);
this.eventHub.emit('requestSettings');
}
override getCurrentState(): MonacoPaneState {
return {
...super.getCurrentState(),
...this.options.get(),
};
}
onOptionsChange(): void {
const options = this.options.get() as LeanCOptions;
this.updateState();
this.showLeanCResults([{text: '<Compiling...>'}]);
this.eventHub.emit('leanCViewOptionsUpdated', this.compilerInfo.compilerId, options, true);
}
override onCompileResult(compilerId: number, compiler: CompilerInfo, result: CompilationResult): void {
if (this.compilerInfo.compilerId !== compilerId) return;
if (result.leanCOutput) {
this.showLeanCResults(result.leanCOutput);
} else if (compiler.supportsLeanCView) {
this.showLeanCResults([{text: '<No output>'}]);
}
}
override onCompiler(
compilerId: number,
compiler: CompilerInfo | null,
options: string,
editorId?: number,
treeId?: number,
): void {
if (this.compilerInfo.compilerId === compilerId) {
this.compilerInfo.compilerName = compiler ? compiler.name : '';
this.compilerInfo.editorId = editorId;
this.compilerInfo.treeId = treeId;
this.updateTitle();
if (compiler && !compiler.supportsLeanCView) {
this.showLeanCResults([{text: '<Lean C output is not supported for this compiler>'}]);
}
}
}
showLeanCResults(result: Record<'text', string>[]): void {
this.editor
.getModel()
?.setValue(result.length ? _.pluck(result, 'text').join('\n') : '<No Lean C output generated>');
if (!this.isAwaitingInitialResults) {
if (this.selection) {
this.editor.setSelection(this.selection);
this.editor.revealLinesInCenter(this.selection.selectionStartLineNumber, this.selection.endLineNumber);
}
this.isAwaitingInitialResults = true;
}
}
override close(): void {
this.eventHub.unsubscribe();
this.eventHub.emit('leanCViewClosed', this.compilerInfo.compilerId);
this.editor.dispose();
}
}

View File

@@ -41,6 +41,7 @@ import {CFGResult} from './cfg.interfaces.js';
import {ClangirBackendOptions} from './clangir.interfaces.js';
import {ConfiguredOverrides} from './compiler-overrides.interfaces.js';
import {LLVMIrBackendOptions} from './ir.interfaces.js';
import type {LeanCOptions} from './lean-c.interfaces.js';
import {OptPipelineBackendOptions, OptPipelineOutput} from './opt-pipeline-output.interfaces.js';
import {YulBackendOptions} from './yul.interfaces.js';
@@ -117,6 +118,7 @@ export type CompilationRequestOptions = {
produceHaskellCore?: boolean;
produceHaskellStg?: boolean;
produceHaskellCmm?: boolean;
produceLeanC?: LeanCOptions | null;
produceClojureMacroExp?: boolean;
produceYul?: YulBackendOptions | null;
cmakeArgs?: string;
@@ -216,6 +218,7 @@ export type CompilationResult = {
haskellCoreOutput?: ResultLine[];
haskellStgOutput?: ResultLine[];
haskellCmmOutput?: ResultLine[];
leanCOutput?: ResultLine[];
clojureMacroExpOutput?: ResultLine[];

View File

@@ -0,0 +1,27 @@
// Copyright (c) 2026, Compiler Explorer Authors
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are met:
//
// * Redistributions of source code must retain the above copyright notice,
// this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above copyright
// notice, this list of conditions and the following disclaimer in the
// documentation and/or other materials provided with the distribution.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
// POSSIBILITY OF SUCH DAMAGE.
export type LeanCOptions = {
'clang-format': boolean;
};

View File

@@ -103,6 +103,7 @@ export type CompilerInfo = {
supportsHaskellCoreView?: boolean;
supportsHaskellStgView?: boolean;
supportsHaskellCmmView?: boolean;
supportsLeanCView?: boolean;
supportsClojureMacroExpView?: boolean;
supportsYulView?: boolean;
supportsCfg?: boolean;

View File

@@ -72,6 +72,7 @@ export type LanguageKey =
| 'julia'
| 'javascript'
| 'kotlin'
| 'lean'
| 'llvm'
| 'llvm_mir'
| 'mlir'

View File

@@ -64,6 +64,7 @@ mixin newPaneButton(classId, text, title, icon)
+newPaneButton("view-haskellCore", "GHC Core", "Show GHC Core Intermediate Representation", "fas fa-water")
+newPaneButton("view-haskellStg", "GHC STG", "Show GHC STG Intermediate Representation", "fas fa-water")
+newPaneButton("view-haskellCmm", "GHC Cmm", "Show GHC Cmm Intermediate Representation", "fas fa-water")
+newPaneButton("view-leanC", "Lean C Output", "Show generated Lean C output", "fas fa-align-center")
+newPaneButton("view-clojuremacroexp", "Clojure Macro Expansion", "Show Clojure macro expansion", "fas fa-arrows-alt")
+newPaneButton("view-yul", "Yul (Solidity IR)", "Show Solidity Intermediate Representation", "fas fa-align-center")
+newPaneButton("view-gccdump", "GCC Tree/RTL", "Show GCC Tree/RTL dump", "fas fa-tree")

View File

@@ -0,0 +1,10 @@
#leanC
.top-bar.btn-toolbar.bg-light(role="toolbar")
include ../../font-size
.btn-group.btn-group-sm.options(role="group" aria-label="Panel options")
.input-group.input-group-sm.mb-auto
.button-checkbox
button.btn.btn-sm.btn-light.clang-format(type="button" title="Apply clang-format to output" data-bind="clang-format" aria-pressed="true" aria-label="Apply clang-format")
span Apply clang-format
input.d-none(type="checkbox" checked=true)
.monaco-placeholder

View File

@@ -20,6 +20,8 @@ mixin monacopane(id)
include panes/pp
include panes/leanc
include panes/ir
include panes/clangir