%YAML 1.2 --- # http://www.sublimetext.com/docs/3/syntax.html name: Perl file_extensions: - pl - pc - pm - pmc - pod - t first_line_match: |- (?xi: ^\#! .* \bperl\b | # shebang ^\# \s* -\*- [^*]* perl [^*]* -\*- # editorconfig ) scope: source.perl variables: break: \b(?!::) identifier: '\b[_[:alpha:]]\w*\b' pod: '={{identifier}}' regexp_delim: '[^\w\s\)\]\}\>]' regexp_flags: '[msixpodualngcer]+\b' # no odd number of backslashes behind (`\` or `\\\`) # note: # 1. For use in `escape` commands only !! # 2. ST's Regex Compatibility Check marks it as incompatible # as the real usage of the variable is not checked. # It's a false positive as long as (1.) is respected. no_escape_behind: '(? - match: (B)(<) captures: 1: entity.name.tag.bold.perl 2: punctuation.definition.tag.begin.perl push: - meta_content_scope: markup.bold.perl - include: comment-pod-format-body # inline code : C - match: (C)(<) captures: 1: entity.name.tag.code.perl 2: punctuation.definition.tag.begin.perl push: - meta_content_scope: markup.quote.perl - include: comment-pod-format-body # escaped character : E - match: (E)(<) captures: 1: entity.name.tag.escaped.perl 2: punctuation.definition.tag.begin.perl push: - meta_content_scope: constant.character.escape.perl - include: comment-pod-format-body # italic text : I - match: (I)(<) captures: 1: entity.name.tag.italic.perl 2: punctuation.definition.tag.begin.perl push: - meta_content_scope: markup.italic.perl - include: comment-pod-format-body # filename : F - match: (F)(<) captures: 1: entity.name.tag.filename.perl 2: punctuation.definition.tag.begin.perl push: - meta_content_scope: meta.string.perl string.unquoted.perl - include: comment-pod-format-body # hyperlink : L - match: (L)(<) captures: 1: entity.name.tag.link.perl 2: punctuation.definition.tag.begin.perl push: - meta_content_scope: markup.underline.link.perl - include: comment-pod-format-body # non-breaking spaces : S - match: (S)(<) captures: 1: entity.name.tag.none-braeking.perl 2: punctuation.definition.tag.begin.perl push: - include: comment-pod-format-body # index entry : X - match: (X)(<) captures: 1: entity.name.tag.index.perl 2: punctuation.definition.tag.begin.perl push: - meta_content_scope: entity.name.label.perl - include: comment-pod-format-body comment-pod-format-body: - match: \> scope: punctuation.definition.tag.end.perl pop: true - include: comment-pod-formatting - include: literal-angle-nested ###[ PACKAGE DEFINITION ]##################################################### package: # https://perldoc.perl.org/functions/package.html # package NAMESPACE # package NAMESPACE VERSION # package NAMESPACE BLOCK # package NAMESPACE VERSION BLOCK - match: \bpackage{{break}} scope: storage.type.namespace.perl keyword.declaration.namespace.perl push: [package-end, package-version, package-namespace] package-namespace: - include: qualified-namespace - include: unqualified-namespace - include: comment-line - include: else-pop package-version: - match: (")((v?)[\d_](\.)[\d_]+(?:(\.)[\d_]+)?)(") scope: meta.string.perl string.quoted.double.perl captures: 1: punctuation.definition.string.begin.perl 2: constant.numeric.version.perl 3: punctuation.definition.version.perl 4: punctuation.separator.decimal.perl 5: punctuation.separator.decimal.perl 6: punctuation.definition.string.end.perl pop: true - match: (')((v?)[\d_](\.)[\d_]+(?:(\.)[\d_]+)?)(') scope: meta.string.perl string.quoted.single.perl captures: 1: punctuation.definition.string.begin.perl 2: constant.numeric.version.perl 3: punctuation.definition.version.perl 4: punctuation.separator.decimal.perl 5: punctuation.separator.decimal.perl 6: punctuation.definition.string.end.perl pop: true - match: (v?)[\d_](\.)[\d_]+(?:(\.)[\d_]+)?\b scope: constant.numeric.version.perl captures: 1: punctuation.definition.version.perl 2: punctuation.separator.decimal.perl 3: punctuation.separator.decimal.perl pop: true - include: comment-line - include: else-pop package-end: - meta_scope: meta.namespace.perl - include: comment-line - include: else-pop qualified-namespace: - match: ({{identifier}})?(::) captures: 1: entity.name.namespace.perl 2: punctuation.accessor.double-colon.perl set: - meta_scope: meta.path.perl - match: ({{identifier}})(::) captures: 1: entity.name.namespace.perl 2: punctuation.accessor.double-colon.perl - include: unqualified-namespace - include: immediately-pop unqualified-namespace: - match: '{{identifier}}' scope: entity.name.namespace.perl pop: true ###[ IMPORT STATEMENTS ]###################################################### imports: # https://perldoc.perl.org/functions/require.html # require MODULE # require EXPR - match: \brequire{{break}} scope: keyword.control.import.require.perl push: [require-end, require-body] # https://perldoc.perl.org/functions/use.html # use MODULE VERSION LIST # use MODULE VERSION # use MODULE LIST # use MODULE # use VERSION - match: \buse{{break}} scope: keyword.control.import.use.perl push: - - meta_scope: meta.preprocessor.use.perl - include: require-expression - expressions-begin - package-version - package-namespace # https://perldoc.perl.org/functions/no.html # no MODULE VERSION LIST # no MODULE VERSION # no MODULE LIST # no MODULE # no VERSION - match: \bno{{break}} scope: keyword.control.import.no.perl push: - - meta_scope: meta.preprocessor.no.perl - include: require-expression - expressions-begin - package-version - package-namespace require-body: - include: require-namespace - include: comment-line - match: (?=\S) set: [require-expression, expressions-begin] require-namespace: - include: qualified-namespace - match: '{{identifier}}(?=\s*(?:[;)}#]|$))' scope: entity.name.namespace.perl pop: true require-expression: - match: (?=[;)}]) pop: true - include: expressions - match: \S scope: meta.string.perl string.unquoted.perl require-end: - meta_scope: meta.preprocessor.require.perl - include: comment-line - include: else-pop ###[ SUB STATEMENTS ]######################################################### sub: - match: \bsub{{break}} scope: storage.type.function.perl keyword.declaration.function.perl push: [sub-end, sub-parameters, sub-path] # special functions which are executed at compile time # SEE: https://perldoc.perl.org/perlmod.html#BEGIN%2c-UNITCHECK%2c-CHECK%2c-INIT-and-END # NOTE: may not start with double colon when used without `sub` keyword - match: (::)?(BEGIN|CHECK|END|INIT|UNITCHECK)(?=\s*(?:[;#{]|$)) captures: 1: invalid.illegal.accessor.perl 2: entity.name.function.prepocessor.perl push: [sub-end, sub-parameters] sub-path: - match: ({{identifier}})?(::) captures: 1: variable.namespace.perl 2: punctuation.accessor.double-colon.perl set: - meta_scope: meta.path.perl - include: qualifier - include: sub-name - include: immediately-pop - include: sub-name - include: sub-common sub-name: # callback hook to auto load or destroy objects/functions - match: \b(AUTOLOAD|DESTROY){{break}} scope: entity.name.function.callback.perl pop: true # special functions which are executed at compile time - match: \b(BEGIN|CHECK|END|INIT|UNITCHECK){{break}} scope: entity.name.function.prepocessor.perl pop: true # ordinary function identifier - match: (?!{{reserved_words}}){{identifier}} scope: entity.name.function.perl pop: true sub-parameters: - match: (?=\() set: - - clear_scopes: 1 - meta_scope: meta.function.parameters.perl - include: parameters-end - match: '[$@%*&\\]+' scope: variable.parameter.perl - match: ; scope: punctuation.separator.sequence.perl - match: \S scope: invalid.illegal.parameter.perl - parameters-begin - include: sub-attributes - include: sub-common sub-attributes: - match: '::' scope: invalid.illegal.identifier.perl - match: ':' scope: punctuation.definition.annotation.perl push: sub-attributes-name sub-attributes-name: - meta_scope: meta.annotation.perl - match: '{{identifier}}' scope: variable.annotation.attribute.perl set: - meta_content_scope: meta.annotation.perl - include: sub-attributes-parameters - include: sub-attributes-parameters sub-attributes-parameters: # NOTE: doesn't look like prototype parameters - match: (?=\((?!\s*[$@%*&]+\s*[;)])) set: - - meta_scope: meta.annotation.parameters.perl - include: parameters-end - include: expressions-nested - expressions-begin - parameters-begin - include: sub-common sub-common: - match: (?=[:;({]|{{qualified_reserved_words}}) pop: true - include: comment-line - match: \S scope: invalid.illegal.identifier.perl sub-end: - meta_scope: meta.function.perl - include: sub-attributes - include: sub-common parameters-begin: - match: \( scope: punctuation.section.parameters.begin.perl pop: true parameters-end: - match: \) scope: punctuation.section.parameters.end.perl pop: true ###[ BLOCKS AND GROUPS ]###################################################### blocks: # can't push into scope due to HEREDOCs! - match: \{ scope: punctuation.section.block.begin.perl push: expressions-begin - match: \} scope: punctuation.section.block.end.perl blocks-nested: - match: \{ scope: punctuation.section.block.begin.perl push: [blocks-nested-body, expressions-begin] blocks-nested-body: - match: \} scope: punctuation.section.block.end.perl pop: true - include: main brackets: # can't push into scope due to HEREDOCs! - match: \[ scope: punctuation.section.brackets.begin.perl push: expressions-begin - match: \] scope: punctuation.section.brackets.end.perl brackets-nested: - match: \[ scope: punctuation.section.brackets.begin.perl push: [brackets-nested-body, expressions-begin] brackets-nested-body: - match: \] scope: punctuation.section.brackets.end.perl pop: true - include: blocks-nested - include: brackets-nested - include: expressions groups: # can't push into scope due to HEREDOCs! - match: \( scope: punctuation.section.group.begin.perl push: expressions-begin - match: \) scope: punctuation.section.group.end.perl groups-nested: - match: \( scope: punctuation.section.group.begin.perl push: [groups-nested-body, expressions-begin] groups-nested-body: - match: \) scope: punctuation.section.group.end.perl pop: true - include: expressions-nested ###[ CONSTANTS ]############################################################## constants: - include: constants-version - include: constants-numbers - include: constants-language - include: constants-other - include: string constants-numbers: # SEE: https://perldoc.perl.org/perlnumber.html # decimal floats - match: |- (?ix: (") ( [-+]? (?: (\.)[\d_]+ (?: e[-+]?[\d_]+ )? | # .1 .1e1 .1e-1 .1e+1 [\d_]+ (?: (\.) (?: [\d_]+ (?: e[-+]?[\d_]+ )? | # 1.1 1.1e1 1.1e-1 1.1e+1 e[-+]?[\d_]+ )? | # 1. 1.e1 1.e-1 1.e+1 e[-+]?[\d_]+ ) # 1e1 1e-1 1e+1 ) ) (") ) scope: meta.string.perl string.quoted.double.perl captures: 1: punctuation.definition.string.begin.perl 2: constant.numeric.float.decimal.perl 3: punctuation.separator.decimal.perl 4: punctuation.separator.decimal.perl 5: punctuation.definition.string.end.perl - match: |- (?ix: (') ( [-+]? (?: (\.)[\d_]+ (?: e[-+]?[\d_]+ )? | # .1 .1e1 .1e-1 .1e+1 [\d_]+ (?: (\.) (?: [\d_]+ (?: e[-+]?[\d_]+ )? | # 1.1 1.1e1 1.1e-1 1.1e+1 e[-+]?[\d_]+ )? | # 1. 1.e1 1.e-1 1.e+1 e[-+]?[\d_]+ ) # 1e1 1e-1 1e+1 ) ) (') ) scope: meta.string.perl string.quoted.single.perl captures: 1: punctuation.definition.string.begin.perl 2: constant.numeric.float.decimal.perl 3: punctuation.separator.decimal.perl 4: punctuation.separator.decimal.perl 5: punctuation.definition.string.end.perl - match: |- (?ix: (\.)[\d_]+ (?: e[-+]?[\d_]+ )? \b | # .1 .1e1 .1e-1 .1e+1 \b[\d_]+ (?: (\.) (?: (?: [\d_]+ (?: e[-+]?[\d_]+ )? \b | # 1.1 1.1e1 1.1e-1 1.1e+1 e[-+]?[\d_]+\b ) | # 1.e1 1.e-1 1.e+1 (?=[^.])) | # 1. (protect the .. operator) e[-+]?[\d_]+\b ) # 1e1 1e-1 1e+1 ) scope: constant.numeric.float.decimal.perl captures: 1: punctuation.separator.decimal.perl 2: punctuation.separator.decimal.perl # decimal integers - match: (")([-+]?[\d_]+)(") scope: meta.string.perl string.quoted.double.perl captures: 1: punctuation.definition.string.begin.perl 2: constant.numeric.integer.decimal.perl 3: punctuation.definition.string.end.perl - match: (')([-+]?[\d_]+)(') scope: meta.string.perl string.quoted.single.perl captures: 1: punctuation.definition.string.begin.perl 2: constant.numeric.integer.decimal.perl 3: punctuation.definition.string.end.perl # binary integers - match: \b(0[bB])[01_]+\b scope: constant.numeric.integer.binary.perl captures: 1: punctuation.definition.numeric.binary.perl # hexadecimal integers - match: \b(0[xX])[\h_]+\b scope: constant.numeric.integer.hexadecimal.perl captures: 1: punctuation.definition.numeric.hexadecimal.perl # octal integers - match: \b(0)[0-7_]+\b scope: constant.numeric.integer.octal.perl captures: 1: punctuation.definition.numeric.octal.perl # decimal integers - match: \b[\d_]+\b scope: constant.numeric.integer.decimal.perl constants-language: - match: \b__(?:END|DATA|FILE|LINE|PACKAGE|SUB)__\b scope: constant.language.perl - match: \b(?:ARGV|ARGVOUT|STDERR|STDIN|STDOUT|DATA|IN|OUT)\b scope: constant.language.filehandle.perl constants-other: - match: '{{identifier}}(?=\s*=>)' scope: constant.other.key.perl # Not looking like a namespace, class, object or function? - match: \b[A-Z_][A-Z0-9_]+\b(?!::|\s*(?:\(|->)) scope: constant.other.perl constants-identifier: - match: '{{identifier}}' scope: constant.other.perl constants-version: # SEE: https://perldoc.perl.org/functions/require.html - match: (v?)[\d_](\.)[\d_]+(\.)[\d_]+\b scope: constant.numeric.version.perl captures: 1: punctuation.definition.version.perl 2: punctuation.separator.decimal.perl 3: punctuation.separator.decimal.perl ###[ STRINGS ]################################################################ string: - include: string-quoted-backtick - include: string-quoted-double - include: string-quoted-single - include: string-format - include: string-heredoc string-quoted-angle-pop: # NOTE: match only if... # - doesn't look like an operator (`<=>`, `<<`) # - closing punctuation `>` found on the same line - match: \<(?!<|=>)(?=.*?>) scope: punctuation.definition.string.begin.perl set: - meta_scope: meta.string.perl string.quoted.angle.perl # prevent variable interpolation to break string termination - match: (?:[$@%&*]#?)?(\>) captures: 1: punctuation.definition.string.end.perl pop: true - include: interpolated-angle-nested - include: interpolated-common string-quoted-backtick: - match: \` scope: punctuation.definition.string.begin.perl push: - meta_scope: meta.string.perl string.quoted.backtick.perl # prevent variable interpolation to break string termination - match: (?:[$@%&*]#?)?(\`) captures: 1: punctuation.definition.string.end.perl pop: true - include: interpolated-common string-quoted-double: - match: \" scope: punctuation.definition.string.begin.perl push: - meta_scope: meta.string.perl string.quoted.double.perl # prevent variable interpolation to break string termination - match: (?:[$@%&*]#?)?(\") captures: 1: punctuation.definition.string.end.perl pop: true - include: interpolated-common string-quoted-single: - match: \' scope: punctuation.definition.string.begin.perl push: - meta_scope: meta.string.perl string.quoted.single.perl - match: \' scope: punctuation.definition.string.end.perl pop: true - include: literal-common string-format: # SEE: https://perldoc.perl.org/perlform.html - match: \bformat{{break}} scope: storage.type.format.perl push: [string-format-meta, string-format-body, string-format-variable] string-format-meta: - meta_scope: meta.format.perl - include: immediately-pop string-format-variable: - match: ({{identifier}})?(::) captures: 1: variable.namespace.perl 2: punctuation.accessor.double-colon.perl push: - meta_scope: meta.path.perl - include: qualifier - include: variable-identifier - include: immediately-pop - include: variable-identifier - include: comment-line - include: else-pop string-format-body: - match: '=(?!=)' scope: keyword.operator.assignment.perl set: - meta_content_scope: meta.string.perl string.unquoted.perl - match: ^\.(?=\s*$) scope: punctuation.terminator.format.perl pop: true - match: \.{4,} # consume any dot sequence longer than 3 chars - match: \.{3} scope: constant.other.placeholder.text.perl - match: ~{3,} # consume any tild sequence longer than 2 chars - match: ~{1,2} scope: constant.other.placeholder.text.perl - match: ([@^])[<|>*]+ scope: constant.other.placeholder.text.perl captures: 1: punctuation.definition.placeholder.begin.perl - match: ([@^])0?[.#]+(?![_\d]) scope: constant.other.placeholder.numeric.perl captures: 1: punctuation.definition.placeholder.begin.perl - include: variables-interpolation - include: comment-line - match: \S+ scope: invalid.illegal.identifier.perl pop: true string-heredoc: # SEE: http://www.perlmeme.org/howtos/syntax/here_document.html - match: <<(?=(\s*['"]\s*)?{{identifier}}) scope: keyword.operator.heredoc.perl push: [string-heredoc-meta, string-heredoc-body] string-heredoc-meta: - meta_scope: meta.string.heredoc.perl - include: immediately-pop string-heredoc-body: # embedded css - match: \s*((['"]?)(\s*CSS)(\2)) captures: 1: meta.tag.heredoc.perl 2: punctuation.definition.tag.begin.perl 3: entity.name.tag.heredoc.css.perl 4: punctuation.definition.tag.end.perl set: [string-heredoc-css, string-heredoc-expr] # embedded html - match: \s*((['"]?)(\s*HTML)(\2)) captures: 1: meta.tag.heredoc.perl 2: punctuation.definition.tag.begin.perl 3: entity.name.tag.heredoc.html.perl 4: punctuation.definition.tag.end.perl set: [string-heredoc-html, string-heredoc-expr] # embedded javascript - match: \s*((['"]?)(\s*JAVASCRIPT)(\2)) captures: 1: meta.tag.heredoc.perl 2: punctuation.definition.tag.begin.perl 3: entity.name.tag.heredoc.js.perl 4: punctuation.definition.tag.end.perl set: [string-heredoc-javascript, string-heredoc-expr] # embedded json - match: \s*((['"]?)(\s*JSON)(\2)) captures: 1: meta.tag.heredoc.perl 2: punctuation.definition.tag.begin.perl 3: entity.name.tag.heredoc.json.perl 4: punctuation.definition.tag.end.perl set: [string-heredoc-json, string-heredoc-expr] # embedded sql - match: \s*((['"]?)(\s*SQL)(\2)) captures: 1: meta.tag.heredoc.perl 2: punctuation.definition.tag.begin.perl 3: entity.name.tag.heredoc.sql.perl 4: punctuation.definition.tag.end.perl set: [string-heredoc-sql, string-heredoc-expr] # embedded xml - match: \s*((['"]?)(\s*XML)(\2)) captures: 1: meta.tag.heredoc.perl 2: punctuation.definition.tag.begin.perl 3: entity.name.tag.heredoc.xml.perl 4: punctuation.definition.tag.end.perl set: [string-heredoc-xml, string-heredoc-expr] # single quoted heredoc string without interpolation - match: \s*((')(\s*{{identifier}})(')) captures: 1: meta.tag.heredoc.perl 2: punctuation.definition.tag.begin.perl 3: entity.name.tag.heredoc.plain.perl 4: punctuation.definition.tag.end.perl set: [string-heredoc-plain, string-heredoc-expr] # unquoted or double quoted heredoc string with interpolation - match: \s*(("?)(\s*{{identifier}})(\2)) captures: 1: meta.tag.heredoc.perl 2: punctuation.definition.tag.begin.perl 3: entity.name.tag.heredoc.plain.perl 4: punctuation.definition.tag.end.perl set: [string-heredoc-interpolated, string-heredoc-expr] - include: immediately-pop string-heredoc-expr: # The rest of the line right after the heredoc tag needs to be handled # as ordinary perl. The embedded syntax starts at the next line. - clear_scopes: 1 # remove 'source' - match: ^ pop: true - include: expressions string-heredoc-css: - meta_content_scope: source.css.embedded.perl # pop off only, if keyword is indented correctly - match: ^\3$ scope: meta.tag.heredoc.perl entity.name.tag.heredoc.css.perl pop: true - match: '' embed: scope:source.css escape: (?=^ *CSS$) string-heredoc-html: - meta_content_scope: text.html.embedded.perl - match: ^\3$ scope: meta.tag.heredoc.perl entity.name.tag.heredoc.html.perl pop: true - match: '' embed: scope:text.html.basic escape: (?=^ *HTML$) string-heredoc-javascript: - meta_content_scope: source.js.embedded.perl - match: ^\3$ scope: meta.tag.heredoc.perl entity.name.tag.heredoc.js.perl pop: true - match: '' embed: scope:source.js escape: (?=^ *JAVASCRIPT$) string-heredoc-json: - meta_content_scope: source.json.embedded.perl - match: ^\3$ scope: meta.tag.heredoc.perl entity.name.tag.heredoc.json.perl pop: true - match: '' embed: scope:source.json escape: (?=^ *JSON$) string-heredoc-sql: - meta_content_scope: source.sql.embedded.perl - match: ^\3$ scope: meta.tag.heredoc.perl entity.name.tag.heredoc.sql.perl pop: true - match: '' embed: scope:source.sql escape: (?=^ *SQL$) string-heredoc-xml: - meta_content_scope: text.xml.embedded.perl - match: ^\3$ scope: meta.tag.heredoc.perl entity.name.tag.heredoc.xml.perl pop: true - match: '' embed: scope:text.xml escape: (?=^ *XML$) string-heredoc-plain: - meta_content_scope: string.unquoted.heredoc.perl - match: ^\3$ scope: meta.tag.heredoc.perl entity.name.tag.heredoc.plain.perl pop: true string-heredoc-interpolated: - meta_content_scope: string.unquoted.heredoc.perl - match: ^\3$ scope: meta.tag.heredoc.perl entity.name.tag.heredoc.plain.perl pop: true - include: interpolated-common ###[ KEYWORDS ]############################################################### keywords: # All keywords can be prefixed with the case sensitive `CORE` namespace. # This context makes sure not to highlight the keywords as functions. # The `meta.path` is not applied for simplicity reasons. - match: \b(CORE)(::)(?={{reserved_words}}) captures: 1: variable.namespace.perl 2: punctuation.accessor.double-colon.perl # declaration - match: \b(?:{{storage_keywords}}){{break}} scope: storage.type.variable.perl # conditional - match: \bdefault{{break}} scope: keyword.control.conditional.default.perl push: expressions-begin - match: \belse{{break}} scope: keyword.control.conditional.else.perl push: expressions-begin - match: \belsif{{break}} scope: keyword.control.conditional.elseif.perl push: expressions-begin - match: \bgiven{{break}} scope: keyword.control.conditional.given.perl push: expressions-begin - match: \bif{{break}} scope: keyword.control.conditional.if.perl push: expressions-begin - match: \bunless{{break}} scope: keyword.control.conditional.unless.perl push: expressions-begin - match: \bwhen{{break}} scope: keyword.control.conditional.when.perl push: expressions-begin # flow - match: \bbreak{{break}} scope: keyword.control.flow.break.perl push: expressions-begin - match: \bcaller{{break}} scope: keyword.control.flow.caller.perl push: expressions-begin - match: \bcontinue{{break}} scope: keyword.control.flow.continue.perl push: expressions-begin - match: \bdie{{break}} scope: keyword.control.flow.die.perl push: expressions-begin - match: \bdo{{break}} scope: keyword.control.flow.do.perl push: expressions-begin - match: \bdump{{break}} scope: keyword.control.flow.dump.perl push: expressions-begin - match: \bexit{{break}} scope: keyword.control.flow.exit.perl push: expressions-begin - match: \bgoto{{break}} scope: keyword.control.flow.goto.perl push: expressions-begin - match: \blast{{break}} scope: keyword.control.flow.last.perl push: label-usage - match: \bnext{{break}} scope: keyword.control.flow.next.perl push: label-usage - match: \bredo{{break}} scope: keyword.control.flow.redo.perl push: label-usage - match: \breturn{{break}} scope: keyword.control.flow.return.perl push: expressions-begin - match: \bwait{{break}} scope: keyword.control.flow.wait.perl push: expressions-begin # loop - match: \bfor{{break}} scope: keyword.control.loop.for.perl push: expressions-begin - match: \bforeach{{break}} scope: keyword.control.loop.foreach.perl push: expressions-begin - match: \buntil{{break}} scope: keyword.control.loop.until.perl push: expressions-begin - match: \bwhile{{break}} scope: keyword.control.loop.while.perl push: expressions-begin label-usage: # reserved words indicate an expression like label - match: (?={{reserved_words}}|{{builtin_functions}}) pop: true - match: '{{identifier}}' scope: variable.label.perl pop: true - include: expressions-begin label: - match: ({{identifier}})(:)(?!:) captures: 1: entity.name.label.perl 2: punctuation.separator.perl push: expressions-begin ###[ OPERATORS ]############################################################## operators: # SEE: https://www.tutorialspoint.com/perl/perl_operators.htm - match: => scope: punctuation.separator.key-value.perl push: expressions-begin - match: ',' scope: punctuation.separator.sequence.perl push: expressions-begin - match: ; scope: punctuation.terminator.statement.perl push: expressions-begin - match: -[rwx0RWXOezsfdlpSbctugkTBMAC]\b scope: keyword.operator.filetest.perl push: expressions-begin - match: '[!~=]~' scope: keyword.operator.binary.perl push: expressions-begin - match: <<|>> scope: keyword.operator.bitwise.perl push: expressions-begin - match: <=>|==|!=|>=|<=|[<>] scope: keyword.operator.comparison.perl push: expressions-begin - match: \&\&|\|\||//|[:!?] scope: keyword.operator.logical.perl push: expressions-begin - match: \*\*=|[-+*/%]=|= scope: keyword.operator.assignment.perl push: expressions-begin - match: --|\+\+ scope: keyword.operator.arithmetic.perl push: maybe-regexp-match - match: \*{1,2}|[-+/%] scope: keyword.operator.arithmetic.perl push: expressions-begin - match: '[&|^~]' scope: keyword.operator.bitwise.perl push: expressions-begin - match: \.\. scope: keyword.operator.range.perl push: expressions-begin # SEE: https://perldoc.perl.org/perlref.html#Making-References - match: \\+ scope: keyword.operator.reference.perl push: expressions-begin # string concat - match: \.=? scope: keyword.operator.concat.perl push: expressions-begin # string concation # "string" x 10 - match: \bx{{break}} scope: keyword.operator.arithmetic.perl push: expressions-begin - match: \b(?:{{operator_keywords}}){{break}} scope: keyword.operator.logical.perl push: expressions-begin ###[ QUOTED LIKE ]############################################################ quoted-like: # SEE: https://perldoc.perl.org/perlop.html#Regexp-Quote-Like-Operators - include: quoted-like-match - include: quoted-like-substitution # fallback - match: \b(?:{{quoted_like_keywords}}){{break}} scope: support.function.perl quoted-like-match: # qq// and qx// with interpolation - match: \bq[qx]{{break}} scope: support.function.perl push: - quoted-like-meta - quoted-like-args-interpolated # q// and qw// without interpolation - match: \bqw?{{break}} scope: support.function.perl push: - quoted-like-meta - quoted-like-args-literal - match: \b(m|qr)(?=\s*[\(\[\{<]) scope: support.function.perl push: - quoted-like-meta - quoted-like-args-flags - quoted-like-args-pattern - match: \b(m|qr)({{regexp_delim}}) captures: 0: meta.function-call.perl 1: support.function.perl 2: punctuation.section.generic.begin.perl embed_scope: meta.function-call.perl meta.string.perl string.regexp.perl embed: scope:source.regexp escape: '{{no_escape_behind}}(\2)({{regexp_flags}})?' escape_captures: 0: meta.function-call.perl 1: punctuation.section.generic.end.perl 2: constant.language.flags.regexp.perl quoted-like-substitution: - match: \b(s|tr|y)(?=\s*[\(\[\{<]) scope: support.function.perl push: - quoted-like-meta - quoted-like-args-flags - quoted-like-args-interpolated - quoted-like-args-pattern - match: (?=\b(s|tr|y){{regexp_delim}}) push: - quoted-like-meta - quoted-like-args-flags - quoted-like-args-substitution quoted-like-meta: - meta_scope: meta.function-call.perl - include: immediately-pop quoted-like-args-flags: - match: '{{regexp_flags}}' scope: constant.language.flags.regexp.perl pop: true - include: immediately-pop quoted-like-args-substitution: # the search pattern - match: (s|tr|y)({{regexp_delim}}) captures: 1: support.function.perl 2: punctuation.section.generic.begin.perl embed_scope: meta.string.perl string.regexp.perl embed: scope:source.regexp escape: (?={{no_escape_behind}}\2) # the replacement with variable interpolation - match: ({{regexp_delim}}) scope: punctuation.separator.sequence.perl set: - meta_content_scope: meta.string.perl string.unquoted.perl # prevent variable interpolation to break string termination - match: ([$@%&*]#?)?(\1) captures: 1: meta.string.perl string.unquoted.perl 2: punctuation.section.generic.end.perl pop: true - include: interpolated-common - include: immediately-pop quoted-like-args-pattern: - match: \{ scope: punctuation.section.braces.begin.perl set: - meta_scope: meta.braces.perl - meta_content_scope: meta.string.perl string.regexp.perl - match: \} scope: punctuation.section.braces.end.perl pop: true - include: scope:source.regexp#base-literal-extended - match: \[ scope: punctuation.section.brackets.begin.perl set: - meta_scope: meta.brackets.perl - meta_content_scope: meta.string.perl string.regexp.perl - match: \] scope: punctuation.section.brackets.end.perl pop: true - include: scope:source.regexp#base-literal-extended - match: \< scope: punctuation.section.generic.begin.perl set: - meta_scope: meta.generic.perl - meta_content_scope: meta.string.perl string.regexp.perl - match: \> scope: punctuation.section.generic.end.perl pop: true - include: scope:source.regexp#base-literal-extended - match: \( scope: punctuation.section.parens.begin.perl set: - meta_scope: meta.parens.perl - meta_content_scope: meta.string.perl string.regexp.perl - match: \) scope: punctuation.section.parens.end.perl pop: true - include: scope:source.regexp#base-literal-extended - include: else-pop quoted-like-args-interpolated: - include: interpolated-braces-pop - include: interpolated-brackets-pop - include: interpolated-angle-pop - include: interpolated-parens-pop - include: interpolated-generic-pop - include: else-pop quoted-like-args-literal: - include: literal-braces-pop - include: literal-brackets-pop - include: literal-angle-pop - include: literal-parens-pop - include: literal-generic-pop - include: else-pop interpolated-braces-pop: - match: \{ scope: punctuation.section.braces.begin.perl set: - meta_scope: meta.braces.perl - meta_content_scope: meta.string.perl string.unquoted.perl # prevent variable interpolation to break string termination - match: ([$@%&*]#?)?(\}) captures: 1: meta.string.perl string.unquoted.perl 2: punctuation.section.braces.end.perl pop: true - include: interpolated-braces-nested - include: interpolated-common interpolated-braces-nested: - match: \{ push: # prevent variable interpolation to break string termination - match: (?:[$@%&*]#?)?\} pop: true - include: interpolated-braces-nested - include: interpolated-common interpolated-brackets-pop: - match: \[ scope: punctuation.section.brackets.begin.perl set: - meta_scope: meta.brackets.perl - meta_content_scope: meta.string.perl string.unquoted.perl # prevent variable interpolation to break string termination - match: ([$@%&*]#?)?(\]) captures: 1: meta.string.perl string.unquoted.perl 2: punctuation.section.brackets.end.perl pop: true - include: interpolated-brackets-nested - include: interpolated-common interpolated-brackets-nested: - match: \[ push: # prevent variable interpolation to break string termination - match: (?:[$@%&*]#?)?\] pop: true - include: interpolated-brackets-nested - include: interpolated-common interpolated-angle-pop: - match: \< scope: punctuation.section.generic.begin.perl set: - meta_scope: meta.generic.perl - meta_content_scope: meta.string.perl string.unquoted.perl # prevent variable interpolation to break string termination - match: ([$@%&*]#?)?(\>) captures: 1: meta.string.perl string.unquoted.perl 2: punctuation.section.generic.end.perl pop: true - include: interpolated-angle-nested - include: interpolated-common interpolated-angle-nested: - match: \< push: # prevent variable interpolation to break string termination - match: (?:[$@%&*]#?)?\> pop: true - include: interpolated-angle-nested - include: interpolated-common interpolated-parens-pop: - match: \( scope: punctuation.section.parens.begin.perl set: - meta_scope: meta.parens.perl - meta_content_scope: meta.string.perl string.unquoted.perl # prevent variable interpolation to break string termination - match: ([$@%&*]#?)?(\)) captures: 1: meta.string.perl string.unquoted.perl 2: punctuation.section.parens.end.perl pop: true - include: interpolated-parens-nested - include: interpolated-common interpolated-parens-nested: - match: \( push: # prevent variable interpolation to break string termination - match: (?:[$@%&*]#?)?\) pop: true - include: interpolated-parens-nested - include: interpolated-common interpolated-generic-pop: - match: ({{regexp_delim}}) scope: punctuation.section.generic.begin.perl set: - meta_scope: meta.generic.perl - meta_content_scope: meta.string.perl string.unquoted.perl # prevent variable interpolation to break string termination - match: ([$@%&*]#?)?(\1) captures: 1: meta.string.perl string.unquoted.perl 2: punctuation.section.generic.end.perl pop: true - include: interpolated-common interpolated-common: - include: literal-common - include: variables-interpolation literal-braces-pop: - match: \{ scope: punctuation.section.braces.begin.perl set: - meta_scope: meta.braces.perl - meta_content_scope: meta.string.perl string.unquoted.perl - match: \} scope: punctuation.section.braces.end.perl pop: true - include: literal-braces-nested - include: literal-common literal-braces-nested: - match: \{ push: - match: \} pop: true - include: literal-braces-nested - include: literal-common literal-brackets-pop: - match: \[ scope: punctuation.section.brackets.begin.perl set: - meta_scope: meta.brackets.perl - meta_content_scope: meta.string.perl string.unquoted.perl - match: \] scope: punctuation.section.brackets.end.perl pop: true - include: literal-brackets-nested - include: literal-common literal-brackets-nested: - match: \[ push: - match: \] pop: true - include: literal-brackets-nested - include: literal-common literal-angle-pop: - match: \< scope: punctuation.section.generic.begin.perl set: - meta_scope: meta.generic.perl - meta_content_scope: meta.string.perl string.unquoted.perl - match: \> scope: punctuation.section.generic.end.perl pop: true - include: literal-angle-nested - include: literal-common literal-angle-nested: - match: \< push: - match: \> pop: true - include: literal-angle-nested - include: literal-common literal-parens-pop: - match: \( scope: punctuation.section.parens.begin.perl set: - meta_scope: meta.parens.perl - meta_content_scope: meta.string.perl string.unquoted.perl - match: \) scope: punctuation.section.parens.end.perl pop: true - include: literal-parens-nested - include: literal-common literal-parens-nested: - match: \( push: - match: \) pop: true - include: literal-parens-nested - include: literal-common literal-generic-pop: - match: ({{regexp_delim}}) scope: punctuation.section.generic.begin.perl set: - meta_scope: meta.generic.perl - meta_content_scope: meta.string.perl string.unquoted.perl - match: \1 scope: punctuation.section.generic.end.perl pop: true - include: literal-common literal-common: # SEE: https://perldoc.perl.org/functions/sprintf.html - match: '%%|\\.' scope: constant.character.escape.perl - match: |- (?x: (%) # punctuation (?: \d+\$ )? # index (?: \s*[-+]\s* | [ #0] )? # flags (?: \* (?: \d+\$ )? | \d+ )? # width (?: v\d* | # vector flag (\.) (?: \* (?: \d+\$ )? | \d+ )? # precision )? (?: hh|ll|[hjlLqtz])? # size [aAbBcdDeEfFgGinoOpsuUxX] # control sequence ) scope: constant.other.placeholder.perl captures: 1: punctuation.definition.placeholder.perl 2: punctuation.separator.decimal.perl ###[ REGEXP ]################################################################# maybe-regexp-match: # /<pattern>/<flags> - match: / scope: punctuation.section.generic.begin.perl embed_scope: meta.string.perl string.regexp.perl embed: scope:source.regexp escape: '{{no_escape_behind}}(/)({{regexp_flags}})?' escape_captures: 1: punctuation.section.generic.end.perl 2: constant.language.flags.regexp.perl - include: else-pop - include: eol-pop ###[ FUNCTION CALL ]########################################################## qualified-function-call: - match: -> scope: punctuation.accessor.arrow.perl push: # member function - match: '{{identifier}}(?!::)' scope: meta.function-call.perl variable.function.member.perl pop: true # item access like $array->[0] - match: (?=[{\[]) set: maybe-item-access - include: comment-line - include: else-pop # Class->member - match: '{{identifier}}(?=\s*->)' scope: variable.namespace.perl # Module::SubModule::function # ::SubModule::function - match: ({{identifier}})?(::) captures: 1: variable.namespace.perl 2: punctuation.accessor.double-colon.perl push: - meta_scope: meta.path.perl - match: '{{identifier}}(?=\s*->)' scope: variable.namespace.perl pop: true - include: qualifier - include: unqualified-variables - include: function-identifier function-identifier: # builtin function calls - match: '{{builtin_functions}}' scope: meta.function-call.perl support.function.perl set: expressions-begin # user defined function calls - match: '{{identifier}}{{break}}' scope: meta.function-call.perl variable.function.perl set: expressions-begin - include: immediately-pop unqualified-function-call: # builtin function calls - match: '{{builtin_functions}}' scope: meta.function-call.perl support.function.perl push: expressions-begin # user defined function calls - match: '{{identifier}}(?=\s*(?:$|[;#/(''"`$@%]|<<|(?!(?:{{operator_keywords}}){{break}})\w))' scope: meta.function-call.perl variable.function.perl push: expressions-begin ###[ VARIABLES INTERPOLATION ]################################################ variables-interpolation: # note: coderefs `&` are not interpolated - match: (?=[@$%]) push: - - clear_scopes: 1 # remove 'string' - meta_scope: meta.interpolation.perl - include: maybe-item-access - variables-interpolation-body variables-interpolation-body: ########################################################################### # 1) This context is a merge of # `qualified-variables` and `unqualified-variables` # # a) The `push: maybe-item-access` is replaced by `pop: true` as the # item access is matched globally. # b) The `&` coderef parts are removed as they are not supported within # string interpolations. # # 2) The interpolation needs to be poped off from after each variable # to ensure to correctly pop off from the owning context if needed. # Otherwise the variable `$/` could break the statement `s//$repl$/g`. ########################################################################### # qualified variable - match: ([$@%*]#?)({{identifier}})?(::) captures: 1: punctuation.definition.variable.perl 2: variable.namespace.perl 3: punctuation.accessor.double-colon.perl set: qualified-variables-path # regexp match - match: ([$@%*]#?)[`&'] scope: variable.language.regexp.match.perl captures: 1: punctuation.definition.variable.perl pop: true # note: `*` may look like an operator - match: ([$@%]#?)(?:[-+]|[1-9]+) scope: variable.language.regexp.match-group.perl captures: 1: punctuation.definition.variable.perl pop: true # builtin - match: ([$@%*]#?){{builtin_variables}} scope: variable.language.perl captures: 1: punctuation.definition.variable.perl pop: true - match: |- (?x: ([$@%*]\#?) (?: \^[A-Z]? | [".:,;\]~?!/\\|()<>] | [$@%](?![-+]) # no regexp match )(?![$@%\w]) ) scope: variable.language.perl captures: 1: punctuation.definition.variable.perl pop: true # builtin variable, which may look like an operator - match: ([$@]\#?)= scope: variable.language.perl captures: 1: punctuation.definition.variable.perl pop: true # user defined variables - match: ([$@%*]#?){{identifier}} scope: variable.other.readwrite.perl captures: 1: punctuation.definition.variable.perl pop: true # deprecated/legacy variables # - $# and $* were variables up to Perl v5.10.0 - match: (\$)[#*\[](?=[^\w{$@%*]) scope: variable.language.deprecated.perl captures: 1: punctuation.definition.variable.perl pop: true # dereferenced variables (without `&`) - match: ([$@%*]#?)(\{)\s*({{identifier}})\s*(\}) scope: meta.variable.perl captures: 1: keyword.operator.dereference.perl 2: punctuation.definition.variable.begin.perl 3: meta.string.perl string.unquoted.perl 4: punctuation.definition.variable.end.perl pop: true - match: ([$@%*]#?)(\{) captures: 1: keyword.operator.dereference.perl 2: punctuation.definition.variable.begin.perl set: [variable-body, expressions-begin] # nothing matches, give up - include: immediately-pop ###[ VARIABLES ]############################################################## qualified-variables: - match: ([$@%*]#?)({{identifier}})?(::) captures: 1: punctuation.definition.variable.perl 2: variable.namespace.perl 3: punctuation.accessor.double-colon.perl push: [maybe-item-access, qualified-variables-path] qualified-variables-path: - meta_scope: meta.path.perl - include: qualifier - include: variable-identifier - include: unqualified-variables - include: immediately-pop qualifier: - match: ({{identifier}})?(::) captures: 1: variable.namespace.perl 2: punctuation.accessor.double-colon.perl variable-identifier: - match: '{{identifier}}' scope: variable.other.readwrite.perl pop: true unqualified-variables: # SEE: https://perldoc.perl.org/perlvar.html # regexp match - match: ([$@%*]#?)[`&'] scope: variable.language.regexp.match.perl captures: 1: punctuation.definition.variable.perl push: maybe-item-access # note: `*` may look like an operator - match: ([$@%]#?)(?:[-+]|[1-9]+) scope: variable.language.regexp.match-group.perl captures: 1: punctuation.definition.variable.perl push: maybe-item-access # builtin - match: ([$@%*]#?){{builtin_variables}} scope: variable.language.perl captures: 1: punctuation.definition.variable.perl push: maybe-item-access - match: |- (?x: ([$@%*]\#?) (?: \^[A-Z]? | [".:,;\]~?!/\\|()<>] | [$@%](?![-+]) # no regexp match )(?![$@%\w]) ) scope: variable.language.perl captures: 1: punctuation.definition.variable.perl push: maybe-item-access # builtin variable, which may look like an operator - match: ([$@]\#?)= scope: variable.language.perl captures: 1: punctuation.definition.variable.perl push: maybe-item-access # user defined variables - match: ([$@%*]#?){{identifier}} scope: variable.other.readwrite.perl captures: 1: punctuation.definition.variable.perl push: maybe-item-access # deprecated/legacy variables # - $# and $* were variables up to Perl v5.10.0 - match: (\$)[#*\[](?=[^\w{$@%*]) scope: variable.language.deprecated.perl captures: 1: punctuation.definition.variable.perl push: maybe-item-access # dereferenced variables - match: ([$@%*&]#?)(\{)\s*({{identifier}})\s*(\}) scope: meta.variable.perl captures: 1: keyword.operator.dereference.perl 2: punctuation.definition.variable.begin.perl 3: meta.string.perl string.unquoted.perl 4: punctuation.definition.variable.end.perl push: maybe-item-access - match: ([$@%*&]#?)(\{) captures: 1: keyword.operator.dereference.perl 2: punctuation.definition.variable.begin.perl push: [maybe-item-access, variable-body, expressions-begin] - match: '[$@%&]#?(?=[\w$@%*]|::)' scope: keyword.operator.dereference.perl push: function-identifier variable-not-operator: # variables that look like operators need special treatment - match: (\*#?)(?:[-+]|[1-9]+) scope: variable.language.regexp.match-group.perl captures: 1: punctuation.definition.variable.perl set: maybe-item-access - match: ([%*])= scope: variable.language.perl captures: 1: punctuation.definition.variable.perl set: maybe-item-access variable-body: - meta_scope: meta.variable.perl - match: \} scope: punctuation.definition.variable.end.perl pop: true - include: expressions-nested maybe-item-access: # SEE: https://perldoc.perl.org/perllol.html - match: \[ scope: punctuation.section.item-access.begin.perl push: - - meta_scope: meta.item-access.perl - match: \] scope: punctuation.section.item-access.end.perl pop: true - include: expressions-nested - expressions-begin - match: (\{)\s*({{identifier}})\s*(\}) scope: meta.item-access.perl captures: 1: punctuation.section.item-access.begin.perl 2: constant.other.key.perl 3: punctuation.section.item-access.end.perl - match: \{ scope: punctuation.section.item-access.begin.perl push: - - meta_scope: meta.item-access.perl - match: \} scope: punctuation.section.item-access.end.perl pop: true - include: expressions-nested - expressions-begin - include: immediately-pop