447 lines
		
	
	
		
			8.6 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
	
	
			
		
		
	
	
			447 lines
		
	
	
		
			8.6 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 } 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;
 |