@lakutata/ffi 0.0.0-stage → 3.1.2

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 (129) hide show
  1. package/LICENSE +23 -0
  2. package/README.md +163 -2
  3. package/README.zh-CN.md +140 -0
  4. package/dist/cjs/components/NativeLibrary.d.ts +229 -0
  5. package/dist/cjs/components/NativeLibrary.js +346 -0
  6. package/dist/cjs/decorators/Library.d.ts +61 -0
  7. package/dist/cjs/decorators/Library.js +44 -0
  8. package/dist/cjs/decorators/Native.d.ts +69 -0
  9. package/dist/cjs/decorators/Native.js +90 -0
  10. package/dist/cjs/exceptions/NativeDeclarationException.d.ts +12 -0
  11. package/dist/cjs/exceptions/NativeDeclarationException.js +19 -0
  12. package/dist/cjs/exceptions/NativeLibraryClosedException.d.ts +12 -0
  13. package/dist/cjs/exceptions/NativeLibraryClosedException.js +19 -0
  14. package/dist/cjs/exceptions/NativeLibraryNotFoundException.d.ts +11 -0
  15. package/dist/cjs/exceptions/NativeLibraryNotFoundException.js +18 -0
  16. package/dist/cjs/exceptions/NativeMemoryException.d.ts +11 -0
  17. package/dist/cjs/exceptions/NativeMemoryException.js +18 -0
  18. package/dist/cjs/exports/FFI.d.ts +12 -0
  19. package/dist/cjs/exports/FFI.js +40 -0
  20. package/dist/cjs/exports/Generator.d.ts +2 -0
  21. package/dist/cjs/exports/Generator.js +10 -0
  22. package/dist/cjs/lib/Memory.d.ts +188 -0
  23. package/dist/cjs/lib/Memory.js +227 -0
  24. package/dist/cjs/lib/Runtime.d.ts +98 -0
  25. package/dist/cjs/lib/Runtime.js +209 -0
  26. package/dist/cjs/lib/backend/Functions.d.ts +65 -0
  27. package/dist/cjs/lib/backend/Functions.js +214 -0
  28. package/dist/cjs/lib/backend/KoffiTypes.d.ts +53 -0
  29. package/dist/cjs/lib/backend/KoffiTypes.js +426 -0
  30. package/dist/cjs/lib/declarations/Builtins.d.ts +33 -0
  31. package/dist/cjs/lib/declarations/Builtins.js +179 -0
  32. package/dist/cjs/lib/declarations/Layout.d.ts +41 -0
  33. package/dist/cjs/lib/declarations/Layout.js +180 -0
  34. package/dist/cjs/lib/declarations/Lexer.d.ts +40 -0
  35. package/dist/cjs/lib/declarations/Lexer.js +325 -0
  36. package/dist/cjs/lib/declarations/Parser.d.ts +90 -0
  37. package/dist/cjs/lib/declarations/Parser.js +1425 -0
  38. package/dist/cjs/lib/declarations/Platform.d.ts +50 -0
  39. package/dist/cjs/lib/declarations/Platform.js +27 -0
  40. package/dist/cjs/lib/declarations/Types.d.ts +173 -0
  41. package/dist/cjs/lib/declarations/Types.js +179 -0
  42. package/dist/cjs/lib/generator/CText.d.ts +14 -0
  43. package/dist/cjs/lib/generator/CText.js +133 -0
  44. package/dist/cjs/lib/generator/Generator.d.ts +136 -0
  45. package/dist/cjs/lib/generator/Generator.js +395 -0
  46. package/dist/cjs/lib/generator/Preprocessor.d.ts +60 -0
  47. package/dist/cjs/lib/generator/Preprocessor.js +94 -0
  48. package/dist/cjs/lib/generator/TypeScriptTypes.d.ts +35 -0
  49. package/dist/cjs/lib/generator/TypeScriptTypes.js +153 -0
  50. package/dist/cjs/lib/library/NativeCallback.d.ts +44 -0
  51. package/dist/cjs/lib/library/NativeCallback.js +66 -0
  52. package/dist/cjs/lib/library/NativeLibraryHandle.d.ts +268 -0
  53. package/dist/cjs/lib/library/NativeLibraryHandle.js +540 -0
  54. package/dist/cjs/lib/library/Resolve.d.ts +49 -0
  55. package/dist/cjs/lib/library/Resolve.js +130 -0
  56. package/dist/cjs/lib/thread/NativeThreads.d.ts +80 -0
  57. package/dist/cjs/lib/thread/NativeThreads.js +384 -0
  58. package/dist/cjs/lib/thread/NativeWorker.d.ts +6 -0
  59. package/dist/cjs/lib/thread/NativeWorker.js +96 -0
  60. package/dist/cjs/lib/thread/NativeWorkerCode.d.ts +5 -0
  61. package/dist/cjs/lib/thread/NativeWorkerCode.js +4 -0
  62. package/dist/cjs/lib/thread/PackageRequire.d.ts +6 -0
  63. package/dist/cjs/lib/thread/PackageRequire.js +4 -0
  64. package/dist/cjs/lib/thread/ThreadProtocol.d.ts +77 -0
  65. package/dist/cjs/lib/thread/ThreadProtocol.js +2 -0
  66. package/dist/cjs/package.json +1 -0
  67. package/dist/esm/components/NativeLibrary.js +338 -0
  68. package/dist/esm/decorators/Library.js +40 -0
  69. package/dist/esm/decorators/Native.js +85 -0
  70. package/dist/esm/exceptions/NativeDeclarationException.js +15 -0
  71. package/dist/esm/exceptions/NativeLibraryClosedException.js +15 -0
  72. package/dist/esm/exceptions/NativeLibraryNotFoundException.js +14 -0
  73. package/dist/esm/exceptions/NativeMemoryException.js +14 -0
  74. package/dist/esm/exports/FFI.js +10 -0
  75. package/dist/esm/exports/Generator.js +2 -0
  76. package/dist/esm/lib/Memory.js +210 -0
  77. package/dist/esm/lib/Runtime.js +192 -0
  78. package/dist/esm/lib/backend/Functions.js +206 -0
  79. package/dist/esm/lib/backend/KoffiTypes.js +418 -0
  80. package/dist/esm/lib/declarations/Builtins.js +174 -0
  81. package/dist/esm/lib/declarations/Layout.js +175 -0
  82. package/dist/esm/lib/declarations/Lexer.js +321 -0
  83. package/dist/esm/lib/declarations/Parser.js +1421 -0
  84. package/dist/esm/lib/declarations/Platform.js +23 -0
  85. package/dist/esm/lib/declarations/Types.js +170 -0
  86. package/dist/esm/lib/generator/CText.js +129 -0
  87. package/dist/esm/lib/generator/Generator.js +388 -0
  88. package/dist/esm/lib/generator/Preprocessor.js +87 -0
  89. package/dist/esm/lib/generator/TypeScriptTypes.js +149 -0
  90. package/dist/esm/lib/library/NativeCallback.js +59 -0
  91. package/dist/esm/lib/library/NativeLibraryHandle.js +532 -0
  92. package/dist/esm/lib/library/Resolve.js +122 -0
  93. package/dist/esm/lib/thread/NativeThreads.js +377 -0
  94. package/dist/esm/lib/thread/NativeWorker.js +93 -0
  95. package/dist/esm/lib/thread/NativeWorkerCode.js +1 -0
  96. package/dist/esm/lib/thread/PackageRequire.js +2 -0
  97. package/dist/esm/lib/thread/ThreadProtocol.js +1 -0
  98. package/dist/types/components/NativeLibrary.d.ts +229 -0
  99. package/dist/types/decorators/Library.d.ts +61 -0
  100. package/dist/types/decorators/Native.d.ts +69 -0
  101. package/dist/types/exceptions/NativeDeclarationException.d.ts +12 -0
  102. package/dist/types/exceptions/NativeLibraryClosedException.d.ts +12 -0
  103. package/dist/types/exceptions/NativeLibraryNotFoundException.d.ts +11 -0
  104. package/dist/types/exceptions/NativeMemoryException.d.ts +11 -0
  105. package/dist/types/exports/FFI.d.ts +12 -0
  106. package/dist/types/exports/Generator.d.ts +2 -0
  107. package/dist/types/lib/Memory.d.ts +188 -0
  108. package/dist/types/lib/Runtime.d.ts +98 -0
  109. package/dist/types/lib/backend/Functions.d.ts +65 -0
  110. package/dist/types/lib/backend/KoffiTypes.d.ts +53 -0
  111. package/dist/types/lib/declarations/Builtins.d.ts +33 -0
  112. package/dist/types/lib/declarations/Layout.d.ts +41 -0
  113. package/dist/types/lib/declarations/Lexer.d.ts +40 -0
  114. package/dist/types/lib/declarations/Parser.d.ts +90 -0
  115. package/dist/types/lib/declarations/Platform.d.ts +50 -0
  116. package/dist/types/lib/declarations/Types.d.ts +173 -0
  117. package/dist/types/lib/generator/CText.d.ts +14 -0
  118. package/dist/types/lib/generator/Generator.d.ts +136 -0
  119. package/dist/types/lib/generator/Preprocessor.d.ts +60 -0
  120. package/dist/types/lib/generator/TypeScriptTypes.d.ts +35 -0
  121. package/dist/types/lib/library/NativeCallback.d.ts +44 -0
  122. package/dist/types/lib/library/NativeLibraryHandle.d.ts +268 -0
  123. package/dist/types/lib/library/Resolve.d.ts +49 -0
  124. package/dist/types/lib/thread/NativeThreads.d.ts +80 -0
  125. package/dist/types/lib/thread/NativeWorker.d.ts +6 -0
  126. package/dist/types/lib/thread/NativeWorkerCode.d.ts +5 -0
  127. package/dist/types/lib/thread/PackageRequire.d.ts +6 -0
  128. package/dist/types/lib/thread/ThreadProtocol.d.ts +77 -0
  129. package/package.json +85 -4
@@ -0,0 +1,175 @@
1
+ import { NativeDeclarationException } from '../../exceptions/NativeDeclarationException.js';
2
+ import { primitiveSize, typeText } from './Types.js';
3
+ const layouts = new WeakMap();
4
+ /**
5
+ * The size of a type, in bytes
6
+ * @param type
7
+ * @param platform
8
+ */
9
+ export function sizeOf(type, platform) {
10
+ switch (type.kind) {
11
+ case 'primitive':
12
+ return primitiveSize(type.name, platform);
13
+ case 'pointer':
14
+ return platform.pointerSize;
15
+ case 'array':
16
+ return sizeOf(type.of, platform) * type.length;
17
+ case 'enum':
18
+ return primitiveSize(type.base, platform);
19
+ case 'struct':
20
+ case 'union':
21
+ return layoutOf(type, platform).size;
22
+ case 'function':
23
+ throw new NativeDeclarationException(`A function has no size: ${typeText(type)}`);
24
+ }
25
+ }
26
+ /**
27
+ * The alignment of a type, in bytes
28
+ * @param type
29
+ * @param platform
30
+ */
31
+ export function alignmentOf(type, platform) {
32
+ switch (type.kind) {
33
+ case 'primitive':
34
+ case 'enum': {
35
+ const size = primitiveSize(type.kind === 'primitive' ? type.name : type.base, platform);
36
+ return size === 8 ? platform.int64Alignment : Math.max(size, 1);
37
+ }
38
+ case 'pointer':
39
+ return platform.pointerSize;
40
+ case 'array':
41
+ return alignmentOf(type.of, platform);
42
+ case 'struct':
43
+ case 'union':
44
+ return layoutOf(type, platform).alignment;
45
+ case 'function':
46
+ throw new NativeDeclarationException(`A function has no alignment: ${typeText(type)}`);
47
+ }
48
+ }
49
+ /**
50
+ * Round up to a multiple
51
+ * @param value
52
+ * @param multiple
53
+ */
54
+ function alignUp(value, multiple) {
55
+ return Math.ceil(value / multiple) * multiple;
56
+ }
57
+ /**
58
+ * The layout of a struct or of a union, as the compilers of the platform lay it out (GCC and Clang, or Microsoft's for
59
+ * the bitfields): the alignment of each member capped by #pragma pack, raised by its aligned attribute
60
+ * @param record
61
+ * @param platform
62
+ */
63
+ export function layoutOf(record, platform) {
64
+ let cached = layouts.get(record);
65
+ const found = cached?.get(platform);
66
+ if (found)
67
+ return found;
68
+ if (!record.members)
69
+ throw new NativeDeclarationException(`The ${record.kind} ${record.tag ?? '(anonymous)'} is declared but not defined: it is used through pointers only`);
70
+ const layout = record.kind === 'union' ? unionLayout(record, platform) : structLayout(record, platform);
71
+ if (!cached)
72
+ layouts.set(record, cached = new Map());
73
+ cached.set(platform, layout);
74
+ return layout;
75
+ }
76
+ /**
77
+ * The alignment of a member: its type's, raised by its aligned attribute, capped by the packing of its struct
78
+ * @param member
79
+ * @param record
80
+ * @param platform
81
+ */
82
+ function memberAlignment(member, record, platform) {
83
+ const natural = Math.max(alignmentOf(member.type, platform), member.align ?? 1);
84
+ return record.pack ? Math.min(natural, record.pack) : natural;
85
+ }
86
+ /**
87
+ * The layout of a union: every member at 0
88
+ * @param record
89
+ * @param platform
90
+ */
91
+ function unionLayout(record, platform) {
92
+ let size = 0;
93
+ let alignment = record.align ?? 1;
94
+ const members = [];
95
+ for (const member of record.members) {
96
+ if (member.bits !== undefined)
97
+ throw new NativeDeclarationException(`The bitfield ${member.name || '(unnamed)'} of a union is not supported`);
98
+ const memberSize = sizeOf(member.type, platform);
99
+ alignment = Math.max(alignment, memberAlignment(member, record, platform));
100
+ size = Math.max(size, memberSize);
101
+ members.push({ member: member, offset: 0, size: memberSize });
102
+ }
103
+ return { size: alignUp(size, alignment), alignment: alignment, members: members, bitfields: false };
104
+ }
105
+ /**
106
+ * The layout of a struct: the members one after the other, each at its alignment; the bitfields in the storage units of
107
+ * their types, by the rules of the platform
108
+ * @param record
109
+ * @param platform
110
+ */
111
+ function structLayout(record, platform) {
112
+ let bit = 0;
113
+ let alignment = record.align ?? 1;
114
+ let bitfields = false;
115
+ //The storage unit of the bitfields being filled (Microsoft's rules): its offset in bytes, its size, the bits used
116
+ let unit;
117
+ const members = [];
118
+ for (const member of record.members) {
119
+ if (member.bits === undefined) {
120
+ unit = undefined;
121
+ const aligned = memberAlignment(member, record, platform);
122
+ const offset = alignUp(Math.ceil(bit / 8), aligned);
123
+ const size = sizeOf(member.type, platform);
124
+ members.push({ member: member, offset: offset, size: size });
125
+ bit = (offset + size) * 8;
126
+ alignment = Math.max(alignment, aligned);
127
+ continue;
128
+ }
129
+ bitfields = true;
130
+ if (record.pack !== undefined)
131
+ throw new NativeDeclarationException(`The bitfield ${member.name || '(unnamed)'} of a packed struct is not supported`);
132
+ if (member.type.kind !== 'primitive' && member.type.kind !== 'enum')
133
+ throw new NativeDeclarationException(`The bitfield ${member.name || '(unnamed)'} must have an integer type`);
134
+ const size = sizeOf(member.type, platform);
135
+ const typeAlignment = alignmentOf(member.type, platform);
136
+ const width = member.bits;
137
+ if (width > size * 8)
138
+ throw new NativeDeclarationException(`The bitfield ${member.name || '(unnamed)'} is wider than its type (${width} bits)`);
139
+ if (platform.bitfields === 'msvc') {
140
+ if (width === 0) {
141
+ //After a bitfield: ends its storage unit, the next member at the alignment of its type; ignored otherwise
142
+ if (unit) {
143
+ bit = alignUp((unit.offset + unit.size) * 8, typeAlignment * 8);
144
+ alignment = Math.max(alignment, typeAlignment);
145
+ }
146
+ unit = undefined;
147
+ continue;
148
+ }
149
+ if (!unit || unit.size !== size || unit.used + width > size * 8) {
150
+ const start = unit ? unit.offset + unit.size : Math.ceil(bit / 8);
151
+ unit = { offset: alignUp(start, typeAlignment), size: size, used: 0 };
152
+ alignment = Math.max(alignment, typeAlignment);
153
+ }
154
+ members.push({ member: member, offset: unit.offset, size: size, bitOffset: unit.offset * 8 + unit.used, bits: width });
155
+ unit.used += width;
156
+ bit = (unit.offset + unit.size) * 8;
157
+ continue;
158
+ }
159
+ //GCC and Clang: in the first unit of the size of its type where it fits, from the current bit. The type of an
160
+ //unnamed bitfield aligns the struct by the rules of ARM only
161
+ if (member.name || platform.bitfields === 'aapcs')
162
+ alignment = Math.max(alignment, typeAlignment);
163
+ if (width === 0) {
164
+ bit = alignUp(bit, typeAlignment * 8);
165
+ continue;
166
+ }
167
+ if ((bit % (size * 8)) + width > size * 8)
168
+ bit = alignUp(bit, typeAlignment * 8);
169
+ members.push({ member: member, offset: Math.floor(bit / (size * 8)) * size, size: size, bitOffset: bit, bits: width });
170
+ bit += width;
171
+ }
172
+ if (unit)
173
+ bit = (unit.offset + unit.size) * 8;
174
+ return { size: alignUp(Math.ceil(bit / 8), alignment), alignment: alignment, members: members, bitfields: bitfields };
175
+ }
@@ -0,0 +1,321 @@
1
+ import { NativeDeclarationException } from '../../exceptions/NativeDeclarationException.js';
2
+ const PUNCTUATORS = ['...', '<<=', '>>=', '<<', '>>', '<=', '>=', '==', '!=', '&&', '||', '->', '++', '--', '##', '::'];
3
+ const SINGLE = '{}()[];,*:=+-/%&|^~!?<>.#';
4
+ const ESCAPES = { n: 10, t: 9, r: 13, '0': 0, a: 7, b: 8, f: 12, v: 11, '\\': 92, '\'': 39, '"': 34, '?': 63, e: 27 };
5
+ /**
6
+ * The directives of the preprocessor that change what is declared: the declarations given must be preprocessed
7
+ */
8
+ const CONDITIONALS = ['if', 'ifdef', 'ifndef', 'elif', 'elifdef', 'elifndef', 'else', 'endif'];
9
+ /**
10
+ * The end of a directive of the preprocessor: the end of its line, its continued lines and the block comments that go on
11
+ * past it included (not the ones inside its strings)
12
+ * @param text
13
+ * @param start
14
+ */
15
+ function directiveEnd(text, start) {
16
+ let end = start;
17
+ while (end < text.length && text[end] !== '\n') {
18
+ const char = text[end];
19
+ if (char === '\\' && text[end + 1] === '\n') {
20
+ end += 2;
21
+ }
22
+ else if (char === '\\' && text[end + 1] === '\r' && text[end + 2] === '\n') {
23
+ end += 3;
24
+ }
25
+ else if (char === '/' && text[end + 1] === '*') {
26
+ const close = text.indexOf('*/', end + 2);
27
+ end = close < 0 ? text.length : close + 2;
28
+ }
29
+ else if (char === '/' && text[end + 1] === '/') {
30
+ while (end < text.length && text[end] !== '\n')
31
+ end++;
32
+ }
33
+ else if (char === '"' || char === '\'') {
34
+ end++;
35
+ while (end < text.length && text[end] !== char && text[end] !== '\n')
36
+ end += text[end] === '\\' ? 2 : 1;
37
+ if (text[end] === char)
38
+ end++;
39
+ }
40
+ else {
41
+ end++;
42
+ }
43
+ }
44
+ return end;
45
+ }
46
+ /**
47
+ * The text of a directive without its comments (the ones inside its strings and characters kept), its continued lines
48
+ * joined
49
+ * @param text
50
+ */
51
+ function withoutComments(text) {
52
+ let result = '';
53
+ let i = 0;
54
+ while (i < text.length) {
55
+ const char = text[i];
56
+ if (char === '/' && text[i + 1] === '*') {
57
+ const close = text.indexOf('*/', i + 2);
58
+ i = close < 0 ? text.length : close + 2;
59
+ result += ' ';
60
+ }
61
+ else if (char === '/' && text[i + 1] === '/') {
62
+ while (i < text.length && text[i] !== '\n')
63
+ i++;
64
+ }
65
+ else if (char === '"' || char === '\'') {
66
+ let end = i + 1;
67
+ while (end < text.length && text[end] !== char && text[end] !== '\n')
68
+ end += text[end] === '\\' ? 2 : 1;
69
+ result += text.slice(i, Math.min(end + 1, text.length));
70
+ i = end + 1;
71
+ }
72
+ else if (char === '\\' && (text[i + 1] === '\n' || (text[i + 1] === '\r' && text[i + 2] === '\n'))) {
73
+ i += text[i + 1] === '\n' ? 2 : 3;
74
+ result += ' ';
75
+ }
76
+ else {
77
+ result += char;
78
+ i++;
79
+ }
80
+ }
81
+ return result.trim();
82
+ }
83
+ /**
84
+ * The offsets the lines of a text start at
85
+ * @param text
86
+ */
87
+ function lineStartsOf(text) {
88
+ const starts = [0];
89
+ for (let i = 0; i < text.length; i++)
90
+ if (text.charCodeAt(i) === 10)
91
+ starts.push(i + 1);
92
+ return starts;
93
+ }
94
+ /**
95
+ * Where an offset of a text is, for the messages: its line and its column (from 1)
96
+ * @param starts the offsets the lines start at
97
+ * @param offset
98
+ */
99
+ function positionOf(starts, offset) {
100
+ let low = 0;
101
+ let high = starts.length - 1;
102
+ while (low < high) {
103
+ const middle = (low + high + 1) >> 1;
104
+ if (starts[middle] <= offset)
105
+ low = middle;
106
+ else
107
+ high = middle - 1;
108
+ }
109
+ return { line: low + 1, column: offset - starts[low] + 1 };
110
+ }
111
+ /**
112
+ * The exception of a declaration that cannot be read, at a line and a column
113
+ * @param message
114
+ * @param line
115
+ * @param column
116
+ */
117
+ export function declarationError(message, line, column) {
118
+ return new NativeDeclarationException(`${message} (line ${line}, column ${column})`);
119
+ }
120
+ /**
121
+ * The file and the line a line marker of a preprocessor gives to the line after it: `# 12 "file.h" 2` (GCC, Clang),
122
+ * `#line 12 "file.h"` (Microsoft); undefined for another directive
123
+ * @param directive the directive, without its #
124
+ */
125
+ function lineMarker(directive) {
126
+ const marker = directive.match(/^(?:line\s+)?(\d+)(?:\s+"((?:[^"\\]|\\.)*)")?/);
127
+ if (!marker)
128
+ return undefined;
129
+ return { line: Number(marker[1]), ...marker[2] !== undefined ? { file: marker[2].replace(/\\(.)/g, '$1') } : {} };
130
+ }
131
+ /**
132
+ * The tokens of C declarations. Comments are skipped; of the directives of the preprocessor, #pragma pack, #define and
133
+ * #undef are kept (as tokens holding the rest of their line), #include and the other #pragma are skipped, and the
134
+ * conditional ones (#if, #ifdef…) are refused: what they select is the work of a preprocessor. The line markers of a
135
+ * preprocessed text give each token the file and the line it comes from
136
+ * @param text
137
+ */
138
+ export function tokenize(text) {
139
+ const tokens = [];
140
+ const length = text.length;
141
+ let i = 0;
142
+ let lineStart = true;
143
+ const starts = lineStartsOf(text);
144
+ //The file of the line markers, and what to add to a line of the text to get its line in that file
145
+ let file;
146
+ let lineOffset = 0;
147
+ let marked = false;
148
+ const fail = (message, offset) => {
149
+ const { line, column } = positionOf(starts, offset);
150
+ throw declarationError(message, line, column);
151
+ };
152
+ const push = (kind, start, end, extra = {}) => {
153
+ const { line, column } = positionOf(starts, start);
154
+ tokens.push({
155
+ kind: kind,
156
+ text: extra.text ?? text.slice(start, end),
157
+ value: extra.value,
158
+ float: extra.float,
159
+ line: line,
160
+ column: column,
161
+ ...marked ? { file: file, fileLine: line + lineOffset } : {},
162
+ ...extra.source !== undefined ? { source: extra.source } : {}
163
+ });
164
+ };
165
+ while (i < length) {
166
+ const char = text[i];
167
+ //Whitespace, the continuations of lines
168
+ if (char === '\n') {
169
+ lineStart = true;
170
+ i++;
171
+ continue;
172
+ }
173
+ if (char === ' ' || char === '\t' || char === '\r' || char === '\f' || char === '\v') {
174
+ i++;
175
+ continue;
176
+ }
177
+ if (char === '\\' && (text[i + 1] === '\n' || (text[i + 1] === '\r' && text[i + 2] === '\n'))) {
178
+ i += text[i + 1] === '\n' ? 2 : 3;
179
+ continue;
180
+ }
181
+ //Comments
182
+ if (char === '/' && text[i + 1] === '/') {
183
+ while (i < length && text[i] !== '\n')
184
+ i++;
185
+ continue;
186
+ }
187
+ if (char === '/' && text[i + 1] === '*') {
188
+ const end = text.indexOf('*/', i + 2);
189
+ if (end < 0)
190
+ fail('A comment is not closed', i);
191
+ i = end + 2;
192
+ continue;
193
+ }
194
+ //Directives of the preprocessor, at the start of a line
195
+ if (char === '#' && lineStart) {
196
+ const start = i;
197
+ const end = directiveEnd(text, i);
198
+ const directive = withoutComments(text.slice(start + 1, end));
199
+ const name = directive.match(/^[A-Za-z_]\w*/)?.[0] ?? '';
200
+ if (CONDITIONALS.includes(name))
201
+ fail(`The directive #${name} is not supported: run the header through the preprocessor first (cc -E, cl /P)`, start);
202
+ const marker = name === '' || name === 'line' ? lineMarker(directive) : undefined;
203
+ if (marker) {
204
+ marked = true;
205
+ if (marker.file !== undefined)
206
+ file = marker.file;
207
+ lineOffset = marker.line - (positionOf(starts, start).line + 1);
208
+ }
209
+ const pack = directive.match(/^pragma\s+pack\s*\(([^)]*)\)/);
210
+ if (pack)
211
+ push('pragma', start, end, { text: `pack(${pack[1].replace(/\s+/g, '')})` });
212
+ //The text of a macro after its name, its comments removed; the name directly followed by ( takes arguments
213
+ if (name === 'define' || name === 'undef')
214
+ push(name, start, end, { text: directive.slice(name.length).trim() });
215
+ i = end;
216
+ continue;
217
+ }
218
+ lineStart = false;
219
+ //Identifiers, and the prefixes of the wide strings and characters
220
+ if (/[A-Za-z_$]/.test(char)) {
221
+ let end = i + 1;
222
+ while (end < length && /[\w$]/.test(text[end]))
223
+ end++;
224
+ const word = text.slice(i, end);
225
+ if ((text[end] === '\'' || text[end] === '"') && ['L', 'u', 'U', 'u8'].includes(word)) {
226
+ i = end;
227
+ continue;
228
+ }
229
+ push('identifier', i, end);
230
+ i = end;
231
+ continue;
232
+ }
233
+ //Numbers
234
+ if (/[0-9]/.test(char) || (char === '.' && /[0-9]/.test(text[i + 1] ?? ''))) {
235
+ let end = i;
236
+ const hex = /^0[xX]/.test(text.slice(i, i + 2));
237
+ if (hex)
238
+ end += 2;
239
+ while (end < length && (/[\w.]/.test(text[end]) || (/[eEpP]/.test(text[end - 1]) && /[+-]/.test(text[end]) && (!hex || /[pP]/.test(text[end - 1])))))
240
+ end++;
241
+ const literal = text.slice(i, end);
242
+ //The suffixes of C, and the ones of Microsoft (i64, ui64, I32…)
243
+ const body = literal.replace(/(?:[uU](?:ll|LL|l|L)?|(?:ll|LL|l|L)[uU]?|[uU]?[iI](?:8|16|32|64))$/, '');
244
+ const float = hex ? /[.pP]/.test(body) : /[.eE]/.test(body) || /[fF]$/.test(literal);
245
+ if (float) {
246
+ push('number', i, end, { float: true });
247
+ }
248
+ else {
249
+ let value;
250
+ try {
251
+ if (/^0[bB]/.test(body))
252
+ value = BigInt(body);
253
+ else if (/^0[0-7]+$/.test(body))
254
+ value = BigInt(`0o${body.slice(1)}`);
255
+ else
256
+ value = BigInt(body);
257
+ }
258
+ catch {
259
+ return fail(`The number ${literal} is not valid`, i);
260
+ }
261
+ push('number', i, end, { value: value });
262
+ }
263
+ i = end;
264
+ continue;
265
+ }
266
+ //Characters and strings
267
+ if (char === '\'' || char === '"') {
268
+ let end = i + 1;
269
+ const codes = [];
270
+ while (end < length && text[end] !== char) {
271
+ if (text[end] === '\n')
272
+ fail(char === '"' ? 'A string is not closed' : 'A character is not closed', i);
273
+ if (text[end] === '\\') {
274
+ const next = text[end + 1];
275
+ if (next === 'x') {
276
+ const digits = text.slice(end + 2).match(/^[0-9a-fA-F]+/)?.[0] ?? '';
277
+ codes.push(parseInt(digits || '0', 16));
278
+ end += 2 + digits.length;
279
+ }
280
+ else if (/[0-7]/.test(next)) {
281
+ const digits = text.slice(end + 1).match(/^[0-7]{1,3}/)[0];
282
+ codes.push(parseInt(digits, 8));
283
+ end += 1 + digits.length;
284
+ }
285
+ else {
286
+ codes.push(ESCAPES[next] ?? next.charCodeAt(0));
287
+ end += 2;
288
+ }
289
+ }
290
+ else {
291
+ codes.push(text.codePointAt(end));
292
+ end += text.codePointAt(end) > 0xFFFF ? 2 : 1;
293
+ }
294
+ }
295
+ if (end >= length)
296
+ fail(char === '"' ? 'A string is not closed' : 'A character is not closed', i);
297
+ if (char === '\'')
298
+ push('char', i, end + 1, { value: BigInt(codes[0] ?? 0) });
299
+ else
300
+ push('string', i, end + 1, { text: String.fromCodePoint(...codes), source: text.slice(i, end + 1) });
301
+ i = end + 1;
302
+ continue;
303
+ }
304
+ //Punctuators
305
+ const punctuator = PUNCTUATORS.find((candidate) => text.startsWith(candidate, i));
306
+ if (punctuator) {
307
+ push('punctuator', i, i + punctuator.length);
308
+ i += punctuator.length;
309
+ continue;
310
+ }
311
+ if (SINGLE.includes(char)) {
312
+ push('punctuator', i, i + 1);
313
+ i++;
314
+ continue;
315
+ }
316
+ fail(`Unexpected character ${JSON.stringify(char)}`, i);
317
+ }
318
+ const { line, column } = positionOf(starts, length);
319
+ tokens.push({ kind: 'end', text: '', line: line, column: column });
320
+ return tokens;
321
+ }