python.js 8.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446
  1. /**
  2. * @param {string} value
  3. * @returns {RegExp}
  4. * */
  5. /**
  6. * @param {RegExp | string } re
  7. * @returns {string}
  8. */
  9. function source(re) {
  10. if (!re) return null;
  11. if (typeof re === "string") return re;
  12. return re.source;
  13. }
  14. /**
  15. * @param {RegExp | string } re
  16. * @returns {string}
  17. */
  18. function lookahead(re) {
  19. return concat('(?=', re, ')');
  20. }
  21. /**
  22. * @param {...(RegExp | string) } args
  23. * @returns {string}
  24. */
  25. function concat(...args) {
  26. const joined = args.map((x) => source(x)).join("");
  27. return joined;
  28. }
  29. /*
  30. Language: Python
  31. Description: Python is an interpreted, object-oriented, high-level programming language with dynamic semantics.
  32. Website: https://www.python.org
  33. Category: common
  34. */
  35. function python(hljs) {
  36. const RESERVED_WORDS = [
  37. 'and',
  38. 'as',
  39. 'assert',
  40. 'async',
  41. 'await',
  42. 'break',
  43. 'class',
  44. 'continue',
  45. 'def',
  46. 'del',
  47. 'elif',
  48. 'else',
  49. 'except',
  50. 'finally',
  51. 'for',
  52. 'from',
  53. 'global',
  54. 'if',
  55. 'import',
  56. 'in',
  57. 'is',
  58. 'lambda',
  59. 'nonlocal|10',
  60. 'not',
  61. 'or',
  62. 'pass',
  63. 'raise',
  64. 'return',
  65. 'try',
  66. 'while',
  67. 'with',
  68. 'yield'
  69. ];
  70. const BUILT_INS = [
  71. '__import__',
  72. 'abs',
  73. 'all',
  74. 'any',
  75. 'ascii',
  76. 'bin',
  77. 'bool',
  78. 'breakpoint',
  79. 'bytearray',
  80. 'bytes',
  81. 'callable',
  82. 'chr',
  83. 'classmethod',
  84. 'compile',
  85. 'complex',
  86. 'delattr',
  87. 'dict',
  88. 'dir',
  89. 'divmod',
  90. 'enumerate',
  91. 'eval',
  92. 'exec',
  93. 'filter',
  94. 'float',
  95. 'format',
  96. 'frozenset',
  97. 'getattr',
  98. 'globals',
  99. 'hasattr',
  100. 'hash',
  101. 'help',
  102. 'hex',
  103. 'id',
  104. 'input',
  105. 'int',
  106. 'isinstance',
  107. 'issubclass',
  108. 'iter',
  109. 'len',
  110. 'list',
  111. 'locals',
  112. 'map',
  113. 'max',
  114. 'memoryview',
  115. 'min',
  116. 'next',
  117. 'object',
  118. 'oct',
  119. 'open',
  120. 'ord',
  121. 'pow',
  122. 'print',
  123. 'property',
  124. 'range',
  125. 'repr',
  126. 'reversed',
  127. 'round',
  128. 'set',
  129. 'setattr',
  130. 'slice',
  131. 'sorted',
  132. 'staticmethod',
  133. 'str',
  134. 'sum',
  135. 'super',
  136. 'tuple',
  137. 'type',
  138. 'vars',
  139. 'zip'
  140. ];
  141. const LITERALS = [
  142. '__debug__',
  143. 'Ellipsis',
  144. 'False',
  145. 'None',
  146. 'NotImplemented',
  147. 'True'
  148. ];
  149. // https://docs.python.org/3/library/typing.html
  150. // TODO: Could these be supplemented by a CamelCase matcher in certain
  151. // contexts, leaving these remaining only for relevance hinting?
  152. const TYPES = [
  153. "Any",
  154. "Callable",
  155. "Coroutine",
  156. "Dict",
  157. "List",
  158. "Literal",
  159. "Generic",
  160. "Optional",
  161. "Sequence",
  162. "Set",
  163. "Tuple",
  164. "Type",
  165. "Union"
  166. ];
  167. const KEYWORDS = {
  168. $pattern: /[A-Za-z]\w+|__\w+__/,
  169. keyword: RESERVED_WORDS,
  170. built_in: BUILT_INS,
  171. literal: LITERALS,
  172. type: TYPES
  173. };
  174. const PROMPT = {
  175. className: 'meta',
  176. begin: /^(>>>|\.\.\.) /
  177. };
  178. const SUBST = {
  179. className: 'subst',
  180. begin: /\{/,
  181. end: /\}/,
  182. keywords: KEYWORDS,
  183. illegal: /#/
  184. };
  185. const LITERAL_BRACKET = {
  186. begin: /\{\{/,
  187. relevance: 0
  188. };
  189. const STRING = {
  190. className: 'string',
  191. contains: [ hljs.BACKSLASH_ESCAPE ],
  192. variants: [
  193. {
  194. begin: /([uU]|[bB]|[rR]|[bB][rR]|[rR][bB])?'''/,
  195. end: /'''/,
  196. contains: [
  197. hljs.BACKSLASH_ESCAPE,
  198. PROMPT
  199. ],
  200. relevance: 10
  201. },
  202. {
  203. begin: /([uU]|[bB]|[rR]|[bB][rR]|[rR][bB])?"""/,
  204. end: /"""/,
  205. contains: [
  206. hljs.BACKSLASH_ESCAPE,
  207. PROMPT
  208. ],
  209. relevance: 10
  210. },
  211. {
  212. begin: /([fF][rR]|[rR][fF]|[fF])'''/,
  213. end: /'''/,
  214. contains: [
  215. hljs.BACKSLASH_ESCAPE,
  216. PROMPT,
  217. LITERAL_BRACKET,
  218. SUBST
  219. ]
  220. },
  221. {
  222. begin: /([fF][rR]|[rR][fF]|[fF])"""/,
  223. end: /"""/,
  224. contains: [
  225. hljs.BACKSLASH_ESCAPE,
  226. PROMPT,
  227. LITERAL_BRACKET,
  228. SUBST
  229. ]
  230. },
  231. {
  232. begin: /([uU]|[rR])'/,
  233. end: /'/,
  234. relevance: 10
  235. },
  236. {
  237. begin: /([uU]|[rR])"/,
  238. end: /"/,
  239. relevance: 10
  240. },
  241. {
  242. begin: /([bB]|[bB][rR]|[rR][bB])'/,
  243. end: /'/
  244. },
  245. {
  246. begin: /([bB]|[bB][rR]|[rR][bB])"/,
  247. end: /"/
  248. },
  249. {
  250. begin: /([fF][rR]|[rR][fF]|[fF])'/,
  251. end: /'/,
  252. contains: [
  253. hljs.BACKSLASH_ESCAPE,
  254. LITERAL_BRACKET,
  255. SUBST
  256. ]
  257. },
  258. {
  259. begin: /([fF][rR]|[rR][fF]|[fF])"/,
  260. end: /"/,
  261. contains: [
  262. hljs.BACKSLASH_ESCAPE,
  263. LITERAL_BRACKET,
  264. SUBST
  265. ]
  266. },
  267. hljs.APOS_STRING_MODE,
  268. hljs.QUOTE_STRING_MODE
  269. ]
  270. };
  271. // https://docs.python.org/3.9/reference/lexical_analysis.html#numeric-literals
  272. const digitpart = '[0-9](_?[0-9])*';
  273. const pointfloat = `(\\b(${digitpart}))?\\.(${digitpart})|\\b(${digitpart})\\.`;
  274. const NUMBER = {
  275. className: 'number',
  276. relevance: 0,
  277. variants: [
  278. // exponentfloat, pointfloat
  279. // https://docs.python.org/3.9/reference/lexical_analysis.html#floating-point-literals
  280. // optionally imaginary
  281. // https://docs.python.org/3.9/reference/lexical_analysis.html#imaginary-literals
  282. // Note: no leading \b because floats can start with a decimal point
  283. // and we don't want to mishandle e.g. `fn(.5)`,
  284. // no trailing \b for pointfloat because it can end with a decimal point
  285. // and we don't want to mishandle e.g. `0..hex()`; this should be safe
  286. // because both MUST contain a decimal point and so cannot be confused with
  287. // the interior part of an identifier
  288. {
  289. begin: `(\\b(${digitpart})|(${pointfloat}))[eE][+-]?(${digitpart})[jJ]?\\b`
  290. },
  291. {
  292. begin: `(${pointfloat})[jJ]?`
  293. },
  294. // decinteger, bininteger, octinteger, hexinteger
  295. // https://docs.python.org/3.9/reference/lexical_analysis.html#integer-literals
  296. // optionally "long" in Python 2
  297. // https://docs.python.org/2.7/reference/lexical_analysis.html#integer-and-long-integer-literals
  298. // decinteger is optionally imaginary
  299. // https://docs.python.org/3.9/reference/lexical_analysis.html#imaginary-literals
  300. {
  301. begin: '\\b([1-9](_?[0-9])*|0+(_?0)*)[lLjJ]?\\b'
  302. },
  303. {
  304. begin: '\\b0[bB](_?[01])+[lL]?\\b'
  305. },
  306. {
  307. begin: '\\b0[oO](_?[0-7])+[lL]?\\b'
  308. },
  309. {
  310. begin: '\\b0[xX](_?[0-9a-fA-F])+[lL]?\\b'
  311. },
  312. // imagnumber (digitpart-based)
  313. // https://docs.python.org/3.9/reference/lexical_analysis.html#imaginary-literals
  314. {
  315. begin: `\\b(${digitpart})[jJ]\\b`
  316. }
  317. ]
  318. };
  319. const COMMENT_TYPE = {
  320. className: "comment",
  321. begin: lookahead(/# type:/),
  322. end: /$/,
  323. keywords: KEYWORDS,
  324. contains: [
  325. { // prevent keywords from coloring `type`
  326. begin: /# type:/
  327. },
  328. // comment within a datatype comment includes no keywords
  329. {
  330. begin: /#/,
  331. end: /\b\B/,
  332. endsWithParent: true
  333. }
  334. ]
  335. };
  336. const PARAMS = {
  337. className: 'params',
  338. variants: [
  339. // Exclude params in functions without params
  340. {
  341. className: "",
  342. begin: /\(\s*\)/,
  343. skip: true
  344. },
  345. {
  346. begin: /\(/,
  347. end: /\)/,
  348. excludeBegin: true,
  349. excludeEnd: true,
  350. keywords: KEYWORDS,
  351. contains: [
  352. 'self',
  353. PROMPT,
  354. NUMBER,
  355. STRING,
  356. hljs.HASH_COMMENT_MODE
  357. ]
  358. }
  359. ]
  360. };
  361. SUBST.contains = [
  362. STRING,
  363. NUMBER,
  364. PROMPT
  365. ];
  366. return {
  367. name: 'Python',
  368. aliases: [
  369. 'py',
  370. 'gyp',
  371. 'ipython'
  372. ],
  373. keywords: KEYWORDS,
  374. illegal: /(<\/|->|\?)|=>/,
  375. contains: [
  376. PROMPT,
  377. NUMBER,
  378. {
  379. // very common convention
  380. begin: /\bself\b/
  381. },
  382. {
  383. // eat "if" prior to string so that it won't accidentally be
  384. // labeled as an f-string
  385. beginKeywords: "if",
  386. relevance: 0
  387. },
  388. STRING,
  389. COMMENT_TYPE,
  390. hljs.HASH_COMMENT_MODE,
  391. {
  392. variants: [
  393. {
  394. className: 'function',
  395. beginKeywords: 'def'
  396. },
  397. {
  398. className: 'class',
  399. beginKeywords: 'class'
  400. }
  401. ],
  402. end: /:/,
  403. illegal: /[${=;\n,]/,
  404. contains: [
  405. hljs.UNDERSCORE_TITLE_MODE,
  406. PARAMS,
  407. {
  408. begin: /->/,
  409. endsWithParent: true,
  410. keywords: KEYWORDS
  411. }
  412. ]
  413. },
  414. {
  415. className: 'meta',
  416. begin: /^[\t ]*@/,
  417. end: /(?=#)|$/,
  418. contains: [
  419. NUMBER,
  420. PARAMS,
  421. STRING
  422. ]
  423. }
  424. ]
  425. };
  426. }
  427. module.exports = python;