scanner.c 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530
  1. #include "tree_sitter/array.h"
  2. #include "tree_sitter/parser.h"
  3. #include <string.h>
  4. #include <wctype.h>
  5. // Mostly a copy paste of tree-sitter-javascript/src/scanner.c
  6. enum TokenType {
  7. AUTOMATIC_SEMICOLON,
  8. IMPORT_LIST_DELIMITER,
  9. SAFE_NAV,
  10. MULTILINE_COMMENT,
  11. STRING_START,
  12. STRING_END,
  13. STRING_CONTENT,
  14. };
  15. /* Pretty much all of this code is taken from the Julia tree-sitter
  16. parser.
  17. Julia has similar problems with multiline comments that can be nested,
  18. line comments, as well as line and multiline strings.
  19. The most heavily edited section is `scan_string_content`,
  20. particularly with respect to interpolation.
  21. */
  22. // Block comments are easy to parse, but strings require extra-attention.
  23. // The main problems that arise when parsing strings are:
  24. // 1. Triple quoted strings allow single quotes inside. e.g. """ "foo" """.
  25. // 2. Non-standard string literals don't allow interpolations or escape
  26. // sequences, but you can always write \" and \`.
  27. // To efficiently store a delimiter, we take advantage of the fact that:
  28. // (int)'"' == 34 && (34 & 1) == 0
  29. // i.e. " has an even numeric representation, so we can store a triple
  30. // quoted delimiter as (delimiter + 1).
  31. #define DELIMITER_LENGTH 3
  32. typedef char Delimiter;
  33. // We use a stack to keep track of the string delimiters.
  34. typedef Array(Delimiter) Stack;
  35. static inline void stack_push(Stack *stack, char chr, bool triple) {
  36. if (stack->size >= TREE_SITTER_SERIALIZATION_BUFFER_SIZE) abort();
  37. array_push(stack, (Delimiter)(triple ? (chr + 1) : chr));
  38. }
  39. static inline Delimiter stack_pop(Stack *stack) {
  40. if (stack->size == 0) abort();
  41. return array_pop(stack);
  42. }
  43. static inline void skip(TSLexer *lexer) { lexer->advance(lexer, true); }
  44. static inline void advance(TSLexer *lexer) { lexer->advance(lexer, false); }
  45. // Scanner functions
  46. static bool scan_string_start(TSLexer *lexer, Stack *stack) {
  47. if (lexer->lookahead != '"') return false;
  48. advance(lexer);
  49. lexer->mark_end(lexer);
  50. for (unsigned count = 1; count < DELIMITER_LENGTH; ++count) {
  51. if (lexer->lookahead != '"') {
  52. // It's not a triple quoted delimiter.
  53. stack_push(stack, '"', false);
  54. return true;
  55. }
  56. advance(lexer);
  57. }
  58. lexer->mark_end(lexer);
  59. stack_push(stack, '"', true);
  60. return true;
  61. }
  62. static bool scan_string_content(TSLexer *lexer, Stack *stack) {
  63. if (stack->size == 0) return false; // Stack is empty. We're not in a string.
  64. Delimiter end_char = stack->contents[stack->size - 1]; // peek
  65. bool is_triple = false;
  66. bool has_content = false;
  67. if (end_char & 1) {
  68. is_triple = true;
  69. end_char -= 1;
  70. }
  71. while (lexer->lookahead) {
  72. if (lexer->lookahead == '$') {
  73. // if we did not just start reading stuff, then we should stop
  74. // lexing right here, so we can offer the opportunity to lex a
  75. // interpolated identifier
  76. if (has_content) {
  77. lexer->result_symbol = STRING_CONTENT;
  78. return has_content;
  79. }
  80. // otherwise, if this is the start, determine if it is an
  81. // interpolated identifier.
  82. // otherwise, it's just string content, so continue
  83. advance(lexer);
  84. if (iswalpha(lexer->lookahead) || lexer->lookahead == '{') {
  85. // this must be a string interpolation, let's
  86. // fail so we parse it as such
  87. return false;
  88. }
  89. lexer->result_symbol = STRING_CONTENT;
  90. lexer->mark_end(lexer);
  91. return true;
  92. }
  93. if (lexer->lookahead == '\\') {
  94. // if we see a \, then this might possibly escape a dollar sign
  95. // in which case, we should not defer to the interpolation
  96. advance(lexer);
  97. // this dollar sign is escaped, so it must be content.
  98. // we consume it here so we don't enter the dollar sign case above,
  99. // which leaves the possibility that it is an interpolation
  100. if (lexer->lookahead == '$') {
  101. advance(lexer);
  102. // however this leaves an edgecase where an escaped dollar sign could
  103. // appear at the end of a string (e.g "aa\$") which isn't handled
  104. // correctly; if we were at the end of the string, terminate properly
  105. if (lexer->lookahead == end_char) {
  106. stack_pop(stack);
  107. advance(lexer);
  108. lexer->mark_end(lexer);
  109. lexer->result_symbol = STRING_END;
  110. return true;
  111. }
  112. }
  113. } else if (lexer->lookahead == end_char) {
  114. if (is_triple) {
  115. lexer->mark_end(lexer);
  116. for (unsigned count = 1; count < DELIMITER_LENGTH; ++count) {
  117. advance(lexer);
  118. if (lexer->lookahead != end_char) {
  119. lexer->mark_end(lexer);
  120. lexer->result_symbol = STRING_CONTENT;
  121. return true;
  122. }
  123. }
  124. /* This is so if we lex something like
  125. """foo"""
  126. ^
  127. where we are at the `f`, we should quit after
  128. reading `foo`, and ascribe it to STRING_CONTENT.
  129. Then, we restart and try to read the end.
  130. This is to prevent `foo` from being absorbed into
  131. the STRING_END token.
  132. */
  133. if (has_content && lexer->lookahead == end_char) {
  134. lexer->result_symbol = STRING_CONTENT;
  135. return true;
  136. }
  137. /* Since the string internals are all hidden in the syntax
  138. tree anyways, there's no point in going to the effort of
  139. specifically separating the string end from string contents.
  140. If we see a bunch of quotes in a row, then we just go until
  141. they stop appearing, then stop lexing and call it the
  142. string's end.
  143. */
  144. lexer->result_symbol = STRING_END;
  145. lexer->mark_end(lexer);
  146. while (lexer->lookahead == end_char) {
  147. advance(lexer);
  148. lexer->mark_end(lexer);
  149. }
  150. stack_pop(stack);
  151. return true;
  152. }
  153. if (has_content) {
  154. lexer->mark_end(lexer);
  155. lexer->result_symbol = STRING_CONTENT;
  156. return true;
  157. }
  158. stack_pop(stack);
  159. advance(lexer);
  160. lexer->mark_end(lexer);
  161. lexer->result_symbol = STRING_END;
  162. return true;
  163. }
  164. advance(lexer);
  165. has_content = true;
  166. }
  167. return false;
  168. }
  169. static bool scan_multiline_comment(TSLexer *lexer) {
  170. if (lexer->lookahead != '/') return false;
  171. advance(lexer);
  172. if (lexer->lookahead != '*') return false;
  173. advance(lexer);
  174. bool after_star = false;
  175. unsigned nesting_depth = 1;
  176. for (;;) {
  177. switch (lexer->lookahead) {
  178. case '*':
  179. advance(lexer);
  180. after_star = true;
  181. break;
  182. case '/':
  183. advance(lexer);
  184. if (after_star) {
  185. after_star = false;
  186. nesting_depth -= 1;
  187. if (nesting_depth == 0) {
  188. lexer->result_symbol = MULTILINE_COMMENT;
  189. lexer->mark_end(lexer);
  190. return true;
  191. }
  192. } else {
  193. after_star = false;
  194. if (lexer->lookahead == '*') {
  195. nesting_depth += 1;
  196. advance(lexer);
  197. }
  198. }
  199. break;
  200. case '\0':
  201. return false;
  202. default:
  203. advance(lexer);
  204. after_star = false;
  205. break;
  206. }
  207. }
  208. }
  209. static bool scan_whitespace_and_comments(TSLexer *lexer) {
  210. while (iswspace(lexer->lookahead)) skip(lexer);
  211. return lexer->lookahead != '/';
  212. }
  213. static bool scan_for_word(TSLexer *lexer, const char* word, unsigned len) {
  214. skip(lexer);
  215. for (unsigned i = 0; i < len; ++i) {
  216. if (lexer->lookahead != word[i]) return false;
  217. skip(lexer);
  218. }
  219. return true;
  220. }
  221. static bool scan_automatic_semicolon(TSLexer *lexer) {
  222. lexer->result_symbol = AUTOMATIC_SEMICOLON;
  223. lexer->mark_end(lexer);
  224. bool sameline = true;
  225. for (;;) {
  226. if (lexer->eof(lexer)) return true;
  227. if (lexer->lookahead == ';') {
  228. advance(lexer);
  229. lexer->mark_end(lexer);
  230. return true;
  231. }
  232. if (!iswspace(lexer->lookahead)) break;
  233. if (lexer->lookahead == '\n') {
  234. skip(lexer);
  235. sameline = false;
  236. break;
  237. }
  238. if (lexer->lookahead == '\r') {
  239. skip(lexer);
  240. if (lexer->lookahead == '\n') skip(lexer);
  241. sameline = false;
  242. break;
  243. }
  244. skip(lexer);
  245. }
  246. // Skip whitespace and comments
  247. if (!scan_whitespace_and_comments(lexer))
  248. return false;
  249. if (sameline) {
  250. switch (lexer->lookahead) {
  251. // Don't insert a semicolon before an else
  252. case 'e':
  253. return !scan_for_word(lexer, "lse", 3);
  254. case 'i':
  255. return scan_for_word(lexer, "mport", 5);
  256. case ';':
  257. advance(lexer);
  258. lexer->mark_end(lexer);
  259. return true;
  260. default:
  261. return false;
  262. }
  263. }
  264. switch (lexer->lookahead) {
  265. case ',':
  266. case '.':
  267. case ':':
  268. case '*':
  269. case '%':
  270. case '>':
  271. case '<':
  272. case '=':
  273. case '{':
  274. case '[':
  275. case '(':
  276. case '?':
  277. case '|':
  278. case '&':
  279. case '/':
  280. return false;
  281. // Insert a semicolon before `--` and `++`, but not before binary `+` or `-`.
  282. // Insert before +/-Float
  283. case '+':
  284. skip(lexer);
  285. if (lexer->lookahead == '+') return true;
  286. return iswdigit(lexer->lookahead);
  287. case '-':
  288. skip(lexer);
  289. if (lexer->lookahead == '-') return true;
  290. return iswdigit(lexer->lookahead);
  291. // Don't insert a semicolon before `!=`, but do insert one before a unary `!`.
  292. case '!':
  293. skip(lexer);
  294. return lexer->lookahead != '=';
  295. // Don't insert a semicolon before an else
  296. case 'e':
  297. return !scan_for_word(lexer, "lse", 3);
  298. // Don't insert a semicolon before `in` or `instanceof`, but do insert one
  299. // before an identifier or an import.
  300. case 'i':
  301. skip(lexer);
  302. if (lexer->lookahead != 'n') return true;
  303. skip(lexer);
  304. if (!iswalpha(lexer->lookahead)) return false;
  305. return !scan_for_word(lexer, "stanceof", 8);
  306. case ';':
  307. advance(lexer);
  308. lexer->mark_end(lexer);
  309. return true;
  310. default:
  311. return true;
  312. }
  313. }
  314. static bool scan_safe_nav(TSLexer *lexer) {
  315. lexer->result_symbol = SAFE_NAV;
  316. lexer->mark_end(lexer);
  317. // skip white space
  318. if (!scan_whitespace_and_comments(lexer))
  319. return false;
  320. if (lexer->lookahead != '?')
  321. return false;
  322. advance(lexer);
  323. if (!scan_whitespace_and_comments(lexer))
  324. return false;
  325. if (lexer->lookahead != '.')
  326. return false;
  327. advance(lexer);
  328. lexer->mark_end(lexer);
  329. return true;
  330. }
  331. static bool scan_line_sep(TSLexer *lexer) {
  332. // Line Seps: [ CR, LF, CRLF ]
  333. int state = 0;
  334. while (true) {
  335. switch(lexer->lookahead) {
  336. case ' ':
  337. case '\t':
  338. case '\v':
  339. // Skip whitespace
  340. advance(lexer);
  341. break;
  342. case '\n':
  343. advance(lexer);
  344. return true;
  345. case '\r':
  346. if (state == 1)
  347. return true;
  348. state = 1;
  349. advance(lexer);
  350. break;
  351. default:
  352. // We read a CR
  353. if (state == 1)
  354. return true;
  355. return false;
  356. }
  357. }
  358. }
  359. static bool scan_import_list_delimiter(TSLexer *lexer) {
  360. // Import lists are terminated either by an empty line or a non import statement
  361. lexer->result_symbol = IMPORT_LIST_DELIMITER;
  362. lexer->mark_end(lexer);
  363. // if eof; return true
  364. if (lexer->eof(lexer))
  365. return true;
  366. // Scan for the first line seperator
  367. if (!scan_line_sep(lexer))
  368. return false;
  369. // if line.sep line.sep; return true
  370. if (scan_line_sep(lexer)) {
  371. lexer->mark_end(lexer);
  372. return true;
  373. }
  374. // if line.sep [^import]; return true
  375. while (true) {
  376. switch (lexer->lookahead) {
  377. case ' ':
  378. case '\t':
  379. case '\v':
  380. // Skip whitespace
  381. advance(lexer);
  382. break;
  383. case 'i':
  384. return !scan_for_word(lexer, "mport", 5);
  385. default:
  386. return true;
  387. }
  388. return false;
  389. }
  390. }
  391. bool tree_sitter_kotlin_external_scanner_scan(void *payload, TSLexer *lexer, const bool *valid_symbols) {
  392. if (valid_symbols[AUTOMATIC_SEMICOLON]) {
  393. bool ret = scan_automatic_semicolon(lexer);
  394. if (!ret && valid_symbols[SAFE_NAV] && lexer->lookahead == '?') {
  395. return scan_safe_nav(lexer);
  396. }
  397. // if we fail to find an automatic semicolon, it's still possible that we may
  398. // want to lex a string or comment later
  399. if (ret) return ret;
  400. }
  401. if (valid_symbols[IMPORT_LIST_DELIMITER]) {
  402. return scan_import_list_delimiter(lexer);
  403. }
  404. // content or end
  405. if (valid_symbols[STRING_CONTENT] && scan_string_content(lexer, payload)) {
  406. return true;
  407. }
  408. // a string might follow after some whitespace, so we can't lookahead
  409. // until we get rid of it
  410. while (iswspace(lexer->lookahead)) skip(lexer);
  411. if (valid_symbols[STRING_START] && scan_string_start(lexer, payload)) {
  412. lexer->result_symbol = STRING_START;
  413. return true;
  414. }
  415. if (valid_symbols[MULTILINE_COMMENT] && scan_multiline_comment(lexer)) {
  416. return true;
  417. }
  418. if (valid_symbols[SAFE_NAV]) {
  419. return scan_safe_nav(lexer);
  420. }
  421. return false;
  422. }
  423. void *tree_sitter_kotlin_external_scanner_create() {
  424. Stack *stack = ts_calloc(1, sizeof(Stack));
  425. if (stack == NULL) abort();
  426. array_init(stack);
  427. return stack;
  428. }
  429. void tree_sitter_kotlin_external_scanner_destroy(void *payload) {
  430. Stack *stack = (Stack *)payload;
  431. array_delete(stack);
  432. ts_free(stack);
  433. }
  434. unsigned tree_sitter_kotlin_external_scanner_serialize(void *payload, char *buffer) {
  435. Stack *stack = (Stack *)payload;
  436. memcpy(buffer, stack->contents, stack->size);
  437. return stack->size;
  438. }
  439. void tree_sitter_kotlin_external_scanner_deserialize(void *payload, const char *buffer, unsigned length) {
  440. Stack *stack = (Stack *)payload;
  441. if (length > 0) {
  442. array_reserve(stack, length);
  443. memcpy(stack->contents, buffer, length);
  444. stack->size = length;
  445. } else {
  446. array_clear(stack);
  447. }
  448. }