Port lib/languages.js to TypeScript (#3740)

This commit is contained in:
Mats Larsen
2022-06-24 13:14:32 +01:00
committed by GitHub
parent f7b069aafb
commit 540981ccef
9 changed files with 435 additions and 282 deletions

View File

@@ -61,7 +61,7 @@ important you can quickly and easily get running. Currently, **Compiler Explorer
variable or `make` parameter).
Running with `make EXTRA_ARGS='--language LANG'` will allow you to load `LANG` exclusively, where `LANG` is one for the
language ids/aliases defined in `lib/languages.js`. For example, to only run **Compiler Explorer** with C++ support,
language ids/aliases defined in `lib/languages.ts`. For example, to only run **Compiler Explorer** with C++ support,
you'd run `make EXTRA_ARGS='--language c++'`. The `Makefile` will automatically install all the third party libraries
needed to run; using `npm` to install server-side and client side components.

View File

@@ -2,7 +2,7 @@
If you want to add a new language to the site, you should follow this steps:
- Add the new language to the exported `languages` variable in `lib/languages.js`:
- Add the new language to the exported `languages` variable in `lib/languages.ts`:
- The syntax is as follows:

View File

@@ -37,6 +37,7 @@ import {
} from '../types/compilation/compilation.interfaces';
import {UnprocessedExecResult} from '../types/execution/execution.interfaces';
import {ParseFilters} from '../types/features/filters.interfaces';
import {Language} from '../types/languages.interfaces';
import {Library, LibraryVersion, SelectedLibraryVersion} from '../types/libraries/libraries.interfaces';
import {ResultLine} from '../types/resultline/resultline.interfaces';
@@ -50,7 +51,7 @@ import * as exec from './exec';
import {getExternalParserByKey} from './external-parsers';
import {ExternalParserBase} from './external-parsers/base';
import {InstructionSets} from './instructionsets';
import {CELanguage, languages} from './languages';
import {languages} from './languages';
import {LlvmAstParser} from './llvm-ast';
import {LlvmIrParser} from './llvm-ir';
import * as compilerOptInfo from './llvm-opt-transformer';
@@ -64,7 +65,7 @@ import * as utils from './utils';
export class BaseCompiler {
public compiler: any;
public lang: CELanguage;
public lang: Language;
protected compileFilename: string;
protected env: any;
protected compilerProps: (key: string, defaultValue?: string) => string;

View File

@@ -186,7 +186,7 @@ export class RouteAPI {
filterCode(req, code, lang) {
let lines = code.split('\n');
if (lang.previewFilter) {
if (lang.previewFilter !== null) {
lines = lines.filter(line => !lang.previewFilter.test(line));
}
return lines.map(line => this.escapeLine(req, line)).join('\n');

View File

@@ -27,239 +27,21 @@ import path from 'path';
import fs from 'fs-extra';
import _ from 'underscore';
/***
* TODO: Use the types/languages once available
* Language object type
*
* @typedef {Object} CELanguage
* @property {string} id - Id of language. Added programmatically based on CELanguages key
* @property {string} name - UI display name of the language
* @property {string} monaco - Monaco Editor language ID (Selects which language Monaco will use to highlight the code)
* @property {string[]} extensions - Usual extensions associated with the language. First one is used as file input etx
* @property {string[]} alias - Different ways in which we can also refer to this language
* @property {string} [formatter] - Format API name to use (See https://godbolt.org/api/formats)
* @property {boolean} supportsExecute - Whether there's at least 1 compiler in this language that supportsExecute
*/
import {LanguageKey, Language} from '../types/languages.interfaces';
/***
* Currently supported languages on Compiler Explorer
*
* @typedef {Object.<string, CELanguage>} CELanguages
*/
type DefKeys = 'name' | 'monaco' | 'extensions' | 'alias' | 'previewFilter' | 'formatter' | 'logoUrl' | 'logoUrlDark';
type LanguageDefinition = Pick<Language, DefKeys>;
/***
* Current supported languages
* @type {CELanguages}
*/
export const languages = {
const definitions: Record<LanguageKey, LanguageDefinition> = {
'c++': {
name: 'C++',
monaco: 'cppp',
extensions: ['.cpp', '.cxx', '.h', '.hpp', '.hxx', '.c', '.cc', '.ixx'],
alias: ['gcc', 'cpp'],
previewFilter: /^\s*#include/,
logoUrl: 'c++.svg',
logoUrlDark: null,
formatter: 'clangformat',
logoUrl: 'c++.svg',
},
llvm: {
name: 'LLVM IR',
monaco: 'llvm-ir',
extensions: ['.ll'],
alias: [],
logoUrl: 'llvm.png',
},
mlir: {
name: 'MLIR',
monaco: 'mlir',
extensions: ['.mlir'],
alias: [],
logoUrl: 'mlir.svg',
monacoDisassembly: 'mlir',
},
cppx: {
name: 'Cppx',
monaco: 'cppp',
extensions: ['.cpp', '.cxx', '.h', '.hpp', '.hxx', '.c'],
alias: [],
previewFilter: /^\s*#include/,
logoUrl: 'c++.svg',
},
cppx_gold: {
name: 'Cppx-Gold',
monaco: 'cppx-gold',
extensions: ['.usyntax', '.cpp', '.cxx', '.h', '.hpp', '.hxx', '.c'],
alias: [],
logoUrl: 'c++.svg', // TODO: Find a better alternative
},
cppx_blue: {
name: 'Cppx-Blue',
monaco: 'cppx-blue',
extensions: ['.blue', '.cpp', '.cxx', '.h', '.hpp', '.hxx', '.c'],
alias: [],
logoUrl: 'c++.svg', // TODO: Find a better alternative
},
c: {
name: 'C',
monaco: 'nc',
extensions: ['.c', '.h'],
alias: [],
previewFilter: /^\s*#include/,
logoUrl: 'c.svg',
},
openclc: {
name: 'OpenCL C',
monaco: 'openclc',
extensions: ['.cl', '.ocl'],
alias: [],
logoUrl: 'opencl.svg',
logoUrlDark: 'opencl-dark.svg',
},
cpp_for_opencl: {
name: 'C++ for OpenCL',
monaco: 'cpp-for-opencl',
extensions: ['.clcpp', '.cl', '.ocl'],
alias: [],
logoUrl: 'opencl.svg', // TODO: Find a better alternative
logoUrlDark: 'opencl-dark.svg',
},
rust: {
name: 'Rust',
monaco: 'rust',
extensions: ['.rs'],
alias: [],
formatter: 'rustfmt',
logoUrl: 'rust.svg',
logoUrlDark: 'rust-dark.svg',
},
d: {
name: 'D',
monaco: 'd',
extensions: ['.d'],
alias: [],
logoUrl: 'd.svg',
},
erlang: {
name: 'Erlang',
monaco: 'erlang',
extensions: ['.erl', '.hrl'],
alias: [],
logoUrl: 'erlang.svg',
},
go: {
name: 'Go',
monaco: 'go',
extensions: ['.go'],
alias: [],
logoUrl: 'go.svg',
},
ispc: {
name: 'ispc',
monaco: 'ispc',
extensions: ['.ispc'],
alias: [],
logoUrl: 'ispc.png',
},
haskell: {
name: 'Haskell',
monaco: 'haskell',
extensions: ['.hs', '.haskell'],
alias: [],
logoUrl: 'haskell.png',
},
java: {
name: 'Java',
monaco: 'java',
extensions: ['.java'],
alias: [],
logoUrl: 'java.svg',
},
kotlin: {
name: 'Kotlin',
monaco: 'kotlin',
extensions: ['.kt'],
alias: [],
logoUrl: 'kotlin.png',
},
scala: {
name: 'Scala',
monaco: 'scala',
extensions: ['.scala'],
alias: [],
logoUrl: 'scala.png',
},
ocaml: {
name: 'OCaml',
monaco: 'ocaml',
extensions: ['.ml', '.mli'],
alias: [],
logoUrl: 'ocaml.svg',
},
python: {
name: 'Python',
monaco: 'python',
extensions: ['.py'],
alias: [],
logoUrl: 'python.svg',
},
swift: {
name: 'Swift',
monaco: 'swift',
extensions: ['.swift'],
alias: [],
logoUrl: 'swift.svg',
},
pascal: {
name: 'Pascal',
monaco: 'pascal',
extensions: ['.pas', '.dpr'],
alias: [],
logoUrl: 'pascal.svg', // TODO: Find a better alternative
logoUrlDark: 'pascal-dark.svg',
},
fortran: {
id: 'fortran',
name: 'Fortran',
monaco: 'fortran',
extensions: ['.f90', '.F90', '.f95', '.F95', '.f'],
alias: [],
logoUrl: 'fortran.svg',
},
assembly: {
name: 'Assembly',
monaco: 'asm',
extensions: ['.asm', '.6502'],
alias: ['asm'],
logoUrl: 'assembly.png', // TODO: Find a better alternative
},
analysis: {
name: 'Analysis',
monaco: 'asm',
extensions: ['.asm'], // maybe add more? Change to a unique one?
alias: ['tool', 'tools'],
logoUrl: 'analysis.png', // TODO: Find a better alternative
},
cuda: {
name: 'CUDA C++',
monaco: 'cuda',
extensions: ['.cu'],
alias: ['nvcc'],
monacoDisassembly: 'ptx',
logoUrl: 'cuda.svg',
logoUrlDark: 'cuda-dark.svg',
},
zig: {
name: 'Zig',
monaco: 'zig',
extensions: ['.zig'],
alias: [],
logoUrl: 'zig.svg',
},
clean: {
name: 'Clean',
monaco: 'clean',
extensions: ['.icl'],
alias: [],
logoUrl: 'clean.svg', // TODO: Find a better alternative
},
ada: {
name: 'Ada',
@@ -268,21 +50,38 @@ export const languages = {
alias: [],
logoUrl: 'ada.svg',
logoUrlDark: 'ada-dark.svg',
formatter: null,
previewFilter: null,
},
nim: {
name: 'Nim',
monaco: 'nim',
extensions: ['.nim'],
alias: [],
logoUrl: 'nim.svg',
analysis: {
name: 'Analysis',
monaco: 'asm',
extensions: ['.asm'], // maybe add more? Change to a unique one?
alias: ['tool', 'tools'],
logoUrl: 'analysis.png', // TODO: Find a better alternative
logoUrlDark: null,
formatter: null,
previewFilter: null,
},
crystal: {
name: 'Crystal',
monaco: 'crystal',
extensions: ['.cr'],
assembly: {
name: 'Assembly',
monaco: 'asm',
extensions: ['.asm', '.6502'],
alias: ['asm'],
logoUrl: 'assembly.png', // TODO: Find a better alternative
logoUrlDark: null,
formatter: null,
previewFilter: null,
},
c: {
name: 'C',
monaco: 'nc',
extensions: ['.c', '.h'],
alias: [],
logoUrl: 'crystal.svg',
logoUrlDark: 'crystal-dark.svg',
logoUrl: 'c.svg',
logoUrlDark: null,
formatter: 'clangformat',
previewFilter: /^\s*#include/,
},
circle: {
name: 'C++ (Circle)',
@@ -291,14 +90,18 @@ export const languages = {
alias: [],
previewFilter: /^\s*#include/,
logoUrl: 'c++.svg', // TODO: Find a better alternative
logoUrlDark: null,
formatter: null,
},
ruby: {
name: 'Ruby',
monaco: 'ruby',
extensions: ['.rb'],
clean: {
name: 'Clean',
monaco: 'clean',
extensions: ['.icl'],
alias: [],
monacoDisassembly: 'asmruby',
logoUrl: 'ruby.svg',
logoUrl: 'clean.svg', // TODO: Find a better alternative
logoUrlDark: null,
formatter: null,
previewFilter: null,
},
cmake: {
name: 'CMake',
@@ -306,6 +109,69 @@ export const languages = {
extensions: ['.txt'],
alias: [],
logoUrl: 'cmake.svg',
logoUrlDark: null,
formatter: null,
previewFilter: null,
},
cpp_for_opencl: {
name: 'C++ for OpenCL',
monaco: 'cpp-for-opencl',
extensions: ['.clcpp', '.cl', '.ocl'],
alias: [],
logoUrl: 'opencl.svg', // TODO: Find a better alternative
logoUrlDark: 'opencl-dark.svg',
formatter: null,
previewFilter: null,
},
mlir: {
name: 'MLIR',
monaco: 'mlir',
extensions: ['.mlir'],
alias: [],
logoUrl: 'mlir.svg',
formatter: null,
logoUrlDark: null,
previewFilter: null,
},
cppx: {
name: 'Cppx',
monaco: 'cppp',
extensions: ['.cpp', '.cxx', '.h', '.hpp', '.hxx', '.c'],
alias: [],
logoUrl: 'c++.svg',
logoUrlDark: null,
formatter: null,
previewFilter: /^\s*#include/,
},
cppx_blue: {
name: 'Cppx-Blue',
monaco: 'cppx-blue',
extensions: ['.blue', '.cpp', '.cxx', '.h', '.hpp', '.hxx', '.c'],
alias: [],
logoUrl: 'c++.svg', // TODO: Find a better alternative
logoUrlDark: null,
formatter: null,
previewFilter: null,
},
cppx_gold: {
name: 'Cppx-Gold',
monaco: 'cppx-gold',
extensions: ['.usyntax', '.cpp', '.cxx', '.h', '.hpp', '.hxx', '.c'],
alias: [],
logoUrl: 'c++.svg', // TODO: Find a better alternative
logoUrlDark: null,
formatter: null,
previewFilter: null,
},
crystal: {
name: 'Crystal',
monaco: 'crystal',
extensions: ['.cr'],
alias: [],
logoUrl: 'crystal.svg',
logoUrlDark: 'crystal-dark.svg',
formatter: null,
previewFilter: null,
},
csharp: {
name: 'C#',
@@ -313,6 +179,59 @@ export const languages = {
extensions: ['.cs'],
alias: [],
logoUrl: 'dotnet.svg',
logoUrlDark: null,
formatter: null,
previewFilter: null,
},
cuda: {
name: 'CUDA C++',
monaco: 'cuda',
extensions: ['.cu'],
alias: ['nvcc'],
logoUrl: 'cuda.svg',
logoUrlDark: 'cuda-dark.svg',
formatter: null,
previewFilter: null,
},
d: {
name: 'D',
monaco: 'd',
extensions: ['.d'],
alias: [],
logoUrl: 'd.svg',
logoUrlDark: null,
formatter: null,
previewFilter: null,
},
dart: {
name: 'Dart',
monaco: 'dart',
extensions: ['.dart'],
alias: [],
logoUrl: 'dart.svg',
logoUrlDark: null,
formatter: 'dartformat',
previewFilter: null,
},
erlang: {
name: 'Erlang',
monaco: 'erlang',
extensions: ['.erl', '.hrl'],
alias: [],
logoUrl: 'erlang.svg',
logoUrlDark: null,
formatter: null,
previewFilter: null,
},
fortran: {
name: 'Fortran',
monaco: 'fortran',
extensions: ['.f90', '.F90', '.f95', '.F95', '.f'],
alias: [],
logoUrl: 'fortran.svg',
logoUrlDark: null,
formatter: null,
previewFilter: null,
},
fsharp: {
name: 'F#',
@@ -320,28 +239,149 @@ export const languages = {
extensions: ['.fs'],
alias: [],
logoUrl: 'fsharp.svg',
logoUrlDark: null,
formatter: null,
previewFilter: null,
},
vb: {
name: 'Visual Basic',
monaco: 'vb',
extensions: ['.vb'],
go: {
name: 'Go',
monaco: 'go',
extensions: ['.go'],
alias: [],
logoUrl: 'dotnet.svg',
logoUrl: 'go.svg',
logoUrlDark: null,
formatter: null,
previewFilter: null,
},
dart: {
name: 'Dart',
monaco: 'dart',
extensions: ['.dart'],
haskell: {
name: 'Haskell',
monaco: 'haskell',
extensions: ['.hs', '.haskell'],
alias: [],
formatter: 'dartformat',
logoUrl: 'dart.svg',
logoUrl: 'haskell.png',
logoUrlDark: null,
formatter: null,
previewFilter: null,
},
typescript: {
name: 'TypeScript Native',
monaco: 'typescript',
extensions: ['.ts', '.d.ts'],
ispc: {
name: 'ispc',
monaco: 'ispc',
extensions: ['.ispc'],
alias: [],
logoUrl: 'ts.svg',
logoUrl: 'ispc.png',
logoUrlDark: null,
formatter: null,
previewFilter: null,
},
java: {
name: 'Java',
monaco: 'java',
extensions: ['.java'],
alias: [],
logoUrl: 'java.svg',
logoUrlDark: null,
formatter: null,
previewFilter: null,
},
kotlin: {
name: 'Kotlin',
monaco: 'kotlin',
extensions: ['.kt'],
alias: [],
logoUrl: 'kotlin.png',
logoUrlDark: null,
formatter: null,
previewFilter: null,
},
llvm: {
name: 'LLVM IR',
monaco: 'llvm-ir',
extensions: ['.ll'],
alias: [],
logoUrl: 'llvm.png',
logoUrlDark: null,
formatter: null,
previewFilter: null,
},
nim: {
name: 'Nim',
monaco: 'nim',
extensions: ['.nim'],
alias: [],
logoUrl: 'nim.svg',
logoUrlDark: null,
formatter: null,
previewFilter: null,
},
ocaml: {
name: 'OCaml',
monaco: 'ocaml',
extensions: ['.ml', '.mli'],
alias: [],
logoUrl: 'ocaml.svg',
logoUrlDark: null,
formatter: null,
previewFilter: null,
},
openclc: {
name: 'OpenCL C',
monaco: 'openclc',
extensions: ['.cl', '.ocl'],
alias: [],
logoUrl: 'opencl.svg',
logoUrlDark: 'opencl-dark.svg',
formatter: null,
previewFilter: null,
},
pascal: {
name: 'Pascal',
monaco: 'pascal',
extensions: ['.pas', '.dpr'],
alias: [],
logoUrl: 'pascal.svg', // TODO: Find a better alternative
logoUrlDark: 'pascal-dark.svg',
formatter: null,
previewFilter: null,
},
python: {
name: 'Python',
monaco: 'python',
extensions: ['.py'],
alias: [],
logoUrl: 'python.svg',
logoUrlDark: null,
formatter: null,
previewFilter: null,
},
ruby: {
name: 'Ruby',
monaco: 'ruby',
extensions: ['.rb'],
alias: [],
logoUrl: 'ruby.svg',
logoUrlDark: null,
formatter: null,
previewFilter: null,
},
rust: {
name: 'Rust',
monaco: 'rust',
extensions: ['.rs'],
alias: [],
logoUrl: 'rust.svg',
logoUrlDark: 'rust-dark.svg',
formatter: 'rustfmt',
previewFilter: null,
},
scala: {
name: 'Scala',
monaco: 'scala',
extensions: ['.scala'],
alias: [],
logoUrl: 'scala.png',
logoUrlDark: null,
formatter: null,
previewFilter: null,
},
solidity: {
name: 'Solidity',
@@ -349,15 +389,65 @@ export const languages = {
extensions: ['.sol'],
alias: [],
logoUrl: 'solidity.svg',
logoUrlDark: null,
formatter: null,
previewFilter: null,
},
swift: {
name: 'Swift',
monaco: 'swift',
extensions: ['.swift'],
alias: [],
logoUrl: 'swift.svg',
logoUrlDark: null,
formatter: null,
previewFilter: null,
},
typescript: {
name: 'TypeScript Native',
monaco: 'typescript',
extensions: ['.ts', '.d.ts'],
alias: [],
logoUrl: 'ts.svg',
logoUrlDark: null,
formatter: null,
previewFilter: null,
},
vb: {
name: 'Visual Basic',
monaco: 'vb',
extensions: ['.vb'],
alias: [],
logoUrl: 'dotnet.svg',
logoUrlDark: null,
formatter: null,
previewFilter: null,
},
zig: {
name: 'Zig',
monaco: 'zig',
extensions: ['.zig'],
alias: [],
logoUrl: 'zig.svg',
logoUrlDark: null,
formatter: null,
previewFilter: null,
},
};
_.each(languages, (lang, key) => {
lang.id = key;
lang.supportsExecute = false;
export const languages: Record<LanguageKey, Language> = _.mapObject(definitions, (lang, key) => {
let example: string;
try {
lang.example = fs.readFileSync(path.join('examples', lang.id, 'default' + lang.extensions[0]), 'utf8');
example = fs.readFileSync(path.join('examples', key, 'default' + lang.extensions[0]), 'utf8');
} catch (error) {
lang.example = 'Oops, something went wrong and we could not get the default code for this language.';
example = 'Oops, something went wrong and we could not get the default code for this language.';
}
const def: Language = {
...lang,
id: key as LanguageKey,
supportsExecute: false,
example,
};
return def;
});

View File

@@ -464,7 +464,7 @@ function setupLanguageLogos(languages) {
function (lang) {
try {
lang.logoData = logos('./' + lang.logoUrl);
if (lang.logoUrlDark) {
if (lang.logoUrlDark !== null) {
lang.logoDataDark = logos('./' + lang.logoUrlDark);
}
} catch (ignored) {

View File

@@ -128,9 +128,19 @@ function Compiler(hub, container, state) {
this.initButtons(state);
var monacoDisassembly = 'asm';
if (languages[this.currentLangId] && languages[this.currentLangId].monacoDisassembly) {
// TODO: If languages[this.currentLangId] is not valid, something went wrong. Find out what
monacoDisassembly = languages[this.currentLangId].monacoDisassembly;
// Bandaid fix to not have to include monacoDisassembly everywhere in languages.js
if (languages[this.currentLangId]) {
switch (languages[this.currentLangId].id) {
case 'cuda':
monacoDisassembly = 'ptx';
break;
case 'ruby':
monacoDisassembly = 'asmruby';
break;
case 'mlir':
monacoDisassembly = 'mlir';
break;
}
}
this.outputEditor = monaco.editor.create(

View File

@@ -62,6 +62,7 @@ describe('Basic unfurls', () => {
languages: {
'c++': {
name: 'C++',
previewFilter: null,
},
},
compilers: [],
@@ -90,6 +91,7 @@ describe('Basic unfurls', () => {
languages: {
'c++': {
name: 'C++',
previewFilter: null,
},
},
compilers: [],
@@ -119,6 +121,7 @@ describe('Basic unfurls', () => {
languages: {
'c++': {
name: 'C++',
previewFilter: null,
},
},
compilers: [],

View File

@@ -22,19 +22,68 @@
// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
// POSSIBILITY OF SUCH DAMAGE.
export type Language = {
// ID of language. Added programmatically based on CELanguages key
id: string;
// UI display name of the language
export type LanguageKey =
| 'mlir'
| 'c++'
| 'llvm'
| 'cppx'
| 'cppx_gold'
| 'cppx_blue'
| 'c'
| 'openclc'
| 'cpp_for_opencl'
| 'rust'
| 'd'
| 'erlang'
| 'go'
| 'ispc'
| 'haskell'
| 'java'
| 'kotlin'
| 'scala'
| 'ocaml'
| 'python'
| 'swift'
| 'pascal'
| 'fortran'
| 'assembly'
| 'analysis'
| 'cuda'
| 'zig'
| 'clean'
| 'ada'
| 'nim'
| 'crystal'
| 'circle'
| 'ruby'
| 'cmake'
| 'csharp'
| 'fsharp'
| 'vb'
| 'dart'
| 'typescript'
| 'solidity';
export interface Language {
/** Id of language. Added programmatically based on CELanguages key */
id: LanguageKey;
/** UI display name of the language */
name: string;
// Monaco Editor language ID (Selects which language Monaco will use to highlight the code)
/** Monaco Editor language ID (Selects which language Monaco will use to highlight the code) */
monaco: string;
// Usual extensions associated with the language. First one is used as file input etx
extensions: string[];
// Different ways in which we can also refer to this language
/** Usual extensions associated with the language. First one is used as file input extension */
extensions: [string, ...string[]];
/** Different ways in which we can also refer to this language */
alias: string[];
// Format API name to use (See https://godbolt.org/api/formats)
/** Format API name to use (See https://godbolt.org/api/formats) */
formatter: string | null;
// Whether there's at least 1 compiler in this language that supportsExecute
/** Whether there's at least 1 compiler in this language that supportsExecute */
supportsExecute: boolean | null;
/** Path in /views/resources/logos to the logo of the language */
logoUrl: string;
/** Path in /views/resources/logos to the logo of the language for dark mode use */
logoUrlDark: string | null;
/** Example code to show in the language's editor */
example: string;
previewFilter: RegExp | null;
};