perlLanguage top-level constant
Grammar
const perlLanguage
Perl 5.
Implementation
const perlLanguage = Grammar(
name: 'perl',
aliases: ['pl', 'pm'],
fileExtensions: ['pl', 'pm', 't', 'psgi'],
shebangs: ['perl'],
signatures: [
r'^\s*use\s+(?:strict|warnings|utf8)\s*;',
r'^\s*my\s+[$@%]\w+',
r'^\s*sub\s+\w+\s*\{',
r'\$_\[\d\]|@_\b',
r'=~\s*(?:s|m|tr|y)?[/{#|!]',
],
rules: [
TokenRule(r'^#!.*$', scope: Scopes.meta),
// POD documentation: `=head1` … `=cut`.
RegionRule(
begin: r'^=[a-zA-Z]\w*',
end: r'^=cut\b.*$|(?![\s\S])',
scope: Scopes.commentDoc,
),
// Everything after `__END__` or `__DATA__` is data.
RegionRule(
begin: r'^__(?:END|DATA)__$',
end: r'(?![\s\S])',
scope: Scopes.comment,
),
IncludeRule('heredocs'),
// Variables come before comments so `$#array` is not a comment.
IncludeRule('variables'),
LineComment('#'),
IncludeRule('quoteLike'),
QuotedString('"', multiline: true, rules: [IncludeRule('interpolated')]),
QuotedString(
"'",
escape: null,
multiline: true,
rules: [TokenRule(r"\\[\\']", scope: Scopes.stringEscape)],
),
QuotedString('`', multiline: true, rules: [IncludeRule('interpolated')]),
// A bare `/regex/` after a binding operator or where an expression
// starts.
TokenRule(
r'(?<=(?:[=!]~|[(,;{]|\b(?:split|grep|if|unless|and|or|return))[ \t]{0,3})'
r'/(?![\s*/=])(?:[^/\\\n]|\\.)+/[msixpodualngcer]*',
scope: Scopes.regexp,
),
Numbers(),
TokenRule(r'(?<=->)[A-Za-z_]\w*', scope: Scopes.function),
KeywordRule(
{
Scopes.keyword: [
'and', 'catch', 'class', 'cmp', 'default', 'defer', 'do', 'else', //
'elsif', 'eq', 'field', 'finally', 'for', 'foreach', 'ge', 'given',
'goto', 'gt', 'if', 'last', 'le', 'local', 'lt', 'method', 'my',
'ne', 'next', 'no', 'not', 'or', 'our', 'package', 'redo',
'require', 'return', 'state', 'sub', 'try', 'unless', 'until',
'use', 'when', 'while', 'xor', 'BEGIN', 'END', 'INIT', 'CHECK',
'UNITCHECK',
],
Scopes.literal: ['undef'],
Scopes.variableLanguage: [
'__PACKAGE__', '__FILE__', '__LINE__', '__SUB__', '__CLASS__', //
],
Scopes.function: [
'bless', 'chdir', 'chomp', 'chop', 'close', 'defined', 'delete', //
'die', 'each', 'eval', 'exists', 'exit', 'grep', 'index', 'join',
'keys', 'lc', 'length', 'localtime', 'map', 'mkdir', 'open',
'pop', 'print', 'printf', 'push', 'ref', 'reverse', 'say',
'scalar', 'shift', 'sort', 'splice', 'split', 'sprintf', 'substr',
'time', 'uc', 'unlink', 'unshift', 'values', 'wantarray', 'warn',
],
},
otherwise: [
TokenRule(r'(?<=\bsub\s)[A-Za-z_]\w*', scope: Scopes.function),
// A bareword before `=>` is a quoted key.
TokenRule(r'[A-Za-z_]\w*(?=[ \t]*=>)', scope: Scopes.property),
TokenRule(r'_*[A-Z]\w*(?:::\w+)+|[A-Z]\w*(?=::)', scope: Scopes.type),
_call,
],
),
Operators('+-*/%=<>!&|^~?:.\\'),
],
repository: {
'variables': [
TokenRule(r'\$#\{?[A-Za-z_][\w:]*\}?', scope: Scopes.variable),
TokenRule(r'[$@%&]\{[A-Za-z_][\w:]*\}', scope: Scopes.variable),
TokenRule(r'[$@%][A-Za-z_][\w]*(?:::\w+)*', scope: Scopes.variable),
TokenRule(
r'''\$(?:[0-9]+|[_&`'+!@/\\,;.0<>\[\]^]|\^\w)''',
scope: Scopes.variable,
),
TokenRule(r'@_|@ARGV|%ENV|%INC|@INC', scope: Scopes.variable),
],
// Interpolation inside double-quoted text.
'interpolated': [
TokenRule(r'\$#[A-Za-z_]\w*', scope: Scopes.variable),
TokenRule(
r'[$@][A-Za-z_]\w*(?:->)?(?:\[[^\]\n]*\]|\{[^}\n]*\})*',
scope: Scopes.variable,
),
TokenRule(r'\$\{[^}\n]*\}', scope: Scopes.variable),
],
// `<<"EOF"`, `<<'EOF'`, `<<EOF`, `<<~EOF`.
'heredocs': [
RegionRule(
begin: r"(<<~?)'([A-Za-z_]\w*)'([^\n]*)",
end: r'^[ \t]*\2$',
beginCaptures: {1: Scopes.operator, 2: Scopes.meta},
endScope: Scopes.meta,
endReferencesBegin: true,
rules: [TokenRule(r'[^\n]+', scope: Scopes.string)],
),
RegionRule(
begin: r'(<<~?)"?([A-Za-z_]\w*)"?([^\n]*)',
end: r'^[ \t]*\2$',
beginCaptures: {1: Scopes.operator, 2: Scopes.meta},
endScope: Scopes.meta,
endReferencesBegin: true,
rules: [
TokenRule(r'\\[\s\S]', scope: Scopes.stringEscape),
IncludeRule('interpolated'),
TokenRule(r'[^$@\\\n]+|[$@]', scope: Scopes.string),
],
),
],
'quoteLike': [
_QuoteLike('qq|qx', Scopes.string, interpolate: true),
_QuoteLike('qw|q', Scopes.string),
_QuoteLike(
'qr|m',
Scopes.regexp,
interpolate: true,
flags: '[msixpodualngc]*',
),
// Substitution and transliteration: two parts with one delimiter.
TokenRule(
r'(?<![\w$@%&>-])(?:s|tr|y)([/|!#,])(?:[^\\\n]|\\.)*?\1(?:[^\\\n]|\\.)*?\1[msixpodualngcer]*',
scope: Scopes.regexp,
),
TokenRule(
r'(?<![\w$@%&>-])(?:s|tr|y)\{(?:[^}\\\n]|\\.)*\}\s*\{(?:[^}\\\n]|\\.)*\}[msixpodualngcer]*',
scope: Scopes.regexp,
),
],
},
);