highlightjs_carbon_lang.js 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467
  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. '_',
  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 SELF_PATTERN = {
  267. scope: 'carbon-self-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 = [
  279. SELF_PATTERN,
  280. BINDING_PATTERN,
  281. VALUE_PATTERN,
  282. ];
  283. const PARENTHESIZED_PATTERN = {
  284. scope: 'carbon-parenthesized-pattern',
  285. begin: /\(/,
  286. beginScope: 'punctuation',
  287. end: /\)/,
  288. endScope: 'punctuation',
  289. contains: ['self', COMMA_SEPARATOR, ...UNPARENTHESIZED_PATTERNS],
  290. };
  291. const PATTERN_SEQUENCE = [
  292. COMMA_SEPARATOR,
  293. PARENTHESIZED_PATTERN,
  294. ...UNPARENTHESIZED_PATTERNS,
  295. ];
  296. // Both variable and `let` value declarations.
  297. const INITIALIZER = {
  298. scope: 'carbon-initializer',
  299. begin: [/\s*/, /=/, /\s*/],
  300. beginScope: {
  301. 2: 'punctuation',
  302. },
  303. end: /[,;\)\]\}]/,
  304. returnEnd: true,
  305. contains: [...EXPRESSION],
  306. };
  307. const VARIABLE = {
  308. variants: [
  309. {
  310. scope: 'carbon-var-declaration',
  311. begin: [/\bvar/, /\s+/],
  312. },
  313. {
  314. scope: 'carbon-let-declaration',
  315. begin: [/\blet/, /\s+/],
  316. },
  317. ],
  318. beginScope: {
  319. 1: 'keyword',
  320. },
  321. end: /;/,
  322. endScope: 'punctuation',
  323. contains: [PARENTHESIZED_PATTERN, BINDING_PATTERN, INITIALIZER],
  324. };
  325. // Parameters.
  326. const PARAMETER_LIST = {
  327. variants: [
  328. {
  329. scope: 'params',
  330. begin: /\(/,
  331. end: /\)/,
  332. },
  333. {
  334. scope: 'params',
  335. begin: /\[/,
  336. end: /\]/,
  337. },
  338. ],
  339. beginScope: 'punctuation',
  340. endScope: 'punctuation',
  341. contains: [INITIALIZER, ...PATTERN_SEQUENCE],
  342. };
  343. // Functions.
  344. const FUNCTION_NAME_COMPONENT = {
  345. begin: [/\s*/, /\.?/, hljs.UNDERSCORE_IDENT_RE],
  346. beginScope: {
  347. 2: 'punctuation',
  348. 3: 'title.function',
  349. },
  350. end: /\s*(\.|->|;|\{)/,
  351. returnEnd: true,
  352. contains: [PARAMETER_LIST],
  353. };
  354. const RETURNED_TYPE = {
  355. scope: 'carbon-return-type',
  356. end: /\s*[;{]/,
  357. returnEnd: true,
  358. contains: [...EXPRESSION],
  359. };
  360. const RETURN_SPECIFIER = {
  361. begin: [/\s*/, /->/, /\s*/],
  362. beginScope: {
  363. 2: 'punctuation',
  364. },
  365. starts: RETURNED_TYPE,
  366. };
  367. const FUNCTION = {
  368. scope: 'carbon-function',
  369. begin: [/(\b(virtual|abstract|impl)\b)?/, /\s*/, /\bfn\b/],
  370. beginScope: { 1: 'keyword', 3: 'keyword' },
  371. end: /\s*[;{]/,
  372. returnEnd: true,
  373. contains: [FUNCTION_NAME_COMPONENT, RETURN_SPECIFIER],
  374. };
  375. // Classes, interfaces, and constraints.
  376. const CLASS_NAME_COMPONENT = {
  377. begin: [/\s*/, /\.?/, /(?!extends\b)[a-zA-Z_]\w*/],
  378. beginScope: {
  379. 2: 'punctuation',
  380. 3: 'title.class',
  381. },
  382. end: /\s*(\.|extends|;|\{)/,
  383. returnEnd: true,
  384. contains: [PARAMETER_LIST],
  385. };
  386. const EXTENDED_TYPE = {
  387. scope: 'title.class.inherited',
  388. end: /\s*[;{]/,
  389. returnEnd: true,
  390. contains: [...EXPRESSION],
  391. };
  392. const EXTENDS_KEYWORD = {
  393. begin: [/extends/, /\s+/],
  394. beginScope: {
  395. 1: 'keyword',
  396. },
  397. starts: EXTENDED_TYPE,
  398. };
  399. const CLASS = {
  400. variants: [
  401. {
  402. scope: 'carbon-class',
  403. begin: [/(\b(base|abstract)\b)?/, /\s*/, /\bclass\b/],
  404. beginScope: {
  405. 1: 'keyword',
  406. 3: 'keyword',
  407. },
  408. },
  409. {
  410. scope: 'carbon-interface',
  411. beginKeywords: 'interface',
  412. },
  413. {
  414. scope: 'carbon-constraint',
  415. beginKeywords: 'constraint',
  416. },
  417. ],
  418. end: /\s*[;{]/,
  419. returnEnd: true,
  420. contains: [CLASS_NAME_COMPONENT, EXTENDS_KEYWORD],
  421. };
  422. // Statements -- very loosely. We bundle together top-level declaration
  423. // constructs, blocks, and statements.
  424. const STATEMENTS = [
  425. VARIABLE,
  426. FUNCTION,
  427. CLASS,
  428. {
  429. // We only match the assignment operator at the statement level.
  430. scope: 'operator',
  431. match: /=/,
  432. },
  433. {
  434. scope: 'punctuation',
  435. match: /;/,
  436. },
  437. ...EXPRESSION,
  438. ];
  439. const BLOCK = {
  440. scope: 'carbon-block',
  441. begin: /\{/,
  442. beginScope: 'punctuation',
  443. end: /\}/,
  444. endScope: 'punctuation',
  445. contains: ['self', hljs.C_LINE_COMMENT_MODE, ...STATEMENTS],
  446. };
  447. // Stitch everything together into the language definition.
  448. return {
  449. name: 'Carbon',
  450. aliases: ['carbon', 'carbon-lang'],
  451. keywords: KEYWORDS,
  452. illegal: '</',
  453. contains: [hljs.C_LINE_COMMENT_MODE, ...STATEMENTS, BLOCK],
  454. };
  455. }