From 0bf769e2456128222bac5100acbf5bd7b101613a Mon Sep 17 00:00:00 2001 From: Spydr <58859306+Spydr06@users.noreply.github.com> Date: Wed, 16 Aug 2023 05:16:36 +0200 Subject: [PATCH] New Language: V (#5297) V (`vlang`) is a simple, fast and safe general purpose programming language compiling to human-readable C. #### Key features of V: - simplicity, "only one way to do things" - performance as fast as C - safety: no null, no globals, no undefined behavior, immutability - automatic C to V translation, good C interoperability - hot code reloading - flexible memory management (GC by default, manual, arena allocation, autofree) - other compilation backends like JavaScript, `native` (wip), `wasm` (wip) or interpreted #### Links: Source code: https://github.com/vlang/v Official website: https://vlang.io Web playground: https://play.vlang.io/ infra PR: https://github.com/compiler-explorer/infra/pull/1058 #### Things this PR adds: - General support for V - Code inspection for the C, Go and JavaScript backends - Support for the v formatter `v fmt` - Some V example code #### Things not implemented: - Support for binary-output backends like `native` and `wasm` - Support for running programs --------- Co-authored-by: Matt Godbolt --- .github/labeler.yml | 4 + CONTRIBUTORS.md | 1 + .../compiler-explorer.amazon.properties | 6 +- etc/config/v.amazon.properties | 17 ++ etc/config/v.defaults.properties | 15 ++ examples/v/Sum_over_array.v | 12 + examples/v/default.v | 7 + examples/v/helloworld.v | 3 + lib/compilers/_all.ts | 1 + lib/compilers/v.ts | 211 +++++++++++++++ lib/formatters/_all.ts | 1 + lib/formatters/vfmt.ts | 52 ++++ lib/languages.ts | 11 + static/formatter-registry.ts | 1 + static/modes/_all.ts | 1 + static/modes/v-mode.ts | 252 ++++++++++++++++++ types/languages.interfaces.ts | 1 + views/resources/logos/v.svg | 1 + 18 files changed, 596 insertions(+), 1 deletion(-) create mode 100644 etc/config/v.amazon.properties create mode 100644 etc/config/v.defaults.properties create mode 100644 examples/v/Sum_over_array.v create mode 100644 examples/v/default.v create mode 100644 examples/v/helloworld.v create mode 100644 lib/compilers/v.ts create mode 100644 lib/formatters/vfmt.ts create mode 100644 static/modes/v-mode.ts create mode 100644 views/resources/logos/v.svg diff --git a/.github/labeler.yml b/.github/labeler.yml index 402714960..6755d546a 100644 --- a/.github/labeler.yml +++ b/.github/labeler.yml @@ -181,6 +181,10 @@ lang-toit: lang-typescript: - lib/compilers/typescript.js - etc/config/typescript.*.properties +lang-v: + - lib/compilers/v.ts + - etc/config/v.*.properties + - static/modes/v-mode.ts lang-vala: - lib/compilers/vala.ts - etc/config/vala.*.properties diff --git a/CONTRIBUTORS.md b/CONTRIBUTORS.md index 1c6293e94..07d78226a 100644 --- a/CONTRIBUTORS.md +++ b/CONTRIBUTORS.md @@ -133,4 +133,5 @@ From oldest to newest contributor, we would like to thank: - [Vlad Serebrennikov](https://github.com/endilll) - [Mauro Baladés](https://github.com/mauro-balades) - [Jorge López](https://github.com/jolopezl) +- [Spydr06](https://github.com/spydr06) - [Simon Sobisch](https://github.com/GitMensch) diff --git a/etc/config/compiler-explorer.amazon.properties b/etc/config/compiler-explorer.amazon.properties index a393664c6..1fae96525 100644 --- a/etc/config/compiler-explorer.amazon.properties +++ b/etc/config/compiler-explorer.amazon.properties @@ -41,7 +41,7 @@ useninja=false ld=/usr/bin/ld readelf=/usr/bin/readelf -formatters=clangformat:rustfmt:gofmt:dartformat +formatters=clangformat:rustfmt:gofmt:dartformat:vfmt formatter.clangformat.name=clang-format formatter.clangformat.exe=/opt/compiler-explorer/clang-trunk/bin/clang-format formatter.clangformat.styles=Google:LLVM:Mozilla:Chromium:WebKit:Microsoft:GNU @@ -59,6 +59,10 @@ formatter.dartformat.name=dartformat formatter.dartformat.exe=/opt/compiler-explorer/dart-2.16.1/bin/dart formatter.dartformat.styles= formatter.dartformat.type=dartformat +formatter.vfmt.name=vfmt +formatter.vfmt.exe=/opt/compiler-explorer/v-2023.30/v +formatter.vfmt.styles= +formatter.vfmt.type=vfmt thirdPartyIntegrationEnabled=true statusTrackingEnabled=true diff --git a/etc/config/v.amazon.properties b/etc/config/v.amazon.properties new file mode 100644 index 000000000..4ea12a03d --- /dev/null +++ b/etc/config/v.amazon.properties @@ -0,0 +1,17 @@ +compilers=&v +compilerType=v +versionFlag=--version +objdumper=/opt/compiler-explorer/gcc-12.1.0/bin/objdump +defaultCompiler=v04 +externalparser=CEAsmParser +externalparser.exe=/usr/local/bin/asm-parser +supportsBinary=false +supportsExecute=false + +group.v.compilers=v04 +group.v.groupName=V +group.v.licenseName=MIT License +group.v.licenseLink=https://raw.githubusercontent.com/vlang/v/master/LICENSE + +compiler.v04.exe=/opt/compiler-explorer/v-2023.30/v +compiler.v04.name=V 0.4 \ No newline at end of file diff --git a/etc/config/v.defaults.properties b/etc/config/v.defaults.properties new file mode 100644 index 000000000..3b97f2d9a --- /dev/null +++ b/etc/config/v.defaults.properties @@ -0,0 +1,15 @@ +# Default settings for V + +objdumper=objdump +supportsBinary=false +supportsExecute=false +versionFlag=--version +compilerType=v + +compilers=v +defaultCompiler=v + +compiler.v.exe=/usr/local/bin/v +compiler.v.name=V + +libs= diff --git a/examples/v/Sum_over_array.v b/examples/v/Sum_over_array.v new file mode 100644 index 000000000..96b5b90f8 --- /dev/null +++ b/examples/v/Sum_over_array.v @@ -0,0 +1,12 @@ +import arrays + +fn sum_array(array []int) !int { + return arrays.reduce(array, fn (acc int, i int) int { + return acc + i + }) +} + +fn main() { + a := [1, 2, 3, 4, 5] + println(sum_array(a) or { 0 }) +} diff --git a/examples/v/default.v b/examples/v/default.v new file mode 100644 index 000000000..4f99e15c3 --- /dev/null +++ b/examples/v/default.v @@ -0,0 +1,7 @@ +fn square(num int) int { + return num * num +} + +fn main() { + println(square(3)) +} diff --git a/examples/v/helloworld.v b/examples/v/helloworld.v new file mode 100644 index 000000000..88a46c5c4 --- /dev/null +++ b/examples/v/helloworld.v @@ -0,0 +1,3 @@ +fn main() { + println('Hello, World!') +} diff --git a/lib/compilers/_all.ts b/lib/compilers/_all.ts index 422d320d0..69ad93391 100644 --- a/lib/compilers/_all.ts +++ b/lib/compilers/_all.ts @@ -109,6 +109,7 @@ export {TinyCCompiler} from './tinyc.js'; export {ToitCompiler} from './toit.js'; export {TurboCCompiler} from './turboc.js'; export {TypeScriptNativeCompiler} from './typescript-native.js'; +export {VCompiler} from './v.js'; export {ValaCompiler} from './vala.js'; export {VBCompiler} from './dotnet.js'; export {V8Compiler} from './v8.js'; diff --git a/lib/compilers/v.ts b/lib/compilers/v.ts new file mode 100644 index 000000000..a3f26280d --- /dev/null +++ b/lib/compilers/v.ts @@ -0,0 +1,211 @@ +// Copyright (c) 2023, 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 'path'; + +import {unwrap} from '../assert.js'; +import {ParseFiltersAndOutputOptions} from '../../types/features/filters.interfaces.js'; +import {BaseCompiler} from '../base-compiler.js'; +import { ExecutionOptions } from '../../types/compilation/compilation.interfaces.js'; + +const V_DEFAULT_BACKEND = 'c'; + +export class VCompiler extends BaseCompiler { + outputFileExt = `.${V_DEFAULT_BACKEND}`; + + static get key() { + return 'v'; + } + + override optionsForFilter(filters: ParseFiltersAndOutputOptions, outputFilename: string, userOptions?: string[]) { + const options = unwrap(userOptions); + if (options) { + if (options.includes('-h') || options.includes('--help')) { + return []; + } + + const backend = this.getBackendFromOptions(options); + const outputFileExt = this.getFileExtForBackend(backend); + if (outputFileExt !== undefined) { + this.outputFileExt = outputFileExt; + } + } + + const compilerOptions: string[] = []; + if (!filters.binary) { + compilerOptions.push('-o'); + compilerOptions.push(this.filename(this.patchOutputFilename(outputFilename))); + } + + if (!filters.labels) { + compilerOptions.push('-skip-unused'); + } + + return compilerOptions; + } + + override async processAsm(result: any, filters, options: string[]): Promise { + const backend = this.getBackendFromOptions(options); + switch (backend) { + case 'c': + case 'js': + case 'js_node': + case 'js_browser': + case 'js_freestanding': + case 'go': + return this.processCLike(result, filters); + default: + return this.asm.process(result.asm, filters); + } + } + + override getSharedLibraryPathsAsArguments(libraries, libDownloadPath) { + return []; + } + + override getSharedLibraryLinks(libraries: any[]): string[] { + return []; + } + + override getOutputFilename(dirPath: string, outputFilebase: string, key?: any): string { + return path.join(dirPath, 'output' + this.outputFileExt); + } + + override getDefaultExecOptions(): ExecutionOptions & { env: Record; } { + const options = super.getDefaultExecOptions(); + options.env['VMODULES'] = path.join(path.dirname(this.compiler.exe), '.vmodules'); + return options; + } + + getBackendFromOptions(options: string[]): string { + const backendOpt = options.indexOf('-b'); + if (backendOpt >= 0 && options[backendOpt + 1]) return options[backendOpt + 1].toLowerCase(); + if (options.includes('-native')) return 'native'; + if (options.includes('-interpret')) return 'interpret'; + + return V_DEFAULT_BACKEND; // default V backend + } + + getFileExtForBackend(backend: string): string | undefined { + switch (backend) { + case 'c': + case 'go': + case 'wasm': + return '.' + backend; + case 'js': + case 'js_node': + case 'js_browser': + case 'js_freestanding': + return '.js'; + case 'native': + return ''; + default: + return undefined; + } + } + + patchOutputFilename(outputFilename: string): string { + const parts = outputFilename.split('.'); + + if (this.outputFileExt === '') { + parts.pop(); + return parts.join('.'); + } + + parts[parts.length - 1] = this.outputFileExt.split('.')[1]; + return parts.join('.'); + } + + removeUnusedLabels(input: string[]): string[] { + const output: string[] = []; + + const lineRe = /^.*main__.*$/; + const mainFunctionCall = '\tmain__main();'; + + let scopeDepth = 0; + let insertNewLine = false; + + for (const lineNo in input) { + const line = input[lineNo]; + if (!line) continue; + + if (insertNewLine) { + output.push(''); + insertNewLine = false; + } + + if ((scopeDepth === 0 && line.match(lineRe) && line !== mainFunctionCall) || scopeDepth > 0) { + const opening = (line.match(/{/g) || []).length - 1; + const closing = (line.match(/}/g) || []).length - 1; + scopeDepth += opening - closing; + + output.push(line); + + insertNewLine = scopeDepth === 0; + } + } + + return output; + } + + removeWhitespaceLines = (input: string[]) => input.map(line => line.trimStart()).filter(line => line !== ''); + removeComments = (input: string[]) => + input + .filter(line => !line.trimStart().startsWith('//')) + .map(line => line.split('//')[0].replaceAll(/(\/\*).*?(\*\/)/g, '')); + removeDirectives = (input: string[]) => input.filter(line => !line.trimStart().startsWith('#')); + + async processCLike(result, filters): Promise { + let lines = result.asm.split('\n'); + + // remove non-user defined code + if (!filters.labels) lines = this.removeUnusedLabels(lines); + + // remove comments + if (!filters.commentOnly) lines = this.removeComments(lines); + + // remove whitespace + if (filters.trim) lines = this.removeWhitespaceLines(lines); + + // remove preprocessor directives + if (!filters.directives) lines = this.removeDirectives(lines); + + // finally, remove unnecessary newlines to make the output nicer + const finalLines: string[] = []; + let emptyLineEncountered = false; + + for (const lineNo in lines) { + const line = lines[lineNo]; + + if (line.trimStart() === '') { + if (emptyLineEncountered) continue; + emptyLineEncountered = true; + } else emptyLineEncountered = false; + + finalLines.push(line); + } + + return {asm: finalLines.map(line => ({text: line}))}; + } +} diff --git a/lib/formatters/_all.ts b/lib/formatters/_all.ts index 459455e53..5a498afb5 100644 --- a/lib/formatters/_all.ts +++ b/lib/formatters/_all.ts @@ -26,3 +26,4 @@ export {ClangFormatFormatter} from './clang-format.js'; export {DartFormatFormatter} from './dartformat.js'; export {GoFmtFormatter} from './gofmt.js'; export {RustFmtFormatter} from './rustfmt.js'; +export {VFmtFormatter} from './vfmt.js'; diff --git a/lib/formatters/vfmt.ts b/lib/formatters/vfmt.ts new file mode 100644 index 000000000..6369989e7 --- /dev/null +++ b/lib/formatters/vfmt.ts @@ -0,0 +1,52 @@ +// Copyright (c) 2023, 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 {UnprocessedExecResult} from '../../types/execution/execution.interfaces.js'; +import {FormatOptions} from './base.interfaces.js'; +import {BaseFormatter} from './base.js'; + +import * as exec from '../exec.js'; + +export class VFmtFormatter extends BaseFormatter { + static get key() { + return 'vfmt'; + } + + /** + * Format the provided source code + * + * This function does not use any options, because v fmt does not have any + * options except the default provided ones (v fmt -w ). + */ + override async format(source: string, options: FormatOptions): Promise { + return await exec.execute(this.formatterInfo.exe, ['fmt', '-w'], {input: source}); + } + + /** + * v fmt has no styling options + */ + override isValidStyle(style: string): boolean { + return true; + } +} diff --git a/lib/languages.ts b/lib/languages.ts index 4ec435b91..33c232a2b 100644 --- a/lib/languages.ts +++ b/lib/languages.ts @@ -648,6 +648,17 @@ const definitions: Record = { previewFilter: null, monacoDisassembly: null, }, + v: { + name: 'V', + monaco: 'v', + extensions: ['.v', '.vsh'], + alias: [], + logoUrl: 'v.svg', + logoUrlDark: null, + formatter: 'vfmt', + previewFilter: null, + monacoDisassembly: 'nc', + }, vala: { name: 'Vala', monaco: 'vala', diff --git a/static/formatter-registry.ts b/static/formatter-registry.ts index 4363ef9ce..d4cc5cf95 100644 --- a/static/formatter-registry.ts +++ b/static/formatter-registry.ts @@ -104,3 +104,4 @@ register('nc', 'clangformat', false); register('go', 'gofmt', true); register('rust', 'rustfmt', true); register('dart', 'dartformat', true); +register('v', 'vfmt', true); diff --git a/static/modes/_all.ts b/static/modes/_all.ts index 2a1952b45..7c69a28ec 100644 --- a/static/modes/_all.ts +++ b/static/modes/_all.ts @@ -57,5 +57,6 @@ import './ocaml-mode'; import './openclc-mode'; import './ptx-mode'; import './spirv-mode'; +import './v-mode'; import './vala-mode'; import './zig-mode'; diff --git a/static/modes/v-mode.ts b/static/modes/v-mode.ts new file mode 100644 index 000000000..64043f8de --- /dev/null +++ b/static/modes/v-mode.ts @@ -0,0 +1,252 @@ +// Copyright (c) 2023, 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 { + defaultToken: 'invalid', + + keywords: [ + '__global', + '_likely_', + '_unlikely_', + 'as', + 'asm', + 'assert', + 'atomic', + 'break', + 'const', + 'continue', + 'defer', + 'dump', + 'else', + 'enum', + 'false', + 'fn', + 'for', + 'go', + 'goto', + 'if', + 'import', + 'in', + 'interface', + 'is', + 'isreftype', + 'it', + 'like', + 'lock', + 'match', + 'module', + 'mut', + 'nil', + 'none', + 'offsetof', + 'or', + 'pub', + 'return', + 'rlock', + 'select', + 'shared', + 'sizeof', + 'spawn', + 'static', + 'struct', + 'true', + 'type', + 'typeof', + 'union', + 'unsafe', + 'volatile', + ], + + typeKeywords: [ + 'i8', + 'u8', + 'i16', + 'u16', + 'int', + 'u32', + 'i64', + 'u64', + 'f32', + 'f64', + 'string', + 'map', + 'struct', + 'bool', + 'voidptr', + 'charptr', + 'isize', + 'usize', + ], + + operators: [ + '+', + '-', + '*', + '/', + '%', + '^', + '~', + '|', + '#', + '&', + '++', + '--', + '&&', + '||', + '!', + '.', + '!in', + '!is', + ';', + ':', + '<-', + '=', + ':=', + '+=', + '-=', + '*=', + '/=', + '%=', + '|=', + '&=', + '>>=', + '>>>=', + '<<=', + '==', + '!=', + '>', + '<', + '>=', + '<=', + '?', + '<<', + '>>', + '>>>', + '$', + ], + + symbols: /[=>](?!@symbols)/, '@brackets'], + [ + /@symbols/, + { + cases: { + '@operators': 'operator', + '@default': '', + }, + }, + ], + + // numbers + [/[0-9_]*\.[0-9_]+([eE][-+]?[0-9_]+)?/, 'number.float'], + [/0[xX][0-9a-fA-F_]*[0-9a-fA-F_]/, 'number.hex'], + [/0o[0-7_]*[0-7_]/, 'number.octal'], + [/0[bB][0-1_]*[0-1_]/, 'number.binary'], + [/[0-9_]+/, 'number'], + + // delimiter: after number because of .\d floats + [/[;,.]/, 'delimiter'], + + // single-quoted strings + [/'([^'\\]|\\.)*$/, 'string.invalid'], + [/c?\\\\.*$/, 'string'], + [/c?'/, 'string', '@single_quoted_string'], + + // double-quoted strings + [/"([^"\\]|\\.)*$/, 'string.invalid'], // non-teminated string + [/c?\\\\.*$/, 'string'], + [/c?"/, 'string', '@double_quoted_string'], + + // runes + [/`[^\\`]`/, 'string'], + [/(`)(@escapes)(`)/, ['string', 'string.escape', 'string']], + [/`/, 'string.invalid'], + ], + + whitespace: [ + [/[ \r\n]+/, 'white'], + [/\/\*/, 'comment', '@comment'], + [/\/\+/, 'comment', '@comment'], + [/\/\/.*$/, 'comment'], + [/\t/, 'comment.invalid'], + ], + + comment: [ + [/[^/*]+/, 'comment'], + [/\/\*/, 'comment', '@comment'], + [/\*\//, 'comment', '@pop'], + [/[/*]/, 'comment'], + ], + + single_quoted_string: [ + [/[^\\']+/, 'string'], + [/@escapes/, 'string.escape'], + [/\\./, 'string.escape.invalid'], + [/'/, 'string', '@pop'], + ], + + double_quoted_string: [ + [/[^\\"]+/, 'string'], + [/@escapes/, 'string.escape'], + [/\\./, 'string.escape.invalid'], + [/"/, 'string', '@pop'], + ], + }, + }; +} + +const def = definition(); +monaco.languages.register({id: 'v'}); +monaco.languages.setMonarchTokensProvider('v', def); + +export = def; diff --git a/types/languages.interfaces.ts b/types/languages.interfaces.ts index acdef9b88..8d80bd3d3 100644 --- a/types/languages.interfaces.ts +++ b/types/languages.interfaces.ts @@ -79,6 +79,7 @@ export type LanguageKey = | 'swift' | 'toit' | 'typescript' + | 'v' | 'vala' | 'vb' | 'zig'; diff --git a/views/resources/logos/v.svg b/views/resources/logos/v.svg new file mode 100644 index 000000000..9a4ec604c --- /dev/null +++ b/views/resources/logos/v.svg @@ -0,0 +1 @@ +