aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--CODEOWNERS6
-rw-r--r--Makefile4
-rw-r--r--README.md49
-rw-r--r--Roadmap.md8
-rwxr-xr-xapp.js343
-rw-r--r--etc/config/builtin.d.properties2
-rw-r--r--etc/config/builtin.default.properties1
-rw-r--r--etc/config/builtin.go.properties2
-rw-r--r--etc/config/builtin.haskell.properties2
-rw-r--r--etc/config/builtin.ispc.properties2
-rw-r--r--etc/config/builtin.pascal.properties2
-rw-r--r--etc/config/builtin.rust.properties2
-rw-r--r--etc/config/builtin.swift.properties2
-rw-r--r--etc/config/c++.amazon.properties1
-rw-r--r--etc/config/c++.cppx.properties1
-rw-r--r--etc/config/c++.danger.properties1
-rw-r--r--etc/config/c++.defaults.properties1
-rw-r--r--etc/config/c.amazon.properties6
-rw-r--r--etc/config/c.defaults.properties10
-rw-r--r--etc/config/compiler-explorer.defaults.properties2
-rw-r--r--etc/config/compiler-explorer.wud-mgodbolt01.properties1
-rw-r--r--etc/config/cppx.amazon.properties13
-rw-r--r--etc/config/cppx.defaults.properties9
-rw-r--r--etc/config/d.amazon.properties1
-rw-r--r--etc/config/d.defaults.properties1
-rw-r--r--etc/config/go.amazon.properties2
-rw-r--r--etc/config/go.defaults.properties1
-rw-r--r--etc/config/go.lud-mgodbolt01.properties3
-rw-r--r--etc/config/haskell.defaults.properties1
-rw-r--r--etc/config/ispc.defaults.properties1
-rw-r--r--etc/config/pascal.defaults.properties1
-rw-r--r--etc/config/rust.amazon.properties1
-rw-r--r--etc/config/rust.defaults.properties1
-rw-r--r--etc/config/swift.defaults.properties2
-rwxr-xr-xetc/scripts/pre-commit1
-rw-r--r--examples/c++/default.cpp (renamed from views/example.cpp)2
-rw-r--r--examples/c/Max_array.c5
-rw-r--r--examples/c/Sum_over_array.c7
-rw-r--r--examples/c/default.c4
-rw-r--r--examples/cppx/Interface.cpp61
-rw-r--r--examples/cppx/default.cpp (renamed from views/example.cppx)0
-rw-r--r--examples/d/default.d (renamed from views/example.d)0
-rw-r--r--examples/go/default.go (renamed from views/example.go)0
-rw-r--r--examples/haskell/default.hs (renamed from views/example.hs)0
-rw-r--r--examples/ispc/default.ispc (renamed from views/example.ispc)0
-rw-r--r--examples/pascal/Max_array.pas (renamed from examples/pascal/max_array.pas)0
-rw-r--r--examples/pascal/Sum_over_array.pas (renamed from examples/pascal/sum_over_array.pas)0
-rw-r--r--examples/pascal/default.pas (renamed from views/example.pas)0
-rw-r--r--examples/rust/default.rs (renamed from views/example.rs)2
-rw-r--r--examples/swift/default.swift (renamed from views/example.swift)0
-rw-r--r--lib/base-compiler.js37
-rw-r--r--lib/compilation-env.js18
-rw-r--r--lib/compile-handler.js109
-rw-r--r--lib/compilers/WSL-CL.js6
-rw-r--r--lib/compilers/Wine-CL.js8
-rw-r--r--lib/compilers/default.js4
-rw-r--r--lib/compilers/golang.js6
-rw-r--r--lib/compilers/haskell.js4
-rw-r--r--lib/compilers/ispc.js4
-rw-r--r--lib/compilers/ldc.js4
-rw-r--r--lib/compilers/pascal.js5
-rw-r--r--lib/compilers/rust.js4
-rw-r--r--lib/compilers/swift.js4
-rw-r--r--lib/languages.js116
-rw-r--r--lib/sources/builtin.js42
-rw-r--r--static/ast-view.js1
-rw-r--r--static/compiler-service.js26
-rw-r--r--static/compiler.js302
-rw-r--r--static/conformance-view.js29
-rw-r--r--static/editor.js197
-rw-r--r--static/explorer.css4
-rw-r--r--static/hub.js10
-rw-r--r--static/loadSave.js48
-rw-r--r--static/main.js7
-rw-r--r--static/themes/dark/theme-dark.css5
-rw-r--r--test/builtin-tests.js68
-rw-r--r--test/lang-tests.js47
-rw-r--r--views/head.pug21
-rw-r--r--views/index.pug24
-rw-r--r--views/sitemap.pug27
-rw-r--r--views/templates.pug2
81 files changed, 1110 insertions, 646 deletions
diff --git a/CODEOWNERS b/CODEOWNERS
index d1e7e7673..6abaf69c0 100644
--- a/CODEOWNERS
+++ b/CODEOWNERS
@@ -8,8 +8,13 @@
static/conformance-view.js @RabsRincon
static/cppp-mode.js @RabsRincon
static/themes.js @RabsRincon
+static/editor.js @RabsRincon
+static/compiler.js @RabsRincon
lib/asm-docs.js @RabsRincon
lib/asm-docs-api.js @RabsRincon
+lib/languages.js @RabsRincon
+lib/base-compiler.js @RabsRincon
+lib/compilers/ @RabsRincon
views/sitemap.pug @RabsRincon
@@ -21,7 +26,6 @@ lib/compilers/argument-parsers.js @jaredwy
static/ast-view.js @TartanLlama
static/haskell-mode.js @TartanLlama
lib/compilers/haskell.js @TartanLlama
-views/example.hs @TartanLlama
# cppchedy
static/cfg-view.js @cppchedy
diff --git a/Makefile b/Makefile
index 61252e890..8db70a908 100644
--- a/Makefile
+++ b/Makefile
@@ -72,8 +72,6 @@ lint: $(NODE_MODULES)
$(NODE) ./node_modules/.bin/jshint --config etc/jshintrc.server app.js $(shell find lib test -name '*.js')
$(NODE) ./node_modules/.bin/jshint --config etc/jshintrc.client $(shell find static -name '*.js' -not -path 'static/ext/*' -not -path static/analytics.js)
-LANG:=C++
-
node_modules: $(NODE_MODULES)
bower_modules: $(BOWER_MODULES)
@@ -90,7 +88,7 @@ clean:
$(MAKE) -C c-preload clean
run: prereqs
- $(NODE) ./node_modules/.bin/supervisor -w app.js,lib,etc/config -e 'js|node|properties' --exec $(NODE) $(NODE_ARGS) -- ./app.js --language $(LANG) $(EXTRA_ARGS)
+ $(NODE) ./node_modules/.bin/supervisor -w app.js,lib,etc/config -e 'js|node|properties' --exec $(NODE) $(NODE_ARGS) -- ./app.js $(EXTRA_ARGS)
HASH := $(shell git rev-parse HEAD)
dist: prereqs
diff --git a/README.md b/README.md
index 1d87a4adc..93fc7f4da 100644
--- a/README.md
+++ b/README.md
@@ -4,22 +4,14 @@
Compiler Explorer
------------
-Compiler Explorer is an interactive compiler. The left-hand pane shows editable C/C++/Rust/Go/D/Haskell/Swift code. The right, the
-assembly output of having compiled the code with a given compiler and settings. Multiple compilers are supported, and
+Compiler Explorer is an interactive compiler. The left-hand pane shows editable C, C++, Rust, Go, D, Haskell, Swift and Pascal code.
+The right, the assembly output of having compiled the code with a given compiler and settings. Multiple compilers are supported, and
the UI layout is configurable (the [Golden Layout](https://www.golden-layout.com/) library is used for this).
There is also an ispc compiler for a C variant with extensions for SPMD.
-Try out one of the demo sites: [C++][cpp], [Rust][rust], [D][d], [Go][go], [Haskell][haskell], [Swift][swift], [ispc][ispc].
+Try out at [godbolt.org](https://godbolt.org)
-[cpp]: https://gcc.godbolt.org/ "Compiler Explorer for C++"
-[rust]: https://rust.godbolt.org/ "Compiler Explorer for Rust"
-[d]: https://d.godbolt.org/ "Compiler Explorer for D"
-[go]: https://go.godbolt.org/ "Compiler Explorer for Go"
-[ispc]: https://ispc.godbolt.org/ "Compiler Explorer for ispc"
-[haskell]: https://haskell.godbolt.org/ "Compiler Explorer for Haskell"
-[swift]: https://swift.godbolt.org/ "Compiler Explorer for Swift"
-
-You can support this [this project on Patreon](https://patreon.com/mattgodbolt).
+You can support [this project on Patreon](https://patreon.com/mattgodbolt).
##### Contact us
@@ -47,12 +39,6 @@ Feel free to raise an issue on [github](https://github.com/mattgodbolt/compiler-
There's now a [Road map](Roadmap.md) that gives a little insight into future plans for Compiler Explorer.
-### Credits
-
-Compiler Explorer is maintained by [Matt Godbolt](http://xania.org), [Rubén Rincón](https://github.com/RabsRincon) and [Simon Brand](https://blog.tartanllama.xyz/).
-Multiple compiler and difference view initially implemented by [Gabriel Devillers](https://github.com/voxelf),
-while working for [Kalray](http://www.kalrayinc.com/). Clang optview output by [Jared Wyles](https://github.com/jaredwy).
-
### RESTful API
There's a simple restful API that can be used to do compiles to asm and to list compilers. In general
@@ -65,10 +51,21 @@ future (for the main Compiler Explorer site anyway).
The following endpoints are defined:
+#### `GET /api/languages` - return a list of languages
+
+Returns a list of the currently supported languages, as pairs of languages IDs and their names.
+
#### `GET /api/compilers` - return a list of compilers
-Returns a list of compilers. In text form, there's a simple formatting of the ID of the compiler and its
-description. In JSON, all the information is returned as an array of compilers, with the `id` key being the
+Returns a list of compilers. In text form, there's a simple formatting of the ID of the compiler, its
+description and its languge ID. In JSON, all the information is returned as an array of compilers, with the `id` key being the
+primary identifier of each compiler.
+
+
+#### `GET /api/compilers/<language-id>` - return a list of compilers with mstching language
+
+Returns a list of compilers for the provided language id. In text form, there's a simple formatting of the ID of the compiler, its
+description and its languge ID. In JSON, all the information is returned as an array of compilers, with the `id` key being the
primary identifier of each compiler.
#### `POST /api/compiler/<compiler-id>/compile` - perform a compilation
@@ -151,3 +148,15 @@ Optional values are marked with a '**'
}
}
```
+
+### Credits
+
+Compiler Explorer is maintained by [Matt Godbolt](http://xania.org), [Rubén Rincón](https://github.com/RabsRincon) and [Simon Brand](https://blog.tartanllama.xyz/).
+- [Gabriel Devillers](https://github.com/voxelf) - Initial multiple compilers and difference view (while working for [Kalray](http://www.kalrayinc.com/)).
+- [Jared Wyles](https://github.com/jaredwy) - Clang OPT Output view and maintenance
+- [Johan Engelen](https://github.com/JohanEngelen) - D support and its maintenance
+- [Chedy Najjar](https://github.com/CppChedy) - CFG View and maintenance
+- [Patrick Quist](https://github.com/partouf) - Pascal support
+- [Joshua Sheard](https://github.com/jsheard) - ISPC support
+- [Marc Poulhiès](https://github.com/dkm) - GCC Dumps view
+- [Andrew Pardoe](https://github.com/AndrewPardoe) - WSL-CL support
diff --git a/Roadmap.md b/Roadmap.md
index 3909046cf..9bbb50444 100644
--- a/Roadmap.md
+++ b/Roadmap.md
@@ -1,9 +1,9 @@
# Compiler Explorer Road Map
-CE was started in 2012 to serve my needs at [my company](https:/drw.com) to show how
+CE was started in 2012 to serve my needs at [my company](https://drw.com) to show how
C++ constructs translated to assembly code. It started out as a `tmux` session with `vi` running in one
pane and `watch gcc -S foo.cc -o -` running in the other. Since then, it became a public website
-serving the C++, Rust, Go, Haskell, Ispc and D communities and performs around 20,000 compilations per day.
+serving the C++, Rust, Go, Haskell, Ispc, D, Swift and Pascal communities and performs around 20,000 compilations per day.
This document is an attempt to capture thoughts on the future direction of Compiler Explorer.
@@ -53,7 +53,8 @@ development is done on a laptop during a commute (with little or no internet acc
## Priorities
-Above all, the priority is to keep the main CE site up, stable and dependable.
+Above all, the priority is to keep the main CE site up, stable and dependable.
+That also means that URLs should live forever once they are created
## Non-goals
CE will remain ad-free, open-source and non-commercial. There's no plans at all to add "freemium" content,
@@ -66,6 +67,7 @@ With all this in mind, the tentative goals for 2017 are:
- [X] Move to the Monaco editor
- [X] Implement diff view
- [X] Come up with a decent secure solution for code execution
+- [X] Unify every language
- [ ] Design an API that can handle remote code execution and download needs
- [ ] Implement remote execution UIs
diff --git a/app.js b/app.js
index 78d1a867d..c2a1db02b 100755
--- a/app.js
+++ b/app.js
@@ -45,7 +45,6 @@ const nopt = require('nopt'),
var opts = nopt({
'env': [String, Array],
'rootDir': [String],
- 'language': [String],
'host': [String],
'port': [Number],
'propDebug': [Boolean],
@@ -82,7 +81,6 @@ else if (process.env.wsl) {
// Set default values for omitted arguments
var rootDir = opts.rootDir || './etc';
-var language = opts.language || "C++";
var env = opts.env || ['dev'];
var hostname = opts.host;
var port = opts.port || 10240;
@@ -104,7 +102,6 @@ var fetchCompilersFromRemote = !opts.noRemoteFetch;
var propHierarchy = _.flatten([
'defaults',
env,
- language,
_.map(env, function (e) {
return e + '.' + process.platform;
}),
@@ -126,22 +123,46 @@ const CompileHandler = require('./lib/compile-handler').CompileHandler,
// Instantiate a function to access records concerning "compiler-explorer"
// in hidden object props.properties
-var gccProps = props.propsFor("compiler-explorer");
+var ceProps = props.propsFor("compiler-explorer");
+
+const languages = require('./lib/languages').list;
// Instantiate a function to access records concerning the chosen language
// in hidden object props.properties
-var compilerPropsFunc = props.propsFor(language.toLowerCase());
-
-// If no option for the compiler ... use gcc's options (??)
-function compilerProps(property, defaultValue) {
- // My kingdom for ccs... [see Matt's github page]
- var forCompiler = compilerPropsFunc(property, undefined);
- if (forCompiler !== undefined) return forCompiler;
- return gccProps(property, defaultValue); // gccProps comes from lib/compile-handler.js
+var compilerPropsFuncsL = {};
+_.each(languages, lang => compilerPropsFuncsL[lang.id] = props.propsFor(lang.id));
+
+// Get a property from the specified langId, and if not found, use defaults from CE,
+// and at last return whatever default value was set by the caller
+function compilerPropsL(lang, property, defaultValue) {
+ const forLanguage = compilerPropsFuncsL[lang];
+ if (forLanguage) {
+ const forCompiler = forLanguage(property);
+ if (forCompiler !== undefined) return forCompiler;
+ }
+ return ceProps(property, defaultValue);
}
-var staticMaxAgeSecs = gccProps('staticMaxAgeSecs', 0);
-let extraBodyClass = gccProps('extraBodyClass', '');
+// For every lang passed, get its corresponding compiler property
+function compilerPropsA(langs, property, defaultValue) {
+ let forLanguages = {};
+ _.each(langs, lang => {
+ forLanguages[lang.id] = compilerPropsL(lang.id, property, defaultValue);
+ });
+ return forLanguages;
+}
+
+// Same as A version, but transfroms each value by fn(original, lang)
+function compilerPropsAT(langs, transform, property, defaultValue) {
+ let forLanguages = {};
+ _.each(langs, lang => {
+ forLanguages[lang.id] = transform(compilerPropsL(lang.id, property, defaultValue), lang);
+ });
+ return forLanguages;
+}
+
+var staticMaxAgeSecs = ceProps('staticMaxAgeSecs', 0);
+let extraBodyClass = ceProps('extraBodyClass', '');
function staticHeaders(res) {
if (staticMaxAgeSecs) {
@@ -171,73 +192,71 @@ function loadSources() {
const fileSources = loadSources();
const clientOptionsHandler = new ClientOptionsHandler(fileSources);
-const compileHandler = new CompileHandler(gccProps, compilerProps);
+const compileHandler = new CompileHandler(ceProps, compilerPropsL);
const ApiHandler = require('./lib/handlers/api').ApiHandler;
const apiHandler = new ApiHandler(compileHandler);
const SourceHandler = require('./lib/handlers/source').Handler;
const sourceHandler = new SourceHandler(fileSources, staticHeaders);
function ClientOptionsHandler(fileSources) {
- const sources = _.sortBy(fileSources.map(source => ({name: source.name, urlpart: source.urlpart})), "name");
- const languages = _.compact(_.map(gccProps("languages", '').split(':'), thing => {
- if (!thing) return null;
- var splat = thing.split("=");
- return {language: splat[0], url: splat[1]};
- }));
- const supportsBinary = !!compilerProps("supportsBinary", true);
- const supportsExecute = supportsBinary && !!compilerProps("supportsExecute", true);
- const libs = {};
-
- const baseLibs = compilerProps("libs");
-
- if (baseLibs) {
- _.each(baseLibs.split(':'), function (lib) {
- libs[lib] = {name: compilerProps('libs.' + lib + '.name')};
- libs[lib].versions = {};
- const listedVersions = compilerProps("libs." + lib + '.versions');
- if (listedVersions) {
- _.each(listedVersions.split(':'), function (version) {
- libs[lib].versions[version] = {};
- libs[lib].versions[version].version = compilerProps("libs." + lib + '.versions.' + version + '.version');
- libs[lib].versions[version].path = [];
- var listedIncludes = compilerProps("libs." + lib + '.versions.' + version + '.path');
- if (listedIncludes) {
- _.each(listedIncludes.split(':'), function (path) {
- libs[lib].versions[version].path.push(path);
- });
- } else {
- logger.warn("No paths found for " + lib + " version " + version);
- }
- });
- } else {
- logger.warn("No versions found for " + lib + " library");
- }
- });
- }
- const options = {
- googleAnalyticsAccount: gccProps('clientGoogleAnalyticsAccount', 'UA-55180-6'),
- googleAnalyticsEnabled: gccProps('clientGoogleAnalyticsEnabled', false),
- sharingEnabled: gccProps('clientSharingEnabled', true),
- githubEnabled: gccProps('clientGitHubRibbonEnabled', true),
- gapiKey: gccProps('googleApiKey', ''),
- googleShortLinkRewrite: gccProps('googleShortLinkRewrite', '').split('|'),
- defaultSource: gccProps('defaultSource', ''),
- language: language,
+ const sources = _.sortBy(fileSources.map(function (source) {
+ return {name: source.name, urlpart: source.urlpart};
+ }), "name");
+
+ var supportsBinary = compilerPropsAT(languages, res => !!res, "supportsBinary", true);
+ var supportsExecute = supportsBinary && !!compilerPropsAT(languages, (res, lang) => supportsBinary[lang.id] && !!res, "supportsExecute", true);
+ var libs = {};
+
+ var baseLibs = compilerPropsA(languages, "libs");
+ _.each(baseLibs, function (forLang, lang) {
+ if (lang && forLang) {
+ libs[lang] = {};
+ _.each(forLang.split(':'), function (lib) {
+ libs[lang][lib] = {name: compilerPropsL(lang, 'libs.' + lib + '.name')};
+ libs[lang][lib].versions = {};
+ var listedVersions = compilerPropsL(lang, "libs." + lib + '.versions');
+ if (listedVersions) {
+ _.each(listedVersions.split(':'), function (version) {
+ libs[lang][lib].versions[version] = {};
+ libs[lang][lib].versions[version].version = compilerPropsL(lang, "libs." + lib + '.versions.' + version + '.version');
+ libs[lang][lib].versions[version].path = [];
+ var listedIncludes = compilerPropsL(lang, "libs." + lib + '.versions.' + version + '.path');
+ if (listedIncludes) {
+ _.each(listedIncludes.split(':'), function (path) {
+ libs[lang][lib].versions[version].path.push(path);
+ });
+ } else {
+ logger.warn("No paths found for " + lib + " version " + version);
+ }
+ });
+ } else {
+ logger.warn("No versions found for " + lib + " library");
+ }
+ });
+ }
+ });
+ var options = {
+ googleAnalyticsAccount: ceProps('clientGoogleAnalyticsAccount', 'UA-55180-6'),
+ googleAnalyticsEnabled: ceProps('clientGoogleAnalyticsEnabled', false),
+ sharingEnabled: ceProps('clientSharingEnabled', true),
+ githubEnabled: ceProps('clientGitHubRibbonEnabled', true),
+ gapiKey: ceProps('googleApiKey', ''),
+ googleShortLinkRewrite: ceProps('googleShortLinkRewrite', '').split('|'),
+ defaultSource: ceProps('defaultSource', ''),
compilers: [],
libs: libs,
- sourceExtension: compilerProps('compileFilename').split('.', 2)[1],
- defaultCompiler: compilerProps('defaultCompiler', ''),
- compileOptions: compilerProps('defaultOptions', ''),
+ defaultCompiler: compilerPropsA(languages, 'defaultCompiler', ''),
+ compileOptions: compilerPropsA(languages, 'defaultOptions', ''),
supportsBinary: supportsBinary,
supportsExecute: supportsExecute,
languages: languages,
sources: sources,
- raven: gccProps('ravenUrl', ''),
+ raven: ceProps('ravenUrl', ''),
release: gitReleaseName,
environment: env,
- localStoragePrefix: gccProps('localStoragePrefix'),
- cvCompilerCountMax: gccProps('cvCompilerCountMax', 6),
- defaultFontScale: gccProps('defaultFontScale', 1.0)
+ localStoragePrefix: ceProps('localStoragePrefix'),
+ cvCompilerCountMax: ceProps('cvCompilerCountMax', 6),
+ defaultFontScale: ceProps('defaultFontScale', 1.0)
};
this.setCompilers = function (compilers) {
options.compilers = compilers;
@@ -278,63 +297,60 @@ function retryPromise(promiseFunc, name, maxFails, retryMs) {
}
function findCompilers() {
- var exes = compilerProps("compilers", "/usr/bin/g++").split(":");
- var ndk = compilerProps('androidNdk');
- if (ndk) {
- var toolchains = fs.readdirSync(ndk + "/toolchains");
- toolchains.forEach(function (v, i, a) {
- var path = ndk + "/toolchains/" + v + "/prebuilt/linux-x86_64/bin/";
- if (fs.existsSync(path)) {
- var cc = fs.readdirSync(path).filter(function (filename) {
- return filename.indexOf("g++") !== -1;
- });
- a[i] = path + cc[0];
- } else {
- a[i] = null;
- }
- });
- toolchains = toolchains.filter(function (x) {
- return x !== null;
- });
- exes.push.apply(exes, toolchains);
- }
+ let exes = compilerPropsAT(languages, exs => _.compact(exs.split(":")), "compilers", "");
+
+ const ndk = compilerPropsA(languages, 'androidNdk');
+ _.each(ndk, (ndkPath, langId) => {
+ if (ndkPath) {
+ let toolchains = fs.readdirSync(ndkPath + "/toolchains");
+ toolchains.forEach((version, index, a) => {
+ const path = ndkPath + "/toolchains/" + version + "/prebuilt/linux-x86_64/bin/";
+ if (fs.existsSync(path)) {
+ const cc = fs.readdirSync(path).filter(filename => filename.indexOf("g++") !== -1);
+ a[index] = path + cc[0];
+ } else {
+ a[index] = null;
+ }
+ });
+ toolchains = toolchains.filter(x => x !== null);
+ exes[langId].push(toolchains);
+ }
+ });
function fetchRemote(host, port, props) {
logger.info("Fetching compilers from remote source " + host + ":" + port);
- return retryPromise(function () {
- return new Promise(function (resolve, reject) {
- var request = http.get({
+ return retryPromise(() => {
+ return new Promise((resolve, reject) => {
+ let request = http.get({
hostname: host,
port: port,
path: "/api/compilers",
headers: {
'Accept': 'application/json'
}
- }, function (res) {
- var str = '';
- res.on('data', function (chunk) {
+ }, res => {
+ let str = '';
+ res.on('data', chunk => {
str += chunk;
});
- res.on('end', function () {
- var compilers = JSON.parse(str).map(function (compiler) {
+ res.on('end', () => {
+ let compilers = JSON.parse(str).map(compiler => {
compiler.exe = null;
compiler.remote = "http://" + host + ":" + port;
return compiler;
});
resolve(compilers);
});
- }).on('error', function (e) {
- reject(e);
- }).on('timeout', function () {
- reject("timeout");
- });
+ })
+ .on('error', reject)
+ .on('timeout', () => reject("timeout"));
request.setTimeout(awsProps('proxyTimeout', 1000));
});
},
host + ":" + port,
props('proxyRetries', 5),
props('proxyRetryMs', 500))
- .catch(function () {
+ .catch(() => {
logger.warn("Unable to contact " + host + ":" + port + "; skipping");
return [];
});
@@ -342,10 +358,10 @@ function findCompilers() {
function fetchAws() {
logger.info("Fetching instances from AWS");
- return awsInstances().then(function (instances) {
- return Promise.all(instances.map(function (instance) {
+ return awsInstances().then(instances => {
+ return Promise.all(instances.map(instance => {
logger.info("Checking instance " + instance.InstanceId);
- var address = instance.PrivateDnsName;
+ let address = instance.PrivateDnsName;
if (awsProps("externalTestMode", false)) {
address = instance.PublicDnsName;
}
@@ -354,20 +370,23 @@ function findCompilers() {
});
}
- function compilerConfigFor(name, parentProps) {
- const base = "compiler." + name,
- exe = compilerProps(base + ".exe", name);
+ function compilerConfigFor(langId, compilerName, parentProps) {
+ const base = "compiler." + compilerName + ".";
- function props(name, def) {
- return parentProps(base + "." + name, parentProps(name, def));
+ function props(propName, def) {
+ let propsForCompiler = parentProps(langId, base + propName, undefined);
+ if (propsForCompiler === undefined) {
+ propsForCompiler = parentProps(langId, propName, def);
+ }
+ return propsForCompiler;
}
- var supportsBinary = !!props("supportsBinary", true);
- var supportsExecute = supportsBinary && !!props("supportsExecute", true);
- var compilerInfo = {
- id: name,
- exe: exe,
- name: props("name", name),
+ const supportsBinary = !!props("supportsBinary", true);
+ const supportsExecute = supportsBinary && !!props("supportsExecute", true);
+ const compilerInfo = {
+ id: compilerName,
+ exe: props("exe", compilerName),
+ name: props("name", compilerName),
alias: props("alias"),
options: props("options"),
versionFlag: props("versionFlag"),
@@ -379,43 +398,68 @@ function findCompilers() {
needsMulti: !!props("needsMulti", true),
supportsBinary: supportsBinary,
supportsExecute: supportsExecute,
- postProcess: props("postProcess", "").split("|")
+ postProcess: props("postProcess", "").split("|"),
+ lang: langId
};
- logger.info("Found compiler", compilerInfo);
+ logger.debug("Found compiler", compilerInfo);
return Promise.resolve(compilerInfo);
}
- function recurseGetCompilers(name, parentProps) {
- if (fetchCompilersFromRemote && name.indexOf("@") !== -1) {
- var bits = name.split("@");
- var host = bits[0];
- var port = parseInt(bits[1]);
- return fetchRemote(host, port, gccProps);
+ function recurseGetCompilers(langId, compilerName, parentProps) {
+ if (fetchCompilersFromRemote && compilerName.indexOf("@") !== -1) {
+ const bits = compilerName.split("@");
+ const host = bits[0];
+ const port = parseInt(bits[1]);
+ return fetchRemote(host, port, ceProps);
}
- if (name.indexOf("&") === 0) {
- var groupName = name.substr(1);
+ if (compilerName.indexOf("&") === 0) {
+ const groupName = compilerName.substr(1);
- var props = function (name, def) {
- if (name === "group") {
+ const props = function (langId, propName, def) {
+ if (propName === "group") {
return groupName;
}
- return compilerProps("group." + groupName + "." + name, parentProps(name, def));
+ return compilerPropsL(langId, "group." + groupName + "." + propName, parentProps(langId, propName, def));
};
- var exes = props('compilers', '').split(":");
- logger.info("Processing compilers from group " + groupName);
- return Promise.all(exes.map(function (compiler) {
- return recurseGetCompilers(compiler, props);
- }));
+ const compilerExes = _.compact(props(langId, 'compilers', '').split(":"));
+ logger.debug("Processing compilers from group " + groupName);
+ return Promise.all(compilerExes.map(compiler => recurseGetCompilers(langId, compiler, props)));
}
- if (name === "AWS") return fetchAws();
- return compilerConfigFor(name, parentProps);
+ if (compilerName === "AWS") return fetchAws();
+ return compilerConfigFor(langId, compilerName, parentProps);
+ }
+
+ function getCompilers() {
+ let compilers = [];
+ _.each(exes, (exs, langId) => {
+ _.each(exs, exe => compilers.push(recurseGetCompilers(langId, exe, compilerPropsL)));
+ });
+ return compilers;
+ }
+
+ function ensureDistinct(compilers) {
+ let ids = {};
+ _.each(compilers, compiler => {
+ if (!ids[compiler.id]) ids[compiler.id] = [];
+ ids[compiler.id].push(compiler);
+ });
+ _.each(ids, (list, id) => {
+ if (list.length !== 1) {
+ logger.error(`Compiler ID clash for '${id}' - used by ${
+ _.map(list, o => 'lang:' + o.lang + " name:" + o.name).join(', ')
+ }`);
+ }
+ });
+ return compilers;
}
- return Promise.all(exes.map(compiler => recurseGetCompilers(compiler, compilerProps)))
+ return Promise.all(getCompilers())
.then(_.flatten)
- .then(compilers => compileHandler.setCompilers(compilers))
- .then(compilers => _.sortBy(_.compact(compilers), "name"));
+ .then(compileHandler.setCompilers)
+ .then(compilers => _.compact(compilers))
+ .then(ensureDistinct)
+ .then(compilers => _.sortBy(compilers, "name"));
}
function shortUrlHandler(req, res, next) {
@@ -426,9 +470,9 @@ function shortUrlHandler(req, res, next) {
resolver.resolve(googleUrl)
.then(resultObj => {
var parsed = url.parse(resultObj.longUrl);
- var allowedRe = new RegExp(gccProps('allowedShortUrlHostRe'));
+ var allowedRe = new RegExp(ceProps('allowedShortUrlHostRe'));
if (parsed.host.match(allowedRe) === null) {
- logger.warn("Denied access to short URL " + bits[1] + " - linked to " + resultObj.longUrl);
+ logger.warn(`Denied access to short URL ${bits[1]} - linked to ${resultObj.longUrl}`);
return next();
}
res.writeHead(301, {
@@ -444,7 +488,7 @@ function shortUrlHandler(req, res, next) {
}
Promise.all([findCompilers(), aws.initConfig(awsProps)])
- .then(function (args) {
+ .then(args => {
let compilers = args[0];
var prevCompilers;
@@ -464,7 +508,7 @@ Promise.all([findCompilers(), aws.initConfig(awsProps)])
process.env.NEW_RELIC_NO_CONFIG_FILE = true;
process.env.NEW_RELIC_APP_NAME = 'Compiler Explorer';
process.env.NEW_RELIC_LICENSE_KEY = newRelicLicense;
- process.env.NEW_RELIC_LABELS = 'Language:' + language;
+ process.env.NEW_RELIC_LABELS = 'Languages:' + _.map(languages, languages => languages.name);
require('newrelic');
logger.info('New relic configured with license', newRelicLicense);
}
@@ -473,7 +517,7 @@ Promise.all([findCompilers(), aws.initConfig(awsProps)])
if (JSON.stringify(prevCompilers) === JSON.stringify(compilers)) {
return;
}
- logger.info("Compilers:", compilers);
+ logger.debug("Compilers:", compilers);
if (compilers.length === 0) {
logger.error("#### No compilers found: no compilation will be done!");
}
@@ -484,12 +528,11 @@ Promise.all([findCompilers(), aws.initConfig(awsProps)])
onCompilerChange(compilers);
- var rescanCompilerSecs = gccProps('rescanCompilerSecs', 0);
+ var rescanCompilerSecs = ceProps('rescanCompilerSecs', 0);
if (rescanCompilerSecs) {
- logger.info("Rescanning compilers every " + rescanCompilerSecs + "secs");
- setInterval(function () {
- findCompilers().then(onCompilerChange);
- }, rescanCompilerSecs * 1000);
+ logger.info(`Rescanning compilers every ${rescanCompilerSecs} secs`);
+ setInterval(() => findCompilers().then(onCompilerChange),
+ rescanCompilerSecs * 1000);
}
var webServer = express(),
@@ -553,9 +596,9 @@ Promise.all([findCompilers(), aws.initConfig(awsProps)])
webServer.use('/v', express.static(archivedVersions, {maxAge: Infinity, index: false}));
}
webServer
- .use(bodyParser.json({limit: gccProps('bodyParserLimit', '1mb')}))
+ .use(bodyParser.json({limit: ceProps('bodyParserLimit', '1mb')}))
.use(bodyParser.text({
- limit: gccProps('bodyParserLimit', '1mb'), type: function () {
+ limit: ceProps('bodyParserLimit', '1mb'), type: function () {
return true;
}
}))
diff --git a/etc/config/builtin.d.properties b/etc/config/builtin.d.properties
deleted file mode 100644
index bfc5e8548..000000000
--- a/etc/config/builtin.d.properties
+++ /dev/null
@@ -1,2 +0,0 @@
-sourcepath=./examples/d/
-extensionRe=.*\.d$
diff --git a/etc/config/builtin.default.properties b/etc/config/builtin.default.properties
new file mode 100644
index 000000000..0ebf27c6a
--- /dev/null
+++ b/etc/config/builtin.default.properties
@@ -0,0 +1 @@
+sourcePath=./examples/ \ No newline at end of file
diff --git a/etc/config/builtin.go.properties b/etc/config/builtin.go.properties
deleted file mode 100644
index 1fd2bfefc..000000000
--- a/etc/config/builtin.go.properties
+++ /dev/null
@@ -1,2 +0,0 @@
-sourcepath=./examples/go/
-extensionRe=.*\.go$
diff --git a/etc/config/builtin.haskell.properties b/etc/config/builtin.haskell.properties
deleted file mode 100644
index c865078c3..000000000
--- a/etc/config/builtin.haskell.properties
+++ /dev/null
@@ -1,2 +0,0 @@
-sourcepath=./examples/haskell/
-extensionRe=.*\.hs$
diff --git a/etc/config/builtin.ispc.properties b/etc/config/builtin.ispc.properties
deleted file mode 100644
index eff778fcc..000000000
--- a/etc/config/builtin.ispc.properties
+++ /dev/null
@@ -1,2 +0,0 @@
-sourcepath=./examples/ispc/
-extensionRe=.*\.ispc$
diff --git a/etc/config/builtin.pascal.properties b/etc/config/builtin.pascal.properties
deleted file mode 100644
index 79dea28bb..000000000
--- a/etc/config/builtin.pascal.properties
+++ /dev/null
@@ -1,2 +0,0 @@
-sourcepath=./examples/pascal/
-extensionRe=.*\.pas$
diff --git a/etc/config/builtin.rust.properties b/etc/config/builtin.rust.properties
deleted file mode 100644
index c61faaa7c..000000000
--- a/etc/config/builtin.rust.properties
+++ /dev/null
@@ -1,2 +0,0 @@
-sourcepath=./examples/rust/
-extensionRe=.*\.rs$
diff --git a/etc/config/builtin.swift.properties b/etc/config/builtin.swift.properties
deleted file mode 100644
index 77f22f08e..000000000
--- a/etc/config/builtin.swift.properties
+++ /dev/null
@@ -1,2 +0,0 @@
-sourcepath=./examples/swift/
-extensionRe=.*\.swift$
diff --git a/etc/config/c++.amazon.properties b/etc/config/c++.amazon.properties
index 2db61d026..f04bcded9 100644
--- a/etc/config/c++.amazon.properties
+++ b/etc/config/c++.amazon.properties
@@ -1,6 +1,5 @@
compilers=&gcc86:&icc:&clang:&cl:&cross:&ellcc:&zapcc
defaultCompiler=g72
-textBanner=Compilation provided by Compiler Explorer at https://gcc.godbolt.org/
demangler=/opt/compiler-explorer/gcc-7.2.0/bin/c++filt
objdumper=/opt/compiler-explorer/gcc-7.2.0/bin/objdump
diff --git a/etc/config/c++.cppx.properties b/etc/config/c++.cppx.properties
index 8b2e7c7d1..fc1062552 100644
--- a/etc/config/c++.cppx.properties
+++ b/etc/config/c++.cppx.properties
@@ -1,6 +1,5 @@
compilers=cppx010:cppx_trunk
defaultCompiler=cppx010
-textBanner=Compilation provided by Compiler Explorer at https://cppx.godbolt.org/
intelAsm=-mllvm --x86-asm-syntax=intel
demangler=/opt/compiler-explorer/gcc-snapshot/bin/c++filt
objdumper=/opt/compiler-explorer/gcc-snapshot/bin/objdump
diff --git a/etc/config/c++.danger.properties b/etc/config/c++.danger.properties
index 2ac38ef8d..52592830a 100644
--- a/etc/config/c++.danger.properties
+++ b/etc/config/c++.danger.properties
@@ -1,6 +1,5 @@
# Matt's home development computer
defaultCompiler=g54
-textBanner=Testing the text banner
compilers=g54:&clang:&arm:avrg454:&cl
#compilers=g54:g47:g48:avr
#compilers=localhost@20480
diff --git a/etc/config/c++.defaults.properties b/etc/config/c++.defaults.properties
index 921dc2b1a..be167d937 100644
--- a/etc/config/c++.defaults.properties
+++ b/etc/config/c++.defaults.properties
@@ -2,7 +2,6 @@
# AP: Add &cl group
compilers=/usr/bin/g++-4.4:/usr/bin/g++-4.5:/usr/bin/g++-4.6:/usr/bin/g++-4.7:/usr/bin/g++-4.8:/usr/bin/clang++:/usr/bin/g++-5:/usr/bin/g++-7:/usr/bin/g++-6:/usr/bin/g++:&cl
defaultCompiler=/usr/bin/g++
-compileFilename=example.cpp
postProcess=
demangler=c++filt
objdumper=objdump
diff --git a/etc/config/c.amazon.properties b/etc/config/c.amazon.properties
new file mode 100644
index 000000000..187310ebf
--- /dev/null
+++ b/etc/config/c.amazon.properties
@@ -0,0 +1,6 @@
+# Amazon settings for C
+compilers=
+defaultCompiler=
+demangler=/opt/compiler-explorer/gcc-7.2.0/bin/c++filt
+objdumper=/opt/compiler-explorer/gcc-7.2.0/bin/objdump
+group..compilers=
diff --git a/etc/config/c.defaults.properties b/etc/config/c.defaults.properties
new file mode 100644
index 000000000..d38465abb
--- /dev/null
+++ b/etc/config/c.defaults.properties
@@ -0,0 +1,10 @@
+# Default settings for C
+compilers=/usr/bin/gcc-4.4:/usr/bin/gcc-4.5:/usr/bin/gcc-4.6:/usr/bin/gcc-4.7:/usr/bin/gcc-4.8:/usr/bin/clangcc:/usr/bin/gcc-5:/usr/bin/gcc-7:/usr/bin/gcc-6:/usr/bin/gcc
+defaultCompiler=/usr/bin/gcc
+postProcess=
+demangler=c++filt
+objdumper=objdump
+supportsBinary=true
+binaryHideFuncRe=^(__.*|_(init|start|fini)|(de)?register_tm_clones|call_gmon_start|frame_dummy|\.plt.*)$
+stubRe=\bmain\b
+stubText=int main(void){return 0;/*stub provided by Compiler Explorer*/}
diff --git a/etc/config/compiler-explorer.defaults.properties b/etc/config/compiler-explorer.defaults.properties
index 9fd2a8f8d..3f54a3bac 100644
--- a/etc/config/compiler-explorer.defaults.properties
+++ b/etc/config/compiler-explorer.defaults.properties
@@ -15,3 +15,5 @@ googleShortLinkRewrite=^https?://goo.gl/(.*)$|https://godbolt.org/g/$1
wine=/opt/wine-devel/bin/wine64
cvCompilerCountMax=6
+
+textBanner=Compilation provided by Compiler Explorer at https://godbolt.org/
diff --git a/etc/config/compiler-explorer.wud-mgodbolt01.properties b/etc/config/compiler-explorer.wud-mgodbolt01.properties
index 1b0954c13..6840bb06f 100644
--- a/etc/config/compiler-explorer.wud-mgodbolt01.properties
+++ b/etc/config/compiler-explorer.wud-mgodbolt01.properties
@@ -7,7 +7,6 @@ clientSharingEnabled=false
#compiler.r100.exe=/mnt/data/apps/rust/bin/rustc
#compiler.r100.name=rustc 1.0.0
#compiler.r100.intelAsm=-Cllvm-args=--x86-asm-syntax=intel
-#compileFilename=example.rs
#cacheMb=100
#language=C++
#clientGoogleAnalyticsEnabled=true
diff --git a/etc/config/cppx.amazon.properties b/etc/config/cppx.amazon.properties
new file mode 100644
index 000000000..fc1062552
--- /dev/null
+++ b/etc/config/cppx.amazon.properties
@@ -0,0 +1,13 @@
+compilers=cppx010:cppx_trunk
+defaultCompiler=cppx010
+intelAsm=-mllvm --x86-asm-syntax=intel
+demangler=/opt/compiler-explorer/gcc-snapshot/bin/c++filt
+objdumper=/opt/compiler-explorer/gcc-snapshot/bin/objdump
+
+compiler.cppx010.exe=/opt/compiler-explorer/cppx/current/bin/clang++
+compiler.cppx010.name=Runs Herb's examples
+compiler.cppx010.options=--gcc-toolchain=/opt/compiler-explorer/gcc-snapshot -std=c++1z -Xclang -freflection -I/opt/compiler-explorer/cppx/current/include -stdlib=libc++ -include cppx/meta -include cppx/compiler
+
+compiler.cppx_trunk.exe=/opt/compiler-explorer/clang-cppx-trunk/bin/clang++
+compiler.cppx_trunk.name=Latest trunk
+compiler.cppx_trunk.options=--gcc-toolchain=/opt/compiler-explorer/gcc-snapshot -std=c++1z -Xclang -freflection -I/opt/compiler-explorer/clang-cppx-trunk/include -stdlib=libc++ -include cppx/meta -include cppx/compiler
diff --git a/etc/config/cppx.defaults.properties b/etc/config/cppx.defaults.properties
new file mode 100644
index 000000000..7125b96fb
--- /dev/null
+++ b/etc/config/cppx.defaults.properties
@@ -0,0 +1,9 @@
+# Default settings for cppx
+postProcess=
+demangler=c++filt
+objdumper=objdump
+options=
+supportsBinary=true
+binaryHideFuncRe=^(__.*|_(init|start|fini)|(de)?register_tm_clones|call_gmon_start|frame_dummy|\.plt.*)$
+stubRe=\bmain\b
+stubText=int main(void){return 0;/*stub provided by Compiler Explorer*/}
diff --git a/etc/config/d.amazon.properties b/etc/config/d.amazon.properties
index e09076628..9131f6389 100644
--- a/etc/config/d.amazon.properties
+++ b/etc/config/d.amazon.properties
@@ -1,5 +1,4 @@
compilers=gdc48:gdc49:gdc52:&ldc
-textBanner=Compilation provided by Compiler Explorer at https://d.godbolt.org/
defaultCompiler=ldc160
objdumper=/opt/compiler-explorer/gcc-7.2.0/bin/objdump
diff --git a/etc/config/d.defaults.properties b/etc/config/d.defaults.properties
index 4134b80f9..6f797c16a 100644
--- a/etc/config/d.defaults.properties
+++ b/etc/config/d.defaults.properties
@@ -1,5 +1,4 @@
compilers=/usr/bin/gdc:/usr/bin/gdc-4.4:/usr/bin/gdc-4.6
-compileFilename=example.d
demangler=/opt/compiler-explorer/ldc1.5.0/ldc2-1.5.0-linux-x86_64/bin/ddemangle
supportsBinary=true
objdumper=objdump
diff --git a/etc/config/go.amazon.properties b/etc/config/go.amazon.properties
index f539d2463..94fe625a1 100644
--- a/etc/config/go.amazon.properties
+++ b/etc/config/go.amazon.properties
@@ -1,7 +1,5 @@
defaultCompiler=gl192
-textBanner=Compilation provided by Compiler Explorer at https://go.godbolt.org/
objdumper=/opt/compiler-explorer/gcc-7.2.0/bin/objdump
-compileFilename=file.go
compilers=gccgo494:gccgo630:gccgo720:&gl
group.gl.compilers=6g141:gl172:gl185:gl192
group.gl.versionFlag=version
diff --git a/etc/config/go.defaults.properties b/etc/config/go.defaults.properties
index 12db27f7a..e8856b3ee 100644
--- a/etc/config/go.defaults.properties
+++ b/etc/config/go.defaults.properties
@@ -1,4 +1,3 @@
-compileFilename=file.go
supportsBinary=true
objdumper=objdump
binaryHideFuncRe=^(_.*|(de)?register_tm_clones|frame_dummy|.*@plt)$
diff --git a/etc/config/go.lud-mgodbolt01.properties b/etc/config/go.lud-mgodbolt01.properties
index 845756ab9..92ddd917b 100644
--- a/etc/config/go.lud-mgodbolt01.properties
+++ b/etc/config/go.lud-mgodbolt01.properties
@@ -1,8 +1,7 @@
defaultCompiler=6g
compilers=6g
-compileFilename=file.go
compiler.6g.exe=/home/mgodbolt/apps/go/go/pkg/tool/linux_amd64/6g
compiler.6g.name=Yah
compiler.6g.versionFlag=-V
-compiler.6g.compilerType=6g
+compiler.6g.compilerType=golang
compiler.6g.supportsBinary=false
diff --git a/etc/config/haskell.defaults.properties b/etc/config/haskell.defaults.properties
index a63fe019e..843e9abc3 100644
--- a/etc/config/haskell.defaults.properties
+++ b/etc/config/haskell.defaults.properties
@@ -1,5 +1,4 @@
compilers=/usr/bin/ghc
-compileFilename=example.hs
supportsBinary=false
compilerType=haskell
postProcess=haskell/demangle
diff --git a/etc/config/ispc.defaults.properties b/etc/config/ispc.defaults.properties
index ad57dcd9a..5e4bafb75 100644
--- a/etc/config/ispc.defaults.properties
+++ b/etc/config/ispc.defaults.properties
@@ -1,5 +1,4 @@
compilers=ispc
-compileFilename=example.ispc
supportsBinary=false
compilerType=ispc
diff --git a/etc/config/pascal.defaults.properties b/etc/config/pascal.defaults.properties
index 6c1b9b302..c5d24cd31 100644
--- a/etc/config/pascal.defaults.properties
+++ b/etc/config/pascal.defaults.properties
@@ -1,7 +1,6 @@
# Default settings for Pascal
compilers=/usr/bin/fpc
defaultCompiler=/usr/bin/fpc
-compileFilename=output.pas
postProcess=
demangler=/dev/null
objdumper=objdump
diff --git a/etc/config/rust.amazon.properties b/etc/config/rust.amazon.properties
index f50b34b0a..8aa40d1ba 100644
--- a/etc/config/rust.amazon.properties
+++ b/etc/config/rust.amazon.properties
@@ -1,5 +1,4 @@
compilers=&rust
-textBanner=Compilation provided by Compiler Explorer at https://rust.godbolt.org/
objdumper=/opt/compiler-explorer/gcc-7.2.0/bin/objdump
defaultCompiler=r1210
group.rust.compilers=r1210:r1200:r1190:r1180:r1170:r1160:r1151:r1140:r1130:r1120:r1110:r1100:r190:r180:r170:r160:r150:r140:r130:r120:r110:r100:nightly:beta
diff --git a/etc/config/rust.defaults.properties b/etc/config/rust.defaults.properties
index 722c2d51a..929da01aa 100644
--- a/etc/config/rust.defaults.properties
+++ b/etc/config/rust.defaults.properties
@@ -1,5 +1,4 @@
compilers=/usr/local/bin/rustc
-compileFilename=example.rs
supportsBinary=false
compilerType=rust
demangler=rust/bin/rustfilt
diff --git a/etc/config/swift.defaults.properties b/etc/config/swift.defaults.properties
index 56392e019..fcf7f9370 100644
--- a/etc/config/swift.defaults.properties
+++ b/etc/config/swift.defaults.properties
@@ -1,4 +1,4 @@
-compileFilename=example.swift
+compilers=
compileToAsm=-emit-assembly
supportsBinary=false
compilerType=swift
diff --git a/etc/scripts/pre-commit b/etc/scripts/pre-commit
index c349b8af9..96b4f6834 100755
--- a/etc/scripts/pre-commit
+++ b/etc/scripts/pre-commit
@@ -1,5 +1,4 @@
#!/bin/bash
-
FORBIDDEN='console.log'
FILES_PATTERN='\.(js)(\..+)?$'
diff --git a/views/example.cpp b/examples/c++/default.cpp
index 86b3a4a3d..e8c4fe4ec 100644
--- a/views/example.cpp
+++ b/examples/c++/default.cpp
@@ -1,4 +1,4 @@
// Type your code here, or load an example.
int square(int num) {
return num * num;
-}
+} \ No newline at end of file
diff --git a/examples/c/Max_array.c b/examples/c/Max_array.c
new file mode 100644
index 000000000..bc2ef8799
--- /dev/null
+++ b/examples/c/Max_array.c
@@ -0,0 +1,5 @@
+void maxArray(double* x, double* y) {
+ for (int i = 0; i < 65536; i++) {
+ if (y[i] > x[i]) x[i] = y[i];
+ }
+}
diff --git a/examples/c/Sum_over_array.c b/examples/c/Sum_over_array.c
new file mode 100644
index 000000000..601ef1600
--- /dev/null
+++ b/examples/c/Sum_over_array.c
@@ -0,0 +1,7 @@
+int testFunction(int* input, int length) {
+ int sum = 0;
+ for (int i = 0; i < length; ++i) {
+ sum += input[i];
+ }
+ return sum;
+}
diff --git a/examples/c/default.c b/examples/c/default.c
new file mode 100644
index 000000000..e8c4fe4ec
--- /dev/null
+++ b/examples/c/default.c
@@ -0,0 +1,4 @@
+// Type your code here, or load an example.
+int square(int num) {
+ return num * num;
+} \ No newline at end of file
diff --git a/examples/cppx/Interface.cpp b/examples/cppx/Interface.cpp
new file mode 100644
index 000000000..1af545545
--- /dev/null
+++ b/examples/cppx/Interface.cpp
@@ -0,0 +1,61 @@
+
+//====================================================================
+// Library code: implementing the metaclass (once)
+
+$class interface {
+ constexpr {
+ compiler.require($interface.variables().empty(),
+ "interfaces may not contain data");
+ for... (auto f : $interface.functions()) {
+ compiler.require(!f.is_copy() && !f.is_move(),
+ "interfaces may not copy or move; consider a"
+ " virtual clone() instead");
+ if (!f.has_access()) f.make_public();
+ compiler.require(f.is_public(),
+ "interface functions must be public");
+ f.make_pure_virtual();
+ }
+ }
+ virtual ~interface() noexcept { }
+};
+
+
+//====================================================================
+// User code: using the metaclass to write a type (many times)
+
+interface Shape {
+ int area() const;
+ void scale_by(double factor);
+};
+
+ // try putting any of these lines into Shape to see "interface" rules
+ // enforced => using the metaclass name to declare intent makes
+ // this code more robust to such changes under maintenance
+ //
+ // int i; // error: interfaces may not contain data
+ // private: void g(); // error: interface functions must be public
+ // Shape(const Shape&); // error: interfaces may not copy or move;
+ // // consider a virtual clone() instead
+
+// Godbolt.org note: Click the "triangle ! icon" to see the output
+constexpr {
+ compiler.debug($Shape);
+}
+
+
+//====================================================================
+// And then continue to use it as "just a class" as always... this is
+// normal code just as if we'd written Shape not using a metaclass
+
+class Circle : public Shape {
+public:
+ int area() const override { return 1; }
+ void scale_by(double factor) override { }
+};
+
+#include <memory>
+
+int main() {
+ std::unique_ptr<Shape> shape = std::make_unique<Circle>();
+ shape->area();
+}
diff --git a/views/example.cppx b/examples/cppx/default.cpp
index 503cffe41..503cffe41 100644
--- a/views/example.cppx
+++ b/examples/cppx/default.cpp
diff --git a/views/example.d b/examples/d/default.d
index 3cb5a30c3..3cb5a30c3 100644
--- a/views/example.d
+++ b/examples/d/default.d
diff --git a/views/example.go b/examples/go/default.go
index 65be52b7a..65be52b7a 100644
--- a/views/example.go
+++ b/examples/go/default.go
diff --git a/views/example.hs b/examples/haskell/default.hs
index 97106284d..97106284d 100644
--- a/views/example.hs
+++ b/examples/haskell/default.hs
diff --git a/views/example.ispc b/examples/ispc/default.ispc
index 0924dc0b6..0924dc0b6 100644
--- a/views/example.ispc
+++ b/examples/ispc/default.ispc
diff --git a/examples/pascal/max_array.pas b/examples/pascal/Max_array.pas
index ebacc2b3b..ebacc2b3b 100644
--- a/examples/pascal/max_array.pas
+++ b/examples/pascal/Max_array.pas
diff --git a/examples/pascal/sum_over_array.pas b/examples/pascal/Sum_over_array.pas
index 714a4f6c9..714a4f6c9 100644
--- a/examples/pascal/sum_over_array.pas
+++ b/examples/pascal/Sum_over_array.pas
diff --git a/views/example.pas b/examples/pascal/default.pas
index eddb3978a..eddb3978a 100644
--- a/views/example.pas
+++ b/examples/pascal/default.pas
diff --git a/views/example.rs b/examples/rust/default.rs
index f224705c9..ff272bd4d 100644
--- a/views/example.rs
+++ b/examples/rust/default.rs
@@ -1,4 +1,4 @@
// Type your code here, or load an example.
-pub fn square(num: i32) -&gt; i32 {
+pub fn square(num: i32) -> i32 {
num * num
}
diff --git a/views/example.swift b/examples/swift/default.swift
index af4a0979c..af4a0979c 100644
--- a/views/example.swift
+++ b/examples/swift/default.swift
diff --git a/lib/base-compiler.js b/lib/base-compiler.js
index 21c60f28f..3788aea22 100644
--- a/lib/base-compiler.js
+++ b/lib/base-compiler.js
@@ -34,12 +34,20 @@ const child_process = require('child_process'),
logger = require('./logger').logger,
compilerOptInfo = require("compiler-opt-info"),
argumentParsers = require("./compilers/argument-parsers"),
- cfg = require('./cfg');
+ cfg = require('./cfg'),
+ languages = require('./languages').list;
-function Compile(compiler, env) {
+function Compile(compiler, env, langId) {
this.compiler = compiler;
+ this.lang = langId;
+ this.langInfo = languages[langId];
+ if (!this.langInfo) {
+ throw new Error("Missing language info for " + langId);
+ }
+ this.compileFilename = 'example' + this.langInfo.extensions[0];
this.env = env;
- this.asm = new asm.AsmParser(env.compilerProps);
+ this.compilerProps = _.partial(this.env.compilerPropsL, this.lang);
+ this.asm = new asm.AsmParser(this.compilerProps);
this.compiler.supportsIntel = !!this.compiler.intelAsm;
}
@@ -75,10 +83,10 @@ Compile.prototype.exec = function (compiler, args, options) {
Compile.prototype.getDefaultExecOptions = function () {
return {
- timeoutMs: this.env.gccProps("compileTimeoutMs", 100),
- maxErrorOutput: this.env.gccProps("max-error-output", 5000),
+ timeoutMs: this.env.ceProps("compileTimeoutMs", 100),
+ maxErrorOutput: this.env.ceProps("max-error-output", 5000),
env: this.env.getEnv(this.compiler.needsMulti),
- wrapper: this.env.compilerProps("compiler-wrapper")
+ wrapper: this.compilerProps("compiler-wrapper")
};
};
@@ -226,13 +234,13 @@ Compile.prototype.compile = function (source, options, backendOptions, filters)
return Promise.resolve(cached);
}
- if (filters.binary && !source.match(this.env.compilerProps("stubRe"))) {
- source += "\n" + this.env.compilerProps("stubText") + "\n";
+ if (filters.binary && !source.match(this.compilerProps("stubRe"))) {
+ source += "\n" + this.compilerProps("stubText") + "\n";
}
return this.env.enqueue(() => {
const tempFileAndDirPromise = this.newTempDir()
.then(dirPath => {
- const inputFilename = path.join(dirPath, this.env.compilerProps("compileFilename"));
+ const inputFilename = path.join(dirPath, this.compileFilename);
return this.writeFile(inputFilename, source).then(() => ({
inputFilename: inputFilename,
dirPath: dirPath
@@ -357,8 +365,7 @@ Compile.prototype.postProcessAsm = function (result) {
};
Compile.prototype.processOptOutput = function (hasOptOutput, optPath) {
- const output = [];
- const inputFile = this.env.compilerProps("compileFilename", "");
+ let output = [];
return new Promise(
resolve => {
fs.createReadStream(optPath, {encoding: "utf-8"})
@@ -366,7 +373,7 @@ Compile.prototype.processOptOutput = function (hasOptOutput, optPath) {
.on("data", opt => {
if (opt.DebugLoc &&
opt.DebugLoc.File &&
- opt.DebugLoc.File.indexOf(inputFile) > -1) {
+ opt.DebugLoc.File.indexOf(this.compileFilename) > -1) {
output.push(opt);
}
@@ -519,7 +526,7 @@ Compile.prototype.processGccDumpOutput = function (opts, result) {
Compile.prototype.postProcess = function (result, outputFilename, filters) {
const postProcess = _.compact(this.compiler.postProcess);
- const maxSize = this.env.gccProps("max-asm-size", 8 * 1024 * 1024);
+ const maxSize = this.env.ceProps("max-asm-size", 8 * 1024 * 1024);
let optPromise, asmPromise, execPromise;
if (result.hasOptOutput) {
optPromise = this.processOptOutput(result.hasOptOutput, result.optPath);
@@ -555,7 +562,7 @@ Compile.prototype.postProcess = function (result, outputFilename, filters) {
);
}
if (filters.execute) {
- const maxExecOutputSize = this.env.gccProps("max-executable-output-size", 32 * 1024);
+ const maxExecOutputSize = this.env.ceProps("max-executable-output-size", 32 * 1024);
execPromise = this.execBinary(outputFilename, result, maxExecOutputSize);
} else {
execPromise = Promise.resolve("");
@@ -635,7 +642,7 @@ Compile.prototype.initialise = function () {
'with re', versionRe);
return null;
}
- logger.info(compiler + " is version '" + version + "'");
+ logger.debug(compiler + " is version '" + version + "'");
this.compiler.version = version;
return argumentParser(this);
},
diff --git a/lib/compilation-env.js b/lib/compilation-env.js
index 81f21e54a..1d2d7b6cb 100644
--- a/lib/compilation-env.js
+++ b/lib/compilation-env.js
@@ -32,20 +32,18 @@ const LRU = require('lru-cache'),
Queue.configure(Promise);
-function CompilationEnvironment(gccProps, compilerProps) {
- this.gccProps = gccProps;
- this.compilerProps = compilerProps;
- this.okOptions = new RegExp(gccProps('optionsWhitelistRe', '.*'));
- this.badOptions = new RegExp(gccProps('optionsBlacklistRe', '(?!)'));
+function CompilationEnvironment(ceProps, compilerPropsL) {
+ this.ceProps = ceProps;
+ this.compilerPropsL = compilerPropsL;
+ this.okOptions = new RegExp(ceProps('optionsWhitelistRe', '.*'));
+ this.badOptions = new RegExp(ceProps('optionsBlacklistRe', '(?!)'));
this.cache = LRU({
- max: gccProps('cacheMb') * 1024 * 1024,
- length: function (n) {
- return JSON.stringify(n).length;
- }
+ max: ceProps('cacheMb') * 1024 * 1024,
+ length: n => JSON.stringify(n).length
});
this.cacheHits = 0;
this.cacheMisses = 0;
- this.compileQueue = new Queue(gccProps("maxConcurrentCompiles", 1), Infinity);
+ this.compileQueue = new Queue(ceProps("maxConcurrentCompiles", 1), Infinity);
this.multiarch = null;
try {
var multi = child_process.execSync("gcc -print-multiarch").toString().trim();
diff --git a/lib/compile-handler.js b/lib/compile-handler.js
index 1cbde43f4..159c5eeb5 100644
--- a/lib/compile-handler.js
+++ b/lib/compile-handler.js
@@ -22,8 +22,7 @@
// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
// POSSIBILITY OF SUCH DAMAGE.
-var child_process = require('child_process'),
- temp = require('temp'),
+const temp = require('temp'),
fs = require('fs'),
path = require('path'),
httpProxy = require('http-proxy'),
@@ -37,12 +36,12 @@ var child_process = require('child_process'),
temp.track();
-var oneTimeInit = false;
+let oneTimeInit = false;
-function initialise(gccProps, compilerEnv) {
+function initialise(ceProps, compilerEnv) {
if (oneTimeInit) return;
oneTimeInit = true;
- var tempDirCleanupSecs = gccProps("tempDirCleanupSecs", 600);
+ const tempDirCleanupSecs = ceProps("tempDirCleanupSecs", 600);
logger.info("Cleaning temp dirs every " + tempDirCleanupSecs + " secs");
setInterval(function () {
if (compilerEnv.isBusy()) {
@@ -56,71 +55,61 @@ function initialise(gccProps, compilerEnv) {
}, tempDirCleanupSecs * 1000);
}
-function CompileHandler(gccProps, compilerProps) {
+function CompileHandler(ceProps, compilerPropsL) {
+ const self = this;
this.compilersById = {};
- this.compilerEnv = new CompilationEnvironment(gccProps, compilerProps);
- initialise(gccProps, this.compilerEnv);
+ this.compilerEnv = new CompilationEnvironment(ceProps, compilerPropsL);
+ initialise(ceProps, this.compilerEnv);
this.factories = {};
this.stat = Promise.denodeify(fs.stat);
+ this.proxy = httpProxy.createProxyServer({});
+ this.textBanner = ceProps('textBanner');
+
this.create = function (compiler) {
- var type = compiler.compilerType || "default";
- if (this.factories[type] === undefined) {
- var compilerPath = './compilers/' + type;
- logger.info("Loading compiler from", compilerPath);
- this.factories[type] = require(compilerPath);
+ const type = compiler.compilerType || "default";
+ if (self.factories[type] === undefined) {
+ const compilerPath = './compilers/' + type;
+ logger.debug("Loading compiler from", compilerPath);
+ self.factories[type] = require(compilerPath);
}
if (path.isAbsolute(compiler.exe)) {
// Try stat'ing the compiler to cache its mtime and only re-run it if it
// has changed since the last time.
- return this.stat(compiler.exe)
- .then(_.bind(function (res) {
- var cached = this.compilersById[compiler.id];
+ return self.stat(compiler.exe)
+ .then(_.bind(res => {
+ const cached = self.findCompiler(compiler.lang, compiler.id);
if (cached && cached.mtime.getTime() === res.mtime.getTime()) {
logger.debug(compiler.id + " is unchanged");
return cached;
}
- return this.factories[type](compiler, this.compilerEnv).then(function (compiler) {
+ return self.factories[type](compiler, self.compilerEnv, compiler.lang).then(compiler => {
compiler.mtime = res.mtime;
return compiler;
});
- }, this))
- .catch(function (err) {
+ }, self))
+ .catch(err => {
logger.warn("Unable to stat compiler binary", err);
return null;
});
} else {
- return this.factories[type](compiler, this.compilerEnv);
+ return self.factories[type](compiler, self.compilerEnv, compiler.lang);
}
};
-
- this.setCompilers = function (compilers) {
- return Promise.all(_.map(compilers, this.create, this))
- .then(_.compact)
- .then(compilers => {
- _.each(compilers, compiler => this.compilersById[compiler.compiler.id] = compiler);
- return _.map(compilers, compiler => compiler.getInfo());
- })
- .catch(err => logger.error(err));
- };
- var proxy = httpProxy.createProxyServer({});
- var textBanner = compilerProps('textBanner');
-
- this.handler = _.bind(function compile(req, res, next) {
- var source, options, backendOptions, filters, compiler;
+ this.handler = function compile(req, res, next) {
+ let source, options, backendOptions, filters,
+ compiler = self.findCompiler(req.lang || req.body.lang, req.compiler || req.body.compiler);
+ if (!compiler) return next();
if (req.is('json')) {
// JSON-style request
- compiler = this.compilersById[req.compiler || req.body.compiler];
- if (!compiler) return next();
- var requestOptions = req.body.options;
+ const requestOptions = req.body.options;
source = req.body.source;
options = requestOptions.userArguments;
backendOptions = requestOptions.compilerOptions;
filters = requestOptions.filters || compiler.getDefaultFilters();
} else {
// API-style
- compiler = this.compilersById[req.compiler];
- if (!compiler) return next();
+ // Find the compiler the user is interested in...
source = req.body;
options = req.query.options;
// By default we get the default filters.
@@ -140,10 +129,10 @@ function CompileHandler(gccProps, compilerProps) {
delete filters[filter];
});
}
- var remote = compiler.getRemote();
+ const remote = compiler.getRemote();
if (remote) {
req.url = req.originalUrl; // Undo any routing that was done to get here (i.e. /api/* path has been removed)
- proxy.web(req, res, {target: remote}, function (e) {
+ self.proxy.web(req, res, {target: remote}, function (e) {
logger.error("Proxy error: ", e);
next(e);
});
@@ -170,7 +159,7 @@ function CompileHandler(gccProps, compilerProps) {
} else {
res.set('Content-Type', 'text/plain');
try {
- if (!_.isEmpty(textBanner)) res.write('# ' + textBanner + "\n");
+ if (!_.isEmpty(self.textBanner)) res.write('# ' + self.textBanner + "\n");
res.write(textify(result.asm));
if (result.code !== 0) res.write("\n# Compiler exited with result code " + result.code);
if (!_.isEmpty(result.stdout)) res.write("\nStandard out:\n" + textify(result.stdout));
@@ -198,7 +187,39 @@ function CompileHandler(gccProps, compilerProps) {
res.end(JSON.stringify({code: -1, stderr: [{text: error}]}));
}
);
- }, this);
+ };
+ this.setCompilers = function (newCompilers) {
+ // Delete every compiler first...
+ self.compilersById = {};
+ return Promise.all(_.map(newCompilers, self.create))
+ .then(_.compact)
+ .then(compilers => {
+ _.each(compilers, compiler => {
+ const langId = compiler.compiler.lang;
+ if (!self.compilersById[langId]) self.compilersById[langId] = {};
+ self.compilersById[langId][compiler.compiler.id] = compiler;
+ }, self);
+ return _.map(compilers,compiler => compiler.getInfo());
+ })
+ .catch(logger.error);
+ };
+ this.findCompiler = function (langId, compilerId) {
+ if (langId && self.compilersById[langId]) {
+ return self.compilersById[langId][compilerId];
+ }
+ // If the lang is bad, try to find it in every language
+ let response;
+ _.each(self.compilersById, compilerInLang => {
+ if (response === undefined) {
+ _.each(compilerInLang, compiler => {
+ if (response === undefined && compiler.compiler.id === compilerId) {
+ response = compiler;
+ }
+ });
+ }
+ });
+ return response;
+ };
}
module.exports = {
diff --git a/lib/compilers/WSL-CL.js b/lib/compilers/WSL-CL.js
index 4582e546b..be3bc21c4 100644
--- a/lib/compilers/WSL-CL.js
+++ b/lib/compilers/WSL-CL.js
@@ -32,9 +32,9 @@ const Compile = require('../base-compiler'),
asm = require('../asm-cl'),
temp = require('temp');
-function compileCl(info, env) {
- const compile = new Compile(info, env);
- compile.asm = new asm.AsmParser(env.compilerProps);
+function compileCl(info, env, langId) {
+ var compile = new Compile(info, env, langId);
+ compile.asm = new asm.AsmParser(compile.compilerProps);
info.supportsFiltersInBinary = true;
if (process.platform === "linux") {
const origExec = compile.exec;
diff --git a/lib/compilers/Wine-CL.js b/lib/compilers/Wine-CL.js
index 0e92fe4de..d206bb0f2 100644
--- a/lib/compilers/Wine-CL.js
+++ b/lib/compilers/Wine-CL.js
@@ -25,12 +25,12 @@
var Compile = require('../base-compiler');
var asm = require('../asm-cl');
-function compileCl(info, env) {
- var compile = new Compile(info, env);
- compile.asm = new asm.AsmParser(env.compilerProps);
+function compileCl(info, env, langId) {
+ var compile = new Compile(info, env, langId);
+ compile.asm = new asm.AsmParser(compile.compilerProps);
info.supportsFiltersInBinary = true;
if (process.platform == "linux") {
- var wine = env.gccProps("wine");
+ var wine = env.ceProps("wine");
var origExec = compile.exec;
compile.exec = function (command, args, options) {
if (command.toLowerCase().endsWith(".exe")) {
diff --git a/lib/compilers/default.js b/lib/compilers/default.js
index 759d2bbe8..64a542760 100644
--- a/lib/compilers/default.js
+++ b/lib/compilers/default.js
@@ -24,7 +24,7 @@
const Compile = require('../base-compiler');
-module.exports = function (info, env) {
- var comp = new Compile(info, env);
+module.exports = function (info, env, langId) {
+ var comp = new Compile(info, env, langId);
return comp.initialise();
}; \ No newline at end of file
diff --git a/lib/compilers/golang.js b/lib/compilers/golang.js
index 18a299f17..df63bc8bf 100644
--- a/lib/compilers/golang.js
+++ b/lib/compilers/golang.js
@@ -25,8 +25,8 @@
const Compile = require('../base-compiler'),
_ = require('underscore-node');
-function compilenewgol(info, env) {
- const compiler = new Compile(info, env);
+function compilenewgol(info, env, langId) {
+ const compiler = new Compile(info, env, langId);
compiler.originalGetDefaultExecOptions = compiler.getDefaultExecOptions;
function convertNewGoL(code) {
@@ -70,7 +70,7 @@ function compilenewgol(info, env) {
compiler.getDefaultExecOptions = function () {
const execOptions = this.originalGetDefaultExecOptions();
- const goroot = this.env.compilerProps("compiler." + this.compiler.id + ".goroot");
+ const goroot = this.compilerProps("compiler." + this.compiler.id + ".goroot");
if (goroot) {
execOptions.env.GOROOT = goroot;
}
diff --git a/lib/compilers/haskell.js b/lib/compilers/haskell.js
index a4080192f..78963748e 100644
--- a/lib/compilers/haskell.js
+++ b/lib/compilers/haskell.js
@@ -1,7 +1,7 @@
var Compile = require('../base-compiler');
-function compileHaskell(info, env) {
- var compiler = new Compile(info, env);
+function compileHaskell(info, env, langId) {
+ var compiler = new Compile(info, env, langId);
compiler.optionsForFilter = function (filters, outputFilename, userOptions) {
return ['-S', '-g', '-o', this.filename(outputFilename)];
};
diff --git a/lib/compilers/ispc.js b/lib/compilers/ispc.js
index 3bb0f124f..ff2b8054f 100644
--- a/lib/compilers/ispc.js
+++ b/lib/compilers/ispc.js
@@ -1,7 +1,7 @@
var Compile = require('../base-compiler');
-function compileISPC(info, env) {
- var compiler = new Compile(info, env);
+function compileISPC(info, env, langId) {
+ var compiler = new Compile(info, env, langId);
compiler.optionsForFilter = function (filters, outputFilename, userOptions) {
return ['--target=sse2-i32x4', '--emit-asm', '-g', '-o', this.filename(outputFilename)];
};
diff --git a/lib/compilers/ldc.js b/lib/compilers/ldc.js
index 11de1e66c..ffee8bf95 100644
--- a/lib/compilers/ldc.js
+++ b/lib/compilers/ldc.js
@@ -25,8 +25,8 @@
var Compile = require('../base-compiler'),
argumentParsers = require("./argument-parsers");
-function compileLdc(info, env) {
- var compiler = new Compile(info, env);
+function compileLdc(info, env, langId) {
+ var compiler = new Compile(info, env, langId);
compiler.compiler.supportsIntel = true;
compiler.optionsForFilter = function (filters, outputFilename, userOptions) {
var options = ['-g', '-of', this.filename(outputFilename)];
diff --git a/lib/compilers/pascal.js b/lib/compilers/pascal.js
index f5855f6cd..e4f0fcbc0 100644
--- a/lib/compilers/pascal.js
+++ b/lib/compilers/pascal.js
@@ -24,15 +24,14 @@
"use strict";
var Compile = require('../base-compiler'),
- logger = require('../logger').logger,
PascalDemangler = require('../pascal-support').demangler,
utils = require('../utils'),
fs = require("fs"),
path = require("path");
-function compileFPC(info, env) {
+function compileFPC(info, env, langId) {
var demangler = new PascalDemangler();
- var compiler = new Compile(info, env);
+ var compiler = new Compile(info, env, langId);
compiler.supportsOptOutput = false;
var originalExecBinary = compiler.execBinary;
diff --git a/lib/compilers/rust.js b/lib/compilers/rust.js
index 6d2295cbe..2fc60fa2b 100644
--- a/lib/compilers/rust.js
+++ b/lib/compilers/rust.js
@@ -25,8 +25,8 @@
var Compile = require('../base-compiler'),
_ = require('underscore-node');
-function compileRust(info, env) {
- var compiler = new Compile(info, env);
+function compileRust(info, env, langId) {
+ var compiler = new Compile(info, env, langId);
compiler.compiler.supportsIntel = true;
compiler.optionsForFilter = function (filters, outputFilename, userOptions) {
var options = ['-C', 'debuginfo=1', '-o', this.filename(outputFilename)];
diff --git a/lib/compilers/swift.js b/lib/compilers/swift.js
index 884828852..ffd39a295 100644
--- a/lib/compilers/swift.js
+++ b/lib/compilers/swift.js
@@ -1,8 +1,8 @@
const Compile = require('../base-compiler'),
logger = require('../logger').logger;
-function compileSwift(info, env) {
- const compiler = new Compile(info, env);
+function compileSwift(info, env, langId) {
+ const compiler = new Compile(info, env, langId);
compiler.handlePostProcessResult = function (result, postResult) {
result.asm = postResult.stdout;
diff --git a/lib/languages.js b/lib/languages.js
new file mode 100644
index 000000000..258a9b646
--- /dev/null
+++ b/lib/languages.js
@@ -0,0 +1,116 @@
+// Copyright (c) 2012-2017, Matt Godbolt
+//
+// 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.
+
+/* id is what we internally use to identify the language.
+ *MUST BE* Same as the key so we can always query for it, everywhere.
+ Used in: properties, url redirect, lang dropdown
+ name is what we display to the user
+ Used in: UI
+ monaco is how we tell the monaco editor which language is this
+ Used in: Monaco.editor.language
+ extension is an array for the usual extensions of the language.
+ The first one declared will be used as the default, else txt
+ Leading point is needed
+ Used in: Save to file extension
+*/
+
+const languages = {
+ 'c++': {
+ id: 'c++',
+ name: 'C++',
+ monaco: 'cppp',
+ extensions: ['.cpp', '.cxx', '.h', '.hpp', '.hxx', '.c']
+ },
+ cppx: {
+ id: 'cppx',
+ name: 'Cppx',
+ monaco: 'cppp',
+ extensions: ['.cpp', '.cxx', '.h', '.hpp', '.hxx', '.c']
+ },
+ c: {
+ id: 'c',
+ name: 'C',
+ monaco: 'c',
+ extensions: ['.c', '.h']
+ },
+ rust: {
+ id: 'rust',
+ name: 'Rust',
+ monaco: 'rust',
+ extensions: ['.rs']
+ },
+ d: {
+ id: 'd',
+ name: 'D',
+ monaco: 'd',
+ extensions: ['.d']
+ },
+ go: {
+ id: 'go',
+ name: 'Go',
+ monaco: 'go',
+ extensions: ['.go']
+ },
+ ispc: {
+ id: 'ispc',
+ name: 'ispc',
+ monaco: 'ispc',
+ extensions: ['.ispc']
+ },
+ haskell: {
+ id: 'haskell',
+ name: 'Haskell',
+ monaco: 'haskell',
+ extensions: ['.hs', '.haskell']
+ },
+ swift: {
+ id: 'swift',
+ name: 'Swift',
+ monaco: 'swift',
+ extensions: ['.swift']
+ },
+ pascal: {
+ id: 'pascal',
+ name: 'Pascal',
+ monaco: 'pascal',
+ extensions: ['.pas']
+ }
+};
+
+const fs = require('fs-extra');
+const _ = require('underscore-node');
+const path = require('path');
+_.each(languages, lang => {
+ try {
+ const example = fs.readFileSync(path.join('examples', lang.id, 'default' + lang.extensions[0]), 'utf8');
+ lang.example = example;
+ } catch (error) {
+ lang.example = "Oops, something went wrong and we could not get the default code for this language.";
+ }
+});
+
+module.exports = {
+ list: languages
+};
diff --git a/lib/sources/builtin.js b/lib/sources/builtin.js
index d5252bc05..aaf78b3be 100644
--- a/lib/sources/builtin.js
+++ b/lib/sources/builtin.js
@@ -24,27 +24,31 @@
const props = require('../properties.js'),
path = require('path'),
- fs = require('fs');
+ fs = require('fs'),
+ _ = require('underscore-node');
+const basePath = props.get('builtin', 'sourcePath', './examples/');
+const replacer = new RegExp('_', 'g');
+const examples = _.flatten(
+ fs.readdirSync(basePath)
+ .map(folder => {
+ const folerPath = path.join(basePath, folder);
+ return fs.readdirSync(folerPath)
+ .map(file => {
+ const filePath = path.join(folerPath, file);
+ const fileName = path.parse(file).name;
+ return {lang: folder, name: fileName.replace(replacer, ' '), path: filePath, file: fileName};
+ })
+ .filter(descriptor => descriptor.name !== "default")
+ .sort((x, y) => x.name.localeCompare(y.name));
+ }));
-const sourcePath = props.get('builtin', 'sourcepath', './examples/c++');
-const sourceMatch = new RegExp(props.get('builtin', 'extensionRe', '.*\\.cpp$'));
-const examples = fs.readdirSync(sourcePath)
- .filter(file => file.match(sourceMatch))
- .map(file => {
- const nicename = file.replace(/\.cpp$/, '');
- return {urlpart: nicename, name: nicename.replace(/_/g, ' '), path: path.join(sourcePath, file)};
- }).sort((x, y) => x.name.localeCompare(y.name));
-
-const byUrlpart = {};
-examples.forEach(e => byUrlpart[e.urlpart] = e.path);
-
-function load(filename) {
- const path = byUrlpart[filename];
- if (!path) {
+function load(lang, filename) {
+ const example = _.find(examples, example => example.lang === lang && example.file === filename);
+ if (!example) {
return Promise.reject("No such path");
}
return new Promise((resolve, reject) => {
- fs.readFile(path, 'utf-8', function (err, res) {
+ fs.readFile(example.path, 'utf-8', (err, res) => {
if (err) {
reject(err);
} else {
@@ -55,7 +59,9 @@ function load(filename) {
}
function list() {
- return Promise.resolve(examples.map(example => ({urlpart: example.urlpart, name: example.name})));
+ return Promise.resolve(examples.map(example => {
+ return {file: example.file, name: example.name, lang: example.lang};
+ }));
}
module.exports.load = load;
diff --git a/static/ast-view.js b/static/ast-view.js
index c14f14884..d45ffef27 100644
--- a/static/ast-view.js
+++ b/static/ast-view.js
@@ -28,7 +28,6 @@ define(function (require) {
var FontScale = require('fontscale');
var monaco = require('monaco');
- var options = require('options');
var _ = require('underscore');
var $ = require('jquery');
diff --git a/static/compiler-service.js b/static/compiler-service.js
index 6cc50dd04..2c521f616 100644
--- a/static/compiler-service.js
+++ b/static/compiler-service.js
@@ -38,20 +38,22 @@ define(function (require) {
return JSON.stringify(n).length;
}
});
- this.compilersById = _.chain(options.compilers)
- .map(function (compiler) {
- var aliases = [];
- aliases.push([compiler.id, compiler]);
- if (compiler.alias) aliases.push([compiler.alias, compiler]);
- return aliases;
- })
- .flatten(true)
- .object()
- .value();
+ this.compilersByLang = {};
+ _.each(options.compilers, function (compiler) {
+ if (!this.compilersByLang[compiler.lang]) this.compilersByLang[compiler.lang] = {};
+ this.compilersByLang[compiler.lang][compiler.id] = compiler;
+ if (compiler.alias) {
+ this.compilersByLang[compiler.lang][compiler.alias] = compiler;
+ }
+ }, this);
}
- CompilerService.prototype.getCompilerById = function (id) {
- return this.compilersById[id];
+ CompilerService.prototype.getCompilersForLang = function (langId) {
+ return this.compilersByLang[langId];
+ };
+
+ CompilerService.prototype.findCompiler = function (langId, compilerId) {
+ return this.getCompilersForLang(langId)[compilerId];
};
CompilerService.prototype.submit = function (request) {
diff --git a/static/compiler.js b/static/compiler.js
index 2ab24dc30..a4fe29c58 100644
--- a/static/compiler.js
+++ b/static/compiler.js
@@ -44,7 +44,7 @@ define(function (require) {
var OpcodeCache = new LruCache({
max: 64 * 1024,
- length: function (n) {
+ length:function (n) {
return JSON.stringify(n).length;
}
});
@@ -72,11 +72,14 @@ define(function (require) {
this.domRoot.html($('#compiler').html());
this.id = state.id || hub.nextCompilerId();
this.sourceEditorId = state.source || 1;
- this.compiler = this.compilerService.getCompilerById(state.compiler) ||
- this.compilerService.getCompilerById(options.defaultCompiler);
+ this.currentLangId = state.lang || "c++";
+ this.originalCompilerId = state.compiler;
+ this.compiler = this.compilerService.findCompiler(this.currentLangId, this.originalCompilerId) ||
+ this.compilerService.findCompiler(this.currentLangId, options.defaultCompiler[this.currentLangId]);
+ this.selectedCompilerByLang = {};
this.deferCompiles = hub.deferred;
this.needsCompile = false;
- this.options = state.options || options.compileOptions;
+ this.options = state.options || options.compileOptions[this.currentLangId];
this.filters = new Toggles(this.domRoot.find('.filters'), patchOldFilters(state.filters));
this.source = '';
this.assembly = [];
@@ -96,16 +99,21 @@ define(function (require) {
this.gccDumpButton = this.domRoot.find('.btn.view-gccdump');
this.cfgButton = this.domRoot.find('.btn.view-cfg');
this.libsButton = this.domRoot.find('.btn.show-libs');
-
- this.availableLibs = $.extend(true, {}, options.libs);
-
this.compileTimeLabel = this.domRoot.find('.compile-time');
this.compileClearCache = this.domRoot.find('.clear-cache');
+ this.availableLibs = {};
+ this.updateAvailableLibs = function() {
+ if (!this.availableLibs[this.currentLangId]) {
+ this.availableLibs[this.currentLangId] = $.extend(true, {}, options.libs[this.currentLangId]);
+ }
+ };
+ this.updateAvailableLibs();
_.each(state.libs, _.bind(function (lib) {
- if (this.availableLibs[lib.name] && this.availableLibs[lib.name].versions &&
- this.availableLibs[lib.name].versions[lib.ver]) {
- this.availableLibs[lib.name].versions[lib.ver].used = true;
+ if (this.availableLibs[this.currentLangId][lib.name] &&
+ this.availableLibs[this.currentLangId][lib.name].versions &&
+ this.availableLibs[this.currentLangId][lib.name].versions[lib.ver]) {
+ this.availableLibs[this.currentLangId][lib.name].versions[lib.ver].used = true;
}
}, this));
@@ -116,16 +124,18 @@ define(function (require) {
valueField: 'id',
labelField: 'name',
searchField: ['name'],
- options: options.compilers,
+ options: _.map(this.getCurrentLangCompilers(), _.identity),
items: this.compiler ? [this.compiler.id] : []
}).on('change', _.bind(function (e) {
var val = $(e.target).val();
- ga('send', {
- hitType: 'event',
- eventCategory: 'SelectCompiler',
- eventAction: val
- });
- this.onCompilerChange(val);
+ if (val) {
+ ga('send', {
+ hitType: 'event',
+ eventCategory: 'SelectCompiler',
+ eventAction: val
+ });
+ this.onCompilerChange(val);
+ }
}, this));
var optionsChange = _.debounce(_.bind(function (e) {
this.onOptionsChange($(e.target).val());
@@ -260,6 +270,7 @@ define(function (require) {
this.eventHub.emit('filtersChange', this.id, this.getEffectiveFilters());
}
}, this);
+ this.eventHub.on('languageChange', this.onLanguageChange, this);
this.eventHub.emit('requestSettings');
this.sendCompiler();
this.updateCompilerName();
@@ -345,110 +356,7 @@ define(function (require) {
insertPoint.addChild(createCfgView);
}, this));
-
- var updateLibsUsed = _.bind(function () {
- var libsCount = Object.keys(this.availableLibs).length;
- if (libsCount === 0) {
- return $('<p></p>')
- .text('No libs configured for this language yet. ')
- .append($('<a></a>')
- .attr('target', '_blank')
- .attr('rel', 'noopener noreferrer')
- .attr('href', 'https://github.com/mattgodbolt/compiler-explorer/issues/new')
- .text('You can suggest us one at any time ')
- .append($('<sup></sup>')
- .addClass('glyphicon glyphicon-new-window')
- .width('16px')
- .height('16px')
- .attr('title', 'Opens in a new window')
- )
- );
- }
- var columnCount = Math.ceil(libsCount / 5);
- var currentLibIndex = -1;
-
- var libLists = [];
- for (var i = 0; i < columnCount; i++) {
- libLists.push($('<ul></ul>').addClass('lib-list'));
- }
-
- // Utility function so we can iterate indefinetly over our lists
- var getNextList = function () {
- currentLibIndex = (currentLibIndex + 1) % columnCount;
- return libLists[currentLibIndex];
- };
-
- var onChecked = _.bind(function (e) {
- var elem = $(e.target);
- // Uncheck every lib checkbox with the same name if we're checking the target
- if (elem.prop('checked')) {
- var others = $.find('input[name=\'' + elem.prop('name') + '\']');
- _.each(others, function (other) {
- $(other).prop('checked', false);
- });
- // Recheck the targeted one
- elem.prop('checked', true);
- }
- // And now do the same with the availableLibs object
- _.each(this.availableLibs[elem.prop('data-lib')].versions, function (version) {
- version.used = false;
- });
- this.availableLibs[elem.prop('data-lib')].versions[elem.prop('data-version')].used = elem.prop('checked');
- this.saveState();
- this.compile();
- }, this);
-
- _.each(this.availableLibs, function (lib, libKey) {
- var libsList = getNextList();
- var libCat = $('<li></li>')
- .append($('<span></span>')
- .text(lib.name)
- .addClass('lib-header')
- )
- .addClass('lib-item');
-
- var libGroup = $('<div></div>');
-
- if (libsList.children().length > 0)
- libsList.append($('<hr>').addClass('lib-separator'));
-
- _.each(lib.versions, function (version, vKey) {
- libGroup.append($('<div></div>')
- .append($('<input type="checkbox">')
- .addClass('lib-checkbox')
- .prop('data-lib', libKey)
- .prop('data-version', vKey)
- .prop('checked', version.used)
- .prop('name', libKey)
- .on('change', onChecked)
- ).append($('<label></label>')
- .addClass('lib-label')
- .text(lib.name + ' ' + version.version)
- .on('click', function () {
- $(this).parent().find('.lib-checkbox').trigger('click');
- })
- )
- );
- });
- libGroup.appendTo(libCat);
- libCat.appendTo(libsList);
- });
- return $('<div></div>').addClass('libs-container').append(libLists);
- }, this);
-
- this.libsButton.popover({
- container: 'body',
- content: updateLibsUsed(),
- html: true,
- placement: 'bottom',
- trigger: 'manual'
- }).click(_.bind(function () {
- this.libsButton.popover('show');
- }, this)).on('inserted.bs.popover', function (e) {
- $(e.target).content = updateLibsUsed().html();
- }).on('show.bs.popover', function () {
- $(this).data('bs.popover').tip().css('max-width', '100%').css('width', 'auto');
- });
+ this.updateLibsDropdown();
this.compileClearCache.on('click', _.bind(function () {
this.compilerService.cache.reset();
@@ -541,7 +449,8 @@ define(function (require) {
var request = {
source: expanded || '',
compiler: this.compiler ? this.compiler.id : '',
- options: options
+ options: options,
+ lang: this.currentLangId
};
if (!this.compiler) {
this.onCompileResponse(request, errorResult('<Please select a compiler>'), false);
@@ -861,7 +770,7 @@ define(function (require) {
};
Compiler.prototype.onCompilerChange = function (value) {
- this.compiler = this.compilerService.getCompilerById(value);
+ this.compiler = this.compilerService.findCompiler(this.currentLangId, value);
this.saveState();
this.compile();
this.updateButtons();
@@ -906,7 +815,8 @@ define(function (require) {
// NB must *not* be effective filters
filters: this.filters.get(),
wantOptInfo: this.wantOptInfo,
- libs: libs
+ libs: libs,
+ lang: this.currentLangId
};
this.fontScale.addState(state);
return state;
@@ -1128,6 +1038,150 @@ define(function (require) {
);
};
+ Compiler.prototype.updateLibsDropdown = function () {
+ this.updateAvailableLibs();
+ this.libsButton.popover({
+ container: 'body',
+ content: _.bind(function () {
+ var libsCount = Object.keys(this.availableLibs[this.currentLangId]).length;
+ if (libsCount === 0) {
+ return $('<p></p>')
+ .text('No libs configured for ' + options.languages[this.currentLangId].name + ' yet. ')
+ .append($('<a></a>')
+ .attr('target', '_blank')
+ .attr('rel', 'noopener noreferrer')
+ .attr('href', 'https://github.com/mattgodbolt/compiler-explorer/issues/new')
+ .text('You can suggest us one at any time ')
+ .append($('<sup></sup>')
+ .addClass('glyphicon glyphicon-new-window')
+ .width('16px')
+ .height('16px')
+ .attr('title', 'Opens in a new window')
+ )
+ );
+ }
+ var columnCount = Math.ceil(libsCount / 5);
+ var currentLibIndex = -1;
+
+ var libLists = [];
+ for (var i = 0; i < columnCount; i++) {
+ libLists.push($('<ul></ul>').addClass('lib-list'));
+ }
+
+ // Utility function so we can iterate indefinetly over our lists
+ var getNextList = function () {
+ currentLibIndex = (currentLibIndex + 1) % columnCount;
+ return libLists[currentLibIndex];
+ };
+
+ var onChecked = _.bind(function (e) {
+ var elem = $(e.target);
+ // Uncheck every lib checkbox with the same name if we're checking the target
+ if (elem.prop('checked')) {
+ var others = $.find('input[name=\'' + elem.prop('name') + '\']');
+ _.each(others, function (other) {
+ $(other).prop('checked', false);
+ });
+ // Recheck the targeted one
+ elem.prop('checked', true);
+ }
+ // And now do the same with the availableLibs object
+ _.each(this.availableLibs[this.currentLangId][elem.prop('data-lib')].versions, function (version) {
+ version.used = false;
+ });
+ this.availableLibs[this.currentLangId][elem.prop('data-lib')].versions[elem.prop('data-version')].used = elem.prop('checked');
+ this.saveState();
+ this.compile();
+ }, this);
+
+ _.each(this.availableLibs[this.currentLangId], function (lib, libKey) {
+ var libsList = getNextList();
+ var libCat = $('<li></li>')
+ .append($('<span></span>')
+ .text(lib.name)
+ .addClass('lib-header')
+ )
+ .addClass('lib-item');
+
+ var libGroup = $('<div></div>');
+
+ if (libsList.children().length > 0)
+ libsList.append($('<hr>').addClass('lib-separator'));
+
+ _.each(lib.versions, function (version, vKey) {
+ libGroup.append($('<div></div>')
+ .append($('<input type="checkbox">')
+ .addClass('lib-checkbox')
+ .prop('data-lib', libKey)
+ .prop('data-version', vKey)
+ .prop('checked', version.used)
+ .prop('name', libKey)
+ .on('change', onChecked)
+ ).append($('<label></label>')
+ .addClass('lib-label')
+ .text(lib.name + ' ' + version.version)
+ .on('click', function () {
+ $(this).parent().find('.lib-checkbox').trigger('click');
+ })
+ )
+ );
+ });
+ libGroup.appendTo(libCat);
+ libCat.appendTo(libsList);
+ });
+ return $('<div></div>').addClass('libs-container').append(libLists);
+ }, this),
+ html: true,
+ placement: 'bottom',
+ trigger: 'manual'
+ }).click(_.bind(function () {
+ this.libsButton.popover('show');
+ }, this)).on('show.bs.popover', function () {
+ $(this).data('bs.popover').tip().css('max-width', '100%').css('width', 'auto');
+ });
+ };
+
+ Compiler.prototype.onLanguageChange = function (editorId, newLangId) {
+ if (this.sourceEditorId === editorId) {
+ var oldLangId = this.currentLangId;
+ this.currentLangId = newLangId;
+ // Store the current selected compiler to come back to it later in the same session (Not state sotred!)
+ this.selectedCompilerByLang[oldLangId] = this.compiler ? this.compiler.id : options.defaultCompiler[oldLangId];
+ this.updateCompilersSelector();
+ this.updateLibsDropdown();
+ }
+ };
+
+ Compiler.prototype.getCurrentLangCompilers = function () {
+ return this.compilerService.getCompilersForLang(this.currentLangId);
+ };
+
+ Compiler.prototype.updateCompilersSelector = function () {
+ var selector = this.domRoot.find('.compiler-picker')[0].selectize;
+ selector.clearOptions();
+ selector.load(_.bind(function (callback) {
+ callback(_.map(this.getCurrentLangCompilers(), _.identity));
+ }, this));
+ var defaultOrFirst = _.bind(function defaultOrFirst () {
+ // If the default is a valid compiler, return it
+ var defaultCompiler = options.defaultCompiler[this.currentLangId];
+ if (defaultCompiler && options.compilers[defaultCompiler]) return defaultCompiler;
+ // Else try to find the first one for this language
+ var value = _.find(options.compilers, _.bind(function (compiler) {
+ return compiler.lang === this.currentLangId;
+ }, this));
+
+ // Return the first, or an empty string if none found (Should prob report this one...)
+ return value && value.id ? value.id : "";
+ }, this);
+
+ selector.setValue([this.selectedCompilerByLang[this.currentLangId] || defaultOrFirst()]);
+ };
+
+ Compiler.prototype.findCompiler = function (langId, compilerId) {
+ return this.compilerService.findCompiler(langId, compilerId);
+ };
+
return {
Compiler: Compiler
};
diff --git a/static/conformance-view.js b/static/conformance-view.js
index b50ab0a20..39a51603e 100644
--- a/static/conformance-view.js
+++ b/static/conformance-view.js
@@ -42,9 +42,9 @@ define(function (require) {
this.addCompilerButton = this.domRoot.find('.add-compiler');
this.selectorTemplate = $('#compiler-selector .compiler-row');
this.editorId = state.editorid;
- this.source = state.source || "";
this.nextSelectorId = 0;
this.maxCompilations = options.cvCompilerCountMax || 6;
+ this.langId = state.langId || 'c++';
this.status = {
allowCompile: false,
@@ -67,6 +67,7 @@ define(function (require) {
this.eventHub.on('editorChange', this.onEditorChange, this);
this.eventHub.on('editorClose', this.onEditorClose, this);
+ this.eventHub.on('languageChange', this.onLanguageChange, this);
this.addCompilerButton.on('click', _.bind(function () {
this.addCompilerSelector();
@@ -129,6 +130,10 @@ define(function (require) {
this.selectorList.append(newEntry);
var status = newEntry.find('.status').attr("data-cv", config.cv);
+ var langId = this.langId;
+ var isVisible = function (compiler) {
+ return compiler.lang == langId;
+ };
newEntry.find('.compiler-picker')
.attr("data-cv", config.cv)
@@ -137,7 +142,7 @@ define(function (require) {
valueField: 'id',
labelField: 'name',
searchField: ['name'],
- options: options.compilers,
+ options: _.filter(options.compilers, isVisible),
items: config.compilerId ? [config.compilerId] : []
})
.on('change', _.bind(function () {
@@ -149,6 +154,7 @@ define(function (require) {
}, this));
this.handleStatusIcon(status, {code: 0, text: ""});
this.handleToolbarUI();
+ this.saveState();
};
Conformance.prototype.removeCompilerSelector = function (cv) {
@@ -162,8 +168,9 @@ define(function (require) {
this.saveState();
};
- Conformance.prototype.onEditorChange = function (editorId, newSource) {
+ Conformance.prototype.onEditorChange = function (editorId, newSource, langId) {
if (editorId == this.editorId) {
+ this.langId = langId;
this.source = newSource;
this.compileAll();
}
@@ -190,6 +197,7 @@ define(function (require) {
};
Conformance.prototype.compileAll = function () {
+ if (!this.source) return;
// Hide previous status icons
this.selectorList.find('.status').css("visibility", "hidden");
this.compilerService.expand(this.source).then(_.bind(function (expanded) {
@@ -251,11 +259,10 @@ define(function (require) {
Conformance.prototype.currentState = function () {
var state = {
editorid: this.editorId,
- source: this.source,
+ langId: this.langId,
compilers: []
};
_.each(this.selectorList.children(), _.bind(function (child) {
- var status = $(child).find('.status');
state.compilers.push({
// Code we have
cv: $(child).attr("data-cv"),
@@ -276,6 +283,18 @@ define(function (require) {
this.selectorList.css("height", this.domRoot.height() - this.domRoot.find('.top-bar').outerHeight(true));
};
+ Conformance.prototype.onLanguageChange = function (editorId, newLangId) {
+ if (editorId === this.editorId) {
+ this.langId = newLangId;
+ // Sorry for this future me. You promised to come back and enchance this after the unification merge
+ // Hopefuly it's been soon enough for noone to notice :)
+ this.selectorList.children().remove();
+ this.nextSelectorId = 0;
+ this.handleToolbarUI();
+ this.saveState();
+ }
+ };
+
return {
Conformance: Conformance
};
diff --git a/static/editor.js b/static/editor.js
index 843defb6f..366f993be 100644
--- a/static/editor.js
+++ b/static/editor.js
@@ -41,10 +41,13 @@ define(function (require) {
require('./haskell-mode');
require('./swift-mode');
require('./pascal-mode');
+ require('selectize');
var loadSave = new loadSaveLib.LoadSave();
- function Editor(hub, state, container, lang, defaultSrc) {
+ var languages = options.languages;
+
+ function Editor(hub, state, container) {
this.id = state.id || hub.nextEditorId();
this.container = container;
this.domRoot = container.getElement();
@@ -57,63 +60,28 @@ define(function (require) {
this.asmByCompiler = {};
this.busyCompilers = {};
this.colours = [];
- this.lastCompilerIDResponse = -1;
this.decorations = {};
this.prevDecorations = [];
this.fadeTimeoutId = -1;
- var cmMode;
- // The first one is used as the default file extension when saving to local file.
- // All of them are used as the contents of the accept attribute of the file input
- var extensions = [];
- switch (lang.toLowerCase()) {
- default:
- cmMode = "cppp";
- extensions = ['.cpp', '.cxx', '.h', '.hpp', '.hxx'];
- break;
- case "c":
- // C Plus Plus Plus. C++ without invalid keywords!
- cmMode = "cppp";
- extensions = ['.cpp', '.cxx', '.h', '.hpp', '.hxx'];
- break;
- case "rust":
- cmMode = "rust";
- extensions = ['.rs'];
- break;
- case "d":
- cmMode = "d";
- extensions = ['.d'];
- break;
- case "go":
- cmMode = "go";
- extensions = ['.go'];
- break;
- case "ispc":
- cmMode = "ispc";
- extensions = ['.ispc'];
- break;
- case "haskell":
- cmMode = "haskell";
- extensions = ['.hs'];
- break;
- case "swift":
- cmMode = "swift";
- extensions = ['.swift'];
- break;
- case "pascal":
- cmMode = "pascal";
- extensions = ['.pas'];
- break;
+ this.editorSourceByLang = {};
+
+ this.languageBtn = this.domRoot.find('.change-language');
+ this.needsLanguageUpdate = !(state.lang && languages[state.lang]);
+ this.currentLanguage = languages["c++"];
+ if (state.lang && languages[state.lang]) {
+ this.currentLanguage = languages[state.lang];
+ } else if (hub.lastOpenedLangId && languages[hub.lastOpenedLangId]) {
+ this.currentLanguage = languages[hub.lastOpenedLangId];
}
var root = this.domRoot.find(".monaco-placeholder");
var legacyReadOnly = state.options && !!state.options.readOnly;
this.editor = monaco.editor.create(root[0], {
- value: state.source || defaultSrc || "",
scrollBeyondLastLine: false,
- language: cmMode,
+ language: this.currentLanguage.monaco,
fontFamily: 'Fira Mono',
readOnly: !!options.readOnly || legacyReadOnly,
glyphMargin: !options.embedded,
@@ -125,9 +93,17 @@ define(function (require) {
folding: true,
lineNumbersMinChars: options.embedded ? 1 : 3,
emptySelectionClipboard: true,
- autoIndent: true,
+ autoIndent: true
});
+ if (state.source) {
+ this.setSource(state.source);
+ } else {
+ this.updateEditorCode();
+ }
+
+ this.initLoadSaver();
+
var startFolded = /^[/*#;]+\s*setup.*/;
if (state.source && state.source.match(startFolded)) {
var foldAction = this.editor.getAction('editor.fold');
@@ -175,6 +151,18 @@ define(function (require) {
}, this)
});
+ this.editor.addAction({
+ id: 'viewasm',
+ label: 'Scroll to assembly',
+ keybindings: [monaco.KeyMod.CtrlCmd | monaco.KeyCode.F10],
+ keybindingContext: null,
+ contextMenuGroupId: 'navigation',
+ contextMenuOrder: 1.5,
+ run: function (ed) {
+ tryCompilerLinkLine(ed.getPosition().lineNumber, true);
+ }
+ });
+
var tryCompilerLinkLine = _.bind(function (thisLineNumber, reveal) {
_.each(this.asmByCompiler, _.bind(function (asms, compilerId) {
var targetLines = [];
@@ -194,18 +182,6 @@ define(function (require) {
}, this));
}, this);
- this.editor.addAction({
- id: 'viewasm',
- label: 'Scroll to assembly',
- keybindings: [monaco.KeyMod.CtrlCmd | monaco.KeyCode.F10],
- keybindingContext: null,
- contextMenuGroupId: 'navigation',
- contextMenuOrder: 1.5,
- run: function (ed) {
- tryCompilerLinkLine(ed.getPosition().lineNumber, true);
- }
- });
-
this.editor.onMouseLeave(_.bind(function () {
this.fadeTimeoutId = setTimeout(_.bind(function () {
clearCompilerLinkedLines();
@@ -217,10 +193,7 @@ define(function (require) {
if (e !== null && e.target !== null && this.settings.hoverShowSource && e.target.position !== null) {
tryCompilerLinkLine(e.target.position.lineNumber, false);
}
- },
- this),
- 250
- );
+ }, this), 250);
this.editor.onMouseMove(_.bind(function (e) {
this.mouseMoveThrottledFunction(e);
@@ -231,9 +204,29 @@ define(function (require) {
}
}, this));
+ this.updateEditorLayout = _.bind(function () {
+ var topBarHeight = this.domRoot.find(".top-bar").outerHeight(true) || 0;
+ this.editor.layout({width: this.domRoot.width(), height: this.domRoot.height() - topBarHeight});
+ }, this);
+
this.fontScale = new FontScale(this.domRoot, state, this.editor);
this.fontScale.on('change', _.bind(this.updateState, this));
+ this.languageBtn.selectize({
+ sortField: 'name',
+ valueField: 'id',
+ labelField: 'name',
+ searchField: ['name'],
+ options: _.map(languages, _.identity),
+ items: [this.currentLanguage.id]
+ }).on('change', _.bind(function (e) {
+ this.onLanguageChange($(e.target).val());
+ }, this));
+
+ this.changeLanguage = function (newLang) {
+ this.languageBtn[0].selectize.setValue(newLang);
+ };
+
// We suppress posting changes until the user has stopped typing by:
// * Using _.debounce() to run emitChange on any key event or change
// only after a delay.
@@ -252,21 +245,8 @@ define(function (require) {
// this.debouncedEmitChange();
// }, this));
- var layout = _.bind(function () {
- var topBarHeight = this.domRoot.find(".top-bar").outerHeight(true) || 0;
- this.editor.layout({width: this.domRoot.width(), height: this.domRoot.height() - topBarHeight});
- }, this);
-
- this.domRoot.find('.load-save').click(_.bind(function () {
- loadSave.run(_.bind(function (text) {
- this.editor.setValue(text);
- this.updateState();
- this.maybeEmitChange();
- }, this), this.getSource(), extensions);
- }, this));
-
- container.on('resize', layout);
- container.on('shown', layout);
+ container.on('resize', this.updateEditorLayout);
+ container.on('shown', this.updateEditorLayout);
container.on('open', _.bind(function () {
this.eventHub.emit('editorOpen', this.id);
}, this));
@@ -275,7 +255,6 @@ define(function (require) {
this.eventHub.emit('editorClose', this.id);
this.editor.dispose();
}, this));
- container.setTitle(lang + " source #" + this.id);
this.container.layoutManager.on('initialised', function () {
// Once initialized, let everyone know what text we have.
this.maybeEmitChange();
@@ -290,7 +269,7 @@ define(function (require) {
this.eventHub.on('settingsChange', this.onSettingsChange, this);
this.eventHub.on('conformanceViewOpen', this.onConformanceViewOpen, this);
this.eventHub.on('conformanceViewClose', this.onConformanceViewClose, this);
- this.eventHub.on('resize', layout, this);
+ this.eventHub.on('resize', this.updateEditorLayout, this);
this.eventHub.emit('requestSettings');
// NB a new compilerConfig needs to be created every time; else the state is shared
@@ -324,21 +303,24 @@ define(function (require) {
this.container.layoutManager.root.contentItems[0];
insertPoint.addChild(conformanceConfig);
}, this));
+ this.container.setTitle(this.currentLanguage.name + " source #" + this.id);
+ this.eventHub.on('initialised', this.maybeEmitChange, this);
this.updateState();
}
Editor.prototype.maybeEmitChange = function (force) {
var source = this.getSource();
- if (!force && source == this.lastChangeEmitted) return;
+ if (!force && source === this.lastChangeEmitted) return;
this.lastChangeEmitted = source;
- this.eventHub.emit('editorChange', this.id, this.lastChangeEmitted);
+ this.eventHub.emit('editorChange', this.id, this.lastChangeEmitted, this.currentLanguage.id);
};
Editor.prototype.updateState = function () {
var state = {
id: this.id,
- source: this.getSource()
+ source: this.getSource(),
+ lang: this.currentLanguage.id
};
this.fontScale.addState(state);
this.container.setState(state);
@@ -420,6 +402,20 @@ define(function (require) {
Editor.prototype.onCompilerOpen = function (compilerId, editorId) {
if (editorId === this.id) {
// On any compiler open, rebroadcast our state in case they need to know it.
+ if (this.needsLanguageUpdate) {
+ var glCompiler =_.find(this.container.layoutManager.root.getComponentsByName("compiler"), function (compiler) {
+ return compiler.id === compilerId;
+ });
+ if (glCompiler) {
+ var selected = _.find(options.compilers, function (compiler) {
+ return compiler.id === glCompiler.originalCompilerId;
+ });
+ if (selected) {
+ this.needsLanguageUpdate = false;
+ this.changeLanguage(selected.lang);
+ }
+ }
+ }
this.maybeEmitChange(true);
this.ourCompilers[compilerId] = true;
}
@@ -470,7 +466,6 @@ define(function (require) {
}, this);
this.updateDecorations();
this.asmByCompiler[compilerId] = result.asm;
- this.lastCompilerIDResponse = compilerId;
this.numberUsedLines();
};
@@ -518,6 +513,40 @@ define(function (require) {
}
};
+ Editor.prototype.initLoadSaver = function () {
+ this.domRoot.find('.load-save')
+ .off('click')
+ .click(_.bind(function () {
+ loadSave.run(_.bind(function (text) {
+ this.setSource(text);
+ this.updateState();
+ this.maybeEmitChange();
+ }, this), this.getSource(), this.currentLanguage);
+ }, this));
+ };
+
+ Editor.prototype.onLanguageChange = function (newLangId) {
+ if (newLangId !== this.currentLanguage.id && languages[newLangId]) {
+ var oldLangId = this.currentLanguage.id;
+ // Save the current source, so we can come back to it later
+ this.editorSourceByLang[oldLangId] = this.getSource();
+ this.currentLanguage = languages[newLangId];
+ this.initLoadSaver();
+ monaco.editor.setModelLanguage(this.editor.getModel(), this.currentLanguage.monaco);
+ // And now set the editor value to either the saved one or the default to the new lang
+ this.updateEditorCode();
+ this.container.setTitle(this.currentLanguage.name + " source #" + this.id);
+ this.updateState();
+ // Broadcast the change to other panels
+ this.eventHub.emit("languageChange", this.id, newLangId);
+ }
+ };
+
+ // Called every time we change language, so we get the relevant code
+ Editor.prototype.updateEditorCode = function () {
+ this.setSource(this.editorSourceByLang[this.currentLanguage.id] || languages[this.currentLanguage.id].example);
+ };
+
return {
Editor: Editor
};
diff --git a/static/explorer.css b/static/explorer.css
index 194b4bccd..6b65ab21c 100644
--- a/static/explorer.css
+++ b/static/explorer.css
@@ -263,3 +263,7 @@ pre.content {
position: absolute;
right: 0;
}
+
+.change-language {
+ min-width: 100px !important;
+}
diff --git a/static/hub.js b/static/hub.js
index cf31d03c8..1fc27dd55 100644
--- a/static/hub.js
+++ b/static/hub.js
@@ -27,7 +27,6 @@ define(function (require) {
'use strict';
var _ = require('underscore');
- var options = require('options');
var editor = require('editor');
var compiler = require('compiler');
var output = require('output');
@@ -60,14 +59,14 @@ define(function (require) {
throw 'Ran out of ids!?';
};
- function Hub(layout, defaultSrc) {
+ function Hub(layout) {
this.layout = layout;
- this.defaultSrc = defaultSrc;
this.editorIds = new Ids();
this.compilerIds = new Ids();
this.compilerService = new CompilerService();
this.deferred = true;
this.deferredEmissions = [];
+ this.lastOpenedLangId = null;
// FIXME
// We can't avoid this self as _ is undefined at this point
@@ -122,6 +121,9 @@ define(function (require) {
layout.eventHub.on('compilerClose', function (id) {
this.compilerIds.remove(id);
}, this);
+ layout.eventHub.on('languageChange', function (editorId, langId) {
+ this.lastOpenedLangId = langId;
+ }, this);
layout.init();
this.undefer();
layout.eventHub.emit('initialised');
@@ -148,7 +150,7 @@ define(function (require) {
// NB there doesn't seem to be a better way to do this than reach into the config and rely on the fact nothing
// has used it yet.
container.parent.config.isClosable = true;
- return new editor.Editor(this, state, container, options.language, this.defaultSrc);
+ return new editor.Editor(this, state, container);
};
Hub.prototype.compilerFactory = function (container, state) {
diff --git a/static/loadSave.js b/static/loadSave.js
index 884e87088..c1d16a455 100644
--- a/static/loadSave.js
+++ b/static/loadSave.js
@@ -52,23 +52,31 @@ define(function (require) {
this.modal.find('.save-button').click(_.bind(this.onSaveToBrowserStorage, this));
this.modal.find('.save-file').click(_.bind(this.onSaveToFile, this));
- this.populateBuiltins();
+ this.fetchBuiltins();
}
- LoadSave.prototype.populateBuiltins = function () {
+ LoadSave.prototype.fetchBuiltins = function () {
$.getJSON('source/builtin/list', _.bind(function (list) {
- this.populate(
- this.modal.find('.examples'),
- _.map(list, _.bind(function (elem) {
- return {
- name: elem.name, load: _.bind(function () {
- this.doLoad(elem.urlpart);
- }, this)
- };
- }, this)));
+ this.savedBuiltins = list;
}, this));
};
+ LoadSave.prototype.populateBuiltins = function () {
+ var isVisible = _.bind(function (entry) {
+ return this.currentLanguage && this.currentLanguage.id === entry.lang;
+ }, this);
+ this.populate(this.modal.find('.examples'),
+ _.map(_.filter(this.savedBuiltins, isVisible), _.bind(function (elem) {
+ return {
+ name: elem.name,
+ load: _.bind(function () {
+ this.doLoad(elem);
+ }, this)
+ };
+ }, this))
+ );
+ };
+
LoadSave.prototype.populateLocalStorage = function () {
this.populate(
this.modal.find('.local-storage'),
@@ -108,13 +116,15 @@ define(function (require) {
this.modal.modal('hide');
};
- LoadSave.prototype.run = function (onLoad, editorText, extensions) {
+ LoadSave.prototype.run = function (onLoad, editorText, currentLanguage) {
this.populateLocalStorage();
this.onLoad = onLoad;
this.editorText = editorText;
// In case we don't send anything...
- this.extension = extensions[0] || '.txt';
- this.modal.find('.local-file').attr('accept', _.map(extensions, function (extension) {
+ this.currentLanguage = currentLanguage;
+ this.populateBuiltins();
+ this.extension = currentLanguage.extensions[0] || '.txt';
+ this.modal.find('.local-file').attr('accept', _.map(currentLanguage.extensions, function (extension) {
return extension + ', ';
}, this));
this.modal.modal();
@@ -126,6 +136,7 @@ define(function (require) {
this.alert.alert("Save name", "Invalid save name");
return;
}
+ name += " (" + this.currentLanguage.name + ")";
var done = _.bind(function () {
setLocalFile(name, this.editorText);
}, this);
@@ -153,11 +164,12 @@ define(function (require) {
}
};
- LoadSave.prototype.doLoad = function (urlpart) {
+ LoadSave.prototype.doLoad = function (element) {
// TODO: handle errors. consider promises...
- $.getJSON('source/builtin/load/' + urlpart, _.bind(function (response) {
- this.onLoad(response.file);
- }, this));
+ $.getJSON('source/builtin/load/' + element.lang + '/' + element.file,
+ _.bind(function (response) {
+ this.onLoad(response.file);
+ }, this));
this.modal.modal('hide');
};
diff --git a/static/main.js b/static/main.js
index 948dabfed..f424162f8 100644
--- a/static/main.js
+++ b/static/main.js
@@ -99,12 +99,11 @@ define(function (require) {
var options = require('options');
- var defaultSrc = $('.template .lang').text().trim();
var defaultConfig = {
settings: {showPopoutIcon: false},
content: [{type: 'row', content: [Components.getEditor(1), Components.getCompiler(1)]}]
};
-
+
$(window).bind('hashchange', function () {
// punt on hash events and just reload the page if there's a hash
if (window.location.hash.substr(1))
@@ -141,11 +140,11 @@ define(function (require) {
var hub;
try {
layout = new GoldenLayout(config, root);
- hub = new Hub(layout, defaultSrc);
+ hub = new Hub(layout);
} catch (e) {
Raven.captureException(e);
layout = new GoldenLayout(defaultConfig, root);
- hub = new Hub(layout, defaultSrc);
+ hub = new Hub(layout);
}
layout.on('stateChanged', function () {
var config = layout.toConfig();
diff --git a/static/themes/dark/theme-dark.css b/static/themes/dark/theme-dark.css
index 2ffe1c248..6a7844866 100644
--- a/static/themes/dark/theme-dark.css
+++ b/static/themes/dark/theme-dark.css
@@ -94,6 +94,11 @@ body {
background-color: #666 !important;
}
+.selectize-control.single .selectize-input.disabled {
+ color: #eee !important;
+ background: #000 !important;
+}
+
.dropdown-menu {
color: #eee !important;
background-color: #303030 !important;
diff --git a/test/builtin-tests.js b/test/builtin-tests.js
new file mode 100644
index 000000000..4201abf8b
--- /dev/null
+++ b/test/builtin-tests.js
@@ -0,0 +1,68 @@
+// Copyright (c) 2017, Matt Godbolt & Rubén Rincón
+// 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.
+
+const should = require('chai').should(),
+ builtin = require('../lib/sources/builtin'),
+ languages = require('../lib/languages').list,
+ _ = require('underscore-node');
+
+describe('Builtin sources', () => {
+ it('Does not include default code', () => {
+ builtin.list((_, list) => {
+ list.forEach(example => {
+ should.not.equal(example.name, "default");
+ });
+ });
+ });
+ it('Has a valid language listed', () => {
+ builtin.list((_, list) => {
+ list.forEach(example => {
+ should.not.equal(languages[example.lang], undefined, `Builtin ${example.name} has unrecognised language ${example.lang}`);
+ });
+ });
+ });
+ it('Has at least one example for each language', () => {
+ builtin.list((placeholder, list) => {
+ _.each(languages, lang => {
+ should.not.equal(_.find(list, elem => elem.lang === lang.id), undefined, `Language ${lang.name} does not have any builtins`);
+ });
+ });
+ });
+ it('Reports a string error if no example found', () => {
+ builtin.load('BADLANG', 'BADFILE', (error, result) => {
+ should.equal(result, undefined, 'A result should not be returned for bad requests');
+ error.should.be.a("string");
+ });
+ });
+ it('Reports something for every defined example', () => {
+ builtin.list((placeholder, examples) => {
+ examples.forEach(example => {
+ builtin.load(example.lang, example.file, (error, result) => {
+ should.not.exist(error, `Can't read ${example.name} for ${example.lang} in ${example.file}`);
+ result.file.should.be.a('string');
+ });
+ });
+ });
+ });
+}); \ No newline at end of file
diff --git a/test/lang-tests.js b/test/lang-tests.js
new file mode 100644
index 000000000..1393d0fc0
--- /dev/null
+++ b/test/lang-tests.js
@@ -0,0 +1,47 @@
+// Copyright (c) 2017, Matt Godbolt & Rubén Rincón
+// 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.
+
+var should = require('chai').should(),
+ languages = require('../lib/languages').list,
+ fs = require('fs-extra'),
+ path = require('path');
+
+
+describe('Language definitions tests', () => {
+ it('Has id equal to object key', () => {
+ Object.keys(languages).forEach(languageKey => should.equal(languages[languageKey].id, languageKey));
+ });
+ it ('Has extensions with leading dots', () => {
+ Object.keys(languages).forEach(languageKey => should.equal(languages[languageKey].extensions[0][0], '.'));
+ });
+ it('Has examples & are initialized', () => {
+ Object.keys(languages).forEach(languageKey => {
+ const lang = languages[languageKey];
+ fs.stat(path.join('examples', lang.id, 'default' + lang.extensions[0]), (err, fd) => {
+ should.equal(err, null);
+ should.equal(fd, lang.example);
+ });
+ });
+ });
+}); \ No newline at end of file
diff --git a/views/head.pug b/views/head.pug
index 2acb3f76f..766cefba7 100644
--- a/views/head.pug
+++ b/views/head.pug
@@ -1,24 +1,7 @@
-title Compiler Explorer - #{language}
+title Compiler Explorer
meta(charset="utf-8")
meta(http-equiv="X-UA-Compatible" content="IE=edge")
-case language
- when "Rust"
- meta(name="description" content="Compiler Explorer for Rust is an interactive online compiler which shows the assembly output of compiled Rust code.")
- when "Go"
- meta(name="description" content="Compiler Explorer for Go is an interactive online compiler which shows the assembly output of compiled Go code.")
- when "D"
- meta(name="description" content="Compiler Explorer for D is an interactive online compiler which shows the assembly output of compiled D code.")
- when "Haskell"
- meta(name="description" content="Compiler Explorer for Haskell is an interactive online compiler which shows the assembly output of compiled Haskell code.")
- when "ispc"
- meta(name="description" content="Compiler Explorer for the C variant ispc is an interactive online compiler which shows the assembly output of compiled ispc code.")
- when "swift"
- meta(name="description" content="Compiler Explorer for Swift is an interactive online compiler which shows the assembly output of compiled Swift code.")
- when "Pascal"
- meta(name="description" content="Compiler Explorer for Pascal is an interactive online compiler which shows the assembly output of compiled Pascal code.")
- when "C++"
- default
- meta(name="description" content="Compiler Explorer is an interactive online compiler which shows the assembly output of compiled C/C++/Rust/Go/D/Haskell/Swift/Pascal code.")
+meta(name="description" content="Compiler Explorer is an interactive online compiler which shows the assembly output of compiled C, C++, Rust, Go, D, Haskell, Swift & Pascal code.")
meta(name="author" content="Matt Godbolt")
meta(name="google" content="nositelinkssearchbox")
meta(name="google" content="notranslate")
diff --git a/views/index.pug b/views/index.pug
index af386444b..fa1260751 100644
--- a/views/index.pug
+++ b/views/index.pug
@@ -15,17 +15,6 @@ html(lang="en")
a.navbar-brand(href="#" title="Compiler Explorer") Compiler Explorer
li.navbar-collapse.collapse#navbar-collapse
ul.nav.navbar-nav.navbar-left
- if languages.length
- li.dropdown#lang-dropdown
- a(href="#" data-toggle="dropdown" role="button" aria-haspopup="true" aria-expanded="false")
- span= language
- span.caret
- ul.dropdown-menu.dropdown-brand
- each language in languages
- li: a(href=language.url)= language.language
- else
- li#lang-dropdown
- a(href="#"): span= language
li: a#add-editor(href="#" title="Click or drag to desired destination") Editor
li: a#add-diff(href="#" title="Click or drag to desired destination") Diff View
li.dropdown
@@ -95,18 +84,5 @@ html(lang="en")
src=root + "assets/clippy.svg" width="13px"
alt="Copy to clipboard")
.lang
- case language
- when "C++"
- if environment.indexOf("cppx") >= 0
- include example.cppx
- else
- include example.cpp
- when "Rust": include example.rs
- when "Go": include example.go
- when "D": include example.d
- when "ispc": include example.ispc
- when "Haskell": include example.hs
- when "Swift": include example.swift
- when "Pascal": include example.pas
include popups.pug
diff --git a/views/sitemap.pug b/views/sitemap.pug
index dbc3921b5..bf4698e9c 100644
--- a/views/sitemap.pug
+++ b/views/sitemap.pug
@@ -3,30 +3,3 @@ urlset(xmlns="http://www.sitemaps.org/schemas/sitemap/0.9")
url
loc
| https://godbolt.org
- priority
- | 0.8
- url
- loc
- | https://gcc.godbolt.org
- url
- loc
- | https://rust.godbolt.org
- url
- loc
- | https://d.godbolt.org
- url
- loc
- | https://go.godbolt.org
- url
- loc
- | https://cppx.godbolt.org
- url
- loc
- | https://ispc.godbolt.org
- url
- loc
- | https://haskell.godbolt.org
- url
- loc
- | https://swift.godbolt.org
-
diff --git a/views/templates.pug b/views/templates.pug
index b100ee2ea..4eb31f2cb 100644
--- a/views/templates.pug
+++ b/views/templates.pug
@@ -11,6 +11,8 @@
span.glyphicon.glyphicon-open
button.btn.btn-default.btn-sm.conformance(title="Add a new conformance view")
span.glyphicon.glyphicon-list-alt
+ .btn-group.btn-group-sm.pull-right
+ select.change-language(title="Change this editor's (and associated panels) language" disabled=embedded)
.monaco-placeholder
#compiler