xbintsc 0.2.9 → 0.3.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (311) hide show
  1. package/README.md +17 -2
  2. package/README.zh-CN.md +16 -2
  3. package/bin/xbintsc.js +49 -10
  4. package/dist/src/ast/expressions.d.ts +2 -0
  5. package/dist/src/binder/binder.d.ts +24 -98
  6. package/dist/src/binder/binder.js +13 -559
  7. package/dist/src/binder/binder.js.map +1 -1
  8. package/dist/src/binder/declarations.d.ts +16 -0
  9. package/dist/src/binder/declarations.js +168 -0
  10. package/dist/src/binder/declarations.js.map +1 -0
  11. package/dist/src/binder/helpers.d.ts +11 -0
  12. package/dist/src/binder/helpers.js +46 -0
  13. package/dist/src/binder/helpers.js.map +1 -0
  14. package/dist/src/binder/references.d.ts +21 -0
  15. package/dist/src/binder/references.js +349 -0
  16. package/dist/src/binder/references.js.map +1 -0
  17. package/dist/src/binder/types.d.ts +114 -0
  18. package/dist/src/binder/types.js +27 -0
  19. package/dist/src/binder/types.js.map +1 -0
  20. package/dist/src/cli/main.d.ts +1 -0
  21. package/dist/src/cli/main.js +40 -0
  22. package/dist/src/cli/main.js.map +1 -1
  23. package/dist/src/codegen/generator/calls/access.d.ts +13 -0
  24. package/dist/src/codegen/generator/calls/access.js +115 -0
  25. package/dist/src/codegen/generator/calls/access.js.map +1 -0
  26. package/dist/src/codegen/generator/calls/closures.d.ts +11 -0
  27. package/dist/src/codegen/generator/calls/closures.js +50 -0
  28. package/dist/src/codegen/generator/calls/closures.js.map +1 -0
  29. package/dist/src/codegen/generator/calls/invocation.d.ts +18 -0
  30. package/dist/src/codegen/generator/calls/invocation.js +242 -0
  31. package/dist/src/codegen/generator/calls/invocation.js.map +1 -0
  32. package/dist/src/codegen/generator/calls/literals.d.ts +10 -0
  33. package/dist/src/codegen/generator/calls/literals.js +69 -0
  34. package/dist/src/codegen/generator/calls/literals.js.map +1 -0
  35. package/dist/src/codegen/generator/calls/super.d.ts +10 -0
  36. package/dist/src/codegen/generator/calls/super.js +47 -0
  37. package/dist/src/codegen/generator/calls/super.js.map +1 -0
  38. package/dist/src/codegen/generator/calls.d.ts +15 -24
  39. package/dist/src/codegen/generator/calls.js +15 -482
  40. package/dist/src/codegen/generator/calls.js.map +1 -1
  41. package/dist/src/codegen/generator/context.d.ts +1 -1
  42. package/dist/src/codegen/generator/context.js +2 -2
  43. package/dist/src/codegen/generator/context.js.map +1 -1
  44. package/dist/src/codegen/generator/expressions/assignment.d.ts +13 -0
  45. package/dist/src/codegen/generator/expressions/assignment.js +203 -0
  46. package/dist/src/codegen/generator/expressions/assignment.js.map +1 -0
  47. package/dist/src/codegen/generator/expressions/operators.d.ts +14 -0
  48. package/dist/src/codegen/generator/expressions/operators.js +163 -0
  49. package/dist/src/codegen/generator/expressions/operators.js.map +1 -0
  50. package/dist/src/codegen/generator/expressions/primary.d.ts +17 -0
  51. package/dist/src/codegen/generator/expressions/primary.js +260 -0
  52. package/dist/src/codegen/generator/expressions/primary.js.map +1 -0
  53. package/dist/src/codegen/generator/expressions.d.ts +11 -21
  54. package/dist/src/codegen/generator/expressions.js +11 -494
  55. package/dist/src/codegen/generator/expressions.js.map +1 -1
  56. package/dist/src/codegen/generator/module.js +16 -4
  57. package/dist/src/codegen/generator/module.js.map +1 -1
  58. package/dist/src/codegen/generator/state.js +8 -0
  59. package/dist/src/codegen/generator/state.js.map +1 -1
  60. package/dist/src/codegen/generator/statements/control-flow.d.ts +14 -0
  61. package/dist/src/codegen/generator/statements/control-flow.js +199 -0
  62. package/dist/src/codegen/generator/statements/control-flow.js.map +1 -0
  63. package/dist/src/codegen/generator/statements/dispatch.d.ts +12 -0
  64. package/dist/src/codegen/generator/statements/dispatch.js +100 -0
  65. package/dist/src/codegen/generator/statements/dispatch.js.map +1 -0
  66. package/dist/src/codegen/generator/statements/loops.d.ts +13 -0
  67. package/dist/src/codegen/generator/statements/loops.js +152 -0
  68. package/dist/src/codegen/generator/statements/loops.js.map +1 -0
  69. package/dist/src/codegen/generator/statements/variables.d.ts +13 -0
  70. package/dist/src/codegen/generator/statements/variables.js +146 -0
  71. package/dist/src/codegen/generator/statements/variables.js.map +1 -0
  72. package/dist/src/codegen/generator/statements.d.ts +13 -21
  73. package/dist/src/codegen/generator/statements.js +13 -568
  74. package/dist/src/codegen/generator/statements.js.map +1 -1
  75. package/dist/src/codegen/generator/tables.js +7 -0
  76. package/dist/src/codegen/generator/tables.js.map +1 -1
  77. package/dist/src/codegen/values.d.ts +17 -17
  78. package/dist/src/codegen/values.js +21 -57
  79. package/dist/src/codegen/values.js.map +1 -1
  80. package/dist/src/diagnostics/diagnostic.d.ts +1 -0
  81. package/dist/src/diagnostics/diagnostic.js +1 -0
  82. package/dist/src/diagnostics/diagnostic.js.map +1 -1
  83. package/dist/src/diagnostics/source.d.ts +1 -1
  84. package/dist/src/diagnostics/source.js +7 -12
  85. package/dist/src/diagnostics/source.js.map +1 -1
  86. package/dist/src/driver/compiler.d.ts +4 -0
  87. package/dist/src/driver/compiler.js +51 -16
  88. package/dist/src/driver/compiler.js.map +1 -1
  89. package/dist/src/driver/index.d.ts +5 -2
  90. package/dist/src/driver/index.js +5 -2
  91. package/dist/src/driver/index.js.map +1 -1
  92. package/dist/src/driver/paths.d.ts +20 -0
  93. package/dist/src/driver/paths.js +49 -0
  94. package/dist/src/driver/paths.js.map +1 -1
  95. package/dist/src/driver/runtime-lib.d.ts +18 -0
  96. package/dist/src/driver/runtime-lib.js +31 -0
  97. package/dist/src/driver/runtime-lib.js.map +1 -0
  98. package/dist/src/driver/toolchain-download.d.ts +50 -0
  99. package/dist/src/driver/toolchain-download.js +76 -0
  100. package/dist/src/driver/toolchain-download.js.map +1 -0
  101. package/dist/src/driver/toolchain-provider.d.ts +33 -0
  102. package/dist/src/driver/toolchain-provider.js +71 -0
  103. package/dist/src/driver/toolchain-provider.js.map +1 -0
  104. package/dist/src/driver/toolchain.d.ts +9 -4
  105. package/dist/src/driver/toolchain.js +6 -10
  106. package/dist/src/driver/toolchain.js.map +1 -1
  107. package/dist/src/extensions/node/child_process/index.d.ts +4 -2
  108. package/dist/src/extensions/node/child_process/index.js +4 -2
  109. package/dist/src/extensions/node/child_process/index.js.map +1 -1
  110. package/dist/src/extensions/node/events/index.d.ts +10 -0
  111. package/dist/src/extensions/node/events/index.js +22 -0
  112. package/dist/src/extensions/node/events/index.js.map +1 -0
  113. package/dist/src/extensions/node/index.js +6 -0
  114. package/dist/src/extensions/node/index.js.map +1 -1
  115. package/dist/src/extensions/node/querystring/index.d.ts +10 -0
  116. package/dist/src/extensions/node/querystring/index.js +25 -0
  117. package/dist/src/extensions/node/querystring/index.js.map +1 -0
  118. package/dist/src/extensions/node/util/index.d.ts +11 -0
  119. package/dist/src/extensions/node/util/index.js +41 -0
  120. package/dist/src/extensions/node/util/index.js.map +1 -0
  121. package/dist/src/extensions/registry.d.ts +6 -0
  122. package/dist/src/extensions/registry.js.map +1 -1
  123. package/dist/src/index.d.ts +1 -1
  124. package/dist/src/index.js +1 -1
  125. package/dist/src/index.js.map +1 -1
  126. package/dist/src/lexer/scanner/identifiers.d.ts +9 -0
  127. package/dist/src/lexer/scanner/identifiers.js +33 -0
  128. package/dist/src/lexer/scanner/identifiers.js.map +1 -0
  129. package/dist/src/lexer/scanner/numbers.d.ts +10 -0
  130. package/dist/src/lexer/scanner/numbers.js +89 -0
  131. package/dist/src/lexer/scanner/numbers.js.map +1 -0
  132. package/dist/src/lexer/scanner/punctuation.d.ts +9 -0
  133. package/dist/src/lexer/scanner/punctuation.js +151 -0
  134. package/dist/src/lexer/scanner/punctuation.js.map +1 -0
  135. package/dist/src/lexer/scanner/regex.d.ts +11 -0
  136. package/dist/src/lexer/scanner/regex.js +69 -0
  137. package/dist/src/lexer/scanner/regex.js.map +1 -0
  138. package/dist/src/lexer/scanner/strings.d.ts +13 -0
  139. package/dist/src/lexer/scanner/strings.js +118 -0
  140. package/dist/src/lexer/scanner/strings.js.map +1 -0
  141. package/dist/src/lexer/scanner.d.ts +20 -27
  142. package/dist/src/lexer/scanner.js +11 -415
  143. package/dist/src/lexer/scanner.js.map +1 -1
  144. package/dist/src/parser/declarations/classes.d.ts +14 -0
  145. package/dist/src/parser/declarations/classes.js +191 -0
  146. package/dist/src/parser/declarations/classes.js.map +1 -0
  147. package/dist/src/parser/declarations/functions.d.ts +17 -0
  148. package/dist/src/parser/declarations/functions.js +241 -0
  149. package/dist/src/parser/declarations/functions.js.map +1 -0
  150. package/dist/src/parser/declarations/type-members.d.ts +16 -0
  151. package/dist/src/parser/declarations/type-members.js +156 -0
  152. package/dist/src/parser/declarations/type-members.js.map +1 -0
  153. package/dist/src/parser/declarations.d.ts +13 -24
  154. package/dist/src/parser/declarations.js +13 -571
  155. package/dist/src/parser/declarations.js.map +1 -1
  156. package/dist/src/parser/helpers.js +13 -4
  157. package/dist/src/parser/helpers.js.map +1 -1
  158. package/dist/src/parser/literals.js +10 -1
  159. package/dist/src/parser/literals.js.map +1 -1
  160. package/dist/tests/codegen/llvm.test.js +24 -0
  161. package/dist/tests/codegen/llvm.test.js.map +1 -1
  162. package/dist/tests/driver/compiler.test.js +3 -0
  163. package/dist/tests/driver/compiler.test.js.map +1 -1
  164. package/dist/tests/driver/paths.test.d.ts +1 -0
  165. package/dist/tests/driver/paths.test.js +26 -0
  166. package/dist/tests/driver/paths.test.js.map +1 -0
  167. package/dist/tests/driver/toolchain-download.test.d.ts +1 -0
  168. package/dist/tests/driver/toolchain-download.test.js +38 -0
  169. package/dist/tests/driver/toolchain-download.test.js.map +1 -0
  170. package/dist/tests/driver/toolchain-provider.test.d.ts +1 -0
  171. package/dist/tests/driver/toolchain-provider.test.js +37 -0
  172. package/dist/tests/driver/toolchain-provider.test.js.map +1 -0
  173. package/dist/tests/e2e/compile-node.test.d.ts +7 -0
  174. package/dist/tests/e2e/compile-node.test.js +123 -0
  175. package/dist/tests/e2e/compile-node.test.js.map +1 -0
  176. package/dist/tests/e2e/compile.test.d.ts +4 -1
  177. package/dist/tests/e2e/compile.test.js +78 -130
  178. package/dist/tests/e2e/compile.test.js.map +1 -1
  179. package/dist/tests/e2e/harness.d.ts +36 -0
  180. package/dist/tests/e2e/harness.js +64 -0
  181. package/dist/tests/e2e/harness.js.map +1 -0
  182. package/dist/tests/e2e/node-modules.test.js +60 -0
  183. package/dist/tests/e2e/node-modules.test.js.map +1 -1
  184. package/dist/tests/extensions/registry.test.js +4 -0
  185. package/dist/tests/extensions/registry.test.js.map +1 -1
  186. package/package.json +5 -3
  187. package/runtime/ext_node/buffer/buffer.c +12 -552
  188. package/runtime/ext_node/buffer/parts/byte-access.inc +35 -0
  189. package/runtime/ext_node/buffer/parts/construction.inc +60 -0
  190. package/runtime/ext_node/buffer/parts/integer-io.inc +119 -0
  191. package/runtime/ext_node/buffer/parts/methods.inc +135 -0
  192. package/runtime/ext_node/buffer/parts/prototype.inc +54 -0
  193. package/runtime/ext_node/buffer/parts/search.inc +86 -0
  194. package/runtime/ext_node/buffer/parts/static-api.inc +58 -0
  195. package/runtime/ext_node/child_process/child_process.c +37 -3
  196. package/runtime/ext_node/events/events.c +285 -0
  197. package/runtime/ext_node/http/http.c +11 -736
  198. package/runtime/ext_node/http/parts/client.inc +290 -0
  199. package/runtime/ext_node/http/parts/helpers.inc +45 -0
  200. package/runtime/ext_node/http/parts/prototypes.inc +57 -0
  201. package/runtime/ext_node/http/parts/response.inc +149 -0
  202. package/runtime/ext_node/http/parts/server.inc +133 -0
  203. package/runtime/ext_node/http/parts/string-builder.inc +58 -0
  204. package/runtime/ext_node/net/net.c +12 -504
  205. package/runtime/ext_node/net/parts/construction.inc +22 -0
  206. package/runtime/ext_node/net/parts/platform.inc +60 -0
  207. package/runtime/ext_node/net/parts/prototypes.inc +35 -0
  208. package/runtime/ext_node/net/parts/read-loop.inc +58 -0
  209. package/runtime/ext_node/net/parts/server.inc +131 -0
  210. package/runtime/ext_node/net/parts/socket-methods.inc +100 -0
  211. package/runtime/ext_node/net/parts/static-api.inc +93 -0
  212. package/runtime/ext_node/querystring/querystring.c +251 -0
  213. package/runtime/ext_node/util/util.c +456 -0
  214. package/runtime/rt.h +8 -2
  215. package/runtime/rt_internal.h +34 -0
  216. package/runtime/xt_bigint/arithmetic.inc +135 -0
  217. package/runtime/xt_bigint/bits.inc +98 -0
  218. package/runtime/xt_bigint/bitwise.inc +103 -0
  219. package/runtime/xt_bigint/compare.inc +30 -0
  220. package/runtime/xt_bigint/conversions.inc +51 -0
  221. package/runtime/xt_bigint/division.inc +85 -0
  222. package/runtime/xt_bigint/format.inc +145 -0
  223. package/runtime/xt_bigint/helpers.inc +92 -0
  224. package/runtime/xt_bigint/magnitude.inc +88 -0
  225. package/runtime/xt_bigint/public.inc +59 -0
  226. package/runtime/xt_bigint/small-arithmetic.inc +58 -0
  227. package/runtime/xt_bigint.c +42 -0
  228. package/runtime/xt_builtins.c +1 -0
  229. package/runtime/xt_io.c +7 -0
  230. package/runtime/xt_stdlib/arrays.inc +161 -0
  231. package/runtime/xt_stdlib/dispatch.inc +116 -0
  232. package/runtime/xt_stdlib/helpers.inc +23 -0
  233. package/runtime/xt_stdlib/math.inc +72 -0
  234. package/runtime/xt_stdlib/strings.inc +178 -0
  235. package/runtime/xt_stdlib.c +10 -549
  236. package/runtime/xt_stdlib2/array-statics.inc +37 -0
  237. package/runtime/xt_stdlib2/arrays.inc +189 -0
  238. package/runtime/xt_stdlib2/container.inc +23 -0
  239. package/runtime/xt_stdlib2/date.inc +72 -0
  240. package/runtime/xt_stdlib2/error.inc +22 -0
  241. package/runtime/xt_stdlib2/helpers.inc +43 -0
  242. package/runtime/xt_stdlib2/json.inc +213 -0
  243. package/runtime/xt_stdlib2/map.inc +95 -0
  244. package/runtime/xt_stdlib2/number-string-statics.inc +73 -0
  245. package/runtime/xt_stdlib2/numbers.inc +38 -0
  246. package/runtime/xt_stdlib2/objects.inc +41 -0
  247. package/runtime/xt_stdlib2/regexp-match.inc +204 -0
  248. package/runtime/xt_stdlib2/regexp.inc +20 -0
  249. package/runtime/xt_stdlib2/set.inc +105 -0
  250. package/runtime/xt_stdlib2/strings.inc +94 -0
  251. package/runtime/xt_stdlib2.c +19 -1278
  252. package/runtime/xt_values.c +155 -12
  253. package/scripts/build-runtime.ts +109 -0
  254. package/scripts/fetch-toolchain.ts +101 -0
  255. package/scripts/package-release.ts +192 -0
  256. package/src/ast/expressions.ts +2 -0
  257. package/src/binder/binder.ts +40 -725
  258. package/src/binder/declarations.ts +203 -0
  259. package/src/binder/helpers.ts +47 -0
  260. package/src/binder/references.ts +410 -0
  261. package/src/binder/types.ts +134 -0
  262. package/src/cli/main.ts +43 -0
  263. package/src/codegen/generator/calls/access.ts +139 -0
  264. package/src/codegen/generator/calls/closures.ts +60 -0
  265. package/src/codegen/generator/calls/invocation.ts +282 -0
  266. package/src/codegen/generator/calls/literals.ts +77 -0
  267. package/src/codegen/generator/calls/super.ts +61 -0
  268. package/src/codegen/generator/calls.ts +30 -552
  269. package/src/codegen/generator/context.ts +2 -2
  270. package/src/codegen/generator/expressions/assignment.ts +226 -0
  271. package/src/codegen/generator/expressions/operators.ts +182 -0
  272. package/src/codegen/generator/expressions/primary.ts +304 -0
  273. package/src/codegen/generator/expressions.ts +22 -553
  274. package/src/codegen/generator/module.ts +14 -4
  275. package/src/codegen/generator/state.ts +8 -0
  276. package/src/codegen/generator/statements/control-flow.ts +225 -0
  277. package/src/codegen/generator/statements/dispatch.ts +122 -0
  278. package/src/codegen/generator/statements/loops.ts +173 -0
  279. package/src/codegen/generator/statements/variables.ts +152 -0
  280. package/src/codegen/generator/statements.ts +26 -612
  281. package/src/codegen/generator/tables.ts +7 -0
  282. package/src/codegen/values.ts +22 -57
  283. package/src/diagnostics/diagnostic.ts +1 -0
  284. package/src/diagnostics/source.ts +7 -12
  285. package/src/driver/compiler.ts +61 -18
  286. package/src/driver/index.ts +17 -2
  287. package/src/driver/paths.ts +54 -0
  288. package/src/driver/runtime-lib.ts +32 -0
  289. package/src/driver/toolchain-download.ts +108 -0
  290. package/src/driver/toolchain-provider.ts +91 -0
  291. package/src/driver/toolchain.ts +29 -11
  292. package/src/extensions/node/child_process/index.ts +4 -2
  293. package/src/extensions/node/events/index.ts +27 -0
  294. package/src/extensions/node/index.ts +6 -0
  295. package/src/extensions/node/querystring/index.ts +29 -0
  296. package/src/extensions/node/util/index.ts +48 -0
  297. package/src/extensions/registry.ts +6 -0
  298. package/src/index.ts +1 -0
  299. package/src/lexer/scanner/identifiers.ts +37 -0
  300. package/src/lexer/scanner/numbers.ts +84 -0
  301. package/src/lexer/scanner/punctuation.ts +132 -0
  302. package/src/lexer/scanner/regex.ts +79 -0
  303. package/src/lexer/scanner/strings.ts +128 -0
  304. package/src/lexer/scanner.ts +35 -401
  305. package/src/parser/declarations/classes.ts +215 -0
  306. package/src/parser/declarations/functions.ts +254 -0
  307. package/src/parser/declarations/type-members.ts +181 -0
  308. package/src/parser/declarations.ts +32 -609
  309. package/src/parser/helpers.ts +13 -6
  310. package/src/parser/literals.ts +10 -1
  311. package/scripts/build-runtime.mjs +0 -55
@@ -0,0 +1,128 @@
1
+ /**
2
+ * String and template literal scanning, including escape sequences and the
3
+ * template continuation protocol used by the parser.
4
+ */
5
+
6
+ import { DiagnosticCode } from "../../diagnostics/diagnostic.js";
7
+ import { Token, TokenKind } from "../token.js";
8
+ import { Char, isDigit, isLineBreak } from "../char.js";
9
+ import type { Scanner } from "../scanner.js";
10
+
11
+ export interface StringMethods {
12
+ scanString(this: Scanner, start: number, precededByLineBreak: boolean, quote: number): Token;
13
+ scanEscapeSequence(this: Scanner): string;
14
+ scanTemplate(this: Scanner, start: number, precededByLineBreak: boolean, isHead: boolean): Token;
15
+ continueTemplate(this: Scanner, offset: number): Token;
16
+ }
17
+
18
+ export const stringMethods: StringMethods = {
19
+ scanString(this: Scanner, start: number, precededByLineBreak: boolean, quote: number): Token {
20
+ this.pos++;
21
+ let result = "";
22
+ while (this.pos < this.text.length) {
23
+ const ch = this.text.charCodeAt(this.pos);
24
+ if (ch === quote) {
25
+ this.pos++;
26
+ return this.makeToken(TokenKind.StringLiteral, start, this.pos, this.text.slice(start, this.pos), precededByLineBreak, result);
27
+ }
28
+ if (ch === Char.Backslash) {
29
+ result += this.scanEscapeSequence();
30
+ continue;
31
+ }
32
+ if (isLineBreak(ch)) {
33
+ this.error(DiagnosticCode.UnterminatedString, "Unterminated string literal", start);
34
+ break;
35
+ }
36
+ result += this.text[this.pos];
37
+ this.pos++;
38
+ }
39
+ this.error(DiagnosticCode.UnterminatedString, "Unterminated string literal", start);
40
+ return this.makeToken(TokenKind.StringLiteral, start, this.pos, this.text.slice(start, this.pos), precededByLineBreak, result);
41
+ },
42
+
43
+ scanEscapeSequence(this: Scanner): string {
44
+ this.pos++; // backslash
45
+ const ch = this.text.charCodeAt(this.pos);
46
+ switch (ch) {
47
+ case Char.LowerN:
48
+ this.pos++;
49
+ return "\n";
50
+ case Char.LowerT:
51
+ this.pos++;
52
+ return "\t";
53
+ case Char.LowerR:
54
+ this.pos++;
55
+ return "\r";
56
+ case Char.LowerB:
57
+ this.pos++;
58
+ return "\b";
59
+ case Char.LowerF:
60
+ this.pos++;
61
+ return "\f";
62
+ case Char.LowerU:
63
+ this.pos++;
64
+ if (this.text.charCodeAt(this.pos) === Char.OpenBrace) {
65
+ const close = this.text.indexOf("}", this.pos);
66
+ const hex = this.text.slice(this.pos + 1, close);
67
+ this.pos = close + 1;
68
+ return String.fromCodePoint(parseInt(hex, 16) || 0);
69
+ } else {
70
+ const hex = this.text.slice(this.pos, this.pos + 4);
71
+ this.pos += 4;
72
+ return String.fromCharCode(parseInt(hex, 16) || 0);
73
+ }
74
+ case Char.LowerX: {
75
+ const hex = this.text.slice(this.pos + 1, this.pos + 3);
76
+ this.pos += 3;
77
+ return String.fromCharCode(parseInt(hex, 16) || 0);
78
+ }
79
+ case Char.Zero:
80
+ // \0 is only a null escape when not followed by a digit.
81
+ if (!isDigit(this.text.charCodeAt(this.pos + 1))) {
82
+ this.pos++;
83
+ return "\0";
84
+ }
85
+ this.pos++;
86
+ return "\0";
87
+ default:
88
+ this.pos++;
89
+ return this.text[this.pos - 1] ?? "";
90
+ }
91
+ },
92
+
93
+ /** Scans a template literal chunk starting at a backtick or after `}`. */
94
+ scanTemplate(this: Scanner, start: number, precededByLineBreak: boolean, isHead: boolean): Token {
95
+ if (isHead) this.pos++; // opening backtick
96
+ let result = "";
97
+ for (;;) {
98
+ if (this.pos >= this.text.length) {
99
+ this.error(DiagnosticCode.UnterminatedTemplate, "Unterminated template literal", start);
100
+ return this.makeToken(TokenKind.NoSubstitutionTemplateLiteral, start, this.pos, this.text.slice(start, this.pos), precededByLineBreak, result);
101
+ }
102
+ const ch = this.text.charCodeAt(this.pos);
103
+ if (ch === Char.Backtick) {
104
+ this.pos++;
105
+ return this.makeToken(TokenKind.NoSubstitutionTemplateLiteral, start, this.pos, this.text.slice(start, this.pos), precededByLineBreak, result);
106
+ }
107
+ if (ch === Char.Dollar && this.text.charCodeAt(this.pos + 1) === Char.OpenBrace) {
108
+ this.pos += 2;
109
+ return this.makeToken(TokenKind.TemplateHead, start, this.pos, this.text.slice(start, this.pos), precededByLineBreak, result);
110
+ }
111
+ if (ch === Char.Backslash) {
112
+ result += this.scanEscapeSequence();
113
+ continue;
114
+ }
115
+ result += this.text[this.pos];
116
+ this.pos++;
117
+ }
118
+ },
119
+
120
+ /** Called by the parser after a template substitution expression. */
121
+ continueTemplate(this: Scanner, offset: number): Token {
122
+ // `offset` points at the `}` that closes the substitution; skip it so the
123
+ // following characters are scanned as template text.
124
+ this.pos = Math.max(0, Math.min(offset + 1, this.text.length));
125
+ this._token = this.scanTemplate(this.pos, false, /* isHead */ false);
126
+ return this._token;
127
+ },
128
+ };
@@ -1,28 +1,28 @@
1
1
  import { DiagnosticBag, DiagnosticCode } from "../diagnostics/diagnostic.js";
2
2
  import type { SourceFile } from "../diagnostics/source.js";
3
- import { KEYWORDS, Token, TokenKind } from "./token.js";
4
- import {
5
- Char,
6
- isDigit,
7
- isHexDigit,
8
- isIdentifierPart,
9
- isIdentifierStart,
10
- isLineBreak,
11
- isWhiteSpace,
12
- } from "./char.js";
3
+ import { Token, TokenKind } from "./token.js";
4
+ import { Char, isDigit, isIdentifierStart, isLineBreak, isWhiteSpace } from "./char.js";
5
+ import { identifierMethods, type IdentifierMethods } from "./scanner/identifiers.js";
6
+ import { numberMethods, type NumberMethods } from "./scanner/numbers.js";
7
+ import { stringMethods, type StringMethods } from "./scanner/strings.js";
8
+ import { regexMethods, type RegexMethods } from "./scanner/regex.js";
9
+ import { punctuationMethods, type PunctuationMethods } from "./scanner/punctuation.js";
13
10
 
14
11
  /**
15
12
  * Hand-written scanner for the TypeScript language. It produces a flat token
16
13
  * stream consumed by the parser. Comments and trivia are skipped, but line
17
14
  * breaks are tracked so the parser can implement ASI (automatic semicolon
18
15
  * insertion) correctly.
16
+ *
17
+ * The scanning routines are split by token category under `scanner/` and
18
+ * merged onto the prototype below so each group can share the scanner state.
19
19
  */
20
20
  export class Scanner {
21
- private readonly text: string;
22
- private pos = 0;
23
- private readonly diagnostics: DiagnosticBag;
24
- private readonly source: SourceFile;
25
- private _token: Token = {
21
+ text: string;
22
+ pos = 0;
23
+ readonly diagnostics: DiagnosticBag;
24
+ readonly source: SourceFile;
25
+ _token: Token = {
26
26
  kind: TokenKind.EndOfFile,
27
27
  start: 0,
28
28
  end: 0,
@@ -69,11 +69,11 @@ export class Scanner {
69
69
  return tokens;
70
70
  }
71
71
 
72
- private error(code: DiagnosticCode, message: string, start: number, end = this.pos): void {
72
+ error(code: DiagnosticCode, message: string, start: number, end = this.pos): void {
73
73
  this.diagnostics.error(code, message, { start, end }, this.source.fileName);
74
74
  }
75
75
 
76
- private scan(): Token {
76
+ scan(): Token {
77
77
  let precededByLineBreak = false;
78
78
  // Skip trivia.
79
79
  for (;;) {
@@ -156,7 +156,7 @@ export class Scanner {
156
156
  return this.scanPunctuation(start, precededByLineBreak);
157
157
  }
158
158
 
159
- private makeToken(
159
+ makeToken(
160
160
  kind: TokenKind,
161
161
  start: number,
162
162
  end: number,
@@ -166,387 +166,21 @@ export class Scanner {
166
166
  ): Token {
167
167
  return { kind, start, end, text, value, precededByLineBreak };
168
168
  }
169
-
170
- private scanIdentifier(start: number, precededByLineBreak: boolean): Token {
171
- this.pos++;
172
- while (this.pos < this.text.length) {
173
- const ch = this.text.charCodeAt(this.pos);
174
- if (isIdentifierPart(ch)) {
175
- this.pos++;
176
- } else if (ch === Char.Backslash && this.text.charCodeAt(this.pos + 1) === Char.LowerU) {
177
- this.pos += 2;
178
- const hex = this.text.slice(this.pos, this.pos + 4);
179
- this.pos += 4;
180
- if (!/^[0-9a-fA-F]{4}$/.test(hex)) {
181
- this.error(DiagnosticCode.InvalidEscape, "Invalid unicode escape in identifier", this.pos - 4, this.pos);
182
- }
183
- } else {
184
- break;
185
- }
186
- }
187
- const text = this.text.slice(start, this.pos);
188
- const keyword = KEYWORDS.get(text);
189
- const kind = keyword ?? TokenKind.Identifier;
190
- return this.makeToken(kind, start, this.pos, text, precededByLineBreak);
191
- }
192
-
193
- private scanNumber(start: number, precededByLineBreak: boolean): Token {
194
- const text = this.text;
195
- let isBigInt = false;
196
- // Radix prefixes.
197
- if (text.charCodeAt(this.pos) === Char.Zero) {
198
- const next = text.charCodeAt(this.pos + 1);
199
- if (next === Char.LowerX || next === Char.UpperX) {
200
- this.pos += 2;
201
- while (isHexDigit(text.charCodeAt(this.pos)) || text.charCodeAt(this.pos) === Char.Underscore) this.pos++;
202
- isBigInt = text.charCodeAt(this.pos) === Char.LowerN;
203
- if (isBigInt) this.pos++;
204
- return this.finishNumber(start, precededByLineBreak, isBigInt);
205
- }
206
- if (next === Char.LowerO || next === Char.UpperO || next === Char.LowerB || next === Char.UpperB) {
207
- this.pos += 2;
208
- while (isDigit(text.charCodeAt(this.pos)) || text.charCodeAt(this.pos) === Char.Underscore) this.pos++;
209
- isBigInt = text.charCodeAt(this.pos) === Char.LowerN;
210
- if (isBigInt) this.pos++;
211
- return this.finishNumber(start, precededByLineBreak, isBigInt);
212
- }
213
- }
214
-
215
- while (isDigit(text.charCodeAt(this.pos)) || text.charCodeAt(this.pos) === Char.Underscore) this.pos++;
216
- if (text.charCodeAt(this.pos) === Char.Dot) {
217
- this.pos++;
218
- while (isDigit(text.charCodeAt(this.pos)) || text.charCodeAt(this.pos) === Char.Underscore) this.pos++;
219
- }
220
- const e = text.charCodeAt(this.pos);
221
- if (e === Char.LowerE || e === Char.UpperE) {
222
- const save = this.pos;
223
- this.pos++;
224
- const sign = text.charCodeAt(this.pos);
225
- if (sign === Char.Plus || sign === Char.Minus) this.pos++;
226
- if (isDigit(text.charCodeAt(this.pos))) {
227
- while (isDigit(text.charCodeAt(this.pos)) || text.charCodeAt(this.pos) === Char.Underscore) this.pos++;
228
- } else {
229
- // Not an exponent, roll back.
230
- this.pos = save;
231
- }
232
- }
233
- isBigInt = text.charCodeAt(this.pos) === Char.LowerN;
234
- if (isBigInt) this.pos++;
235
- return this.finishNumber(start, precededByLineBreak, isBigInt);
236
- }
237
-
238
- private finishNumber(start: number, precededByLineBreak: boolean, isBigInt: boolean): Token {
239
- let raw = this.text.slice(start, this.pos);
240
- const cleaned = raw.replace(/_/g, "");
241
- if (isBigInt) {
242
- raw = raw.slice(0, -1).replace(/_/g, "");
243
- const parsed = Number(raw);
244
- if (parsed !== parsed) {
245
- this.error(DiagnosticCode.InvalidNumber, `Invalid BigInt literal '${raw}'`, start, this.pos);
246
- return this.makeToken(TokenKind.BigIntLiteral, start, this.pos, raw, precededByLineBreak, 0);
247
- }
248
- return this.makeToken(TokenKind.BigIntLiteral, start, this.pos, raw, precededByLineBreak, parsed);
249
- }
250
- let value: number;
251
- if (/^0[xX]/.test(cleaned)) value = parseInt(cleaned.slice(2), 16);
252
- else if (/^0[oO]/.test(cleaned)) value = parseInt(cleaned.slice(2), 8);
253
- else if (/^0[bB]/.test(cleaned)) value = parseInt(cleaned.slice(2), 2);
254
- else value = Number(cleaned);
255
- if (Number.isNaN(value)) {
256
- this.error(DiagnosticCode.InvalidNumber, `Invalid numeric literal '${raw}'`, start, this.pos);
257
- value = 0;
258
- }
259
- return this.makeToken(TokenKind.NumericLiteral, start, this.pos, raw, precededByLineBreak, value);
260
- }
261
-
262
- private scanString(start: number, precededByLineBreak: boolean, quote: number): Token {
263
- this.pos++;
264
- let result = "";
265
- while (this.pos < this.text.length) {
266
- const ch = this.text.charCodeAt(this.pos);
267
- if (ch === quote) {
268
- this.pos++;
269
- return this.makeToken(TokenKind.StringLiteral, start, this.pos, this.text.slice(start, this.pos), precededByLineBreak, result);
270
- }
271
- if (ch === Char.Backslash) {
272
- result += this.scanEscapeSequence();
273
- continue;
274
- }
275
- if (isLineBreak(ch)) {
276
- this.error(DiagnosticCode.UnterminatedString, "Unterminated string literal", start);
277
- break;
278
- }
279
- result += this.text[this.pos];
280
- this.pos++;
281
- }
282
- this.error(DiagnosticCode.UnterminatedString, "Unterminated string literal", start);
283
- return this.makeToken(TokenKind.StringLiteral, start, this.pos, this.text.slice(start, this.pos), precededByLineBreak, result);
284
- }
285
-
286
- private scanEscapeSequence(): string {
287
- this.pos++; // backslash
288
- const ch = this.text.charCodeAt(this.pos);
289
- switch (ch) {
290
- case Char.LowerN:
291
- this.pos++;
292
- return "\n";
293
- case Char.LowerT:
294
- this.pos++;
295
- return "\t";
296
- case Char.LowerR:
297
- this.pos++;
298
- return "\r";
299
- case Char.LowerB:
300
- this.pos++;
301
- return "\b";
302
- case Char.LowerF:
303
- this.pos++;
304
- return "\f";
305
- case Char.LowerU:
306
- this.pos++;
307
- if (this.text.charCodeAt(this.pos) === Char.OpenBrace) {
308
- const close = this.text.indexOf("}", this.pos);
309
- const hex = this.text.slice(this.pos + 1, close);
310
- this.pos = close + 1;
311
- return String.fromCodePoint(parseInt(hex, 16) || 0);
312
- } else {
313
- const hex = this.text.slice(this.pos, this.pos + 4);
314
- this.pos += 4;
315
- return String.fromCharCode(parseInt(hex, 16) || 0);
316
- }
317
- case Char.LowerX: {
318
- const hex = this.text.slice(this.pos + 1, this.pos + 3);
319
- this.pos += 3;
320
- return String.fromCharCode(parseInt(hex, 16) || 0);
321
- }
322
- case Char.Zero:
323
- // \0 is only a null escape when not followed by a digit.
324
- if (!isDigit(this.text.charCodeAt(this.pos + 1))) {
325
- this.pos++;
326
- return "\0";
327
- }
328
- this.pos++;
329
- return "\0";
330
- default:
331
- this.pos++;
332
- return this.text[this.pos - 1] ?? "";
333
- }
334
- }
335
-
336
- /** Scans a template literal chunk starting at a backtick or after `}`. */
337
- scanTemplate(start: number, precededByLineBreak: boolean, isHead: boolean): Token {
338
- if (isHead) this.pos++; // opening backtick
339
- let result = "";
340
- for (;;) {
341
- if (this.pos >= this.text.length) {
342
- this.error(DiagnosticCode.UnterminatedTemplate, "Unterminated template literal", start);
343
- return this.makeToken(TokenKind.NoSubstitutionTemplateLiteral, start, this.pos, this.text.slice(start, this.pos), precededByLineBreak, result);
344
- }
345
- const ch = this.text.charCodeAt(this.pos);
346
- if (ch === Char.Backtick) {
347
- this.pos++;
348
- return this.makeToken(TokenKind.NoSubstitutionTemplateLiteral, start, this.pos, this.text.slice(start, this.pos), precededByLineBreak, result);
349
- }
350
- if (ch === Char.Dollar && this.text.charCodeAt(this.pos + 1) === Char.OpenBrace) {
351
- this.pos += 2;
352
- return this.makeToken(TokenKind.TemplateHead, start, this.pos, this.text.slice(start, this.pos), precededByLineBreak, result);
353
- }
354
- if (ch === Char.Backslash) {
355
- result += this.scanEscapeSequence();
356
- continue;
357
- }
358
- result += this.text[this.pos];
359
- this.pos++;
360
- }
361
- }
362
-
363
- /** Called by the parser after a template substitution expression. */
364
- continueTemplate(offset: number): Token {
365
- // `offset` points at the `}` that closes the substitution; skip it so the
366
- // following characters are scanned as template text.
367
- this.pos = Math.max(0, Math.min(offset + 1, this.text.length));
368
- this._token = this.scanTemplate(this.pos, false, /* isHead */ false);
369
- return this._token;
370
- }
371
-
372
- /**
373
- * True when a `/` in this position starts a regular expression rather than a
374
- * division. The decision is made from the previous significant token.
375
- */
376
- private regexAllowed(): boolean {
377
- switch (this._token.kind) {
378
- case TokenKind.Identifier:
379
- case TokenKind.PrivateIdentifier:
380
- case TokenKind.NumericLiteral:
381
- case TokenKind.BigIntLiteral:
382
- case TokenKind.StringLiteral:
383
- case TokenKind.NoSubstitutionTemplateLiteral:
384
- case TokenKind.TemplateTail:
385
- case TokenKind.RegularExpressionLiteral:
386
- case TokenKind.CloseParen:
387
- case TokenKind.CloseBracket:
388
- case TokenKind.TrueKeyword:
389
- case TokenKind.FalseKeyword:
390
- case TokenKind.NullKeyword:
391
- case TokenKind.UndefinedKeyword:
392
- case TokenKind.ThisKeyword:
393
- case TokenKind.PlusPlus:
394
- case TokenKind.MinusMinus:
395
- return false;
396
- default:
397
- return true;
398
- }
399
- }
400
-
401
- /** Scan a `/pattern/flags` literal, starting at the leading slash. */
402
- private scanRegex(start: number, precededByLineBreak: boolean): Token {
403
- this.pos++; // opening slash
404
- let inClass = false;
405
- for (;;) {
406
- if (this.pos >= this.text.length) {
407
- this.error(DiagnosticCode.InvalidCharacter, "Unterminated regular expression", start);
408
- break;
409
- }
410
- const ch = this.text.charCodeAt(this.pos);
411
- if (ch === Char.Backslash) {
412
- this.pos += 2;
413
- continue;
414
- }
415
- if (ch === Char.LineFeed || ch === Char.CarriageReturn) break;
416
- if (ch === 91 /* [ */) inClass = true;
417
- else if (ch === 93 /* ] */) inClass = false;
418
- else if (ch === Char.Slash && !inClass) {
419
- this.pos++;
420
- break;
421
- }
422
- this.pos++;
423
- }
424
- // Flags.
425
- while (this.pos < this.text.length && isIdentifierPart(this.text.charCodeAt(this.pos))) this.pos++;
426
- return this.makeToken(
427
- TokenKind.RegularExpressionLiteral,
428
- start,
429
- this.pos,
430
- this.text.slice(start, this.pos),
431
- precededByLineBreak,
432
- );
433
- }
434
-
435
- private scanPunctuation(start: number, precededByLineBreak: boolean): Token {
436
- const text = this.text;
437
- const ch = text.charCodeAt(this.pos);
438
- const next = text.charCodeAt(this.pos + 1);
439
- const third = text.charCodeAt(this.pos + 2);
440
- const fourth = text.charCodeAt(this.pos + 3);
441
-
442
- const emit = (kind: TokenKind, length: number): Token => {
443
- this.pos += length;
444
- return this.makeToken(kind, start, this.pos, text.slice(start, this.pos), precededByLineBreak);
445
- };
446
-
447
- switch (ch) {
448
- case Char.OpenBrace:
449
- return emit(TokenKind.OpenBrace, 1);
450
- case Char.CloseBrace:
451
- return emit(TokenKind.CloseBrace, 1);
452
- case Char.OpenParen:
453
- return emit(TokenKind.OpenParen, 1);
454
- case Char.CloseParen:
455
- return emit(TokenKind.CloseParen, 1);
456
- case Char.OpenBracket:
457
- return emit(TokenKind.OpenBracket, 1);
458
- case Char.CloseBracket:
459
- return emit(TokenKind.CloseBracket, 1);
460
- case Char.Semicolon:
461
- return emit(TokenKind.Semicolon, 1);
462
- case Char.Comma:
463
- return emit(TokenKind.Comma, 1);
464
- case Char.At:
465
- return emit(TokenKind.At, 1);
466
- case Char.Tilde:
467
- return emit(TokenKind.Tilde, 1);
468
- case Char.Colon:
469
- return emit(TokenKind.Colon, 1);
470
- case Char.Dot:
471
- if (next === Char.Dot && third === Char.Dot) return emit(TokenKind.DotDotDot, 3);
472
- return emit(TokenKind.Dot, 1);
473
- case Char.Question:
474
- if (next === Char.Question) {
475
- if (third === Char.Equals) return emit(TokenKind.QuestionQuestionEquals, 3);
476
- return emit(TokenKind.QuestionQuestion, 2);
477
- }
478
- if (next === Char.Dot && !isDigit(third)) return emit(TokenKind.QuestionDot, 2);
479
- return emit(TokenKind.Question, 1);
480
- case Char.Equals:
481
- if (next === Char.Equals && third === Char.Equals) return emit(TokenKind.EqualsEqualsEquals, 3);
482
- if (next === Char.Equals) return emit(TokenKind.EqualsEquals, 2);
483
- if (next === Char.GreaterThan) return emit(TokenKind.EqualsGreaterThan, 2);
484
- return emit(TokenKind.Equals, 1);
485
- case Char.Exclamation:
486
- if (next === Char.Equals && third === Char.Equals) return emit(TokenKind.ExclamationEqualsEquals, 3);
487
- if (next === Char.Equals) return emit(TokenKind.ExclamationEquals, 2);
488
- return emit(TokenKind.Exclamation, 1);
489
- case Char.Plus:
490
- if (next === Char.Plus) return emit(TokenKind.PlusPlus, 2);
491
- if (next === Char.Equals) return emit(TokenKind.PlusEquals, 2);
492
- return emit(TokenKind.Plus, 1);
493
- case Char.Minus:
494
- if (next === Char.Minus) return emit(TokenKind.MinusMinus, 2);
495
- if (next === Char.Equals) return emit(TokenKind.MinusEquals, 2);
496
- return emit(TokenKind.Minus, 1);
497
- case Char.Asterisk:
498
- if (next === Char.Asterisk) {
499
- if (third === Char.Equals) return emit(TokenKind.AsteriskAsteriskEquals, 3);
500
- return emit(TokenKind.AsteriskAsterisk, 2);
501
- }
502
- if (next === Char.Equals) return emit(TokenKind.AsteriskEquals, 2);
503
- return emit(TokenKind.Asterisk, 1);
504
- case Char.Slash:
505
- if (next === Char.Equals) return emit(TokenKind.SlashEquals, 2);
506
- if (this.regexAllowed()) return this.scanRegex(start, precededByLineBreak);
507
- return emit(TokenKind.Slash, 1);
508
- case Char.Percent:
509
- if (next === Char.Equals) return emit(TokenKind.PercentEquals, 2);
510
- return emit(TokenKind.Percent, 1);
511
- case Char.LessThan:
512
- if (next === Char.LessThan) {
513
- if (third === Char.Equals) return emit(TokenKind.LessThanLessThanEquals, 3);
514
- return emit(TokenKind.LessThanLessThan, 2);
515
- }
516
- if (next === Char.Equals) return emit(TokenKind.LessThanEquals, 2);
517
- return emit(TokenKind.LessThan, 1);
518
- case Char.GreaterThan:
519
- if (next === Char.GreaterThan) {
520
- if (third === Char.GreaterThan) {
521
- if (fourth === Char.Equals) return emit(TokenKind.GreaterThanGreaterThanGreaterThanEquals, 4);
522
- return emit(TokenKind.GreaterThanGreaterThanGreaterThan, 3);
523
- }
524
- if (third === Char.Equals) return emit(TokenKind.GreaterThanGreaterThanEquals, 3);
525
- return emit(TokenKind.GreaterThanGreaterThan, 2);
526
- }
527
- if (next === Char.Equals) return emit(TokenKind.GreaterThanEquals, 2);
528
- return emit(TokenKind.GreaterThan, 1);
529
- case Char.Ampersand:
530
- if (next === Char.Ampersand) {
531
- if (third === Char.Equals) return emit(TokenKind.AmpersandAmpersandEquals, 3);
532
- return emit(TokenKind.AmpersandAmpersand, 2);
533
- }
534
- if (next === Char.Equals) return emit(TokenKind.AmpersandEquals, 2);
535
- return emit(TokenKind.Ampersand, 1);
536
- case Char.Bar:
537
- if (next === Char.Bar) {
538
- if (third === Char.Equals) return emit(TokenKind.BarBarEquals, 3);
539
- return emit(TokenKind.BarBar, 2);
540
- }
541
- if (next === Char.Equals) return emit(TokenKind.BarEquals, 2);
542
- return emit(TokenKind.Bar, 1);
543
- case Char.Caret:
544
- if (next === Char.Equals) return emit(TokenKind.CaretEquals, 2);
545
- return emit(TokenKind.Caret, 1);
546
- default:
547
- this.pos++;
548
- this.error(DiagnosticCode.InvalidCharacter, `Invalid character '${text[start]}'`, start, this.pos);
549
- return this.makeToken(TokenKind.Identifier, start, this.pos, text[start] ?? "", precededByLineBreak);
550
- }
551
- }
552
169
  }
170
+
171
+ // eslint-disable-next-line @typescript-eslint/no-empty-interface
172
+ export interface Scanner
173
+ extends IdentifierMethods,
174
+ NumberMethods,
175
+ StringMethods,
176
+ RegexMethods,
177
+ PunctuationMethods {}
178
+
179
+ Object.assign(
180
+ Scanner.prototype,
181
+ identifierMethods,
182
+ numberMethods,
183
+ stringMethods,
184
+ regexMethods,
185
+ punctuationMethods,
186
+ );