highlightjs_carbon_lang.js 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463
  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. 'me',
  58. 'namespace',
  59. 'not',
  60. 'observe',
  61. 'or',
  62. 'override',
  63. 'package',
  64. 'partial',
  65. 'private',
  66. 'protected',
  67. 'return',
  68. 'returned',
  69. 'then',
  70. '_',
  71. 'var',
  72. 'virtual',
  73. 'where',
  74. 'while',
  75. ],
  76. literal: ['false', 'true'],
  77. type: ['bool'],
  78. built_in: ['Type', 'As'],
  79. };
  80. // Punctuation and operator regex lists that are expanded into the expression
  81. // context. Note that we don't necessarily use all of these in Carbon today.
  82. // There are a number of operators in particular that aren't yet being used.
  83. // But it is harmless to give them nicer syntax highlighting. Eventually, as
  84. // the language settles, we can explore specifically highlighting operators
  85. // that aren't valid as errors.
  86. const PUNCTUATION = [/->/, /\./];
  87. const OPERATORS = [
  88. />>=/,
  89. /<=>/,
  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. const COMMA_SEPARATOR = {
  128. begin: [/,/, /\s*/],
  129. beginScope: {
  130. 1: 'punctuation',
  131. },
  132. };
  133. // The core expression patterns.
  134. const NUMBER_LITERAL = {
  135. scope: 'number',
  136. variants: [
  137. { match: /[1-9][_0-9]*(\.[_0-9]+(e[-+]?[1-9][0-9]*)?)?/ },
  138. { match: /0x[_0-9A-F]+(\.[_0-9A-F]+(p[-+]?[1-9][0-9]*)?)?/ },
  139. { match: /0b[_01]+/ },
  140. ],
  141. };
  142. const TYPE_LITERAL = {
  143. scope: 'type',
  144. match: /[iuf][1-9][0-9]*/,
  145. };
  146. const ESCAPE_SEQUENCE = {
  147. scope: 'char.escape',
  148. match: /\\([tnr'"0\\0]|x[0-9A-F]{2}|u\{[0-9A-F]{4,}\})/,
  149. };
  150. const BLOCK_STRING_LITERAL = {
  151. scope: 'string',
  152. begin: [/"""/, /\S*/, /\n/],
  153. // TODO: `subLanguages` doesn't support referencing part of the match yet.
  154. beginScope: {
  155. 2: 'symbol',
  156. },
  157. end: /\s*"""/,
  158. contains: [
  159. ESCAPE_SEQUENCE,
  160. {
  161. scope: 'char.escape',
  162. match: /\\\n/,
  163. },
  164. ],
  165. };
  166. const STRING_LITERAL = {
  167. scope: 'string',
  168. begin: /"/,
  169. end: /"/,
  170. illegal: /\n/,
  171. contains: [ESCAPE_SEQUENCE],
  172. };
  173. const UNPARENTHESIZED_EXPRESSION = [
  174. TYPE_LITERAL,
  175. NUMBER_LITERAL,
  176. BLOCK_STRING_LITERAL,
  177. STRING_LITERAL,
  178. {
  179. scope: 'punctuation',
  180. match: '(' + PUNCTUATION.map((re) => re.source).join('|') + ')',
  181. },
  182. {
  183. scope: 'operator',
  184. match: '(' + OPERATORS.map((re) => re.source).join('|') + ')',
  185. },
  186. {
  187. scope: 'variable.language',
  188. match: /\bSelf\b/,
  189. },
  190. {
  191. // Catch-all sub-mode at the end but excluding any nesting characters.
  192. match: /\w+/,
  193. keywords: KEYWORDS,
  194. },
  195. ];
  196. const STRUCT_LITERAL_DESIGNATOR = {
  197. begin: [/\./, /[a-zA-Z]\w*/, /\s*/, /[:=]/, /\s*/],
  198. beginScope: {
  199. 1: 'punctuation',
  200. 2: 'symbol',
  201. 4: 'punctuation',
  202. },
  203. };
  204. // Use a nesting structure so that we directly track balanced delimiters and
  205. // consume them. This allows patterns below to reliably "end" on closing
  206. // delimiters without being confused by others in the expression stream.
  207. // Sadly, Highlight.js doesn't support indirect mode recursion so we have to
  208. // fuse all the delimited modes into a single one to support arbitrary
  209. // recursing. We use callbacks to make sure that the closing delimiters match
  210. // the opening ones.
  211. const PARENTHESIZED_EXPRESSION = {
  212. scope: 'carbon-delimited-expression',
  213. begin: /(\(|\{(?=\s*\.[a-zA-Z]\w*\s*[:=])|\[)/,
  214. beginScope: 'punctuation',
  215. end: /(\)|\}|\])/,
  216. endScope: 'punctuation',
  217. contains: [
  218. 'self',
  219. // Tuple literals and struct literals are comma-separated parenthesized
  220. // expressions.
  221. COMMA_SEPARATOR,
  222. // Struct literals include designators. These aren't allowed in tuple
  223. // literals, but included here because we fuse into a single recursive mode.
  224. STRUCT_LITERAL_DESIGNATOR,
  225. ...UNPARENTHESIZED_EXPRESSION,
  226. ],
  227. };
  228. const EXPRESSION = [PARENTHESIZED_EXPRESSION, ...UNPARENTHESIZED_EXPRESSION];
  229. // The pattern patterns, including comma-separated sequences.
  230. const VALUE_PATTERN = {
  231. scope: 'carbon-value-pattern',
  232. // This begins to match on *any* (zero-length) input that *isn't* one of the
  233. // terminating sequences. This is important so that once we end one of these
  234. // patterns we don't immediately begin it again. The mode that introduces
  235. // this has already determined that a value pattern can start immediately,
  236. // so we don't need other characters in the beginning.
  237. begin: /(?![=,;\)\]\}])/,
  238. end: /\s*[=,;\)\]\}]/,
  239. returnEnd: true,
  240. contains: [...EXPRESSION],
  241. };
  242. const BINDING_PATTERN = {
  243. scope: 'carbon-binding-pattern',
  244. variants: [
  245. {
  246. begin: [/(\bvar\b)?/, /\s*/, /[a-zA-Z]\w*/, /:!?/, /\s*/],
  247. beginScope: {
  248. 1: 'keyword',
  249. 3: 'variable',
  250. 4: 'punctuation',
  251. },
  252. },
  253. {
  254. begin: [/(\bvar\b)?/, /\s*/, /_/, /:!?/, /\s*/],
  255. beginScope: {
  256. 1: 'keyword',
  257. 3: 'keyword',
  258. 4: 'punctuation',
  259. },
  260. },
  261. ],
  262. end: /\s*[=,;\)\]\}]/,
  263. returnEnd: true,
  264. contains: [...EXPRESSION],
  265. };
  266. const ME_PATTERN = {
  267. scope: 'carbon-me-pattern',
  268. begin: [/(\b(addr|var)\b)?/, /\s*/, /\bme/, /:/, /\s*/],
  269. beginScope: {
  270. 1: 'keyword',
  271. 3: 'keyword',
  272. 4: 'punctuation',
  273. },
  274. end: /[=,;\)\]\}]/,
  275. returnEnd: true,
  276. contains: [...EXPRESSION],
  277. };
  278. const UNPARENTHESIZED_PATTERNS = [ME_PATTERN, BINDING_PATTERN, VALUE_PATTERN];
  279. const PARENTHESIZED_PATTERN = {
  280. scope: 'carbon-parenthesized-pattern',
  281. begin: /\(/,
  282. beginScope: 'punctuation',
  283. end: /\)/,
  284. endScope: 'punctuation',
  285. contains: ['self', COMMA_SEPARATOR, ...UNPARENTHESIZED_PATTERNS],
  286. };
  287. const PATTERN_SEQUENCE = [
  288. COMMA_SEPARATOR,
  289. PARENTHESIZED_PATTERN,
  290. ...UNPARENTHESIZED_PATTERNS,
  291. ];
  292. // Both variable and `let` value declarations.
  293. const INITIALIZER = {
  294. scope: 'carbon-initializer',
  295. begin: [/\s*/, /=/, /\s*/],
  296. beginScope: {
  297. 2: 'punctuation',
  298. },
  299. end: /[,;\)\]\}]/,
  300. returnEnd: true,
  301. contains: [...EXPRESSION],
  302. };
  303. const VARIABLE = {
  304. variants: [
  305. {
  306. scope: 'carbon-var-declaration',
  307. begin: [/\bvar/, /\s+/],
  308. },
  309. {
  310. scope: 'carbon-let-declaration',
  311. begin: [/\blet/, /\s+/],
  312. },
  313. ],
  314. beginScope: {
  315. 1: 'keyword',
  316. },
  317. end: /;/,
  318. endScope: 'punctuation',
  319. contains: [PARENTHESIZED_PATTERN, BINDING_PATTERN, INITIALIZER],
  320. };
  321. // Parameters.
  322. const PARAMETER_LIST = {
  323. variants: [
  324. {
  325. scope: 'params',
  326. begin: /\(/,
  327. end: /\)/,
  328. },
  329. {
  330. scope: 'params',
  331. begin: /\[/,
  332. end: /\]/,
  333. },
  334. ],
  335. beginScope: 'punctuation',
  336. endScope: 'punctuation',
  337. contains: [INITIALIZER, ...PATTERN_SEQUENCE],
  338. };
  339. // Functions.
  340. const FUNCTION_NAME_COMPONENT = {
  341. begin: [/\s*/, /\.?/, hljs.UNDERSCORE_IDENT_RE],
  342. beginScope: {
  343. 2: 'punctuation',
  344. 3: 'title.function',
  345. },
  346. end: /\s*(\.|->|;|\{)/,
  347. returnEnd: true,
  348. contains: [PARAMETER_LIST],
  349. };
  350. const RETURNED_TYPE = {
  351. scope: 'carbon-return-type',
  352. end: /\s*[;{]/,
  353. returnEnd: true,
  354. contains: [...EXPRESSION],
  355. };
  356. const RETURN_SPECIFIER = {
  357. begin: [/\s*/, /->/, /\s*/],
  358. beginScope: {
  359. 2: 'punctuation',
  360. },
  361. starts: RETURNED_TYPE,
  362. };
  363. const FUNCTION = {
  364. scope: 'carbon-function',
  365. begin: [/(\b(virtual|abstract|impl)\b)?/, /\s*/, /\bfn\b/],
  366. beginScope: { 1: 'keyword', 3: 'keyword' },
  367. end: /\s*[;{]/,
  368. returnEnd: true,
  369. contains: [FUNCTION_NAME_COMPONENT, RETURN_SPECIFIER],
  370. };
  371. // Classes, interfaces, and constraints.
  372. const CLASS_NAME_COMPONENT = {
  373. begin: [/\s*/, /\.?/, /(?!extends\b)[a-zA-Z_]\w*/],
  374. beginScope: {
  375. 2: 'punctuation',
  376. 3: 'title.class',
  377. },
  378. end: /\s*(\.|extends|;|\{)/,
  379. returnEnd: true,
  380. contains: [PARAMETER_LIST],
  381. };
  382. const EXTENDED_TYPE = {
  383. scope: 'title.class.inherited',
  384. end: /\s*[;{]/,
  385. returnEnd: true,
  386. contains: [...EXPRESSION],
  387. };
  388. const EXTENDS_KEYWORD = {
  389. begin: [/extends/, /\s+/],
  390. beginScope: {
  391. 1: 'keyword',
  392. },
  393. starts: EXTENDED_TYPE,
  394. };
  395. const CLASS = {
  396. variants: [
  397. {
  398. scope: 'carbon-class',
  399. begin: [/(\b(base|abstract)\b)?/, /\s*/, /\bclass\b/],
  400. beginScope: {
  401. 1: 'keyword',
  402. 3: 'keyword',
  403. },
  404. },
  405. {
  406. scope: 'carbon-interface',
  407. beginKeywords: 'interface',
  408. },
  409. {
  410. scope: 'carbon-constraint',
  411. beginKeywords: 'constraint',
  412. },
  413. ],
  414. end: /\s*[;{]/,
  415. returnEnd: true,
  416. contains: [CLASS_NAME_COMPONENT, EXTENDS_KEYWORD],
  417. };
  418. // Statements -- very loosely. We bundle together top-level declaration
  419. // constructs, blocks, and statements.
  420. const STATEMENTS = [
  421. VARIABLE,
  422. FUNCTION,
  423. CLASS,
  424. {
  425. // We only match the assignment operator at the statement level.
  426. scope: 'operator',
  427. match: /=/,
  428. },
  429. {
  430. scope: 'punctuation',
  431. match: /;/,
  432. },
  433. ...EXPRESSION,
  434. ];
  435. const BLOCK = {
  436. scope: 'carbon-block',
  437. begin: /\{/,
  438. beginScope: 'punctuation',
  439. end: /\}/,
  440. endScope: 'punctuation',
  441. contains: ['self', hljs.C_LINE_COMMENT_MODE, ...STATEMENTS],
  442. };
  443. // Stitch everything together into the language definition.
  444. return {
  445. name: 'Carbon',
  446. aliases: ['carbon', 'carbon-lang'],
  447. keywords: KEYWORDS,
  448. illegal: '</',
  449. contains: [hljs.C_LINE_COMMENT_MODE, ...STATEMENTS, BLOCK],
  450. };
  451. }