highlightjs_carbon_lang.js 11 KB

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