mirror of
https://github.com/compiler-explorer/compiler-explorer.git
synced 2025-12-27 10:33:59 -05:00
Clojure language support (#8146)
<img width="1405" height="474" alt="Clojure in Compiler Explorer 2" src="https://github.com/user-attachments/assets/76dfed9b-d0eb-4764-b371-9c6023088a50" /> With Macro Expansion: <img width="1642" height="594" alt="image" src="https://github.com/user-attachments/assets/8b511af9-3617-426e-868d-5a99e5db5756" /> TODO - [x] Language configuration - [x] Compile via wrapper - Inject namespace if necessary to simplify minimal code sample - Parse Unix style command line parameters into compiler bindings - Place file in path according to namespace - [x] Install some versions of Clojure [PR here](https://github.com/compiler-explorer/infra/pull/1849) - [x] Macroexpansion view (modeled on Rust macro expansion view) - [x] Filter out command line options that would break wrapper operation - [x] ~~Parse `--help` output to a list of options~~ Reverted because not applicable. - [x] Short form compiler options - [x] Support Clojure compiler settings via env var, like `JAVA_OPTS=-Dclojure.compiler.direct-linking=true -Dclojure.compiler.elide-meta=[:doc,:file]` NOT DOING - [x] ~~Support loading dependencies~~ Non-trivial enhancement. Not necessary for initial release. --------- Co-authored-by: Matt Godbolt <matt@godbolt.org>
This commit is contained in:
6
.github/labeler.yml
vendored
6
.github/labeler.yml
vendored
@@ -98,6 +98,12 @@
|
|||||||
- 'etc/config/clean.*.properties'
|
- 'etc/config/clean.*.properties'
|
||||||
- 'static/modes/clean-mode.ts'
|
- 'static/modes/clean-mode.ts'
|
||||||
|
|
||||||
|
'lang-clojure':
|
||||||
|
- changed-files:
|
||||||
|
- any-glob-to-any-file:
|
||||||
|
- 'lib/compilers/clojure.ts'
|
||||||
|
- 'etc/config/clojure.*.properties'
|
||||||
|
|
||||||
'lang-cobol':
|
'lang-cobol':
|
||||||
- changed-files:
|
- changed-files:
|
||||||
- any-glob-to-any-file:
|
- any-glob-to-any-file:
|
||||||
|
|||||||
@@ -167,3 +167,4 @@ From oldest to newest contributor, we would like to thank:
|
|||||||
- [Alex Trotta](https://github.com/Ahajha)
|
- [Alex Trotta](https://github.com/Ahajha)
|
||||||
- [natinusala](https://github.com/natinusala)
|
- [natinusala](https://github.com/natinusala)
|
||||||
- [LJ](https://github.com/elle-j)
|
- [LJ](https://github.com/elle-j)
|
||||||
|
- [Frank Leon Rose](https://github.com/frankleonrose)
|
||||||
|
|||||||
@@ -15,6 +15,7 @@ const PANE_DATA_MAP = {
|
|||||||
core: {name: 'Core', selector: 'view-haskellCore'},
|
core: {name: 'Core', selector: 'view-haskellCore'},
|
||||||
stg: {name: 'STG', selector: 'view-haskellStg'},
|
stg: {name: 'STG', selector: 'view-haskellStg'},
|
||||||
cmm: {name: 'Cmm', selector: 'view-haskellCmm'},
|
cmm: {name: 'Cmm', selector: 'view-haskellCmm'},
|
||||||
|
clojure_macro: {name: 'Clojure Macro', selector: 'view-clojuremacroexp'},
|
||||||
dump: {name: 'Tree/RTL', selector: 'view-gccdump'},
|
dump: {name: 'Tree/RTL', selector: 'view-gccdump'},
|
||||||
tree: {name: 'Tree', selector: 'view-gnatdebugtree'},
|
tree: {name: 'Tree', selector: 'view-gnatdebugtree'},
|
||||||
debug: {name: 'Debug', selector: 'view-gnatdebug'},
|
debug: {name: 'Debug', selector: 'view-gnatdebug'},
|
||||||
|
|||||||
60
etc/config/clojure.amazon.properties
Normal file
60
etc/config/clojure.amazon.properties
Normal file
@@ -0,0 +1,60 @@
|
|||||||
|
compilers=&clojure
|
||||||
|
compilerType=clojure
|
||||||
|
versionFlag=--version
|
||||||
|
objdumper=/opt/compiler-explorer/jdk-21.0.2/bin/javap
|
||||||
|
instructionSet=java
|
||||||
|
defaultCompiler=clojure1123
|
||||||
|
demangler=
|
||||||
|
postProcess=
|
||||||
|
options=
|
||||||
|
supportsBinary=false
|
||||||
|
needsMulti=false
|
||||||
|
supportsExecute=true
|
||||||
|
interpreted=true
|
||||||
|
|
||||||
|
group.clojure.compilers=clojure1123:clojure1122:clojure1121:clojure1120:clojure1114:clojure1113:clojure1112:clojure1111
|
||||||
|
group.clojure.groupName=Clojure
|
||||||
|
group.clojure.baseName=clojure
|
||||||
|
group.clojure.isSemVer=true
|
||||||
|
group.clojure.licenseName=Eclipse Public License 1.0
|
||||||
|
group.clojure.licenseLink=https://github.com/clojure/clojure/blob/master/epl-v10.html
|
||||||
|
|
||||||
|
compiler.clojure1123.exe=/opt/compiler-explorer/clojure/1.12.3.1577/bin/clojure
|
||||||
|
compiler.clojure1123.semver=1.12.3
|
||||||
|
compiler.clojure1123.java_home=/opt/compiler-explorer/jdk-21.0.2
|
||||||
|
compiler.clojure1123.runtime=/opt/compiler-explorer/jdk-21.0.2/bin/java
|
||||||
|
|
||||||
|
compiler.clojure1122.exe=/opt/compiler-explorer/clojure/1.12.2.1571/bin/clojure
|
||||||
|
compiler.clojure1122.semver=1.12.2
|
||||||
|
compiler.clojure1122.java_home=/opt/compiler-explorer/jdk-21.0.2
|
||||||
|
compiler.clojure1122.runtime=/opt/compiler-explorer/jdk-21.0.2/bin/java
|
||||||
|
|
||||||
|
compiler.clojure1121.exe=/opt/compiler-explorer/clojure/1.12.1.1550/bin/clojure
|
||||||
|
compiler.clojure1121.semver=1.12.1
|
||||||
|
compiler.clojure1121.java_home=/opt/compiler-explorer/jdk-21.0.2
|
||||||
|
compiler.clojure1121.runtime=/opt/compiler-explorer/jdk-21.0.2/bin/java
|
||||||
|
|
||||||
|
compiler.clojure1120.exe=/opt/compiler-explorer/clojure/1.12.0.1530/bin/clojure
|
||||||
|
compiler.clojure1120.semver=1.12.0
|
||||||
|
compiler.clojure1120.java_home=/opt/compiler-explorer/jdk-21.0.2
|
||||||
|
compiler.clojure1120.runtime=/opt/compiler-explorer/jdk-21.0.2/bin/java
|
||||||
|
|
||||||
|
compiler.clojure1114.exe=/opt/compiler-explorer/clojure/1.11.4.1474/bin/clojure
|
||||||
|
compiler.clojure1114.semver=1.11.4
|
||||||
|
compiler.clojure1114.java_home=/opt/compiler-explorer/jdk-21.0.2
|
||||||
|
compiler.clojure1114.runtime=/opt/compiler-explorer/jdk-21.0.2/bin/java
|
||||||
|
|
||||||
|
compiler.clojure1113.exe=/opt/compiler-explorer/clojure/1.11.3.1463/bin/clojure
|
||||||
|
compiler.clojure1113.semver=1.11.3
|
||||||
|
compiler.clojure1113.java_home=/opt/compiler-explorer/jdk-21.0.2
|
||||||
|
compiler.clojure1113.runtime=/opt/compiler-explorer/jdk-21.0.2/bin/java
|
||||||
|
|
||||||
|
compiler.clojure1112.exe=/opt/compiler-explorer/clojure/1.11.2.1446/bin/clojure
|
||||||
|
compiler.clojure1112.semver=1.11.2
|
||||||
|
compiler.clojure1112.java_home=/opt/compiler-explorer/jdk-21.0.2
|
||||||
|
compiler.clojure1112.runtime=/opt/compiler-explorer/jdk-21.0.2/bin/java
|
||||||
|
|
||||||
|
compiler.clojure1111.exe=/opt/compiler-explorer/clojure/1.11.1.1435/bin/clojure
|
||||||
|
compiler.clojure1111.semver=1.11.1
|
||||||
|
compiler.clojure1111.java_home=/opt/compiler-explorer/jdk-21.0.2
|
||||||
|
compiler.clojure1111.runtime=/opt/compiler-explorer/jdk-21.0.2/bin/java
|
||||||
18
etc/config/clojure.defaults.properties
Normal file
18
etc/config/clojure.defaults.properties
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
# Default settings for Clojure/JVM
|
||||||
|
compilers=&clojure
|
||||||
|
compilerType=clojure
|
||||||
|
versionFlag=--version
|
||||||
|
objdumper=javap
|
||||||
|
instructionSet=java
|
||||||
|
|
||||||
|
group.clojure.compilers=clojuredefault
|
||||||
|
compiler.clojuredefault.exe=/usr/local/bin/clojure
|
||||||
|
compiler.clojuredefault.name=clojure default
|
||||||
|
|
||||||
|
defaultCompiler=clojuredefault
|
||||||
|
demangler=
|
||||||
|
postProcess=
|
||||||
|
options=
|
||||||
|
supportsBinary=false
|
||||||
|
needsMulti=false
|
||||||
|
supportsExecute=false
|
||||||
141
etc/scripts/clojure_wrapper.clj
Normal file
141
etc/scripts/clojure_wrapper.clj
Normal file
@@ -0,0 +1,141 @@
|
|||||||
|
(ns clojure-wrapper
|
||||||
|
(:require [clojure.java.io :as io]
|
||||||
|
[clojure.pprint :as pp]
|
||||||
|
[clojure.string :as str]
|
||||||
|
[clojure.walk :as walk])
|
||||||
|
(:import [java.io PushbackReader]))
|
||||||
|
|
||||||
|
(def help-text
|
||||||
|
"Compiler options supported:
|
||||||
|
-h --help - Shows this text and flags sent to compiler
|
||||||
|
-dl --direct-linking - Eliminates var indirection in fn invocation
|
||||||
|
-dlc --disable-locals-clearing - Eliminates instructions setting locals to null
|
||||||
|
-em --elide-meta [:doc,:arglists,:added,:file,...] - Drops metadata keys from classfiles
|
||||||
|
-omm --omit-macro-meta - Omit metadata from macro-expanded output")
|
||||||
|
|
||||||
|
(defn parse-command-line []
|
||||||
|
(loop [params {}
|
||||||
|
positional []
|
||||||
|
ignored []
|
||||||
|
args *command-line-args*]
|
||||||
|
(if-let [arg (first args)]
|
||||||
|
(case arg
|
||||||
|
("-h" "--help")
|
||||||
|
(recur (assoc params :show-help true)
|
||||||
|
positional ignored (rest args))
|
||||||
|
|
||||||
|
"--macro-expand"
|
||||||
|
(recur (assoc params :macro-expand true)
|
||||||
|
positional ignored (rest args))
|
||||||
|
|
||||||
|
("-omm" "--omit-macro-meta")
|
||||||
|
(recur (assoc params :print-meta false)
|
||||||
|
positional ignored (rest args))
|
||||||
|
|
||||||
|
("-dlc" "--disable-locals-clearing")
|
||||||
|
(recur (assoc params :disable-locals-clearing true)
|
||||||
|
positional ignored (rest args))
|
||||||
|
|
||||||
|
("-dl" "--direct-linking")
|
||||||
|
(recur (assoc params :direct-linking true)
|
||||||
|
positional ignored (rest args))
|
||||||
|
|
||||||
|
("-em" "--elide-meta")
|
||||||
|
(let [elisions (try (some-> args second read-string) (catch Exception _e))]
|
||||||
|
(when-not (and (sequential? elisions)
|
||||||
|
(every? keyword? elisions))
|
||||||
|
(println (str "Invalid elide-meta parameter: '" (second args) "'\n")
|
||||||
|
"-em flag must be followed by a vector of keywords, like '-em [:doc,:arglists]'")
|
||||||
|
(System/exit 1))
|
||||||
|
(recur (assoc params :elide-meta elisions)
|
||||||
|
positional ignored (drop 2 args)))
|
||||||
|
|
||||||
|
(if (or (re-matches #"-.*" arg)
|
||||||
|
(not (re-matches #".*\.clj" arg)))
|
||||||
|
(recur params positional (conj ignored arg) (rest args))
|
||||||
|
(recur params (conj positional arg) ignored (rest args))))
|
||||||
|
[params positional ignored])))
|
||||||
|
|
||||||
|
(defn forms
|
||||||
|
([input-file]
|
||||||
|
;; Default is to load all forms while file is open
|
||||||
|
(forms input-file doall))
|
||||||
|
([input-file extract]
|
||||||
|
(with-open [rdr (-> input-file io/reader PushbackReader.)]
|
||||||
|
(->> #(try (read rdr) (catch Exception _e nil))
|
||||||
|
(repeatedly)
|
||||||
|
(take-while some?)
|
||||||
|
extract))))
|
||||||
|
|
||||||
|
(defn read-namespace [input-file]
|
||||||
|
(let [parse-ns-name (fn [forms]
|
||||||
|
(some->> forms
|
||||||
|
(filter (fn [form]
|
||||||
|
(and (= 'ns (first form))
|
||||||
|
(symbol? (second form)))))
|
||||||
|
first ;; ns form
|
||||||
|
second ;; namespace symbol
|
||||||
|
name))]
|
||||||
|
(forms input-file parse-ns-name)))
|
||||||
|
|
||||||
|
(defn ns->filename [namespace]
|
||||||
|
(-> namespace
|
||||||
|
(str/replace "." "/")
|
||||||
|
(str/replace "-" "_")
|
||||||
|
(str ".clj")))
|
||||||
|
|
||||||
|
(defn path-of-file [file]
|
||||||
|
(.getParent file))
|
||||||
|
|
||||||
|
(defn print-macro-expansion [input-file macro-params]
|
||||||
|
(binding [clojure.pprint/*print-pprint-dispatch* clojure.pprint/code-dispatch
|
||||||
|
clojure.pprint/*print-right-margin* 60
|
||||||
|
clojure.pprint/*print-miser-width* 20
|
||||||
|
*print-meta* (:print-meta macro-params true)]
|
||||||
|
(doseq [form (forms input-file)]
|
||||||
|
(pp/pprint (walk/macroexpand-all form))
|
||||||
|
(println))))
|
||||||
|
|
||||||
|
(defn compile-input [input-file {:keys [show-help] :as params}]
|
||||||
|
(let [working-dir (path-of-file input-file)
|
||||||
|
namespace (read-namespace input-file)
|
||||||
|
missing-namespace? (nil? namespace)
|
||||||
|
namespace (or namespace "sample")
|
||||||
|
compile-filename (io/file working-dir (ns->filename namespace))
|
||||||
|
compile-path (path-of-file compile-filename)
|
||||||
|
compiler-options (select-keys params
|
||||||
|
[:disable-locals-clearing
|
||||||
|
:direct-linking
|
||||||
|
:elide-meta])]
|
||||||
|
(.mkdirs (io/file working-dir "classes"))
|
||||||
|
(when compile-path
|
||||||
|
(.mkdirs (io/file compile-path)))
|
||||||
|
(with-open [out (io/writer (io/output-stream compile-filename))]
|
||||||
|
(when missing-namespace?
|
||||||
|
(let [ns-form (str "(ns " namespace ")")]
|
||||||
|
(println "Injecting namespace form on first line:" ns-form)
|
||||||
|
(.write out ns-form)))
|
||||||
|
(io/copy input-file out))
|
||||||
|
|
||||||
|
(when show-help
|
||||||
|
(when (seq *compiler-options*)
|
||||||
|
(println "*compiler-options* set via environment:" *compiler-options*))
|
||||||
|
(when (seq compiler-options)
|
||||||
|
(println "*compiler-options* set via flags:" compiler-options)))
|
||||||
|
(binding [*compiler-options* (merge *compiler-options* compiler-options)]
|
||||||
|
(compile (symbol namespace)))))
|
||||||
|
|
||||||
|
(let [[params positional ignored] (parse-command-line)
|
||||||
|
input-file (io/file (first positional))]
|
||||||
|
(if (:macro-expand params)
|
||||||
|
(print-macro-expansion input-file params)
|
||||||
|
(let [count-ignored (count ignored)]
|
||||||
|
(doseq [param ignored]
|
||||||
|
(println (format "unrecognized option '%s' ignored" param)))
|
||||||
|
(when (pos-int? count-ignored)
|
||||||
|
(println (format "%d warning%s found" count-ignored
|
||||||
|
(if (= 1 count-ignored) "" "s"))))
|
||||||
|
(when (or (:show-help params)
|
||||||
|
(pos-int? count-ignored))
|
||||||
|
(println help-text))
|
||||||
|
(compile-input input-file params))))
|
||||||
5
examples/clojure/default.clj
Normal file
5
examples/clojure/default.clj
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
;; Type your code here, or load an example.
|
||||||
|
(ns example)
|
||||||
|
|
||||||
|
(defn square [num]
|
||||||
|
(* num num))
|
||||||
@@ -1680,6 +1680,10 @@ export class BaseCompiler {
|
|||||||
return [{text: 'Internal error; unable to open output path'}];
|
return [{text: 'Internal error; unable to open output path'}];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async generateClojureMacroExpansion(inputFilename: string, options: string[]): Promise<ResultLine[]> {
|
||||||
|
return [{text: 'Clojure Macro Expansion not applicable to current compiler.'}];
|
||||||
|
}
|
||||||
|
|
||||||
async processHaskellExtraOutput(outpath: string, output: CompilationResult): Promise<ResultLine[]> {
|
async processHaskellExtraOutput(outpath: string, output: CompilationResult): Promise<ResultLine[]> {
|
||||||
if (output.code !== 0) {
|
if (output.code !== 0) {
|
||||||
return [{text: 'Failed to run compiler to get Haskell Core'}];
|
return [{text: 'Failed to run compiler to get Haskell Core'}];
|
||||||
@@ -2430,6 +2434,7 @@ export class BaseCompiler {
|
|||||||
const makeGnatDebugTree = backendOptions.produceGnatDebugTree && this.compiler.supportsGnatDebugViews;
|
const makeGnatDebugTree = backendOptions.produceGnatDebugTree && this.compiler.supportsGnatDebugViews;
|
||||||
const makeIr = backendOptions.produceIr && this.compiler.supportsIrView;
|
const makeIr = backendOptions.produceIr && this.compiler.supportsIrView;
|
||||||
const makeClangir = backendOptions.produceClangir && this.compiler.supportsClangirView;
|
const makeClangir = backendOptions.produceClangir && this.compiler.supportsClangirView;
|
||||||
|
const makeClojureMacroExp = backendOptions.produceClojureMacroExp && this.compiler.supportsClojureMacroExpView;
|
||||||
const makeOptPipeline = backendOptions.produceOptPipeline && this.compiler.optPipeline;
|
const makeOptPipeline = backendOptions.produceOptPipeline && this.compiler.optPipeline;
|
||||||
const makeRustMir = backendOptions.produceRustMir && this.compiler.supportsRustMirView;
|
const makeRustMir = backendOptions.produceRustMir && this.compiler.supportsRustMirView;
|
||||||
const makeRustMacroExp = backendOptions.produceRustMacroExp && this.compiler.supportsRustMacroExpView;
|
const makeRustMacroExp = backendOptions.produceRustMacroExp && this.compiler.supportsRustMacroExpView;
|
||||||
@@ -2448,6 +2453,7 @@ export class BaseCompiler {
|
|||||||
optPipelineResult,
|
optPipelineResult,
|
||||||
rustHirResult,
|
rustHirResult,
|
||||||
rustMacroExpResult,
|
rustMacroExpResult,
|
||||||
|
clojureMacroExpResult,
|
||||||
toolsResult,
|
toolsResult,
|
||||||
] = await Promise.all([
|
] = await Promise.all([
|
||||||
this.runCompiler(this.compiler.exe, options, inputFilenameSafe, execOptions, filters),
|
this.runCompiler(this.compiler.exe, options, inputFilenameSafe, execOptions, filters),
|
||||||
@@ -2468,6 +2474,7 @@ export class BaseCompiler {
|
|||||||
: undefined,
|
: undefined,
|
||||||
makeRustHir ? this.generateRustHir(inputFilename, options) : undefined,
|
makeRustHir ? this.generateRustHir(inputFilename, options) : undefined,
|
||||||
makeRustMacroExp ? this.generateRustMacroExpansion(inputFilename, options) : undefined,
|
makeRustMacroExp ? this.generateRustMacroExpansion(inputFilename, options) : undefined,
|
||||||
|
makeClojureMacroExp ? this.generateClojureMacroExpansion(inputFilename, options) : undefined,
|
||||||
Promise.all(
|
Promise.all(
|
||||||
this.runToolsOfType(
|
this.runToolsOfType(
|
||||||
tools,
|
tools,
|
||||||
@@ -2552,6 +2559,8 @@ export class BaseCompiler {
|
|||||||
asmResult.haskellStgOutput = haskellStgResult;
|
asmResult.haskellStgOutput = haskellStgResult;
|
||||||
asmResult.haskellCmmOutput = haskellCmmResult;
|
asmResult.haskellCmmOutput = haskellCmmResult;
|
||||||
|
|
||||||
|
asmResult.clojureMacroExpOutput = clojureMacroExpResult;
|
||||||
|
|
||||||
if (asmResult.code !== 0) {
|
if (asmResult.code !== 0) {
|
||||||
return [{...asmResult, asm: '<Compilation failed>'}, [], []];
|
return [{...asmResult, asm: '<Compilation failed>'}, [], []];
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -47,6 +47,7 @@ export {
|
|||||||
} from './clang.js';
|
} from './clang.js';
|
||||||
export {ClangCLCompiler} from './clangcl.js';
|
export {ClangCLCompiler} from './clangcl.js';
|
||||||
export {CleanCompiler} from './clean.js';
|
export {CleanCompiler} from './clean.js';
|
||||||
|
export {ClojureCompiler} from './clojure.js';
|
||||||
export {CLSPVCompiler} from './clspv.js';
|
export {CLSPVCompiler} from './clspv.js';
|
||||||
export {CMakeScriptCompiler} from './cmakescript.js';
|
export {CMakeScriptCompiler} from './cmakescript.js';
|
||||||
export {CoccinelleCCompiler, CoccinelleCPlusPlusCompiler} from './coccinelle.js';
|
export {CoccinelleCCompiler, CoccinelleCPlusPlusCompiler} from './coccinelle.js';
|
||||||
|
|||||||
@@ -701,6 +701,12 @@ export class JavaParser extends BaseParser {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export class ClojureParser extends BaseParser {
|
||||||
|
override async parse() {
|
||||||
|
return this.compiler;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
export class KotlinParser extends BaseParser {
|
export class KotlinParser extends BaseParser {
|
||||||
override async parse() {
|
override async parse() {
|
||||||
await this.getOptions('-help');
|
await this.getOptions('-help');
|
||||||
|
|||||||
155
lib/compilers/clojure.ts
Normal file
155
lib/compilers/clojure.ts
Normal file
@@ -0,0 +1,155 @@
|
|||||||
|
// Copyright (c) 2025, 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 fs from 'node:fs/promises';
|
||||||
|
import path from 'node:path';
|
||||||
|
import _ from 'underscore';
|
||||||
|
import type {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 type {ResultLine} from '../../types/resultline/resultline.interfaces.js';
|
||||||
|
import {CompilationEnvironment} from '../compilation-env.js';
|
||||||
|
import * as utils from '../utils.js';
|
||||||
|
import {ClojureParser} from './argument-parsers.js';
|
||||||
|
import {JavaCompiler} from './java.js';
|
||||||
|
|
||||||
|
export class ClojureCompiler extends JavaCompiler {
|
||||||
|
public compilerWrapperPath: string;
|
||||||
|
|
||||||
|
static override get key() {
|
||||||
|
return 'clojure';
|
||||||
|
}
|
||||||
|
|
||||||
|
javaHome: string;
|
||||||
|
|
||||||
|
constructor(compilerInfo: PreliminaryCompilerInfo, env: CompilationEnvironment) {
|
||||||
|
super(compilerInfo, env);
|
||||||
|
// Use invalid Clojure filename to avoid clashing with name determined by namespace
|
||||||
|
this.compileFilename = `example-source${this.lang.extensions[0]}`;
|
||||||
|
this.javaHome = this.compilerProps<string>(`compiler.${this.compiler.id}.java_home`);
|
||||||
|
this.compilerWrapperPath =
|
||||||
|
this.compilerProps('compilerWrapper', '') ||
|
||||||
|
utils.resolvePathFromAppRoot('etc', 'scripts', 'clojure_wrapper.clj');
|
||||||
|
this.compiler.supportsClojureMacroExpView = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
override getDefaultExecOptions() {
|
||||||
|
const execOptions = super.getDefaultExecOptions();
|
||||||
|
if (this.javaHome) {
|
||||||
|
execOptions.env.JAVA_HOME = this.javaHome;
|
||||||
|
}
|
||||||
|
|
||||||
|
return execOptions;
|
||||||
|
}
|
||||||
|
|
||||||
|
override filterUserOptions(userOptions: string[]) {
|
||||||
|
return userOptions.filter(option => {
|
||||||
|
// Filter out anything that looks like a Clojure source file
|
||||||
|
// that would confuse the wrapper.
|
||||||
|
// Also, don't allow users to specify macro expansion mode used
|
||||||
|
// internally.
|
||||||
|
return !option.match(/^.*\.clj$/) && option !== '--macro-expand';
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
override optionsForFilter(filters: ParseFiltersAndOutputOptions) {
|
||||||
|
// Forcibly enable javap
|
||||||
|
filters.binary = true;
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
|
||||||
|
override getArgumentParserClass() {
|
||||||
|
return ClojureParser;
|
||||||
|
}
|
||||||
|
|
||||||
|
override async readdir(dirPath: string): Promise<string[]> {
|
||||||
|
// Clojure requires recursive walk to find namespace-pathed class files
|
||||||
|
return fs.readdir(dirPath, {recursive: true});
|
||||||
|
}
|
||||||
|
|
||||||
|
async getClojureClasspathArgument(
|
||||||
|
dirPath: string,
|
||||||
|
compiler: string,
|
||||||
|
execOptions: ExecutionOptionsWithEnv,
|
||||||
|
): Promise<string[]> {
|
||||||
|
const pathOption = ['-Spath'];
|
||||||
|
const output = await this.exec(compiler, pathOption, execOptions);
|
||||||
|
const cp = dirPath + ':' + output.stdout.trim();
|
||||||
|
return ['-Scp', cp];
|
||||||
|
}
|
||||||
|
|
||||||
|
override async runCompiler(
|
||||||
|
compiler: string,
|
||||||
|
options: string[],
|
||||||
|
inputFilename: string,
|
||||||
|
execOptions: ExecutionOptionsWithEnv,
|
||||||
|
filters?: ParseFiltersAndOutputOptions,
|
||||||
|
): Promise<CompilationResult> {
|
||||||
|
if (!execOptions) {
|
||||||
|
execOptions = this.getDefaultExecOptions();
|
||||||
|
}
|
||||||
|
if (!execOptions.customCwd) {
|
||||||
|
execOptions.customCwd = path.dirname(inputFilename);
|
||||||
|
}
|
||||||
|
|
||||||
|
// The items in 'options' before the source file are user inputs.
|
||||||
|
const sourceFileOptionIndex = options.findIndex(option => {
|
||||||
|
return option.endsWith('.clj');
|
||||||
|
});
|
||||||
|
const userOptions = options.slice(0, sourceFileOptionIndex);
|
||||||
|
const classpathArgument = await this.getClojureClasspathArgument(execOptions.customCwd, compiler, execOptions);
|
||||||
|
const wrapperInvokeArgument = ['-M', this.compilerWrapperPath];
|
||||||
|
const clojureOptions = _.compact([
|
||||||
|
...classpathArgument,
|
||||||
|
...wrapperInvokeArgument,
|
||||||
|
...userOptions,
|
||||||
|
inputFilename,
|
||||||
|
]);
|
||||||
|
const result = await this.exec(compiler, clojureOptions, execOptions);
|
||||||
|
return {
|
||||||
|
...this.transformToCompilationResult(result, inputFilename),
|
||||||
|
languageId: this.getCompilerResultLanguageId(filters),
|
||||||
|
instructionSet: this.getInstructionSetFromCompilerArgs(options),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
override async generateClojureMacroExpansion(inputFilename: string, options: string[]): Promise<ResultLine[]> {
|
||||||
|
// The items in 'options' before the source file are user inputs.
|
||||||
|
const sourceFileOptionIndex = options.findIndex(option => {
|
||||||
|
return option.endsWith('.clj');
|
||||||
|
});
|
||||||
|
const userOptions = options.slice(0, sourceFileOptionIndex);
|
||||||
|
const clojureOptions = _.compact([...userOptions, '--macro-expand', inputFilename]);
|
||||||
|
const output = await this.runCompiler(
|
||||||
|
this.compiler.exe,
|
||||||
|
clojureOptions,
|
||||||
|
inputFilename,
|
||||||
|
this.getDefaultExecOptions(),
|
||||||
|
);
|
||||||
|
if (output.code !== 0) {
|
||||||
|
return [{text: `Failed to run compiler to get Clojure Macro Expansion`}, ...output.stderr];
|
||||||
|
}
|
||||||
|
return output.stdout;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -103,9 +103,15 @@ export class JavaCompiler extends BaseCompiler implements SimpleOutputFilenameCo
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async readdir(dirPath: string): Promise<string[]> {
|
||||||
|
// Separate method allows override to find classfiles
|
||||||
|
// that are not in root of dirPath
|
||||||
|
return fs.readdir(dirPath);
|
||||||
|
}
|
||||||
|
|
||||||
override async objdump(outputFilename: string, result: any, maxSize: number) {
|
override async objdump(outputFilename: string, result: any, maxSize: number) {
|
||||||
const dirPath = path.dirname(outputFilename);
|
const dirPath = path.dirname(outputFilename);
|
||||||
const files = await fs.readdir(dirPath);
|
const files = await this.readdir(dirPath);
|
||||||
logger.verbose('Class files: ', files);
|
logger.verbose('Class files: ', files);
|
||||||
const results = await Promise.all(
|
const results = await Promise.all(
|
||||||
files
|
files
|
||||||
|
|||||||
@@ -226,6 +226,17 @@ const definitions: Record<LanguageKey, LanguageDefinition> = {
|
|||||||
previewFilter: null,
|
previewFilter: null,
|
||||||
monacoDisassembly: null,
|
monacoDisassembly: null,
|
||||||
},
|
},
|
||||||
|
clojure: {
|
||||||
|
name: 'Clojure',
|
||||||
|
monaco: 'clojure',
|
||||||
|
extensions: ['.clj'],
|
||||||
|
alias: [],
|
||||||
|
logoFilename: 'clojure.svg',
|
||||||
|
logoFilenameDark: null,
|
||||||
|
formatter: null,
|
||||||
|
previewFilter: null,
|
||||||
|
monacoDisassembly: null,
|
||||||
|
},
|
||||||
cmake: {
|
cmake: {
|
||||||
name: 'CMake',
|
name: 'CMake',
|
||||||
monaco: 'cmake',
|
monaco: 'cmake',
|
||||||
|
|||||||
50
public/logos/clojure.svg
Normal file
50
public/logos/clojure.svg
Normal file
@@ -0,0 +1,50 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||||
|
<svg
|
||||||
|
xmlns:dc="http://purl.org/dc/elements/1.1/"
|
||||||
|
xmlns:cc="http://creativecommons.org/ns#"
|
||||||
|
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
||||||
|
xmlns:svg="http://www.w3.org/2000/svg"
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
version="1.1"
|
||||||
|
width="256"
|
||||||
|
height="256"
|
||||||
|
id="svg2">
|
||||||
|
<metadata
|
||||||
|
id="metadata4">
|
||||||
|
<rdf:RDF>
|
||||||
|
<cc:Work
|
||||||
|
rdf:about="">
|
||||||
|
<dc:format>image/svg+xml</dc:format>
|
||||||
|
<dc:type
|
||||||
|
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
|
||||||
|
<dc:title></dc:title>
|
||||||
|
</cc:Work>
|
||||||
|
</rdf:RDF>
|
||||||
|
</metadata>
|
||||||
|
<defs
|
||||||
|
id="defs6" />
|
||||||
|
<path
|
||||||
|
d="M 127.999,0 C 57.423,0 0,57.423 0,128.001 0,198.585 57.423,256.005 127.999,256.005 198.577,256.005 256,198.585 256,128.001 256,57.423 198.577,0 127.999,0"
|
||||||
|
id="path8"
|
||||||
|
style="fill:#ffffff" />
|
||||||
|
<path
|
||||||
|
d="m 123.318,130.303 c -1.15,2.492 -2.419,5.292 -3.733,8.272 -4.645,10.524 -9.789,23.33 -11.668,31.534 -0.675,2.922 -1.093,6.543 -1.085,10.558 0,1.588 0.085,3.257 0.22,4.957 6.567,2.413 13.66,3.74 21.067,3.753 6.743,-0.013 13.221,-1.127 19.284,-3.143 -1.425,-1.303 -2.785,-2.692 -4.023,-4.257 -8.22,-10.482 -12.806,-25.844 -20.062,-51.674"
|
||||||
|
id="path10"
|
||||||
|
style="fill:#91dc47" />
|
||||||
|
<path
|
||||||
|
d="m 92.97,78.225 c -15.699,11.064 -25.972,29.312 -26.011,49.992 0.039,20.371 10.003,38.383 25.307,49.493 3.754,-15.637 13.164,-29.955 27.275,-58.655 -0.838,-2.302 -1.793,-4.822 -2.862,-7.469 -3.909,-9.806 -9.551,-21.194 -14.586,-26.351 -2.567,-2.694 -5.682,-5.022 -9.123,-7.01"
|
||||||
|
id="path12"
|
||||||
|
style="fill:#91dc47" />
|
||||||
|
<path
|
||||||
|
d="m 181.394,198.367 c -8.1,-1.015 -14.785,-2.24 -20.633,-4.303 -9.836,4.884 -20.913,7.643 -32.642,7.643 -40.584,0 -73.483,-32.894 -73.488,-73.49 0,-22.027 9.704,-41.773 25.056,-55.24 -4.106,-0.992 -8.388,-1.571 -12.762,-1.563 -21.562,0.203 -44.323,12.136 -53.799,44.363 -0.886,4.691 -0.675,8.238 -0.675,12.442 0,63.885 51.791,115.676 115.671,115.676 39.122,0 73.682,-19.439 94.611,-49.169 -11.32,2.821 -22.206,4.17 -31.528,4.199 -3.494,0 -6.774,-0.187 -9.811,-0.558"
|
||||||
|
id="path14"
|
||||||
|
style="fill:#63b132" />
|
||||||
|
<path
|
||||||
|
d="m 159.658,175.953 c 0.714,0.354 2.333,0.932 4.586,1.571 15.157,-11.127 25.007,-29.05 25.046,-49.307 l -0.006,0 c -0.057,-33.771 -27.386,-61.096 -61.165,-61.163 -6.714,0.013 -13.164,1.121 -19.203,3.122 12.419,14.156 18.391,34.386 24.168,56.515 0.003,0.01 0.008,0.018 0.01,0.026 0.011,0.018 1.848,6.145 5.002,14.274 3.132,8.118 7.594,18.168 12.46,25.492 3.195,4.908 6.709,8.435 9.102,9.47"
|
||||||
|
id="path16"
|
||||||
|
style="fill:#90b4fe" />
|
||||||
|
<path
|
||||||
|
d="m 128.122,12.541 c -38.744,0 -73.016,19.073 -94.008,48.318 10.925,-6.842 22.08,-9.31 31.815,-9.222 13.446,0.039 24.017,4.208 29.089,7.06 1.225,0.706 2.388,1.466 3.527,2.247 9.05,-3.986 19.05,-6.215 29.574,-6.215 40.589,0.005 73.493,32.899 73.499,73.488 l -0.006,0 c 0,20.464 -8.37,38.967 -21.863,52.291 3.312,0.371 6.844,0.602 10.451,0.584 12.811,0.006 26.658,-2.821 37.039,-11.552 6.769,-5.702 12.44,-14.051 15.585,-26.569 0.615,-4.835 0.969,-9.75 0.969,-14.752 0,-63.882 -51.786,-115.678 -115.671,-115.678"
|
||||||
|
id="path18"
|
||||||
|
style="fill:#5881d8" />
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 3.1 KiB |
@@ -75,6 +75,7 @@ export const GNAT_DEBUG_TREE_VIEW_COMPONENT_NAME = 'gnatdebugtree' as const;
|
|||||||
export const GNAT_DEBUG_VIEW_COMPONENT_NAME = 'gnatdebug' as const;
|
export const GNAT_DEBUG_VIEW_COMPONENT_NAME = 'gnatdebug' as const;
|
||||||
export const RUST_MACRO_EXP_VIEW_COMPONENT_NAME = 'rustmacroexp' as const;
|
export const RUST_MACRO_EXP_VIEW_COMPONENT_NAME = 'rustmacroexp' as const;
|
||||||
export const RUST_HIR_VIEW_COMPONENT_NAME = 'rusthir' as const;
|
export const RUST_HIR_VIEW_COMPONENT_NAME = 'rusthir' as const;
|
||||||
|
export const CLOJURE_MACRO_EXP_VIEW_COMPONENT_NAME = 'clojuremacroexp' as const;
|
||||||
export const DEVICE_VIEW_COMPONENT_NAME = 'device' as const;
|
export const DEVICE_VIEW_COMPONENT_NAME = 'device' as const;
|
||||||
export const EXPLAIN_VIEW_COMPONENT_NAME = 'explain' as const;
|
export const EXPLAIN_VIEW_COMPONENT_NAME = 'explain' as const;
|
||||||
|
|
||||||
@@ -330,6 +331,15 @@ export type PopulatedRustHirViewState = StateWithId & {
|
|||||||
treeid: number;
|
treeid: number;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export type EmptyClojureMacroExpViewState = EmptyState;
|
||||||
|
export type PopulatedClojureMacroExpViewState = StateWithId & {
|
||||||
|
source: string;
|
||||||
|
clojureMacroExpOutput: unknown;
|
||||||
|
compilerName: string;
|
||||||
|
editorid: number;
|
||||||
|
treeid: number;
|
||||||
|
};
|
||||||
|
|
||||||
export type EmptyDeviceViewState = EmptyState;
|
export type EmptyDeviceViewState = EmptyState;
|
||||||
export type PopulatedDeviceViewState = StateWithId & {
|
export type PopulatedDeviceViewState = StateWithId & {
|
||||||
source: string;
|
source: string;
|
||||||
@@ -379,6 +389,7 @@ export interface ComponentStateMap {
|
|||||||
[GNAT_DEBUG_VIEW_COMPONENT_NAME]: EmptyGnatDebugViewState | PopulatedGnatDebugViewState;
|
[GNAT_DEBUG_VIEW_COMPONENT_NAME]: EmptyGnatDebugViewState | PopulatedGnatDebugViewState;
|
||||||
[RUST_MACRO_EXP_VIEW_COMPONENT_NAME]: EmptyRustMacroExpViewState | PopulatedRustMacroExpViewState;
|
[RUST_MACRO_EXP_VIEW_COMPONENT_NAME]: EmptyRustMacroExpViewState | PopulatedRustMacroExpViewState;
|
||||||
[RUST_HIR_VIEW_COMPONENT_NAME]: EmptyRustHirViewState | PopulatedRustHirViewState;
|
[RUST_HIR_VIEW_COMPONENT_NAME]: EmptyRustHirViewState | PopulatedRustHirViewState;
|
||||||
|
[CLOJURE_MACRO_EXP_VIEW_COMPONENT_NAME]: EmptyClojureMacroExpViewState | PopulatedClojureMacroExpViewState;
|
||||||
[DEVICE_VIEW_COMPONENT_NAME]: EmptyDeviceViewState | PopulatedDeviceViewState;
|
[DEVICE_VIEW_COMPONENT_NAME]: EmptyDeviceViewState | PopulatedDeviceViewState;
|
||||||
[EXPLAIN_VIEW_COMPONENT_NAME]: EmptyExplainViewState | PopulatedExplainViewState;
|
[EXPLAIN_VIEW_COMPONENT_NAME]: EmptyExplainViewState | PopulatedExplainViewState;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -32,6 +32,7 @@ import {
|
|||||||
AST_VIEW_COMPONENT_NAME,
|
AST_VIEW_COMPONENT_NAME,
|
||||||
CFG_VIEW_COMPONENT_NAME,
|
CFG_VIEW_COMPONENT_NAME,
|
||||||
CLANGIR_VIEW_COMPONENT_NAME,
|
CLANGIR_VIEW_COMPONENT_NAME,
|
||||||
|
CLOJURE_MACRO_EXP_VIEW_COMPONENT_NAME,
|
||||||
COMPILER_COMPONENT_NAME,
|
COMPILER_COMPONENT_NAME,
|
||||||
CONFORMANCE_VIEW_COMPONENT_NAME,
|
CONFORMANCE_VIEW_COMPONENT_NAME,
|
||||||
ComponentConfig,
|
ComponentConfig,
|
||||||
@@ -909,6 +910,38 @@ export function getRustHirViewWith(
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** Get an empty Clojure macro exp view component. */
|
||||||
|
export function getClojureMacroExpView(): ComponentConfig<typeof CLOJURE_MACRO_EXP_VIEW_COMPONENT_NAME> {
|
||||||
|
return {
|
||||||
|
type: 'component',
|
||||||
|
componentName: CLOJURE_MACRO_EXP_VIEW_COMPONENT_NAME,
|
||||||
|
componentState: {},
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Get a Clojure macro exp view with the given configuration. */
|
||||||
|
export function getClojureMacroExpViewWith(
|
||||||
|
id: number,
|
||||||
|
source: string,
|
||||||
|
clojureMacroExpOutput: unknown,
|
||||||
|
compilerName: string,
|
||||||
|
editorid: number,
|
||||||
|
treeid: number,
|
||||||
|
): ComponentConfig<typeof CLOJURE_MACRO_EXP_VIEW_COMPONENT_NAME> {
|
||||||
|
return {
|
||||||
|
type: 'component',
|
||||||
|
componentName: CLOJURE_MACRO_EXP_VIEW_COMPONENT_NAME,
|
||||||
|
componentState: {
|
||||||
|
id,
|
||||||
|
source,
|
||||||
|
clojureMacroExpOutput,
|
||||||
|
compilerName,
|
||||||
|
editorid,
|
||||||
|
treeid,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
/** Get an empty device view component. */
|
/** Get an empty device view component. */
|
||||||
export function getDeviceView(): ComponentConfig<typeof DEVICE_VIEW_COMPONENT_NAME> {
|
export function getDeviceView(): ComponentConfig<typeof DEVICE_VIEW_COMPONENT_NAME> {
|
||||||
return {
|
return {
|
||||||
@@ -1198,6 +1231,7 @@ function validateComponentState(componentName: string, state: any): boolean {
|
|||||||
case GNAT_DEBUG_VIEW_COMPONENT_NAME:
|
case GNAT_DEBUG_VIEW_COMPONENT_NAME:
|
||||||
case RUST_MACRO_EXP_VIEW_COMPONENT_NAME:
|
case RUST_MACRO_EXP_VIEW_COMPONENT_NAME:
|
||||||
case RUST_HIR_VIEW_COMPONENT_NAME:
|
case RUST_HIR_VIEW_COMPONENT_NAME:
|
||||||
|
case CLOJURE_MACRO_EXP_VIEW_COMPONENT_NAME:
|
||||||
case DEVICE_VIEW_COMPONENT_NAME:
|
case DEVICE_VIEW_COMPONENT_NAME:
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
|
|||||||
@@ -158,6 +158,8 @@ export type EventMap = {
|
|||||||
rustMacroExpViewOpened: (compilerId: number) => void;
|
rustMacroExpViewOpened: (compilerId: number) => void;
|
||||||
rustMirViewClosed: (compilerId: number) => void;
|
rustMirViewClosed: (compilerId: number) => void;
|
||||||
rustMirViewOpened: (compilerId: number) => void;
|
rustMirViewOpened: (compilerId: number) => void;
|
||||||
|
clojureMacroExpViewClosed: (compilerId: number) => void;
|
||||||
|
clojureMacroExpViewOpened: (compilerId: number) => void;
|
||||||
// TODO: There are no emitters for this event
|
// TODO: There are no emitters for this event
|
||||||
selectLine: (editorId: number, lineNumber: number) => void;
|
selectLine: (editorId: number, lineNumber: number) => void;
|
||||||
settingsChange: (newSettings: SiteSettings) => void;
|
settingsChange: (newSettings: SiteSettings) => void;
|
||||||
|
|||||||
@@ -30,6 +30,7 @@ import {
|
|||||||
AST_VIEW_COMPONENT_NAME,
|
AST_VIEW_COMPONENT_NAME,
|
||||||
CFG_VIEW_COMPONENT_NAME,
|
CFG_VIEW_COMPONENT_NAME,
|
||||||
CLANGIR_VIEW_COMPONENT_NAME,
|
CLANGIR_VIEW_COMPONENT_NAME,
|
||||||
|
CLOJURE_MACRO_EXP_VIEW_COMPONENT_NAME,
|
||||||
COMPILER_COMPONENT_NAME,
|
COMPILER_COMPONENT_NAME,
|
||||||
CONFORMANCE_VIEW_COMPONENT_NAME,
|
CONFORMANCE_VIEW_COMPONENT_NAME,
|
||||||
DEVICE_VIEW_COMPONENT_NAME,
|
DEVICE_VIEW_COMPONENT_NAME,
|
||||||
@@ -65,6 +66,7 @@ import {IdentifierSet} from './identifier-set.js';
|
|||||||
import {Ast as AstView} from './panes/ast-view.js';
|
import {Ast as AstView} from './panes/ast-view.js';
|
||||||
import {Cfg as CfgView} from './panes/cfg-view.js';
|
import {Cfg as CfgView} from './panes/cfg-view.js';
|
||||||
import {Clangir as ClangirView} from './panes/clangir-view.js';
|
import {Clangir as ClangirView} from './panes/clangir-view.js';
|
||||||
|
import {ClojureMacroExp as ClojureMacroExpView} from './panes/clojuremacroexp-view.js';
|
||||||
import {Compiler} from './panes/compiler.js';
|
import {Compiler} from './panes/compiler.js';
|
||||||
import {Conformance as ConformanceView} from './panes/conformance-view.js';
|
import {Conformance as ConformanceView} from './panes/conformance-view.js';
|
||||||
import {DeviceAsm as DeviceView} from './panes/device-view.js';
|
import {DeviceAsm as DeviceView} from './panes/device-view.js';
|
||||||
@@ -162,6 +164,9 @@ export class Hub {
|
|||||||
this.rustMacroExpViewFactory(c, s),
|
this.rustMacroExpViewFactory(c, s),
|
||||||
);
|
);
|
||||||
layout.registerComponent(RUST_HIR_VIEW_COMPONENT_NAME, (c: GLC, s: any) => this.rustHirViewFactory(c, s));
|
layout.registerComponent(RUST_HIR_VIEW_COMPONENT_NAME, (c: GLC, s: any) => this.rustHirViewFactory(c, s));
|
||||||
|
layout.registerComponent(CLOJURE_MACRO_EXP_VIEW_COMPONENT_NAME, (c: GLC, s: any) =>
|
||||||
|
this.clojureMacroExpViewFactory(c, s),
|
||||||
|
);
|
||||||
layout.registerComponent(GCC_DUMP_VIEW_COMPONENT_NAME, (c: GLC, s: any) => this.gccDumpViewFactory(c, s));
|
layout.registerComponent(GCC_DUMP_VIEW_COMPONENT_NAME, (c: GLC, s: any) => this.gccDumpViewFactory(c, s));
|
||||||
layout.registerComponent(CFG_VIEW_COMPONENT_NAME, (c: GLC, s: any) => this.cfgViewFactory(c, s));
|
layout.registerComponent(CFG_VIEW_COMPONENT_NAME, (c: GLC, s: any) => this.cfgViewFactory(c, s));
|
||||||
layout.registerComponent(CONFORMANCE_VIEW_COMPONENT_NAME, (c: GLC, s: any) =>
|
layout.registerComponent(CONFORMANCE_VIEW_COMPONENT_NAME, (c: GLC, s: any) =>
|
||||||
@@ -574,6 +579,13 @@ export class Hub {
|
|||||||
return new HaskellCmmView(this, container, state);
|
return new HaskellCmmView(this, container, state);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public clojureMacroExpViewFactory(
|
||||||
|
container: GoldenLayout.Container,
|
||||||
|
state: InferComponentState<ClojureMacroExpView>,
|
||||||
|
): ClojureMacroExpView {
|
||||||
|
return new ClojureMacroExpView(this, container, state);
|
||||||
|
}
|
||||||
|
|
||||||
public gccDumpViewFactory(container: GoldenLayout.Container, state: InferComponentState<GCCDumpView>): GCCDumpView {
|
public gccDumpViewFactory(container: GoldenLayout.Container, state: InferComponentState<GCCDumpView>): GCCDumpView {
|
||||||
return new GCCDumpView(this, container, state);
|
return new GCCDumpView(this, container, state);
|
||||||
}
|
}
|
||||||
|
|||||||
27
static/panes/clojuremacroexp-view.interfaces.ts
Normal file
27
static/panes/clojuremacroexp-view.interfaces.ts
Normal file
@@ -0,0 +1,27 @@
|
|||||||
|
// Copyright (c) 2025, 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 interface ClojureMacroExpState {
|
||||||
|
clojureMacroExpOutput: any;
|
||||||
|
}
|
||||||
129
static/panes/clojuremacroexp-view.ts
Normal file
129
static/panes/clojuremacroexp-view.ts
Normal file
@@ -0,0 +1,129 @@
|
|||||||
|
// Copyright (c) 2025, 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 {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 {ClojureMacroExpState} from './clojuremacroexp-view.interfaces.js';
|
||||||
|
import {MonacoPaneState} from './pane.interfaces.js';
|
||||||
|
import {MonacoPane} from './pane.js';
|
||||||
|
|
||||||
|
export class ClojureMacroExp extends MonacoPane<monaco.editor.IStandaloneCodeEditor, ClojureMacroExpState> {
|
||||||
|
constructor(hub: Hub, container: Container, state: ClojureMacroExpState & MonacoPaneState) {
|
||||||
|
super(hub, container, state);
|
||||||
|
if (state.clojureMacroExpOutput) {
|
||||||
|
this.showClojureMacroExpResults(state.clojureMacroExpOutput);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override getInitialHTML(): string {
|
||||||
|
return $('#clojuremacroexp').html();
|
||||||
|
}
|
||||||
|
|
||||||
|
override createEditor(editorRoot: HTMLElement): void {
|
||||||
|
this.editor = monaco.editor.create(
|
||||||
|
editorRoot,
|
||||||
|
extendConfig({
|
||||||
|
language: 'clojure',
|
||||||
|
readOnly: true,
|
||||||
|
glyphMargin: true,
|
||||||
|
lineNumbersMinChars: 3,
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
override getPrintName() {
|
||||||
|
return 'Clojure Macro Expansion Output';
|
||||||
|
}
|
||||||
|
|
||||||
|
override getDefaultPaneName(): string {
|
||||||
|
return 'Clojure Macro Expansion Viewer';
|
||||||
|
}
|
||||||
|
|
||||||
|
override registerCallbacks(): void {
|
||||||
|
const throttleFunction = _.throttle(
|
||||||
|
(event: monaco.editor.ICursorSelectionChangedEvent) => this.onDidChangeCursorSelection(event),
|
||||||
|
500,
|
||||||
|
);
|
||||||
|
this.editor.onDidChangeCursorSelection(event => throttleFunction(event));
|
||||||
|
this.eventHub.emit('clojureMacroExpViewOpened', this.compilerInfo.compilerId);
|
||||||
|
this.eventHub.emit('requestSettings');
|
||||||
|
}
|
||||||
|
|
||||||
|
override onCompileResult(compilerId: number, compiler: CompilerInfo, result: CompilationResult): void {
|
||||||
|
if (this.compilerInfo.compilerId !== compilerId) return;
|
||||||
|
if (result.clojureMacroExpOutput) {
|
||||||
|
this.showClojureMacroExpResults(result.clojureMacroExpOutput);
|
||||||
|
} else if (compiler.supportsClojureMacroExpView) {
|
||||||
|
this.showClojureMacroExpResults([{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.supportsClojureMacroExpView) {
|
||||||
|
this.showClojureMacroExpResults([
|
||||||
|
{
|
||||||
|
text: '<Clojure Macro Expansion output is not supported for this compiler>',
|
||||||
|
},
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
showClojureMacroExpResults(result: any[]): void {
|
||||||
|
this.editor
|
||||||
|
.getModel()
|
||||||
|
?.setValue(result.length ? _.pluck(result, 'text').join('\n') : '<No Clojure Macro Expansion 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('clojureMacroExpViewClosed', this.compilerInfo.compilerId);
|
||||||
|
this.editor.dispose();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -193,6 +193,7 @@ export class Compiler extends MonacoPane<monaco.editor.IStandaloneCodeEditor, Co
|
|||||||
private haskellCoreButton: JQuery<HTMLButtonElement>;
|
private haskellCoreButton: JQuery<HTMLButtonElement>;
|
||||||
private haskellStgButton: JQuery<HTMLButtonElement>;
|
private haskellStgButton: JQuery<HTMLButtonElement>;
|
||||||
private haskellCmmButton: JQuery<HTMLButtonElement>;
|
private haskellCmmButton: JQuery<HTMLButtonElement>;
|
||||||
|
private clojureMacroExpButton: JQuery<HTMLButtonElement>;
|
||||||
private gccDumpButton: JQuery<HTMLButtonElement>;
|
private gccDumpButton: JQuery<HTMLButtonElement>;
|
||||||
private cfgButton: JQuery<HTMLButtonElement>;
|
private cfgButton: JQuery<HTMLButtonElement>;
|
||||||
private explainButton: JQuery<HTMLButtonElement>;
|
private explainButton: JQuery<HTMLButtonElement>;
|
||||||
@@ -269,6 +270,7 @@ export class Compiler extends MonacoPane<monaco.editor.IStandaloneCodeEditor, Co
|
|||||||
private haskellCoreViewOpen: boolean;
|
private haskellCoreViewOpen: boolean;
|
||||||
private haskellStgViewOpen: boolean;
|
private haskellStgViewOpen: boolean;
|
||||||
private haskellCmmViewOpen: boolean;
|
private haskellCmmViewOpen: boolean;
|
||||||
|
private clojureMacroExpViewOpen: boolean;
|
||||||
private ppOptions: PPOptions;
|
private ppOptions: PPOptions;
|
||||||
private llvmIrOptions: LLVMIrBackendOptions;
|
private llvmIrOptions: LLVMIrBackendOptions;
|
||||||
private clangirOptions: ClangirBackendOptions;
|
private clangirOptions: ClangirBackendOptions;
|
||||||
@@ -623,6 +625,17 @@ export class Compiler extends MonacoPane<monaco.editor.IStandaloneCodeEditor, Co
|
|||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const createClojureMacroExpView = () => {
|
||||||
|
return Components.getClojureMacroExpViewWith(
|
||||||
|
this.id,
|
||||||
|
this.source,
|
||||||
|
this.lastResult?.clojureMacroExpOutput,
|
||||||
|
this.getCompilerName(),
|
||||||
|
this.sourceEditorId ?? 0,
|
||||||
|
this.sourceTreeId ?? 0,
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
const createGccDumpView = () => {
|
const createGccDumpView = () => {
|
||||||
return Components.getGccDumpViewWith(
|
return Components.getGccDumpViewWith(
|
||||||
this.id,
|
this.id,
|
||||||
@@ -896,6 +909,17 @@ export class Compiler extends MonacoPane<monaco.editor.IStandaloneCodeEditor, Co
|
|||||||
insertPoint.addChild(createRustHirView());
|
insertPoint.addChild(createRustHirView());
|
||||||
});
|
});
|
||||||
|
|
||||||
|
createDragSource(this.container.layoutManager, this.clojureMacroExpButton, () =>
|
||||||
|
createClojureMacroExpView(),
|
||||||
|
).on('dragStart', hidePaneAdder);
|
||||||
|
|
||||||
|
this.clojureMacroExpButton.on('click', () => {
|
||||||
|
const insertPoint =
|
||||||
|
this.hub.findParentRowOrColumn(this.container.parent) ||
|
||||||
|
this.container.layoutManager.root.contentItems[0];
|
||||||
|
insertPoint.addChild(createClojureMacroExpView());
|
||||||
|
});
|
||||||
|
|
||||||
createDragSource(this.container.layoutManager, this.gccDumpButton, () => createGccDumpView()).on(
|
createDragSource(this.container.layoutManager, this.gccDumpButton, () => createGccDumpView()).on(
|
||||||
'dragStart',
|
'dragStart',
|
||||||
hidePaneAdder,
|
hidePaneAdder,
|
||||||
@@ -1291,6 +1315,7 @@ export class Compiler extends MonacoPane<monaco.editor.IStandaloneCodeEditor, Co
|
|||||||
produceHaskellCore: this.haskellCoreViewOpen,
|
produceHaskellCore: this.haskellCoreViewOpen,
|
||||||
produceHaskellStg: this.haskellStgViewOpen,
|
produceHaskellStg: this.haskellStgViewOpen,
|
||||||
produceHaskellCmm: this.haskellCmmViewOpen,
|
produceHaskellCmm: this.haskellCmmViewOpen,
|
||||||
|
produceClojureMacroExp: this.clojureMacroExpViewOpen,
|
||||||
overrides: this.getCurrentState().overrides,
|
overrides: this.getCurrentState().overrides,
|
||||||
},
|
},
|
||||||
filters: this.getEffectiveFilters(),
|
filters: this.getEffectiveFilters(),
|
||||||
@@ -2186,6 +2211,21 @@ export class Compiler extends MonacoPane<monaco.editor.IStandaloneCodeEditor, Co
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
onClojureMacroExpViewOpened(id: number): void {
|
||||||
|
if (this.id === id) {
|
||||||
|
this.clojureMacroExpButton.prop('disabled', true);
|
||||||
|
this.clojureMacroExpViewOpen = true;
|
||||||
|
this.compile();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
onClojureMacroExpViewClosed(id: number): void {
|
||||||
|
if (this.id === id) {
|
||||||
|
this.clojureMacroExpButton.prop('disabled', false);
|
||||||
|
this.clojureMacroExpViewOpen = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
onGccDumpUIInit(id: number): void {
|
onGccDumpUIInit(id: number): void {
|
||||||
if (this.id === id) {
|
if (this.id === id) {
|
||||||
this.compile();
|
this.compile();
|
||||||
@@ -2374,6 +2414,7 @@ export class Compiler extends MonacoPane<monaco.editor.IStandaloneCodeEditor, Co
|
|||||||
this.haskellCoreButton = this.domRoot.find('.btn.view-haskellCore');
|
this.haskellCoreButton = this.domRoot.find('.btn.view-haskellCore');
|
||||||
this.haskellStgButton = this.domRoot.find('.btn.view-haskellStg');
|
this.haskellStgButton = this.domRoot.find('.btn.view-haskellStg');
|
||||||
this.haskellCmmButton = this.domRoot.find('.btn.view-haskellCmm');
|
this.haskellCmmButton = this.domRoot.find('.btn.view-haskellCmm');
|
||||||
|
this.clojureMacroExpButton = this.domRoot.find('.btn.view-clojuremacroexp');
|
||||||
this.gccDumpButton = this.domRoot.find('.btn.view-gccdump');
|
this.gccDumpButton = this.domRoot.find('.btn.view-gccdump');
|
||||||
this.cfgButton = this.domRoot.find('.btn.view-cfg');
|
this.cfgButton = this.domRoot.find('.btn.view-cfg');
|
||||||
this.explainButton = this.domRoot.find('.btn.view-explain');
|
this.explainButton = this.domRoot.find('.btn.view-explain');
|
||||||
@@ -2657,6 +2698,7 @@ export class Compiler extends MonacoPane<monaco.editor.IStandaloneCodeEditor, Co
|
|||||||
this.haskellCmmButton.prop('disabled', this.haskellCmmViewOpen);
|
this.haskellCmmButton.prop('disabled', this.haskellCmmViewOpen);
|
||||||
this.rustMacroExpButton.prop('disabled', this.rustMacroExpViewOpen);
|
this.rustMacroExpButton.prop('disabled', this.rustMacroExpViewOpen);
|
||||||
this.rustHirButton.prop('disabled', this.rustHirViewOpen);
|
this.rustHirButton.prop('disabled', this.rustHirViewOpen);
|
||||||
|
this.clojureMacroExpButton.prop('disabled', this.clojureMacroExpViewOpen);
|
||||||
this.gccDumpButton.prop('disabled', this.gccDumpViewOpen);
|
this.gccDumpButton.prop('disabled', this.gccDumpViewOpen);
|
||||||
this.gnatDebugTreeButton.prop('disabled', this.gnatDebugTreeViewOpen);
|
this.gnatDebugTreeButton.prop('disabled', this.gnatDebugTreeViewOpen);
|
||||||
this.gnatDebugButton.prop('disabled', this.gnatDebugViewOpen);
|
this.gnatDebugButton.prop('disabled', this.gnatDebugViewOpen);
|
||||||
@@ -2677,6 +2719,7 @@ export class Compiler extends MonacoPane<monaco.editor.IStandaloneCodeEditor, Co
|
|||||||
this.haskellCoreButton.toggle(!!this.compiler.supportsHaskellCoreView);
|
this.haskellCoreButton.toggle(!!this.compiler.supportsHaskellCoreView);
|
||||||
this.haskellStgButton.toggle(!!this.compiler.supportsHaskellStgView);
|
this.haskellStgButton.toggle(!!this.compiler.supportsHaskellStgView);
|
||||||
this.haskellCmmButton.toggle(!!this.compiler.supportsHaskellCmmView);
|
this.haskellCmmButton.toggle(!!this.compiler.supportsHaskellCmmView);
|
||||||
|
this.clojureMacroExpButton.toggle(!!this.compiler.supportsClojureMacroExpView);
|
||||||
// TODO(jeremy-rifkin): Disable cfg button when binary mode is set?
|
// TODO(jeremy-rifkin): Disable cfg button when binary mode is set?
|
||||||
this.cfgButton.toggle(!!this.compiler.supportsCfg);
|
this.cfgButton.toggle(!!this.compiler.supportsCfg);
|
||||||
this.gccDumpButton.toggle(!!this.compiler.supportsGccDump);
|
this.gccDumpButton.toggle(!!this.compiler.supportsGccDump);
|
||||||
@@ -2853,6 +2896,8 @@ export class Compiler extends MonacoPane<monaco.editor.IStandaloneCodeEditor, Co
|
|||||||
this.eventHub.on('haskellStgViewClosed', this.onHaskellStgViewClosed, this);
|
this.eventHub.on('haskellStgViewClosed', this.onHaskellStgViewClosed, this);
|
||||||
this.eventHub.on('haskellCmmViewOpened', this.onHaskellCmmViewOpened, this);
|
this.eventHub.on('haskellCmmViewOpened', this.onHaskellCmmViewOpened, this);
|
||||||
this.eventHub.on('haskellCmmViewClosed', this.onHaskellCmmViewClosed, this);
|
this.eventHub.on('haskellCmmViewClosed', this.onHaskellCmmViewClosed, this);
|
||||||
|
this.eventHub.on('clojureMacroExpViewOpened', this.onClojureMacroExpViewOpened, this);
|
||||||
|
this.eventHub.on('clojureMacroExpViewClosed', this.onClojureMacroExpViewClosed, this);
|
||||||
this.eventHub.on('outputOpened', this.onOutputOpened, this);
|
this.eventHub.on('outputOpened', this.onOutputOpened, this);
|
||||||
this.eventHub.on('outputClosed', this.onOutputClosed, this);
|
this.eventHub.on('outputClosed', this.onOutputClosed, this);
|
||||||
|
|
||||||
|
|||||||
@@ -37,6 +37,7 @@ export enum DiffType {
|
|||||||
RustMirOutput = 10,
|
RustMirOutput = 10,
|
||||||
RustMacroExpOutput = 11,
|
RustMacroExpOutput = 11,
|
||||||
RustHirOutput = 12,
|
RustHirOutput = 12,
|
||||||
|
ClojureMacroExpOutput = 13,
|
||||||
}
|
}
|
||||||
|
|
||||||
export type DiffState = {
|
export type DiffState = {
|
||||||
|
|||||||
@@ -170,6 +170,11 @@ class DiffStateObject {
|
|||||||
{text: "<select 'Add new...' → 'Rust HIR' in this compiler's pane>"},
|
{text: "<select 'Add new...' → 'Rust HIR' in this compiler's pane>"},
|
||||||
];
|
];
|
||||||
break;
|
break;
|
||||||
|
case DiffType.ClojureMacroExpOutput:
|
||||||
|
output = this.result.clojureMacroExpOutput || [
|
||||||
|
{text: "<select 'Add new...' → 'Clojure Macro Expansion' in this compiler's pane>"},
|
||||||
|
];
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
this.model.setValue(output.map(x => x.text).join('\n'));
|
this.model.setValue(output.map(x => x.text).join('\n'));
|
||||||
@@ -473,6 +478,9 @@ export class Diff extends MonacoPane<monaco.editor.IStandaloneDiffEditor, DiffSt
|
|||||||
if (compiler.supportsRustHirView) {
|
if (compiler.supportsRustHirView) {
|
||||||
options.push({id: DiffType.RustHirOutput.toString(), name: 'Rust HIR'});
|
options.push({id: DiffType.RustHirOutput.toString(), name: 'Rust HIR'});
|
||||||
}
|
}
|
||||||
|
if (compiler.supportsClojureMacroExpView) {
|
||||||
|
options.push({id: DiffType.ClojureMacroExpOutput.toString(), name: 'Clojure Macro Expansion'});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const lhsoptions = this.getDiffableOptions(this.selectize.lhs, lhsextraoptions);
|
const lhsoptions = this.getDiffableOptions(this.selectize.lhs, lhsextraoptions);
|
||||||
|
|||||||
68
test/clojure-tests.ts
Normal file
68
test/clojure-tests.ts
Normal file
@@ -0,0 +1,68 @@
|
|||||||
|
// Copyright (c) 2025, 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 {beforeAll, describe, expect, it} from 'vitest';
|
||||||
|
|
||||||
|
import {CompilationEnvironment} from '../lib/compilation-env.js';
|
||||||
|
import {ClojureCompiler} from '../lib/compilers/index.js';
|
||||||
|
import {CompilerInfo} from '../types/compiler.interfaces.js';
|
||||||
|
|
||||||
|
import {makeCompilationEnvironment} from './utils.js';
|
||||||
|
|
||||||
|
const languages = {
|
||||||
|
clojure: {id: 'clojure'},
|
||||||
|
};
|
||||||
|
|
||||||
|
const info = {
|
||||||
|
exe: null,
|
||||||
|
remote: true,
|
||||||
|
lang: languages.clojure.id,
|
||||||
|
} as unknown as CompilerInfo;
|
||||||
|
|
||||||
|
describe('Basic compiler setup', () => {
|
||||||
|
let env: CompilationEnvironment;
|
||||||
|
|
||||||
|
beforeAll(() => {
|
||||||
|
env = makeCompilationEnvironment({languages});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('Should not crash on instantiation', () => {
|
||||||
|
new ClojureCompiler(info, env);
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('Forbidden compiler arguments', () => {
|
||||||
|
it('ClojureCompiler should not allow a parameter with .clj suffix', () => {
|
||||||
|
// The ClojureCompiler filters out any option that looks like a source file.
|
||||||
|
// Other invalid options are filtered out by the wrapper.
|
||||||
|
const compiler = new ClojureCompiler(info, env);
|
||||||
|
expect(compiler.filterUserOptions(['hello', 'hello.clj', '-d'])).toEqual(['hello', '-d']);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('ClojureCompiler should not allow user to set --macro-expand parameter', () => {
|
||||||
|
// --macro-expand is used internally to produce macro expanded output.
|
||||||
|
const compiler = new ClojureCompiler(info, env);
|
||||||
|
expect(compiler.filterUserOptions(['--macro-expand', '--something-else'])).toEqual(['--something-else']);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
@@ -116,6 +116,7 @@ export type CompilationRequestOptions = {
|
|||||||
produceHaskellCore?: boolean;
|
produceHaskellCore?: boolean;
|
||||||
produceHaskellStg?: boolean;
|
produceHaskellStg?: boolean;
|
||||||
produceHaskellCmm?: boolean;
|
produceHaskellCmm?: boolean;
|
||||||
|
produceClojureMacroExp?: boolean;
|
||||||
cmakeArgs?: string;
|
cmakeArgs?: string;
|
||||||
customOutputFilename?: string;
|
customOutputFilename?: string;
|
||||||
overrides?: ConfiguredOverrides;
|
overrides?: ConfiguredOverrides;
|
||||||
@@ -213,6 +214,8 @@ export type CompilationResult = {
|
|||||||
haskellStgOutput?: ResultLine[];
|
haskellStgOutput?: ResultLine[];
|
||||||
haskellCmmOutput?: ResultLine[];
|
haskellCmmOutput?: ResultLine[];
|
||||||
|
|
||||||
|
clojureMacroExpOutput?: ResultLine[];
|
||||||
|
|
||||||
forceBinaryView?: boolean;
|
forceBinaryView?: boolean;
|
||||||
|
|
||||||
artifacts?: Artifact[];
|
artifacts?: Artifact[];
|
||||||
|
|||||||
@@ -90,6 +90,7 @@ export type CompilerInfo = {
|
|||||||
supportsHaskellCoreView?: boolean;
|
supportsHaskellCoreView?: boolean;
|
||||||
supportsHaskellStgView?: boolean;
|
supportsHaskellStgView?: boolean;
|
||||||
supportsHaskellCmmView?: boolean;
|
supportsHaskellCmmView?: boolean;
|
||||||
|
supportsClojureMacroExpView?: boolean;
|
||||||
supportsCfg?: boolean;
|
supportsCfg?: boolean;
|
||||||
supportsGnatDebugViews?: boolean;
|
supportsGnatDebugViews?: boolean;
|
||||||
supportsLibraryCodeFilter?: boolean;
|
supportsLibraryCodeFilter?: boolean;
|
||||||
|
|||||||
@@ -36,6 +36,7 @@ export type LanguageKey =
|
|||||||
| 'circle'
|
| 'circle'
|
||||||
| 'circt'
|
| 'circt'
|
||||||
| 'clean'
|
| 'clean'
|
||||||
|
| 'clojure'
|
||||||
| 'cmake'
|
| 'cmake'
|
||||||
| 'cmakescript'
|
| 'cmakescript'
|
||||||
| 'cobol'
|
| 'cobol'
|
||||||
|
|||||||
@@ -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-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-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-haskellCmm", "GHC Cmm", "Show GHC Cmm Intermediate Representation", "fas fa-water")
|
||||||
|
+newPaneButton("view-clojuremacroexp", "Clojure Macro Expansion", "Show Clojure macro expansion", "fas fa-arrows-alt")
|
||||||
+newPaneButton("view-gccdump", "GCC Tree/RTL", "Show GCC Tree/RTL dump", "fas fa-tree")
|
+newPaneButton("view-gccdump", "GCC Tree/RTL", "Show GCC Tree/RTL dump", "fas fa-tree")
|
||||||
+newPaneButton("view-gnatdebugtree", "GNAT Debug Tree", "Show GNAT debug tree", "fas fa-tree")
|
+newPaneButton("view-gnatdebugtree", "GNAT Debug Tree", "Show GNAT debug tree", "fas fa-tree")
|
||||||
+newPaneButton("view-gnatdebug", "GNAT Debug Expanded Code", "Show GNAT debug expanded code", "fas fa-tree")
|
+newPaneButton("view-gnatdebug", "GNAT Debug Expanded Code", "Show GNAT debug expanded code", "fas fa-tree")
|
||||||
|
|||||||
@@ -85,3 +85,5 @@ mixin monacopane(id)
|
|||||||
+monacopane("rustmacroexp")
|
+monacopane("rustmacroexp")
|
||||||
|
|
||||||
+monacopane("rusthir")
|
+monacopane("rusthir")
|
||||||
|
|
||||||
|
+monacopane("clojuremacroexp")
|
||||||
|
|||||||
@@ -78,6 +78,7 @@ const plugins: Webpack.WebpackPluginInstance[] = [
|
|||||||
'scheme',
|
'scheme',
|
||||||
'objective-c',
|
'objective-c',
|
||||||
'elixir',
|
'elixir',
|
||||||
|
'clojure',
|
||||||
],
|
],
|
||||||
filename: isDev ? '[name].worker.js' : `[name]${webpackJsHack}worker.[contenthash].js`,
|
filename: isDev ? '[name].worker.js' : `[name]${webpackJsHack}worker.[contenthash].js`,
|
||||||
}),
|
}),
|
||||||
|
|||||||
Reference in New Issue
Block a user