highlightjs_carbon_lang.js 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469
  1. /*
  2. * Part of the Carbon Language project, under the Apache License v2.0 with LLVM
  3. * Exceptions. See /LICENSE for license information.
  4. * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
  5. */
  6. /*
  7. * Language: Carbon
  8. * Category: common, system
  9. * Website: https://github.com/carbon-language/carbon-lang/
  10. *
  11. * TODO: abstract, virtual, impl on methods.
  12. * TODO: private, protected.
  13. * TODO: Dedicated package and import highlighting.
  14. * TODO: Dedicated `impl` highlighting.
  15. * TODO: `impl forall` pattern highlighting.
  16. * TODO: Some way to keep keywords here and in other implementations in sync.
  17. */
  18. /** @type LanguageFn */
  19. export default function (hljs) {
  20. // Common keywords definition used in various contexts to at least highlight
  21. // these correctly. Note that some of these aren't yet used in Carbon but are
  22. // anticipated and we go ahead and syntax-highlight them.
  23. const KEYWORDS = {
  24. keyword: [
  25. 'abstract',
  26. 'addr',
  27. 'alias',
  28. 'and',
  29. 'api',
  30. 'as',
  31. 'auto',
  32. 'base',
  33. 'break',
  34. 'case',
  35. 'class',
  36. 'constraint',
  37. 'continue',
  38. 'default',
  39. 'else',
  40. 'extends',
  41. 'external',
  42. 'final',
  43. 'fn',
  44. 'for',
  45. 'forall',
  46. 'friend',
  47. 'if',
  48. 'impl',
  49. 'import',
  50. 'in',
  51. 'interface',
  52. 'is',
  53. 'let',
  54. 'library',
  55. 'like',
  56. 'match',
  57. 'self',
  58. 'static',
  59. 'namespace',
  60. 'not',
  61. 'observe',
  62. 'or',
  63. 'override',
  64. 'package',
  65. 'partial',
  66. 'private',
  67. 'protected',
  68. 'return',
  69. 'returned',
  70. 'then',
  71. 'type',
  72. '_',
  73. 'var',
  74. 'virtual',
  75. 'where',
  76. 'while',
  77. ],
  78. literal: ['false', 'true'],
  79. type: ['bool'],
  80. built_in: ['As'],
  81. };
  82. // Punctuation and operator regex lists that are expanded into the expression
  83. // context. Note that we don't necessarily use all of these in Carbon today.
  84. // There are a number of operators in particular that aren't yet being used.
  85. // But it is harmless to give them nicer syntax highlighting. Eventually, as
  86. // the language settles, we can explore specifically highlighting operators
  87. // that aren't valid as errors.
  88. const PUNCTUATION = [/->/, /\./];
  89. const OPERATORS = [
  90. />>=/,
  91. /<=>/,
  92. /<<=/,
  93. /&=/,
  94. /\^=/,
  95. /:=/,
  96. /==/,
  97. /=>/,
  98. /!=/,
  99. />=/,
  100. />>/,
  101. /<=/,
  102. /<>/,
  103. /<</,
  104. /<-/,
  105. /-=/,
  106. /--/,
  107. /%=/,
  108. /\|=/,
  109. /\+=/,
  110. /\+\+/,
  111. /\/=/,
  112. /\*=/,
  113. /~=/,
  114. /&/,
  115. /\\/,
  116. /\^/,
  117. /!/,
  118. />/,
  119. /</,
  120. /-/,
  121. /%/,
  122. /\|/,
  123. /\+/,
  124. /\?/,
  125. /\//,
  126. /\*/,
  127. /~/,
  128. ];
  129. const COMMA_SEPARATOR = {
  130. begin: [/,/, /\s*/],
  131. beginScope: {
  132. 1: 'punctuation',
  133. },
  134. };
  135. // The core expression patterns.
  136. const NUMBER_LITERAL = {
  137. scope: 'number',
  138. variants: [
  139. { match: /[1-9][_0-9]*(\.[_0-9]+(e[-+]?[1-9][0-9]*)?)?/ },
  140. { match: /0x[_0-9A-F]+(\.[_0-9A-F]+(p[-+]?[1-9][0-9]*)?)?/ },
  141. { match: /0b[_01]+/ },
  142. ],
  143. };
  144. const TYPE_LITERAL = {
  145. scope: 'type',
  146. match: /[iuf][1-9][0-9]*/,
  147. };
  148. const ESCAPE_SEQUENCE = {
  149. scope: 'char.escape',
  150. match: /\\([tnr'"0\\0]|x[0-9A-F]{2}|u\{[0-9A-F]{4,}\})/,
  151. };
  152. const BLOCK_STRING_LITERAL = {
  153. scope: 'string',
  154. begin: [/"""/, /\S*/, /\n/],
  155. // TODO: `subLanguages` doesn't support referencing part of the match yet.
  156. beginScope: {
  157. 2: 'symbol',
  158. },
  159. end: /\s*"""/,
  160. contains: [
  161. ESCAPE_SEQUENCE,
  162. {
  163. scope: 'char.escape',
  164. match: /\\\n/,
  165. },
  166. ],
  167. };
  168. const STRING_LITERAL = {
  169. scope: 'string',
  170. begin: /"/,
  171. end: /"/,
  172. illegal: /\n/,
  173. contains: [ESCAPE_SEQUENCE],
  174. };
  175. const UNPARENTHESIZED_EXPRESSION = [
  176. TYPE_LITERAL,
  177. NUMBER_LITERAL,
  178. BLOCK_STRING_LITERAL,
  179. STRING_LITERAL,
  180. {
  181. scope: 'punctuation',
  182. match: '(' + PUNCTUATION.map((re) => re.source).join('|') + ')',
  183. },
  184. {
  185. scope: 'operator',
  186. match: '(' + OPERATORS.map((re) => re.source).join('|') + ')',
  187. },
  188. {
  189. scope: 'variable.language',
  190. match: /\bSelf\b/,
  191. },
  192. {
  193. // Catch-all sub-mode at the end but excluding any nesting characters.
  194. match: /\w+/,
  195. keywords: KEYWORDS,
  196. },
  197. ];
  198. const STRUCT_LITERAL_DESIGNATOR = {
  199. begin: [/\./, /[a-zA-Z]\w*/, /\s*/, /[:=]/, /\s*/],
  200. beginScope: {
  201. 1: 'punctuation',
  202. 2: 'symbol',
  203. 4: 'punctuation',
  204. },
  205. };
  206. // Use a nesting structure so that we directly track balanced delimiters and
  207. // consume them. This allows patterns below to reliably "end" on closing
  208. // delimiters without being confused by others in the expression stream.
  209. // Sadly, Highlight.js doesn't support indirect mode recursion so we have to
  210. // fuse all the delimited modes into a single one to support arbitrary
  211. // recursing. We use callbacks to make sure that the closing delimiters match
  212. // the opening ones.
  213. const PARENTHESIZED_EXPRESSION = {
  214. scope: 'carbon-delimited-expression',
  215. begin: /(\(|\{(?=\s*\.[a-zA-Z]\w*\s*[:=])|\[)/,
  216. beginScope: 'punctuation',
  217. end: /(\)|\}|\])/,
  218. endScope: 'punctuation',
  219. contains: [
  220. 'self',
  221. // Tuple literals and struct literals are comma-separated parenthesized
  222. // expressions.
  223. COMMA_SEPARATOR,
  224. // Struct literals include designators. These aren't allowed in tuple
  225. // literals, but included here because we fuse into a single recursive mode.
  226. STRUCT_LITERAL_DESIGNATOR,
  227. ...UNPARENTHESIZED_EXPRESSION,
  228. ],
  229. };
  230. const EXPRESSION = [PARENTHESIZED_EXPRESSION, ...UNPARENTHESIZED_EXPRESSION];
  231. // The pattern patterns, including comma-separated sequences.
  232. const VALUE_PATTERN = {
  233. scope: 'carbon-value-pattern',
  234. // This begins to match on *any* (zero-length) input that *isn't* one of the
  235. // terminating sequences. This is important so that once we end one of these
  236. // patterns we don't immediately begin it again. The mode that introduces
  237. // this has already determined that a value pattern can start immediately,
  238. // so we don't need other characters in the beginning.
  239. begin: /(?![=,;\)\]\}])/,
  240. end: /\s*[=,;\)\]\}]/,
  241. returnEnd: true,
  242. contains: [...EXPRESSION],
  243. };
  244. const BINDING_PATTERN = {
  245. scope: 'carbon-binding-pattern',
  246. variants: [
  247. {
  248. begin: [/(\bvar\b)?/, /\s*/, /[a-zA-Z]\w*/, /:!?/, /\s*/],
  249. beginScope: {
  250. 1: 'keyword',
  251. 3: 'variable',
  252. 4: 'punctuation',
  253. },
  254. },
  255. {
  256. begin: [/(\bvar\b)?/, /\s*/, /_/, /:!?/, /\s*/],
  257. beginScope: {
  258. 1: 'keyword',
  259. 3: 'keyword',
  260. 4: 'punctuation',
  261. },
  262. },
  263. ],
  264. end: /\s*[=,;\)\]\}]/,
  265. returnEnd: true,
  266. contains: [...EXPRESSION],
  267. };
  268. const SELF_PATTERN = {
  269. scope: 'carbon-self-pattern',
  270. begin: [/(\b(addr|var)\b)?/, /\s*/, /\bme/, /:/, /\s*/],
  271. beginScope: {
  272. 1: 'keyword',
  273. 3: 'keyword',
  274. 4: 'punctuation',
  275. },
  276. end: /[=,;\)\]\}]/,
  277. returnEnd: true,
  278. contains: [...EXPRESSION],
  279. };
  280. const UNPARENTHESIZED_PATTERNS = [
  281. SELF_PATTERN,
  282. BINDING_PATTERN,
  283. VALUE_PATTERN,
  284. ];
  285. const PARENTHESIZED_PATTERN = {
  286. scope: 'carbon-parenthesized-pattern',
  287. begin: /\(/,
  288. beginScope: 'punctuation',
  289. end: /\)/,
  290. endScope: 'punctuation',
  291. contains: ['self', COMMA_SEPARATOR, ...UNPARENTHESIZED_PATTERNS],
  292. };
  293. const PATTERN_SEQUENCE = [
  294. COMMA_SEPARATOR,
  295. PARENTHESIZED_PATTERN,
  296. ...UNPARENTHESIZED_PATTERNS,
  297. ];
  298. // Both variable and `let` value declarations.
  299. const INITIALIZER = {
  300. scope: 'carbon-initializer',
  301. begin: [/\s*/, /=/, /\s*/],
  302. beginScope: {
  303. 2: 'punctuation',
  304. },
  305. end: /[,;\)\]\}]/,
  306. returnEnd: true,
  307. contains: [...EXPRESSION],
  308. };
  309. const VARIABLE = {
  310. variants: [
  311. {
  312. scope: 'carbon-var-declaration',
  313. begin: [/\bvar/, /\s+/],
  314. },
  315. {
  316. scope: 'carbon-let-declaration',
  317. begin: [/\blet/, /\s+/],
  318. },
  319. ],
  320. beginScope: {
  321. 1: 'keyword',
  322. },
  323. end: /;/,
  324. endScope: 'punctuation',
  325. contains: [PARENTHESIZED_PATTERN, BINDING_PATTERN, INITIALIZER],
  326. };
  327. // Parameters.
  328. const PARAMETER_LIST = {
  329. variants: [
  330. {
  331. scope: 'params',
  332. begin: /\(/,
  333. end: /\)/,
  334. },
  335. {
  336. scope: 'params',
  337. begin: /\[/,
  338. end: /\]/,
  339. },
  340. ],
  341. beginScope: 'punctuation',
  342. endScope: 'punctuation',
  343. contains: [INITIALIZER, ...PATTERN_SEQUENCE],
  344. };
  345. // Functions.
  346. const FUNCTION_NAME_COMPONENT = {
  347. begin: [/\s*/, /\.?/, hljs.UNDERSCORE_IDENT_RE],
  348. beginScope: {
  349. 2: 'punctuation',
  350. 3: 'title.function',
  351. },
  352. end: /\s*(\.|->|;|\{)/,
  353. returnEnd: true,
  354. contains: [PARAMETER_LIST],
  355. };
  356. const RETURNED_TYPE = {
  357. scope: 'carbon-return-type',
  358. end: /\s*[;{]/,
  359. returnEnd: true,
  360. contains: [...EXPRESSION],
  361. };
  362. const RETURN_SPECIFIER = {
  363. begin: [/\s*/, /->/, /\s*/],
  364. beginScope: {
  365. 2: 'punctuation',
  366. },
  367. starts: RETURNED_TYPE,
  368. };
  369. const FUNCTION = {
  370. scope: 'carbon-function',
  371. begin: [/(\b(virtual|abstract|impl)\b)?/, /\s*/, /\bfn\b/],
  372. beginScope: { 1: 'keyword', 3: 'keyword' },
  373. end: /\s*[;{]/,
  374. returnEnd: true,
  375. contains: [FUNCTION_NAME_COMPONENT, RETURN_SPECIFIER],
  376. };
  377. // Classes, interfaces, and constraints.
  378. const CLASS_NAME_COMPONENT = {
  379. begin: [/\s*/, /\.?/, /(?!extends\b)[a-zA-Z_]\w*/],
  380. beginScope: {
  381. 2: 'punctuation',
  382. 3: 'title.class',
  383. },
  384. end: /\s*(\.|extends|;|\{)/,
  385. returnEnd: true,
  386. contains: [PARAMETER_LIST],
  387. };
  388. const EXTENDED_TYPE = {
  389. scope: 'title.class.inherited',
  390. end: /\s*[;{]/,
  391. returnEnd: true,
  392. contains: [...EXPRESSION],
  393. };
  394. const EXTENDS_KEYWORD = {
  395. begin: [/extends/, /\s+/],
  396. beginScope: {
  397. 1: 'keyword',
  398. },
  399. starts: EXTENDED_TYPE,
  400. };
  401. const CLASS = {
  402. variants: [
  403. {
  404. scope: 'carbon-class',
  405. begin: [/(\b(base|abstract)\b)?/, /\s*/, /\bclass\b/],
  406. beginScope: {
  407. 1: 'keyword',
  408. 3: 'keyword',
  409. },
  410. },
  411. {
  412. scope: 'carbon-interface',
  413. beginKeywords: 'interface',
  414. },
  415. {
  416. scope: 'carbon-constraint',
  417. beginKeywords: 'constraint',
  418. },
  419. ],
  420. end: /\s*[;{]/,
  421. returnEnd: true,
  422. contains: [CLASS_NAME_COMPONENT, EXTENDS_KEYWORD],
  423. };
  424. // Statements -- very loosely. We bundle together top-level declaration
  425. // constructs, blocks, and statements.
  426. const STATEMENTS = [
  427. VARIABLE,
  428. FUNCTION,
  429. CLASS,
  430. {
  431. // We only match the assignment operator at the statement level.
  432. scope: 'operator',
  433. match: /=/,
  434. },
  435. {
  436. scope: 'punctuation',
  437. match: /;/,
  438. },
  439. ...EXPRESSION,
  440. ];
  441. const BLOCK = {
  442. scope: 'carbon-block',
  443. begin: /\{/,
  444. beginScope: 'punctuation',
  445. end: /\}/,
  446. endScope: 'punctuation',
  447. contains: ['self', hljs.C_LINE_COMMENT_MODE, ...STATEMENTS],
  448. };
  449. // Stitch everything together into the language definition.
  450. return {
  451. name: 'Carbon',
  452. aliases: ['carbon', 'carbon-lang'],
  453. keywords: KEYWORDS,
  454. illegal: '</',
  455. contains: [hljs.C_LINE_COMMENT_MODE, ...STATEMENTS, BLOCK],
  456. };
  457. }