516 lines
9.3 KiB
JavaScript
516 lines
9.3 KiB
JavaScript
|
/**
|
||
|
* @param {string} value
|
||
|
* @returns {RegExp}
|
||
|
* */
|
||
|
|
||
|
/**
|
||
|
* @param {RegExp | string } re
|
||
|
* @returns {string}
|
||
|
*/
|
||
|
function source(re) {
|
||
|
if (!re) return null;
|
||
|
if (typeof re === "string") return re;
|
||
|
|
||
|
return re.source;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* @param {...(RegExp | string) } args
|
||
|
* @returns {string}
|
||
|
*/
|
||
|
function concat(...args) {
|
||
|
const joined = args.map((x) => source(x)).join("");
|
||
|
return joined;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Any of the passed expresssions may match
|
||
|
*
|
||
|
* Creates a huge this | this | that | that match
|
||
|
* @param {(RegExp | string)[] } args
|
||
|
* @returns {string}
|
||
|
*/
|
||
|
function either(...args) {
|
||
|
const joined = '(' + args.map((x) => source(x)).join("|") + ")";
|
||
|
return joined;
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
Language: Perl
|
||
|
Author: Peter Leonov <gojpeg@yandex.ru>
|
||
|
Website: https://www.perl.org
|
||
|
Category: common
|
||
|
*/
|
||
|
|
||
|
/** @type LanguageFn */
|
||
|
function perl(hljs) {
|
||
|
const KEYWORDS = [
|
||
|
'abs',
|
||
|
'accept',
|
||
|
'alarm',
|
||
|
'and',
|
||
|
'atan2',
|
||
|
'bind',
|
||
|
'binmode',
|
||
|
'bless',
|
||
|
'break',
|
||
|
'caller',
|
||
|
'chdir',
|
||
|
'chmod',
|
||
|
'chomp',
|
||
|
'chop',
|
||
|
'chown',
|
||
|
'chr',
|
||
|
'chroot',
|
||
|
'close',
|
||
|
'closedir',
|
||
|
'connect',
|
||
|
'continue',
|
||
|
'cos',
|
||
|
'crypt',
|
||
|
'dbmclose',
|
||
|
'dbmopen',
|
||
|
'defined',
|
||
|
'delete',
|
||
|
'die',
|
||
|
'do',
|
||
|
'dump',
|
||
|
'each',
|
||
|
'else',
|
||
|
'elsif',
|
||
|
'endgrent',
|
||
|
'endhostent',
|
||
|
'endnetent',
|
||
|
'endprotoent',
|
||
|
'endpwent',
|
||
|
'endservent',
|
||
|
'eof',
|
||
|
'eval',
|
||
|
'exec',
|
||
|
'exists',
|
||
|
'exit',
|
||
|
'exp',
|
||
|
'fcntl',
|
||
|
'fileno',
|
||
|
'flock',
|
||
|
'for',
|
||
|
'foreach',
|
||
|
'fork',
|
||
|
'format',
|
||
|
'formline',
|
||
|
'getc',
|
||
|
'getgrent',
|
||
|
'getgrgid',
|
||
|
'getgrnam',
|
||
|
'gethostbyaddr',
|
||
|
'gethostbyname',
|
||
|
'gethostent',
|
||
|
'getlogin',
|
||
|
'getnetbyaddr',
|
||
|
'getnetbyname',
|
||
|
'getnetent',
|
||
|
'getpeername',
|
||
|
'getpgrp',
|
||
|
'getpriority',
|
||
|
'getprotobyname',
|
||
|
'getprotobynumber',
|
||
|
'getprotoent',
|
||
|
'getpwent',
|
||
|
'getpwnam',
|
||
|
'getpwuid',
|
||
|
'getservbyname',
|
||
|
'getservbyport',
|
||
|
'getservent',
|
||
|
'getsockname',
|
||
|
'getsockopt',
|
||
|
'given',
|
||
|
'glob',
|
||
|
'gmtime',
|
||
|
'goto',
|
||
|
'grep',
|
||
|
'gt',
|
||
|
'hex',
|
||
|
'if',
|
||
|
'index',
|
||
|
'int',
|
||
|
'ioctl',
|
||
|
'join',
|
||
|
'keys',
|
||
|
'kill',
|
||
|
'last',
|
||
|
'lc',
|
||
|
'lcfirst',
|
||
|
'length',
|
||
|
'link',
|
||
|
'listen',
|
||
|
'local',
|
||
|
'localtime',
|
||
|
'log',
|
||
|
'lstat',
|
||
|
'lt',
|
||
|
'ma',
|
||
|
'map',
|
||
|
'mkdir',
|
||
|
'msgctl',
|
||
|
'msgget',
|
||
|
'msgrcv',
|
||
|
'msgsnd',
|
||
|
'my',
|
||
|
'ne',
|
||
|
'next',
|
||
|
'no',
|
||
|
'not',
|
||
|
'oct',
|
||
|
'open',
|
||
|
'opendir',
|
||
|
'or',
|
||
|
'ord',
|
||
|
'our',
|
||
|
'pack',
|
||
|
'package',
|
||
|
'pipe',
|
||
|
'pop',
|
||
|
'pos',
|
||
|
'print',
|
||
|
'printf',
|
||
|
'prototype',
|
||
|
'push',
|
||
|
'q|0',
|
||
|
'qq',
|
||
|
'quotemeta',
|
||
|
'qw',
|
||
|
'qx',
|
||
|
'rand',
|
||
|
'read',
|
||
|
'readdir',
|
||
|
'readline',
|
||
|
'readlink',
|
||
|
'readpipe',
|
||
|
'recv',
|
||
|
'redo',
|
||
|
'ref',
|
||
|
'rename',
|
||
|
'require',
|
||
|
'reset',
|
||
|
'return',
|
||
|
'reverse',
|
||
|
'rewinddir',
|
||
|
'rindex',
|
||
|
'rmdir',
|
||
|
'say',
|
||
|
'scalar',
|
||
|
'seek',
|
||
|
'seekdir',
|
||
|
'select',
|
||
|
'semctl',
|
||
|
'semget',
|
||
|
'semop',
|
||
|
'send',
|
||
|
'setgrent',
|
||
|
'sethostent',
|
||
|
'setnetent',
|
||
|
'setpgrp',
|
||
|
'setpriority',
|
||
|
'setprotoent',
|
||
|
'setpwent',
|
||
|
'setservent',
|
||
|
'setsockopt',
|
||
|
'shift',
|
||
|
'shmctl',
|
||
|
'shmget',
|
||
|
'shmread',
|
||
|
'shmwrite',
|
||
|
'shutdown',
|
||
|
'sin',
|
||
|
'sleep',
|
||
|
'socket',
|
||
|
'socketpair',
|
||
|
'sort',
|
||
|
'splice',
|
||
|
'split',
|
||
|
'sprintf',
|
||
|
'sqrt',
|
||
|
'srand',
|
||
|
'stat',
|
||
|
'state',
|
||
|
'study',
|
||
|
'sub',
|
||
|
'substr',
|
||
|
'symlink',
|
||
|
'syscall',
|
||
|
'sysopen',
|
||
|
'sysread',
|
||
|
'sysseek',
|
||
|
'system',
|
||
|
'syswrite',
|
||
|
'tell',
|
||
|
'telldir',
|
||
|
'tie',
|
||
|
'tied',
|
||
|
'time',
|
||
|
'times',
|
||
|
'tr',
|
||
|
'truncate',
|
||
|
'uc',
|
||
|
'ucfirst',
|
||
|
'umask',
|
||
|
'undef',
|
||
|
'unless',
|
||
|
'unlink',
|
||
|
'unpack',
|
||
|
'unshift',
|
||
|
'untie',
|
||
|
'until',
|
||
|
'use',
|
||
|
'utime',
|
||
|
'values',
|
||
|
'vec',
|
||
|
'wait',
|
||
|
'waitpid',
|
||
|
'wantarray',
|
||
|
'warn',
|
||
|
'when',
|
||
|
'while',
|
||
|
'write',
|
||
|
'x|0',
|
||
|
'xor',
|
||
|
'y|0'
|
||
|
];
|
||
|
|
||
|
// https://perldoc.perl.org/perlre#Modifiers
|
||
|
const REGEX_MODIFIERS = /[dualxmsipngr]{0,12}/; // aa and xx are valid, making max length 12
|
||
|
const PERL_KEYWORDS = {
|
||
|
$pattern: /[\w.]+/,
|
||
|
keyword: KEYWORDS.join(" ")
|
||
|
};
|
||
|
const SUBST = {
|
||
|
className: 'subst',
|
||
|
begin: '[$@]\\{',
|
||
|
end: '\\}',
|
||
|
keywords: PERL_KEYWORDS
|
||
|
};
|
||
|
const METHOD = {
|
||
|
begin: /->\{/,
|
||
|
end: /\}/
|
||
|
// contains defined later
|
||
|
};
|
||
|
const VAR = {
|
||
|
variants: [
|
||
|
{
|
||
|
begin: /\$\d/
|
||
|
},
|
||
|
{
|
||
|
begin: concat(
|
||
|
/[$%@](\^\w\b|#\w+(::\w+)*|\{\w+\}|\w+(::\w*)*)/,
|
||
|
// negative look-ahead tries to avoid matching patterns that are not
|
||
|
// Perl at all like $ident$, @ident@, etc.
|
||
|
`(?![A-Za-z])(?![@$%])`
|
||
|
)
|
||
|
},
|
||
|
{
|
||
|
begin: /[$%@][^\s\w{]/,
|
||
|
relevance: 0
|
||
|
}
|
||
|
]
|
||
|
};
|
||
|
const STRING_CONTAINS = [
|
||
|
hljs.BACKSLASH_ESCAPE,
|
||
|
SUBST,
|
||
|
VAR
|
||
|
];
|
||
|
const REGEX_DELIMS = [
|
||
|
/!/,
|
||
|
/\//,
|
||
|
/\|/,
|
||
|
/\?/,
|
||
|
/'/,
|
||
|
/"/, // valid but infrequent and weird
|
||
|
/#/ // valid but infrequent and weird
|
||
|
];
|
||
|
/**
|
||
|
* @param {string|RegExp} prefix
|
||
|
* @param {string|RegExp} open
|
||
|
* @param {string|RegExp} close
|
||
|
*/
|
||
|
const PAIRED_DOUBLE_RE = (prefix, open, close = '\\1') => {
|
||
|
const middle = (close === '\\1')
|
||
|
? close
|
||
|
: concat(close, open);
|
||
|
return concat(
|
||
|
concat("(?:", prefix, ")"),
|
||
|
open,
|
||
|
/(?:\\.|[^\\\/])*?/,
|
||
|
middle,
|
||
|
/(?:\\.|[^\\\/])*?/,
|
||
|
close,
|
||
|
REGEX_MODIFIERS
|
||
|
);
|
||
|
};
|
||
|
/**
|
||
|
* @param {string|RegExp} prefix
|
||
|
* @param {string|RegExp} open
|
||
|
* @param {string|RegExp} close
|
||
|
*/
|
||
|
const PAIRED_RE = (prefix, open, close) => {
|
||
|
return concat(
|
||
|
concat("(?:", prefix, ")"),
|
||
|
open,
|
||
|
/(?:\\.|[^\\\/])*?/,
|
||
|
close,
|
||
|
REGEX_MODIFIERS
|
||
|
);
|
||
|
};
|
||
|
const PERL_DEFAULT_CONTAINS = [
|
||
|
VAR,
|
||
|
hljs.HASH_COMMENT_MODE,
|
||
|
hljs.COMMENT(
|
||
|
/^=\w/,
|
||
|
/=cut/,
|
||
|
{
|
||
|
endsWithParent: true
|
||
|
}
|
||
|
),
|
||
|
METHOD,
|
||
|
{
|
||
|
className: 'string',
|
||
|
contains: STRING_CONTAINS,
|
||
|
variants: [
|
||
|
{
|
||
|
begin: 'q[qwxr]?\\s*\\(',
|
||
|
end: '\\)',
|
||
|
relevance: 5
|
||
|
},
|
||
|
{
|
||
|
begin: 'q[qwxr]?\\s*\\[',
|
||
|
end: '\\]',
|
||
|
relevance: 5
|
||
|
},
|
||
|
{
|
||
|
begin: 'q[qwxr]?\\s*\\{',
|
||
|
end: '\\}',
|
||
|
relevance: 5
|
||
|
},
|
||
|
{
|
||
|
begin: 'q[qwxr]?\\s*\\|',
|
||
|
end: '\\|',
|
||
|
relevance: 5
|
||
|
},
|
||
|
{
|
||
|
begin: 'q[qwxr]?\\s*<',
|
||
|
end: '>',
|
||
|
relevance: 5
|
||
|
},
|
||
|
{
|
||
|
begin: 'qw\\s+q',
|
||
|
end: 'q',
|
||
|
relevance: 5
|
||
|
},
|
||
|
{
|
||
|
begin: '\'',
|
||
|
end: '\'',
|
||
|
contains: [ hljs.BACKSLASH_ESCAPE ]
|
||
|
},
|
||
|
{
|
||
|
begin: '"',
|
||
|
end: '"'
|
||
|
},
|
||
|
{
|
||
|
begin: '`',
|
||
|
end: '`',
|
||
|
contains: [ hljs.BACKSLASH_ESCAPE ]
|
||
|
},
|
||
|
{
|
||
|
begin: /\{\w+\}/,
|
||
|
relevance: 0
|
||
|
},
|
||
|
{
|
||
|
begin: '-?\\w+\\s*=>',
|
||
|
relevance: 0
|
||
|
}
|
||
|
]
|
||
|
},
|
||
|
{
|
||
|
className: 'number',
|
||
|
begin: '(\\b0[0-7_]+)|(\\b0x[0-9a-fA-F_]+)|(\\b[1-9][0-9_]*(\\.[0-9_]+)?)|[0_]\\b',
|
||
|
relevance: 0
|
||
|
},
|
||
|
{ // regexp container
|
||
|
begin: '(\\/\\/|' + hljs.RE_STARTERS_RE + '|\\b(split|return|print|reverse|grep)\\b)\\s*',
|
||
|
keywords: 'split return print reverse grep',
|
||
|
relevance: 0,
|
||
|
contains: [
|
||
|
hljs.HASH_COMMENT_MODE,
|
||
|
{
|
||
|
className: 'regexp',
|
||
|
variants: [
|
||
|
// allow matching common delimiters
|
||
|
{ begin: PAIRED_DOUBLE_RE("s|tr|y", either(...REGEX_DELIMS)) },
|
||
|
// and then paired delmis
|
||
|
{ begin: PAIRED_DOUBLE_RE("s|tr|y", "\\(", "\\)") },
|
||
|
{ begin: PAIRED_DOUBLE_RE("s|tr|y", "\\[", "\\]") },
|
||
|
{ begin: PAIRED_DOUBLE_RE("s|tr|y", "\\{", "\\}") }
|
||
|
],
|
||
|
relevance: 2
|
||
|
},
|
||
|
{
|
||
|
className: 'regexp',
|
||
|
variants: [
|
||
|
{
|
||
|
// could be a comment in many languages so do not count
|
||
|
// as relevant
|
||
|
begin: /(m|qr)\/\//,
|
||
|
relevance: 0
|
||
|
},
|
||
|
// prefix is optional with /regex/
|
||
|
{ begin: PAIRED_RE("(?:m|qr)?", /\//, /\//)},
|
||
|
// allow matching common delimiters
|
||
|
{ begin: PAIRED_RE("m|qr", either(...REGEX_DELIMS), /\1/)},
|
||
|
// allow common paired delmins
|
||
|
{ begin: PAIRED_RE("m|qr", /\(/, /\)/)},
|
||
|
{ begin: PAIRED_RE("m|qr", /\[/, /\]/)},
|
||
|
{ begin: PAIRED_RE("m|qr", /\{/, /\}/)}
|
||
|
]
|
||
|
}
|
||
|
]
|
||
|
},
|
||
|
{
|
||
|
className: 'function',
|
||
|
beginKeywords: 'sub',
|
||
|
end: '(\\s*\\(.*?\\))?[;{]',
|
||
|
excludeEnd: true,
|
||
|
relevance: 5,
|
||
|
contains: [ hljs.TITLE_MODE ]
|
||
|
},
|
||
|
{
|
||
|
begin: '-\\w\\b',
|
||
|
relevance: 0
|
||
|
},
|
||
|
{
|
||
|
begin: "^__DATA__$",
|
||
|
end: "^__END__$",
|
||
|
subLanguage: 'mojolicious',
|
||
|
contains: [
|
||
|
{
|
||
|
begin: "^@@.*",
|
||
|
end: "$",
|
||
|
className: "comment"
|
||
|
}
|
||
|
]
|
||
|
}
|
||
|
];
|
||
|
SUBST.contains = PERL_DEFAULT_CONTAINS;
|
||
|
METHOD.contains = PERL_DEFAULT_CONTAINS;
|
||
|
|
||
|
return {
|
||
|
name: 'Perl',
|
||
|
aliases: [
|
||
|
'pl',
|
||
|
'pm'
|
||
|
],
|
||
|
keywords: PERL_KEYWORDS,
|
||
|
contains: PERL_DEFAULT_CONTAINS
|
||
|
};
|
||
|
}
|
||
|
|
||
|
module.exports = perl;
|