diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 68998ab78e..d763151866 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -17,14 +17,14 @@ For a stable release, the commit specified in `vscodeRef` in [package.json](./pa Please understand that we only bundle languages with the monaco editor that have a significant relevance (for example, those that have an article in Wikipedia). -- create `$/src/basic-languages/{myLang}/{myLang}.contribution.ts` -- create `$/src/basic-languages/{myLang}/{myLang}.ts` -- create `$/src/basic-languages/{myLang}/{myLang}.test.ts` -- edit `$/src/basic-languages/monaco.contribution.ts` and register your new language -- create `$/website/index/samples/sample.{myLang}.txt` +- create `$/src/languages/definitions/{myLang}/register.ts` +- create `$/src/languages/definitions/{myLang}/{myLang}.ts` +- create `$/src/languages/definitions/{myLang}/{myLang}.test.ts` +- edit `$/src/languages/definitions/register.all.ts` and register your new language +- create `$/website/src/website/data/home-samples/sample.{myLang}.txt` ```js -import './{myLang}/{myLang}.contribution'; +import './{myLang}/register'; ``` ## Debugging / Developing The Core Editor diff --git a/src/languages/definitions/ocaml/ocaml.test.ts b/src/languages/definitions/ocaml/ocaml.test.ts new file mode 100644 index 0000000000..6c7e157b33 --- /dev/null +++ b/src/languages/definitions/ocaml/ocaml.test.ts @@ -0,0 +1,223 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { testTokenization } from '../test/testRunner'; + +testTokenization('ocaml', [ + // Comment + [ + { + line: '(** documentation *)', + tokens: [ + { startIndex: 0, type: 'comment.ocaml' }, + ] + } + ], + [ + { + line: ' (* comment (* nested comment *) *) ', + tokens: [ + { startIndex: 0, type: '' }, + { startIndex: 1, type: 'comment.ocaml' }, + { startIndex: 35, type: '' } + ] + } + ], + [ + { + line: ' (* comment (* nested comment *)', + tokens: [ + { startIndex: 0, type: '' }, + { startIndex: 1, type: 'comment.ocaml' } + ] + }, + { + line: 'multiline comment', + tokens: [ + { startIndex: 0, type: 'comment.ocaml' } + ] + }, + { + line: ' *)', + tokens: [ + { startIndex: 0, type: 'comment.ocaml' } + ] + }, + ], + // Integer + [ + { + line: '37', + tokens: [ + { startIndex: 0, type: 'number.ocaml' }, + ] + } + ], + [ + { + line: '1_000_000', + tokens: [ + { startIndex: 0, type: 'number.ocaml' }, + ] + } + ], + [ + { + line: '0x00A9', + tokens: [ + { startIndex: 0, type: 'number.hex.ocaml' }, + ] + } + ], + [ + { + line: '0L', + tokens: [ + { startIndex: 0, type: 'number.ocaml' }, + ] + } + ], + [ + { + line: '3.141_592_653_589_793_12', + tokens: [ + { startIndex: 0, type: 'number.float.ocaml' }, + ] + } + ], + [ + { + line: '-1e-5', + tokens: [ + { startIndex: 0, type: 'number.float.ocaml' }, + ] + } + ], + [ + { + line: '0x1p-52', + tokens: [ + { startIndex: 0, type: 'number.float.ocaml' }, + ] + } + ], + // Character + [ + { + line: '\'a\'', + tokens: [ + { startIndex: 0, type: 'string.ocaml' }, + ] + } + ], + [ + { + line: '\'\\\'\'', + tokens: [ + { startIndex: 0, type: 'string.ocaml' }, + { startIndex: 1, type: 'string.escape.ocaml' }, + { startIndex: 3, type: 'string.ocaml' }, + ] + } + ], + [ + { + line: '\'\\xA9\'', + tokens: [ + { startIndex: 0, type: 'string.ocaml' }, + { startIndex: 1, type: 'string.escape.ocaml' }, + { startIndex: 5, type: 'string.ocaml' }, + ] + } + ], + // String + [ + { + line: '"Hello, World!\\n"', + tokens: [ + { startIndex: 0, type: 'string.ocaml' }, + { startIndex: 14, type: 'string.escape.ocaml' }, + { startIndex: 16, type: 'string.ocaml' }, + ] + } + ], + [ + { + line: '"\\u{207A}"', + tokens: [ + { startIndex: 0, type: 'string.ocaml' }, + { startIndex: 1, type: 'string.escape.ocaml' }, + { startIndex: 9, type: 'string.ocaml' }, + ] + } + ], + [ + { + line: 'let longstr =', + tokens: [ + { startIndex: 0, type: 'keyword.ocaml' }, + { startIndex: 3, type: '' }, + { startIndex: 4, type: 'identifier.ocaml' }, + { startIndex: 11, type: '' }, + { startIndex: 12, type: 'operator.ocaml' }, + ] + }, + { + line: ' "Call me Ishmael. Some years ago — never mind how long \\', + tokens: [ + { startIndex: 0, type: '' }, + { startIndex: 2, type: 'string.ocaml' }, + ] + }, + { + line: ' precisely..."', + tokens: [ + { startIndex: 0, type: 'string.ocaml' }, + ] + } + ], + [ + { + line: '{|"Hello, World!"|}', + tokens: [ + { startIndex: 0, type: 'string.ocaml' }, + ] + } + ], + [ + { + line: 'let a = "This is a string"', + tokens: [ + { startIndex: 0, type: 'keyword.ocaml' }, + { startIndex: 3, type: '' }, + { startIndex: 4, type: 'identifier.ocaml' }, + { startIndex: 5, type: '' }, + { startIndex: 6, type: 'operator.ocaml' }, + { startIndex: 7, type: '' }, + { startIndex: 8, type: 'string.ocaml' }, + ] + } + ], + [ + { + line: 'let pi = 4.0 *. atan 1.0', + tokens: [ + { startIndex: 0, type: 'keyword.ocaml' }, + { startIndex: 3, type: '' }, + { startIndex: 4, type: 'identifier.ocaml' }, + { startIndex: 6, type: '' }, + { startIndex: 7, type: 'operator.ocaml' }, + { startIndex: 8, type: '' }, + { startIndex: 9, type: 'number.float.ocaml' }, + { startIndex: 12, type: '' }, + { startIndex: 13, type: 'operator.ocaml' }, + { startIndex: 15, type: '' }, + { startIndex: 16, type: 'identifier.ocaml' }, + { startIndex: 20, type: '' }, + { startIndex: 21, type: 'number.float.ocaml' } + ] + } + ], +]); diff --git a/src/languages/definitions/ocaml/ocaml.ts b/src/languages/definitions/ocaml/ocaml.ts new file mode 100644 index 0000000000..55eb193329 --- /dev/null +++ b/src/languages/definitions/ocaml/ocaml.ts @@ -0,0 +1,242 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import type { languages } from '../../../editor'; + +export const conf: languages.LanguageConfiguration = { + comments: { + blockComment: ['(*', '*)'] + }, + brackets: [ + ['{', '}'], + ['[', ']'], + ['(', ')'], + ['{|', '|}'], + ['[|', '|]'], + ], + autoClosingPairs: [ + { open: '{', close: '}' }, + { open: '[', close: ']' }, + { open: '(', close: ')' }, + { open: '"', close: '"', notIn: ['string'] } + ], + surroundingPairs: [ + { open: '{', close: '}' }, + { open: '[', close: ']' }, + { open: '(', close: ')' }, + { open: '"', close: '"' }, + { open: "'", close: "'" } + ], + folding: { + markers: { + start: new RegExp('^\\s*//\\s*#region\\b|^\\s*\\(\\*\\s*#region(.*)\\*\\)'), + end: new RegExp('^\\s*//\\s*#endregion\\b|^\\s*\\(\\*\\s*#endregion\\s*\\*\\)') + } + } +}; + +export const language = { + defaultToken: 'invalid', + tokenPostfix: '.ocaml', + + keywords: [ + 'and', + 'as', + 'assert', + 'begin', + 'class', + 'constraint', + 'do', + 'done', + 'downto', + 'else', + 'end', + 'exception', + 'external', + 'false', + 'for', + 'fun', + 'function', + 'functor', + 'if', + 'in', + 'include', + 'inherit', + 'initializer', + 'lazy', + 'let', + 'match', + 'method', + 'module', + 'mutable', + 'new', + 'nonrec', + 'object', + 'of', + 'open', + 'open!', + 'or', + 'private', + 'rec', + 'sig', + 'struct', + 'then', + 'to', + 'true', + 'try', + 'type', + 'val', + 'virtual', + 'when', + 'while', + 'with', + ], + operatorKeywords: [ + 'mod', + 'land', + 'lor', + 'lxor', + 'lsl', + 'lsr', + 'asr', + ], + bracketOpenKeywords: [ + 'begin', + 'object', + 'sig', + 'struct' + ], + debuggingConsts: [ + '__FILE__', + '__FUNCTION__', + '__LINE__', + '__LINE_OF__', + '__LOC__', + '__LOC_OF__', + '__MODULE__', + '__POS__', + '__POS_OF__', + ], + + coreOperatorChar: /[$&*+\-\/=>@\^|]/, + operatorChar: /((@coreOperatorChar)|[~!?%<:\.])/, + infixSym: /(((@coreOperatorChar)|[%<])(@operatorChar)*|#(@operatorChar)+)/, + infixOp: /(!=|<|>|\|\||&&|:=|(@infixSym))/, + prefixSym: /(!(@operatorChar)*|[?~](@operatorChar)+)/, + operator: /((@prefixSym)|(@infixOp))/, + + escapes: /\\(?:[abfnrtv\\"']|x[0-9A-Fa-f]{1,4}|u[0-9A-Fa-f]{4}|U[0-9A-Fa-f]{8})/, + + coreDec: /[\d][\d_]*/, + coreHex: /0[xX][\da-fA-F][\da-fA-F_]*/, + exponent: /[eE][+-]?(@coreDec)/, + hexExponent: /[pP][+-]?(@coreDec)/, + integerSuffix: /[lLn]/, + + // The main tokenizer for our languages + tokenizer: { + root: [ + // identifiers and keywords + [ + /[~?]?[a-z_]\w*/, + { + cases: { + 'end': { token: 'keyword.bracket', bracket: '@close' }, + '@bracketOpenKeywords': { token: 'keyword.bracket', bracket: '@open' }, + '@operatorKeywords': 'operator', + '@keywords': 'keyword', + '@debuggingConsts': 'constant', + '@default': 'identifier' + } + } + ], + [/[A-Z]\w*/, 'constructor'], + + { include: '@whitespace' }, + + { include: '@number' }, + + [/[:;,.]/, 'delimiter'], + + // strings + [/"""/, 'string', '@string."""'], + [/"/, 'string', '@string."'], + [/{([a-z_]*)\|/, 'string', '@quoted.$1'], + + // characters + [/'[^\\']'B?/, 'string'], + [/(')(@escapes)(')/, ['string', 'string.escape', 'string']], + [/'/, 'string.invalid'], + + // brackets + [/(\[\||\|\])/, '@brackets'], + [/[{}()\[\]]/, '@brackets'], + + [/@operator/, 'operator'] + ], + + whitespace: [ + [/[ \t\r\n]+/, ''], + // [/\(\*\*/, 'comment.doc', '@comment'], + [/\(\*\*?/, 'comment', '@comment'] + ], + + comment: [ + [/[^\(\)*]+/, 'comment'], + [/\(\*/, 'comment', '@push'], + [/\*\)/, 'comment', '@pop'], + [/[\(\)*]/, 'comment'] + ], + + number: [ + // for float, fractional part and exponent part can be omitted but not both + [/-?(@coreHex)((\.[\da-fA-F_]*)(@hexExponent)?|(@hexExponent))/, 'number.float'], + [/-?(@coreHex)(@integerSuffix)?/, 'number.hex'], + [/-?0[oO][0-7][0-7_]*(@integerSuffix)?/, 'number.octal'], + [/-?0[bB][01][01_]*(@integerSuffix)?/, 'number.binary'], + [/-?(@coreDec)((\.[\d_]*)(@exponent)?|(@exponent))/, 'number.float'], + [/-?(@coreDec)(@integerSuffix)?/, 'number'], + ], + + string: [ + [ + /\|([a-z_]*)}/, + { + cases: { + '$S2==quoted-$1': { token: 'string', next: '@pop' }, + '@default': 'string' + } + } + ], + [/[^\\"]+/, 'string'], + [/\\$/, 'string'], // newline sequence + [/\\u{\w+}/, 'string.escape'], + [/@escapes/, 'string.escape'], + [/\\./, 'string.escape.invalid'], + [ + /("""|")/, + { + cases: { + '$#==$S2': { token: 'string', next: '@pop' }, + '@default': 'string' + } + } + ] + ], + quoted: [ + [/[^|]+/, 'string'], + [ + /\|([a-z_]*)}/, + { + cases: { + '$S2==$1': { token: 'string', next: '@pop' }, + '@default': 'string' + } + } + ], + [/\|/, 'string'] + ] + } +}; diff --git a/src/languages/definitions/ocaml/register.ts b/src/languages/definitions/ocaml/register.ts new file mode 100644 index 0000000000..2bf9cfb2c6 --- /dev/null +++ b/src/languages/definitions/ocaml/register.ts @@ -0,0 +1,13 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { registerLanguage } from '../_.contribution'; + +registerLanguage({ + id: 'ocaml', + extensions: ['.ml', '.mli'], + aliases: ['OCaml', 'ocaml'], + loader: () => import('./ocaml') +}); diff --git a/src/languages/definitions/register.all.ts b/src/languages/definitions/register.all.ts index ff00612225..193a92d5fb 100644 --- a/src/languages/definitions/register.all.ts +++ b/src/languages/definitions/register.all.ts @@ -43,6 +43,7 @@ import './mdx/register'; import './mips/register'; import './msdax/register'; import './mysql/register'; +import './ocaml/register'; import './objective-c/register'; import './pascal/register'; import './pascaligo/register'; diff --git a/website/src/website/data/home-samples/sample.ocaml.txt b/website/src/website/data/home-samples/sample.ocaml.txt new file mode 100644 index 0000000000..f254bf1549 --- /dev/null +++ b/website/src/website/data/home-samples/sample.ocaml.txt @@ -0,0 +1,46 @@ +(* + Welcome to the official OCaml Playground! + + You don't need to install anything - just write your code + and see the results appear in the Output panel. + + This playground is powered by OCaml 5 which comes with + support for shared-memory parallelism through domains and effects. + Below is some naive example code that calculates + the Fibonacci sequence in parallel. + + Happy hacking! +*) + +let num_domains = 2 +let n = 20 + +let rec fib n = + if n < 2 then 1 + else fib (n-1) + fib (n-2) + +let rec fib_par n d = + if d <= 1 then fib n + else + let a = fib_par (n-1) (d-1) in + let b = Domain.spawn (fun _ -> fib_par (n-2) (d-1)) in + a + Domain.join b + +let () = + let res = fib_par n num_domains in + Printf.printf "fib(%d) = %d\n" n res + +(* + By the way, a much better, single-threaded implementation that calculates + the Fibonacci sequence is this: + + let rec fib m n i = + if i < 1 then m + else fib n (n + m) (i - 1) + + let fib = fib 0 1 + + For a more in-depth, realistic example of how to use + parallel computation, take a look at + https://v2.ocaml.org/releases/5.0/manual/parallelism.html#s:par_iterators +*)