]> luflow.net public git repositories - flow-web.git/blob - static/highlight/languages/fsharp.js
Initial commit.
[flow-web.git] / static / highlight / languages / fsharp.js
1 /*! `fsharp` grammar compiled for Highlight.js 11.11.1 */
2 (function(){
3 var hljsGrammar = (function () {
4 'use strict';
5
6 /**
7 * @param {string} value
8 * @returns {RegExp}
9 * */
10 function escape(value) {
11 return new RegExp(value.replace(/[-/\\^$*+?.()|[\]{}]/g, '\\$&'), 'm');
12 }
13
14 /**
15 * @param {RegExp | string } re
16 * @returns {string}
17 */
18 function source(re) {
19 if (!re) return null;
20 if (typeof re === "string") return re;
21
22 return re.source;
23 }
24
25 /**
26 * @param {RegExp | string } re
27 * @returns {string}
28 */
29 function lookahead(re) {
30 return concat('(?=', re, ')');
31 }
32
33 /**
34 * @param {...(RegExp | string) } args
35 * @returns {string}
36 */
37 function concat(...args) {
38 const joined = args.map((x) => source(x)).join("");
39 return joined;
40 }
41
42 /**
43 * @param { Array<string | RegExp | Object> } args
44 * @returns {object}
45 */
46 function stripOptionsFromArgs(args) {
47 const opts = args[args.length - 1];
48
49 if (typeof opts === 'object' && opts.constructor === Object) {
50 args.splice(args.length - 1, 1);
51 return opts;
52 } else {
53 return {};
54 }
55 }
56
57 /** @typedef { {capture?: boolean} } RegexEitherOptions */
58
59 /**
60 * Any of the passed expresssions may match
61 *
62 * Creates a huge this | this | that | that match
63 * @param {(RegExp | string)[] | [...(RegExp | string)[], RegexEitherOptions]} args
64 * @returns {string}
65 */
66 function either(...args) {
67 /** @type { object & {capture?: boolean} } */
68 const opts = stripOptionsFromArgs(args);
69 const joined = '('
70 + (opts.capture ? "" : "?:")
71 + args.map((x) => source(x)).join("|") + ")";
72 return joined;
73 }
74
75 /*
76 Language: F#
77 Author: Jonas Follesø <jonas@follesoe.no>
78 Contributors: Troy Kershaw <hello@troykershaw.com>, Henrik Feldt <henrik@haf.se>, Melvyn Laïly <melvyn.laily@gmail.com>
79 Website: https://docs.microsoft.com/en-us/dotnet/fsharp/
80 Category: functional
81 */
82
83
84 /** @type LanguageFn */
85 function fsharp(hljs) {
86 const KEYWORDS = [
87 "abstract",
88 "and",
89 "as",
90 "assert",
91 "base",
92 "begin",
93 "class",
94 "default",
95 "delegate",
96 "do",
97 "done",
98 "downcast",
99 "downto",
100 "elif",
101 "else",
102 "end",
103 "exception",
104 "extern",
105 // "false", // literal
106 "finally",
107 "fixed",
108 "for",
109 "fun",
110 "function",
111 "global",
112 "if",
113 "in",
114 "inherit",
115 "inline",
116 "interface",
117 "internal",
118 "lazy",
119 "let",
120 "match",
121 "member",
122 "module",
123 "mutable",
124 "namespace",
125 "new",
126 // "not", // built_in
127 // "null", // literal
128 "of",
129 "open",
130 "or",
131 "override",
132 "private",
133 "public",
134 "rec",
135 "return",
136 "static",
137 "struct",
138 "then",
139 "to",
140 // "true", // literal
141 "try",
142 "type",
143 "upcast",
144 "use",
145 "val",
146 "void",
147 "when",
148 "while",
149 "with",
150 "yield"
151 ];
152
153 const BANG_KEYWORD_MODE = {
154 // monad builder keywords (matches before non-bang keywords)
155 scope: 'keyword',
156 match: /\b(yield|return|let|do|match|use)!/
157 };
158
159 const PREPROCESSOR_KEYWORDS = [
160 "if",
161 "else",
162 "endif",
163 "line",
164 "nowarn",
165 "light",
166 "r",
167 "i",
168 "I",
169 "load",
170 "time",
171 "help",
172 "quit"
173 ];
174
175 const LITERALS = [
176 "true",
177 "false",
178 "null",
179 "Some",
180 "None",
181 "Ok",
182 "Error",
183 "infinity",
184 "infinityf",
185 "nan",
186 "nanf"
187 ];
188
189 const SPECIAL_IDENTIFIERS = [
190 "__LINE__",
191 "__SOURCE_DIRECTORY__",
192 "__SOURCE_FILE__"
193 ];
194
195 // Since it's possible to re-bind/shadow names (e.g. let char = 'c'),
196 // these builtin types should only be matched when a type name is expected.
197 const KNOWN_TYPES = [
198 // basic types
199 "bool",
200 "byte",
201 "sbyte",
202 "int8",
203 "int16",
204 "int32",
205 "uint8",
206 "uint16",
207 "uint32",
208 "int",
209 "uint",
210 "int64",
211 "uint64",
212 "nativeint",
213 "unativeint",
214 "decimal",
215 "float",
216 "double",
217 "float32",
218 "single",
219 "char",
220 "string",
221 "unit",
222 "bigint",
223 // other native types or lowercase aliases
224 "option",
225 "voption",
226 "list",
227 "array",
228 "seq",
229 "byref",
230 "exn",
231 "inref",
232 "nativeptr",
233 "obj",
234 "outref",
235 "voidptr",
236 // other important FSharp types
237 "Result"
238 ];
239
240 const BUILTINS = [
241 // Somewhat arbitrary list of builtin functions and values.
242 // Most of them are declared in Microsoft.FSharp.Core
243 // I tried to stay relevant by adding only the most idiomatic
244 // and most used symbols that are not already declared as types.
245 "not",
246 "ref",
247 "raise",
248 "reraise",
249 "dict",
250 "readOnlyDict",
251 "set",
252 "get",
253 "enum",
254 "sizeof",
255 "typeof",
256 "typedefof",
257 "nameof",
258 "nullArg",
259 "invalidArg",
260 "invalidOp",
261 "id",
262 "fst",
263 "snd",
264 "ignore",
265 "lock",
266 "using",
267 "box",
268 "unbox",
269 "tryUnbox",
270 "printf",
271 "printfn",
272 "sprintf",
273 "eprintf",
274 "eprintfn",
275 "fprintf",
276 "fprintfn",
277 "failwith",
278 "failwithf"
279 ];
280
281 const ALL_KEYWORDS = {
282 keyword: KEYWORDS,
283 literal: LITERALS,
284 built_in: BUILTINS,
285 'variable.constant': SPECIAL_IDENTIFIERS
286 };
287
288 // (* potentially multi-line Meta Language style comment *)
289 const ML_COMMENT =
290 hljs.COMMENT(/\(\*(?!\))/, /\*\)/, {
291 contains: ["self"]
292 });
293 // Either a multi-line (* Meta Language style comment *) or a single line // C style comment.
294 const COMMENT = {
295 variants: [
296 ML_COMMENT,
297 hljs.C_LINE_COMMENT_MODE,
298 ]
299 };
300
301 // Most identifiers can contain apostrophes
302 const IDENTIFIER_RE = /[a-zA-Z_](\w|')*/;
303
304 const QUOTED_IDENTIFIER = {
305 scope: 'variable',
306 begin: /``/,
307 end: /``/
308 };
309
310 // 'a or ^a where a can be a ``quoted identifier``
311 const BEGIN_GENERIC_TYPE_SYMBOL_RE = /\B('|\^)/;
312 const GENERIC_TYPE_SYMBOL = {
313 scope: 'symbol',
314 variants: [
315 // the type name is a quoted identifier:
316 { match: concat(BEGIN_GENERIC_TYPE_SYMBOL_RE, /``.*?``/) },
317 // the type name is a normal identifier (we don't use IDENTIFIER_RE because there cannot be another apostrophe here):
318 { match: concat(BEGIN_GENERIC_TYPE_SYMBOL_RE, hljs.UNDERSCORE_IDENT_RE) }
319 ],
320 relevance: 0
321 };
322
323 const makeOperatorMode = function({ includeEqual }) {
324 // List or symbolic operator characters from the FSharp Spec 4.1, minus the dot, and with `?` added, used for nullable operators.
325 let allOperatorChars;
326 if (includeEqual)
327 allOperatorChars = "!%&*+-/<=>@^|~?";
328 else
329 allOperatorChars = "!%&*+-/<>@^|~?";
330 const OPERATOR_CHARS = Array.from(allOperatorChars);
331 const OPERATOR_CHAR_RE = concat('[', ...OPERATOR_CHARS.map(escape), ']');
332 // The lone dot operator is special. It cannot be redefined, and we don't want to highlight it. It can be used as part of a multi-chars operator though.
333 const OPERATOR_CHAR_OR_DOT_RE = either(OPERATOR_CHAR_RE, /\./);
334 // When a dot is present, it must be followed by another operator char:
335 const OPERATOR_FIRST_CHAR_OF_MULTIPLE_RE = concat(OPERATOR_CHAR_OR_DOT_RE, lookahead(OPERATOR_CHAR_OR_DOT_RE));
336 const SYMBOLIC_OPERATOR_RE = either(
337 concat(OPERATOR_FIRST_CHAR_OF_MULTIPLE_RE, OPERATOR_CHAR_OR_DOT_RE, '*'), // Matches at least 2 chars operators
338 concat(OPERATOR_CHAR_RE, '+'), // Matches at least one char operators
339 );
340 return {
341 scope: 'operator',
342 match: either(
343 // symbolic operators:
344 SYMBOLIC_OPERATOR_RE,
345 // other symbolic keywords:
346 // Type casting and conversion operators:
347 /:\?>/,
348 /:\?/,
349 /:>/,
350 /:=/, // Reference cell assignment
351 /::?/, // : or ::
352 /\$/), // A single $ can be used as an operator
353 relevance: 0
354 };
355 };
356
357 const OPERATOR = makeOperatorMode({ includeEqual: true });
358 // This variant is used when matching '=' should end a parent mode:
359 const OPERATOR_WITHOUT_EQUAL = makeOperatorMode({ includeEqual: false });
360
361 const makeTypeAnnotationMode = function(prefix, prefixScope) {
362 return {
363 begin: concat( // a type annotation is a
364 prefix, // should be a colon or the 'of' keyword
365 lookahead( // that has to be followed by
366 concat(
367 /\s*/, // optional space
368 either( // then either of:
369 /\w/, // word
370 /'/, // generic type name
371 /\^/, // generic type name
372 /#/, // flexible type name
373 /``/, // quoted type name
374 /\(/, // parens type expression
375 /{\|/, // anonymous type annotation
376 )))),
377 beginScope: prefixScope,
378 // BUG: because ending with \n is necessary for some cases, multi-line type annotations are not properly supported.
379 // Examples where \n is required at the end:
380 // - abstract member definitions in classes: abstract Property : int * string
381 // - return type annotations: let f f' = f' () : returnTypeAnnotation
382 // - record fields definitions: { A : int \n B : string }
383 end: lookahead(
384 either(
385 /\n/,
386 /=/)),
387 relevance: 0,
388 // we need the known types, and we need the type constraint keywords and literals. e.g.: when 'a : null
389 keywords: hljs.inherit(ALL_KEYWORDS, { type: KNOWN_TYPES }),
390 contains: [
391 COMMENT,
392 GENERIC_TYPE_SYMBOL,
393 hljs.inherit(QUOTED_IDENTIFIER, { scope: null }), // match to avoid strange patterns inside that may break the parsing
394 OPERATOR_WITHOUT_EQUAL
395 ]
396 };
397 };
398
399 const TYPE_ANNOTATION = makeTypeAnnotationMode(/:/, 'operator');
400 const DISCRIMINATED_UNION_TYPE_ANNOTATION = makeTypeAnnotationMode(/\bof\b/, 'keyword');
401
402 // type MyType<'a> = ...
403 const TYPE_DECLARATION = {
404 begin: [
405 /(^|\s+)/, // prevents matching the following: `match s.stype with`
406 /type/,
407 /\s+/,
408 IDENTIFIER_RE
409 ],
410 beginScope: {
411 2: 'keyword',
412 4: 'title.class'
413 },
414 end: lookahead(/\(|=|$/),
415 keywords: ALL_KEYWORDS, // match keywords in type constraints. e.g.: when 'a : null
416 contains: [
417 COMMENT,
418 hljs.inherit(QUOTED_IDENTIFIER, { scope: null }), // match to avoid strange patterns inside that may break the parsing
419 GENERIC_TYPE_SYMBOL,
420 {
421 // For visual consistency, highlight type brackets as operators.
422 scope: 'operator',
423 match: /<|>/
424 },
425 TYPE_ANNOTATION // generic types can have constraints, which are type annotations. e.g. type MyType<'T when 'T : delegate<obj * string>> =
426 ]
427 };
428
429 const COMPUTATION_EXPRESSION = {
430 // computation expressions:
431 scope: 'computation-expression',
432 // BUG: might conflict with record deconstruction. e.g. let f { Name = name } = name // will highlight f
433 match: /\b[_a-z]\w*(?=\s*\{)/
434 };
435
436 const PREPROCESSOR = {
437 // preprocessor directives and fsi commands:
438 begin: [
439 /^\s*/,
440 concat(/#/, either(...PREPROCESSOR_KEYWORDS)),
441 /\b/
442 ],
443 beginScope: { 2: 'meta' },
444 end: lookahead(/\s|$/)
445 };
446
447 // TODO: this definition is missing support for type suffixes and octal notation.
448 // BUG: range operator without any space is wrongly interpreted as a single number (e.g. 1..10 )
449 const NUMBER = {
450 variants: [
451 hljs.BINARY_NUMBER_MODE,
452 hljs.C_NUMBER_MODE
453 ]
454 };
455
456 // All the following string definitions are potentially multi-line.
457 // BUG: these definitions are missing support for byte strings (suffixed with B)
458
459 // "..."
460 const QUOTED_STRING = {
461 scope: 'string',
462 begin: /"/,
463 end: /"/,
464 contains: [
465 hljs.BACKSLASH_ESCAPE
466 ]
467 };
468 // @"..."
469 const VERBATIM_STRING = {
470 scope: 'string',
471 begin: /@"/,
472 end: /"/,
473 contains: [
474 {
475 match: /""/ // escaped "
476 },
477 hljs.BACKSLASH_ESCAPE
478 ]
479 };
480 // """..."""
481 const TRIPLE_QUOTED_STRING = {
482 scope: 'string',
483 begin: /"""/,
484 end: /"""/,
485 relevance: 2
486 };
487 const SUBST = {
488 scope: 'subst',
489 begin: /\{/,
490 end: /\}/,
491 keywords: ALL_KEYWORDS
492 };
493 // $"...{1+1}..."
494 const INTERPOLATED_STRING = {
495 scope: 'string',
496 begin: /\$"/,
497 end: /"/,
498 contains: [
499 {
500 match: /\{\{/ // escaped {
501 },
502 {
503 match: /\}\}/ // escaped }
504 },
505 hljs.BACKSLASH_ESCAPE,
506 SUBST
507 ]
508 };
509 // $@"...{1+1}..."
510 const INTERPOLATED_VERBATIM_STRING = {
511 scope: 'string',
512 begin: /(\$@|@\$)"/,
513 end: /"/,
514 contains: [
515 {
516 match: /\{\{/ // escaped {
517 },
518 {
519 match: /\}\}/ // escaped }
520 },
521 {
522 match: /""/
523 },
524 hljs.BACKSLASH_ESCAPE,
525 SUBST
526 ]
527 };
528 // $"""...{1+1}..."""
529 const INTERPOLATED_TRIPLE_QUOTED_STRING = {
530 scope: 'string',
531 begin: /\$"""/,
532 end: /"""/,
533 contains: [
534 {
535 match: /\{\{/ // escaped {
536 },
537 {
538 match: /\}\}/ // escaped }
539 },
540 SUBST
541 ],
542 relevance: 2
543 };
544 // '.'
545 const CHAR_LITERAL = {
546 scope: 'string',
547 match: concat(
548 /'/,
549 either(
550 /[^\\']/, // either a single non escaped char...
551 /\\(?:.|\d{3}|x[a-fA-F\d]{2}|u[a-fA-F\d]{4}|U[a-fA-F\d]{8})/ // ...or an escape sequence
552 ),
553 /'/
554 )
555 };
556 // F# allows a lot of things inside string placeholders.
557 // Things that don't currently seem allowed by the compiler: types definition, attributes usage.
558 // (Strictly speaking, some of the followings are only allowed inside triple quoted interpolated strings...)
559 SUBST.contains = [
560 INTERPOLATED_VERBATIM_STRING,
561 INTERPOLATED_STRING,
562 VERBATIM_STRING,
563 QUOTED_STRING,
564 CHAR_LITERAL,
565 BANG_KEYWORD_MODE,
566 COMMENT,
567 QUOTED_IDENTIFIER,
568 TYPE_ANNOTATION,
569 COMPUTATION_EXPRESSION,
570 PREPROCESSOR,
571 NUMBER,
572 GENERIC_TYPE_SYMBOL,
573 OPERATOR
574 ];
575 const STRING = {
576 variants: [
577 INTERPOLATED_TRIPLE_QUOTED_STRING,
578 INTERPOLATED_VERBATIM_STRING,
579 INTERPOLATED_STRING,
580 TRIPLE_QUOTED_STRING,
581 VERBATIM_STRING,
582 QUOTED_STRING,
583 CHAR_LITERAL
584 ]
585 };
586
587 return {
588 name: 'F#',
589 aliases: [
590 'fs',
591 'f#'
592 ],
593 keywords: ALL_KEYWORDS,
594 illegal: /\/\*/,
595 classNameAliases: {
596 'computation-expression': 'keyword'
597 },
598 contains: [
599 BANG_KEYWORD_MODE,
600 STRING,
601 COMMENT,
602 QUOTED_IDENTIFIER,
603 TYPE_DECLARATION,
604 {
605 // e.g. [<Attributes("")>] or [<``module``: MyCustomAttributeThatWorksOnModules>]
606 // or [<Sealed; NoEquality; NoComparison; CompiledName("FSharpAsync`1")>]
607 scope: 'meta',
608 begin: /\[</,
609 end: />\]/,
610 relevance: 2,
611 contains: [
612 QUOTED_IDENTIFIER,
613 // can contain any constant value
614 TRIPLE_QUOTED_STRING,
615 VERBATIM_STRING,
616 QUOTED_STRING,
617 CHAR_LITERAL,
618 NUMBER
619 ]
620 },
621 DISCRIMINATED_UNION_TYPE_ANNOTATION,
622 TYPE_ANNOTATION,
623 COMPUTATION_EXPRESSION,
624 PREPROCESSOR,
625 NUMBER,
626 GENERIC_TYPE_SYMBOL,
627 OPERATOR
628 ]
629 };
630 }
631
632 return fsharp;
633
634 })();
635
636 hljs.registerLanguage('fsharp', hljsGrammar);
637 })();