Add Mojo compiler (#7692)

This PR adds support for the Mojo programming language.

Explicitly tested viewing the generated assembly, LLVM IR, and running
the generated executable.
This commit is contained in:
Rob Parolin
2025-05-21 11:53:05 -07:00
committed by GitHub
parent a154c7d514
commit a0b48fb7e0
13 changed files with 319 additions and 0 deletions

7
.github/labeler.yml vendored
View File

@@ -260,6 +260,13 @@
- 'etc/config/modula2.*.properties'
- 'static/modes/modula2-mode.ts'
'lang-mojo':
- changed-files:
- any-glob-to-any-file:
- 'lib/compilers/mojo.ts'
- 'etc/config/mojo.*.properties'
- 'static/modes/mojo-mode.ts'
'lang-nim':
- changed-files:
- any-glob-to-any-file:

View File

@@ -160,3 +160,4 @@ From oldest to newest contributor, we would like to thank:
- [Mathias Gredal](https://github.com/mathiasgredal)
- [RongDong Xu](https://github.com/arong)
- [Adrien Bertrand](https://github.com/adriweb)
- [Roberto Parolin](https://github.com/rparolin)

View File

@@ -0,0 +1,14 @@
compilers=&mojo
mojo.defaultCompiler=mojo_25_3_0
mojo.compilerType=mojo
group.mojo.compilers=mojo_25_3_0:mojo_nightly
group.mojo.isSemVer=true
group.mojo.baseName=Mojo
compiler.mojo_25_3_0.exe=/opt/compiler-explorer/mojo-25.3.0/bin/mojo
compiler.mojo_25_3_0.semver=25.3.0
compiler.mojo_nightly.exe=/opt/compiler-explorer/mojo-nightly/bin/mojo
compiler.mojo_nightly.semver=nightly
compiler.mojo_nightly.isNightly=true

View File

@@ -0,0 +1,9 @@
compilers=&mojo
mojo.defaultCompiler=mojo_nightly
mojo.compilerType=mojo
group.mojo.compilers=mojo_nightly
supportsBinary=true
supportsExecute=true
supportsAsmDocs=true

View File

@@ -0,0 +1,2 @@
fn square(n: Int) -> Int:
return n * n

View File

@@ -103,6 +103,7 @@ export {LLVMMOSCompiler} from './llvm-mos.js';
export {LLVMmcaTool} from './llvm-mca.js';
export {MLIRCompiler} from './mlir.js';
export {MadPascalCompiler} from './madpascal.js';
export {MojoCompiler} from './mojo.js';
export {MovfuscatorCompiler} from './movfuscator.js';
export {MrustcCompiler} from './mrustc.js';
export {Msp430Compiler} from './msp430.js';

View File

@@ -604,6 +604,13 @@ export class PascalParser extends BaseParser {
}
}
export class MojoParser extends BaseParser {
static override async parse(compiler: BaseCompiler) {
await this.getOptions(compiler, '-help');
return compiler;
}
}
export class ICCParser extends GCCParser {
static override async setCompilerSettingsFromOptions(compiler: BaseCompiler, options: Record<string, Argument>) {
const keys = _.keys(options);

59
lib/compilers/mojo.ts Normal file
View File

@@ -0,0 +1,59 @@
// Copyright (c) 2025, Compiler Explorer Authors
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are met:
//
// * Redistributions of source code must retain the above copyright notice,
// this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above copyright
// notice, this list of conditions and the following disclaimer in the
// documentation and/or other materials provided with the distribution.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
// POSSIBILITY OF SUCH DAMAGE.
import path from 'node:path';
import type {ParseFiltersAndOutputOptions} from '../../types/features/filters.interfaces.js';
import {BaseCompiler} from '../base-compiler.js';
export class MojoCompiler extends BaseCompiler {
static get key() {
return 'mojo';
}
constructor(info, env) {
super(info, env);
this.delayCleanupTemp = true;
this.compiler.supportsIrView = true;
this.compiler.irArg = ['--emit=llvm'];
}
override getOutputFilename(dirPath: string, inputFileBase: string) {
// This method tells CE where to find the assembly output
const outputPath = path.join(dirPath, 'example.s');
return outputPath;
}
override optionsForFilter(filters: ParseFiltersAndOutputOptions, outputFilename: string, userOptions: string[]) {
if (filters.binary) return ['build'];
return ['build', '--emit=asm'];
}
override getSharedLibraryPathsAsArguments() {
return [];
}
override getExecutableFilename(dirPath: string, outputFilebase: string) {
return path.join(dirPath, 'example');
}
}

View File

@@ -591,6 +591,18 @@ const definitions: Record<LanguageKey, LanguageDefinition> = {
previewFilter: null,
monacoDisassembly: null,
},
mojo: {
name: 'Mojo',
monaco: 'mojo',
extensions: ['.mojo', '.🔥'],
alias: [],
logoUrl: 'mojo.svg',
logoUrlDark: null,
formatter: 'mblack',
previewFilter: null,
monacoDisassembly: null,
digitSeparator: '_',
},
nim: {
name: 'Nim',
monaco: 'nim',

View File

@@ -54,6 +54,7 @@ import './jakt-mode';
import './llvm-ir-mode';
import './mlir-mode';
import './modula2-mode';
import './mojo-mode';
import './nc-mode';
import './nim-mode';
import './nix-mode';

170
static/modes/mojo-mode.ts Normal file
View File

@@ -0,0 +1,170 @@
// Copyright (c) 2025, Compiler Explorer Authors
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are met:
//
// * Redistributions of source code must retain the above copyright notice,
// this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above copyright
// notice, this list of conditions and the following disclaimer in the
// documentation and/or other materials provided with the distribution.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
// POSSIBILITY OF SUCH DAMAGE.
import * as monaco from 'monaco-editor';
function definition(): monaco.languages.IMonarchLanguage {
return {
// Set defaultToken to invalid to see what you do not tokenize yet
defaultToken: 'invalid',
// C# style strings
escapes: /\\(?:[abfnrtv\\"']|x[0-9A-Fa-f]{1,4}|u[0-9A-Fa-f]{4}|U[0-9A-Fa-f]{8})/,
registers: /%?\b(r[0-9]+[dbw]?|([er]?([abcd][xhl]|cs|fs|ds|ss|sp|bp|ip|sil?|dil?))|[xyz]mm[0-9]+|sp|fp|lr)\b/,
intelOperators: /PTR|(D|Q|[XYZ]MM)?WORD/,
tokenizer: {
root: [
// Error document
[/^<.*>$/, {token: 'annotation'}],
// inline comments
[/\/\*/, 'comment', '@comment'],
// Label definition (anything looking like a label, followed by anything that's not valid in a demangled
// identifier, until we get to a colon followed by any whitespace. This is to avoid finding the colon in
// a scoped (blah::foo) identifier.
[/^[.a-zA-Z0-9_$?@][^#;/]*:(?=\s)/, {token: 'type.identifier'}],
// Label definition (quoted)
[/^"([^"\\]|\\.)*":/, {token: 'type.identifier'}],
// Label definition (ARM style)
[/^\s*[|][^|]*[|]/, {token: 'type.identifier'}],
// Label definition (CL style). This is pretty hideous: we essentially take anything that ends in spaces
// followed by a definition (PROC, ENDP etc) and assume it's a label. That means we have to use
// backtracking and then a lookahead to ensure we don't consume the definition. As a nod to efficiency
// we assume the line has to start with a non-whitespace character before we go all back-tracky.
// See https://github.com/compiler-explorer/compiler-explorer/issues/1645 for examples.
[/^\S.*?(?=\s+(PROC|ENDP|D[BDWQ]))/, {token: 'type.identifier', next: '@rest'}],
// Constant definition
[/^[.a-zA-Z0-9_$?@][^=]*=/, {token: 'type.identifier'}],
// opcode
[/[.a-zA-Z_][.a-zA-Z_0-9]*/, {token: 'keyword', next: '@rest'}],
// braces and parentheses at the start of the line (e.g. nvcc output)
[/[(){}]/, {token: 'operator', next: '@rest'}],
// msvc can have strings at the start of a line in a inSegDirList
[/`/, {token: 'string.backtick', bracket: '@open', next: '@segDirMsvcstring'}],
// whitespace
{include: '@whitespace'},
],
rest: [
// pop at the beginning of the next line and rematch
[/^.*$/, {token: '@rematch', next: '@pop'}],
[/@registers/, 'variable.predefined'],
[/@intelOperators/, 'annotation'],
// inline comments
[/\/\*/, 'comment', '@comment'],
// CL style post-label definition.
[/PROC|ENDP|D[BDWQ]/, 'keyword'],
// brackets
[/[{}<>()[\]]/, '@brackets'],
// ARM-style label reference
[/[|][^|]*[|]*/, 'type.identifier'],
// numbers
[/\d*\.\d+([eE][-+]?\d+)?/, 'number.float'],
[/([$]|0[xX])[0-9a-fA-F]+/, 'number.hex'],
[/\d+/, 'number'],
// ARM-style immediate numbers (which otherwise look like comments)
[/#-?\d+/, 'number'],
// operators
[/[-+,*/!:&{}()]/, 'operator'],
// strings
[/"([^"\\]|\\.)*$/, 'string.invalid'], // non-terminated string
[/"/, {token: 'string.quote', bracket: '@open', next: '@string'}],
// `msvc does this, sometimes'
[/`/, {token: 'string.backtick', bracket: '@open', next: '@msvcstring'}],
[/'/, {token: 'string.singlequote', bracket: '@open', next: '@sstring'}],
// characters
[/'[^\\']'/, 'string'],
[/(')(@escapes)(')/, ['string', 'string.escape', 'string']],
[/'/, 'string.invalid'],
// Assume anything else is a label reference. .NET uses ` in some identifiers
[/%?[.?_$a-zA-Z@][.?_$a-zA-Z0-9@`]*/, 'type.identifier'],
// whitespace
{include: '@whitespace'},
],
comment: [
[/[^/*]+/, 'comment'],
[/\/\*/, 'comment', '@push'], // nested comment
['\\*/', 'comment', '@pop'],
[/[/*]/, 'comment'],
],
string: [
[/[^\\"]+/, 'string'],
[/@escapes/, 'string.escape'],
[/\\./, 'string.escape.invalid'],
[/"/, {token: 'string.quote', bracket: '@close', next: '@pop'}],
],
msvcstringCommon: [
[/[^\\']+/, 'string'],
[/@escapes/, 'string.escape'],
[/''/, 'string.escape'], // ` isn't escaped but ' is escaped as ''
[/\\./, 'string.escape.invalid'],
],
msvcstring: [
{include: '@msvcstringCommon'},
[/'/, {token: 'string.backtick', bracket: '@close', next: '@pop'}],
],
segDirMsvcstring: [
{include: '@msvcstringCommon'},
[/'/, {token: 'string.backtick', bracket: '@close', switchTo: '@rest'}],
],
sstring: [
[/[^\\']+/, 'string'],
[/@escapes/, 'string.escape'],
[/\\./, 'string.escape.invalid'],
[/'/, {token: 'string.singlequote', bracket: '@close', next: '@pop'}],
],
whitespace: [
[/[ \t\r\n]+/, 'white'],
[/\/\*/, 'comment', '@comment'],
[/\/\/.*$/, 'comment'],
[/[#;\\@].*$/, 'comment'],
],
},
};
}
const def = definition();
monaco.languages.register({id: 'mojo'});
monaco.languages.setMonarchTokensProvider('mojo', def);
export default def;

View File

@@ -74,6 +74,7 @@ export type LanguageKey =
| 'llvm_mir'
| 'mlir'
| 'modula2'
| 'mojo'
| 'nim'
| 'nix'
| 'numba'

View File

@@ -0,0 +1,35 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Generator: Adobe Illustrator 26.3.1, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
viewBox="0 0 1000 1000" style="enable-background:new 0 0 1000 1000;" xml:space="preserve">
<style type="text/css">
.st0{fill:#FF4C1F;}
.st1{fill:#FF9C45;}
.st2{fill:#FFDD56;}
.st3{fill:#FCFBCC;}
</style>
<g>
<path class="st0" d="M732.35,443.58c5.88,23.21-19.09,47.98-19.09,47.98s-45.53,30.15-36.72-99.1S481.18,136.31,481.18,136.31
s1.17,12.32,0,97.52c-1.47,107.22-63.16,70.5-63.16,70.5c-61.69,66.1-55.99,215.28-55.99,215.28
c-76.38,67.56-79.14-72.81-79.14-72.81s-145.41,146.88,10.28,304.04c0,0,22.83,22.77-13.64,15.54
c-26.01-5.15-31.89-15.54-31.89-15.54s5,65.09,188.5,105.72c3.56,0.72,60.59,11.9,124.99-0.98
c141.32-31.8,165.96-90.74,165.96-90.74s-2.99,5.32-26.44,7.71c-25.2,2.57-3.58-21.72-3.58-21.72
C863.07,646.56,726.47,420.37,732.35,443.58z"/>
<path class="st1" d="M672.03,658c33.05-51.77,7.71-90.33,7.71-90.33c-51.81,26.64-67.31,8.04-71.91-7.04
c-1.82-5.97-2.13-12.31-1.08-18.46c33.52-196.45-96.61-302.55-93.34-276.32c0.99,7.93-0.37,18.34-2.77,29.03
c-5.67,25.31-16.52,49.15-31.31,70.45c-20.37,29.34-57.18,92.27-77.18,186.92C373.52,687.74,290.9,589.7,290.9,589.7
s-48.28,165.16,148.91,267.54c11.61,2.01,48.35,7.28,92.64,2.67c129.49-60.95,175.93-179.88,175.93-179.88
S638.98,709.77,672.03,658z"/>
<g>
<path class="st2" d="M626.84,622.82c0.02-0.05,0.03-0.07,0.03-0.07L626.84,622.82z"/>
<path class="st2" d="M626.84,622.82c-0.67,1.72-14.54,35.4-66.06,43.99c-52.87,8.81-29.74-175.25-29.74-175.25
c-129.98,61.58-105.75,206.09-105.75,206.09s-20.76,20.03-47.37,0c-9.04-6.8,10.81,116.12,95.8,163.39
c15.77,0.98,35.46,1.17,56.99-0.96c58.47-40.64,89.55-167.94,89.55-167.94l-26.82,16.56L626.84,622.82z"/>
</g>
<path class="st0" d="M253.45,428.88c0,0-7.62-66.62,29.51-96.05c5.8-4.6,9.71-8.33,12.57-15.16c2.33-5.55,5.94-14.37,4.18-20.97
C295.31,280.17,349.28,371.6,253.45,428.88z"/>
<path class="st3" d="M531.11,860.05c23.61-24.8,55.09-123.89,55.09-123.89s-6.82,20.3-32.31,17.63
c-35.06-3.68-54.34-96.94-54.34-96.94s-29.13,79.31-35.25,91.06c-12.89,24.75-36.72,0-36.72,0c-6.3,40.32,43.98,98.08,58.41,113.7
C499.36,861.98,514.7,861.69,531.11,860.05z"/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 2.3 KiB