You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
446 lines
8.6 KiB
446 lines
8.6 KiB
/** |
|
* @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 } re |
|
* @returns {string} |
|
*/ |
|
function lookahead(re) { |
|
return concat('(?=', re, ')'); |
|
} |
|
|
|
/** |
|
* @param {...(RegExp | string) } args |
|
* @returns {string} |
|
*/ |
|
function concat(...args) { |
|
const joined = args.map((x) => source(x)).join(""); |
|
return joined; |
|
} |
|
|
|
/* |
|
Language: Python |
|
Description: Python is an interpreted, object-oriented, high-level programming language with dynamic semantics. |
|
Website: https://www.python.org |
|
Category: common |
|
*/ |
|
|
|
function python(hljs) { |
|
const RESERVED_WORDS = [ |
|
'and', |
|
'as', |
|
'assert', |
|
'async', |
|
'await', |
|
'break', |
|
'class', |
|
'continue', |
|
'def', |
|
'del', |
|
'elif', |
|
'else', |
|
'except', |
|
'finally', |
|
'for', |
|
'from', |
|
'global', |
|
'if', |
|
'import', |
|
'in', |
|
'is', |
|
'lambda', |
|
'nonlocal|10', |
|
'not', |
|
'or', |
|
'pass', |
|
'raise', |
|
'return', |
|
'try', |
|
'while', |
|
'with', |
|
'yield' |
|
]; |
|
|
|
const BUILT_INS = [ |
|
'__import__', |
|
'abs', |
|
'all', |
|
'any', |
|
'ascii', |
|
'bin', |
|
'bool', |
|
'breakpoint', |
|
'bytearray', |
|
'bytes', |
|
'callable', |
|
'chr', |
|
'classmethod', |
|
'compile', |
|
'complex', |
|
'delattr', |
|
'dict', |
|
'dir', |
|
'divmod', |
|
'enumerate', |
|
'eval', |
|
'exec', |
|
'filter', |
|
'float', |
|
'format', |
|
'frozenset', |
|
'getattr', |
|
'globals', |
|
'hasattr', |
|
'hash', |
|
'help', |
|
'hex', |
|
'id', |
|
'input', |
|
'int', |
|
'isinstance', |
|
'issubclass', |
|
'iter', |
|
'len', |
|
'list', |
|
'locals', |
|
'map', |
|
'max', |
|
'memoryview', |
|
'min', |
|
'next', |
|
'object', |
|
'oct', |
|
'open', |
|
'ord', |
|
'pow', |
|
'print', |
|
'property', |
|
'range', |
|
'repr', |
|
'reversed', |
|
'round', |
|
'set', |
|
'setattr', |
|
'slice', |
|
'sorted', |
|
'staticmethod', |
|
'str', |
|
'sum', |
|
'super', |
|
'tuple', |
|
'type', |
|
'vars', |
|
'zip' |
|
]; |
|
|
|
const LITERALS = [ |
|
'__debug__', |
|
'Ellipsis', |
|
'False', |
|
'None', |
|
'NotImplemented', |
|
'True' |
|
]; |
|
|
|
// https://docs.python.org/3/library/typing.html |
|
// TODO: Could these be supplemented by a CamelCase matcher in certain |
|
// contexts, leaving these remaining only for relevance hinting? |
|
const TYPES = [ |
|
"Any", |
|
"Callable", |
|
"Coroutine", |
|
"Dict", |
|
"List", |
|
"Literal", |
|
"Generic", |
|
"Optional", |
|
"Sequence", |
|
"Set", |
|
"Tuple", |
|
"Type", |
|
"Union" |
|
]; |
|
|
|
const KEYWORDS = { |
|
$pattern: /[A-Za-z]\w+|__\w+__/, |
|
keyword: RESERVED_WORDS, |
|
built_in: BUILT_INS, |
|
literal: LITERALS, |
|
type: TYPES |
|
}; |
|
|
|
const PROMPT = { |
|
className: 'meta', |
|
begin: /^(>>>|\.\.\.) / |
|
}; |
|
|
|
const SUBST = { |
|
className: 'subst', |
|
begin: /\{/, |
|
end: /\}/, |
|
keywords: KEYWORDS, |
|
illegal: /#/ |
|
}; |
|
|
|
const LITERAL_BRACKET = { |
|
begin: /\{\{/, |
|
relevance: 0 |
|
}; |
|
|
|
const STRING = { |
|
className: 'string', |
|
contains: [ hljs.BACKSLASH_ESCAPE ], |
|
variants: [ |
|
{ |
|
begin: /([uU]|[bB]|[rR]|[bB][rR]|[rR][bB])?'''/, |
|
end: /'''/, |
|
contains: [ |
|
hljs.BACKSLASH_ESCAPE, |
|
PROMPT |
|
], |
|
relevance: 10 |
|
}, |
|
{ |
|
begin: /([uU]|[bB]|[rR]|[bB][rR]|[rR][bB])?"""/, |
|
end: /"""/, |
|
contains: [ |
|
hljs.BACKSLASH_ESCAPE, |
|
PROMPT |
|
], |
|
relevance: 10 |
|
}, |
|
{ |
|
begin: /([fF][rR]|[rR][fF]|[fF])'''/, |
|
end: /'''/, |
|
contains: [ |
|
hljs.BACKSLASH_ESCAPE, |
|
PROMPT, |
|
LITERAL_BRACKET, |
|
SUBST |
|
] |
|
}, |
|
{ |
|
begin: /([fF][rR]|[rR][fF]|[fF])"""/, |
|
end: /"""/, |
|
contains: [ |
|
hljs.BACKSLASH_ESCAPE, |
|
PROMPT, |
|
LITERAL_BRACKET, |
|
SUBST |
|
] |
|
}, |
|
{ |
|
begin: /([uU]|[rR])'/, |
|
end: /'/, |
|
relevance: 10 |
|
}, |
|
{ |
|
begin: /([uU]|[rR])"/, |
|
end: /"/, |
|
relevance: 10 |
|
}, |
|
{ |
|
begin: /([bB]|[bB][rR]|[rR][bB])'/, |
|
end: /'/ |
|
}, |
|
{ |
|
begin: /([bB]|[bB][rR]|[rR][bB])"/, |
|
end: /"/ |
|
}, |
|
{ |
|
begin: /([fF][rR]|[rR][fF]|[fF])'/, |
|
end: /'/, |
|
contains: [ |
|
hljs.BACKSLASH_ESCAPE, |
|
LITERAL_BRACKET, |
|
SUBST |
|
] |
|
}, |
|
{ |
|
begin: /([fF][rR]|[rR][fF]|[fF])"/, |
|
end: /"/, |
|
contains: [ |
|
hljs.BACKSLASH_ESCAPE, |
|
LITERAL_BRACKET, |
|
SUBST |
|
] |
|
}, |
|
hljs.APOS_STRING_MODE, |
|
hljs.QUOTE_STRING_MODE |
|
] |
|
}; |
|
|
|
// https://docs.python.org/3.9/reference/lexical_analysis.html#numeric-literals |
|
const digitpart = '[0-9](_?[0-9])*'; |
|
const pointfloat = `(\\b(${digitpart}))?\\.(${digitpart})|\\b(${digitpart})\\.`; |
|
const NUMBER = { |
|
className: 'number', |
|
relevance: 0, |
|
variants: [ |
|
// exponentfloat, pointfloat |
|
// https://docs.python.org/3.9/reference/lexical_analysis.html#floating-point-literals |
|
// optionally imaginary |
|
// https://docs.python.org/3.9/reference/lexical_analysis.html#imaginary-literals |
|
// Note: no leading \b because floats can start with a decimal point |
|
// and we don't want to mishandle e.g. `fn(.5)`, |
|
// no trailing \b for pointfloat because it can end with a decimal point |
|
// and we don't want to mishandle e.g. `0..hex()`; this should be safe |
|
// because both MUST contain a decimal point and so cannot be confused with |
|
// the interior part of an identifier |
|
{ |
|
begin: `(\\b(${digitpart})|(${pointfloat}))[eE][+-]?(${digitpart})[jJ]?\\b` |
|
}, |
|
{ |
|
begin: `(${pointfloat})[jJ]?` |
|
}, |
|
|
|
// decinteger, bininteger, octinteger, hexinteger |
|
// https://docs.python.org/3.9/reference/lexical_analysis.html#integer-literals |
|
// optionally "long" in Python 2 |
|
// https://docs.python.org/2.7/reference/lexical_analysis.html#integer-and-long-integer-literals |
|
// decinteger is optionally imaginary |
|
// https://docs.python.org/3.9/reference/lexical_analysis.html#imaginary-literals |
|
{ |
|
begin: '\\b([1-9](_?[0-9])*|0+(_?0)*)[lLjJ]?\\b' |
|
}, |
|
{ |
|
begin: '\\b0[bB](_?[01])+[lL]?\\b' |
|
}, |
|
{ |
|
begin: '\\b0[oO](_?[0-7])+[lL]?\\b' |
|
}, |
|
{ |
|
begin: '\\b0[xX](_?[0-9a-fA-F])+[lL]?\\b' |
|
}, |
|
|
|
// imagnumber (digitpart-based) |
|
// https://docs.python.org/3.9/reference/lexical_analysis.html#imaginary-literals |
|
{ |
|
begin: `\\b(${digitpart})[jJ]\\b` |
|
} |
|
] |
|
}; |
|
const COMMENT_TYPE = { |
|
className: "comment", |
|
begin: lookahead(/# type:/), |
|
end: /$/, |
|
keywords: KEYWORDS, |
|
contains: [ |
|
{ // prevent keywords from coloring `type` |
|
begin: /# type:/ |
|
}, |
|
// comment within a datatype comment includes no keywords |
|
{ |
|
begin: /#/, |
|
end: /\b\B/, |
|
endsWithParent: true |
|
} |
|
] |
|
}; |
|
const PARAMS = { |
|
className: 'params', |
|
variants: [ |
|
// Exclude params in functions without params |
|
{ |
|
className: "", |
|
begin: /\(\s*\)/, |
|
skip: true |
|
}, |
|
{ |
|
begin: /\(/, |
|
end: /\)/, |
|
excludeBegin: true, |
|
excludeEnd: true, |
|
keywords: KEYWORDS, |
|
contains: [ |
|
'self', |
|
PROMPT, |
|
NUMBER, |
|
STRING, |
|
hljs.HASH_COMMENT_MODE |
|
] |
|
} |
|
] |
|
}; |
|
SUBST.contains = [ |
|
STRING, |
|
NUMBER, |
|
PROMPT |
|
]; |
|
|
|
return { |
|
name: 'Python', |
|
aliases: [ |
|
'py', |
|
'gyp', |
|
'ipython' |
|
], |
|
keywords: KEYWORDS, |
|
illegal: /(<\/|->|\?)|=>/, |
|
contains: [ |
|
PROMPT, |
|
NUMBER, |
|
{ |
|
// very common convention |
|
begin: /\bself\b/ |
|
}, |
|
{ |
|
// eat "if" prior to string so that it won't accidentally be |
|
// labeled as an f-string |
|
beginKeywords: "if", |
|
relevance: 0 |
|
}, |
|
STRING, |
|
COMMENT_TYPE, |
|
hljs.HASH_COMMENT_MODE, |
|
{ |
|
variants: [ |
|
{ |
|
className: 'function', |
|
beginKeywords: 'def' |
|
}, |
|
{ |
|
className: 'class', |
|
beginKeywords: 'class' |
|
} |
|
], |
|
end: /:/, |
|
illegal: /[${=;\n,]/, |
|
contains: [ |
|
hljs.UNDERSCORE_TITLE_MODE, |
|
PARAMS, |
|
{ |
|
begin: /->/, |
|
endsWithParent: true, |
|
keywords: KEYWORDS |
|
} |
|
] |
|
}, |
|
{ |
|
className: 'meta', |
|
begin: /^[\t ]*@/, |
|
end: /(?=#)|$/, |
|
contains: [ |
|
NUMBER, |
|
PARAMS, |
|
STRING |
|
] |
|
} |
|
] |
|
}; |
|
} |
|
|
|
module.exports = python;
|
|
|