@hatchingpoint/point 0.0.5 → 0.0.7

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.
package/src/core/lexer.ts CHANGED
@@ -1,240 +1,245 @@
1
- import type { PointSourceSpan } from "./ast.ts";
2
-
3
- export type PointCoreTokenType =
4
- | "identifier"
5
- | "number"
6
- | "string"
7
- | "leftBrace"
8
- | "rightBrace"
9
- | "leftBracket"
10
- | "rightBracket"
11
- | "leftParen"
12
- | "rightParen"
13
- | "comma"
14
- | "colon"
15
- | "dot"
16
- | "equals"
17
- | "plusEquals"
18
- | "equalsEquals"
19
- | "bangEquals"
20
- | "less"
21
- | "lessEquals"
22
- | "greater"
23
- | "greaterEquals"
24
- | "plus"
25
- | "minus"
26
- | "star"
27
- | "slash"
28
- | "eof";
29
-
30
- export interface PointCoreToken {
31
- type: PointCoreTokenType;
32
- value: string;
33
- span: PointSourceSpan;
34
- }
35
-
36
- export class PointCoreLexerError extends Error {
37
- constructor(message: string, public readonly span: PointSourceSpan) {
38
- super(`${message} at ${span.start.line}:${span.start.column}`);
39
- this.name = "PointCoreLexerError";
40
- }
41
- }
42
-
43
- export function lexPointCore(source: string): PointCoreToken[] {
44
- const lexer = new CoreLexer(source);
45
- return lexer.lex();
46
- }
47
-
48
- class CoreLexer {
49
- private index = 0;
50
- private line = 1;
51
- private column = 1;
52
- private readonly tokens: PointCoreToken[] = [];
53
-
54
- constructor(private readonly source: string) {}
55
-
56
- lex(): PointCoreToken[] {
57
- while (!this.done()) {
58
- const char = this.peek();
59
- if (/\s/.test(char)) {
60
- this.advance();
61
- continue;
62
- }
63
- if (char === "/" && this.peek(1) === "/") {
64
- while (!this.done() && this.peek() !== "\n") this.advance();
65
- continue;
66
- }
67
- if (char === "{") {
68
- this.push("leftBrace", this.advance());
69
- continue;
70
- }
71
- if (char === "}") {
72
- this.push("rightBrace", this.advance());
73
- continue;
74
- }
75
- if (char === "(") {
76
- this.push("leftParen", this.advance());
77
- continue;
78
- }
79
- if (char === "[") {
80
- this.push("leftBracket", this.advance());
81
- continue;
82
- }
83
- if (char === "]") {
84
- this.push("rightBracket", this.advance());
85
- continue;
86
- }
87
- if (char === ")") {
88
- this.push("rightParen", this.advance());
89
- continue;
90
- }
91
- if (char === ",") {
92
- this.push("comma", this.advance());
93
- continue;
94
- }
95
- if (char === ":") {
96
- this.push("colon", this.advance());
97
- continue;
98
- }
99
- if (char === ".") {
100
- this.push("dot", this.advance());
101
- continue;
102
- }
103
- if (char === "=") {
104
- if (this.peek(1) === "=") {
105
- this.push("equalsEquals", `${this.advance()}${this.advance()}`);
106
- continue;
107
- }
108
- this.push("equals", this.advance());
109
- continue;
110
- }
111
- if (char === "!" && this.peek(1) === "=") {
112
- this.push("bangEquals", `${this.advance()}${this.advance()}`);
113
- continue;
114
- }
115
- if (char === "<") {
116
- if (this.peek(1) === "=") {
117
- this.push("lessEquals", `${this.advance()}${this.advance()}`);
118
- continue;
119
- }
120
- this.push("less", this.advance());
121
- continue;
122
- }
123
- if (char === ">") {
124
- if (this.peek(1) === "=") {
125
- this.push("greaterEquals", `${this.advance()}${this.advance()}`);
126
- continue;
127
- }
128
- this.push("greater", this.advance());
129
- continue;
130
- }
131
- if (char === "+") {
132
- if (this.peek(1) === "=") {
133
- this.push("plusEquals", `${this.advance()}${this.advance()}`);
134
- continue;
135
- }
136
- this.push("plus", this.advance());
137
- continue;
138
- }
139
- if (char === "-") {
140
- this.push("minus", this.advance());
141
- continue;
142
- }
143
- if (char === "*") {
144
- this.push("star", this.advance());
145
- continue;
146
- }
147
- if (char === "/") {
148
- this.push("slash", this.advance());
149
- continue;
150
- }
151
- if (char === "\"") {
152
- this.readString();
153
- continue;
154
- }
155
- if (/[0-9]/.test(char)) {
156
- this.readNumber();
157
- continue;
158
- }
159
- if (/[A-Za-z_]/.test(char)) {
160
- this.readIdentifier();
161
- continue;
162
- }
163
- throw new PointCoreLexerError(`Unexpected character ${JSON.stringify(char)}`, this.spanAt());
164
- }
165
- this.tokens.push({ type: "eof", value: "", span: this.spanAt() });
166
- return this.tokens;
167
- }
168
-
169
- private readString() {
170
- const start = this.position();
171
- this.advance();
172
- let value = "";
173
- while (!this.done() && this.peek() !== "\"") {
174
- const next = this.advance();
175
- if (next === "\\") {
176
- const escaped = this.advance();
177
- value += escaped === "n" ? "\n" : escaped;
178
- } else {
179
- value += next;
180
- }
181
- }
182
- if (this.peek() !== "\"") throw new PointCoreLexerError("Unterminated string", { start, end: this.position() });
183
- this.advance();
184
- this.tokens.push({ type: "string", value, span: { start, end: this.position() } });
185
- }
186
-
187
- private readNumber() {
188
- const start = this.position();
189
- let value = "";
190
- while (/[0-9.]/.test(this.peek())) value += this.advance();
191
- this.tokens.push({ type: "number", value, span: { start, end: this.position() } });
192
- }
193
-
194
- private readIdentifier() {
195
- const start = this.position();
196
- let value = "";
197
- while (/[A-Za-z0-9_]/.test(this.peek())) value += this.advance();
198
- this.tokens.push({ type: "identifier", value, span: { start, end: this.position() } });
199
- }
200
-
201
- private push(type: PointCoreTokenType, value: string) {
202
- const end = this.position();
203
- this.tokens.push({
204
- type,
205
- value,
206
- span: {
207
- start: { line: end.line, column: end.column - value.length, offset: end.offset - value.length },
208
- end,
209
- },
210
- });
211
- }
212
-
213
- private advance() {
214
- const char = this.source[this.index++] ?? "";
215
- if (char === "\n") {
216
- this.line += 1;
217
- this.column = 1;
218
- } else {
219
- this.column += 1;
220
- }
221
- return char;
222
- }
223
-
224
- private peek(offset = 0) {
225
- return this.source[this.index + offset] ?? "";
226
- }
227
-
228
- private done() {
229
- return this.index >= this.source.length;
230
- }
231
-
232
- private position() {
233
- return { line: this.line, column: this.column, offset: this.index };
234
- }
235
-
236
- private spanAt(): PointSourceSpan {
237
- const position = this.position();
238
- return { start: position, end: position };
239
- }
240
- }
1
+ import type { PointSourceSpan } from "./ast.ts";
2
+
3
+ export type PointCoreTokenType =
4
+ | "identifier"
5
+ | "number"
6
+ | "string"
7
+ | "leftBrace"
8
+ | "rightBrace"
9
+ | "leftBracket"
10
+ | "rightBracket"
11
+ | "leftParen"
12
+ | "rightParen"
13
+ | "comma"
14
+ | "colon"
15
+ | "dot"
16
+ | "equals"
17
+ | "plusEquals"
18
+ | "minusEquals"
19
+ | "equalsEquals"
20
+ | "bangEquals"
21
+ | "less"
22
+ | "lessEquals"
23
+ | "greater"
24
+ | "greaterEquals"
25
+ | "plus"
26
+ | "minus"
27
+ | "star"
28
+ | "slash"
29
+ | "eof";
30
+
31
+ export interface PointCoreToken {
32
+ type: PointCoreTokenType;
33
+ value: string;
34
+ span: PointSourceSpan;
35
+ }
36
+
37
+ export class PointCoreLexerError extends Error {
38
+ constructor(message: string, public readonly span: PointSourceSpan) {
39
+ super(`${message} at ${span.start.line}:${span.start.column}`);
40
+ this.name = "PointCoreLexerError";
41
+ }
42
+ }
43
+
44
+ export function lexPointCore(source: string): PointCoreToken[] {
45
+ const lexer = new CoreLexer(source);
46
+ return lexer.lex();
47
+ }
48
+
49
+ class CoreLexer {
50
+ private index = 0;
51
+ private line = 1;
52
+ private column = 1;
53
+ private readonly tokens: PointCoreToken[] = [];
54
+
55
+ constructor(private readonly source: string) {}
56
+
57
+ lex(): PointCoreToken[] {
58
+ while (!this.done()) {
59
+ const char = this.peek();
60
+ if (/\s/.test(char)) {
61
+ this.advance();
62
+ continue;
63
+ }
64
+ if (char === "/" && this.peek(1) === "/") {
65
+ while (!this.done() && this.peek() !== "\n") this.advance();
66
+ continue;
67
+ }
68
+ if (char === "{") {
69
+ this.push("leftBrace", this.advance());
70
+ continue;
71
+ }
72
+ if (char === "}") {
73
+ this.push("rightBrace", this.advance());
74
+ continue;
75
+ }
76
+ if (char === "(") {
77
+ this.push("leftParen", this.advance());
78
+ continue;
79
+ }
80
+ if (char === "[") {
81
+ this.push("leftBracket", this.advance());
82
+ continue;
83
+ }
84
+ if (char === "]") {
85
+ this.push("rightBracket", this.advance());
86
+ continue;
87
+ }
88
+ if (char === ")") {
89
+ this.push("rightParen", this.advance());
90
+ continue;
91
+ }
92
+ if (char === ",") {
93
+ this.push("comma", this.advance());
94
+ continue;
95
+ }
96
+ if (char === ":") {
97
+ this.push("colon", this.advance());
98
+ continue;
99
+ }
100
+ if (char === ".") {
101
+ this.push("dot", this.advance());
102
+ continue;
103
+ }
104
+ if (char === "=") {
105
+ if (this.peek(1) === "=") {
106
+ this.push("equalsEquals", `${this.advance()}${this.advance()}`);
107
+ continue;
108
+ }
109
+ this.push("equals", this.advance());
110
+ continue;
111
+ }
112
+ if (char === "!" && this.peek(1) === "=") {
113
+ this.push("bangEquals", `${this.advance()}${this.advance()}`);
114
+ continue;
115
+ }
116
+ if (char === "<") {
117
+ if (this.peek(1) === "=") {
118
+ this.push("lessEquals", `${this.advance()}${this.advance()}`);
119
+ continue;
120
+ }
121
+ this.push("less", this.advance());
122
+ continue;
123
+ }
124
+ if (char === ">") {
125
+ if (this.peek(1) === "=") {
126
+ this.push("greaterEquals", `${this.advance()}${this.advance()}`);
127
+ continue;
128
+ }
129
+ this.push("greater", this.advance());
130
+ continue;
131
+ }
132
+ if (char === "+") {
133
+ if (this.peek(1) === "=") {
134
+ this.push("plusEquals", `${this.advance()}${this.advance()}`);
135
+ continue;
136
+ }
137
+ this.push("plus", this.advance());
138
+ continue;
139
+ }
140
+ if (char === "-") {
141
+ if (this.peek(1) === "=") {
142
+ this.push("minusEquals", `${this.advance()}${this.advance()}`);
143
+ continue;
144
+ }
145
+ this.push("minus", this.advance());
146
+ continue;
147
+ }
148
+ if (char === "*") {
149
+ this.push("star", this.advance());
150
+ continue;
151
+ }
152
+ if (char === "/") {
153
+ this.push("slash", this.advance());
154
+ continue;
155
+ }
156
+ if (char === "\"") {
157
+ this.readString();
158
+ continue;
159
+ }
160
+ if (/[0-9]/.test(char)) {
161
+ this.readNumber();
162
+ continue;
163
+ }
164
+ if (/[A-Za-z_]/.test(char)) {
165
+ this.readIdentifier();
166
+ continue;
167
+ }
168
+ throw new PointCoreLexerError(`Unexpected character ${JSON.stringify(char)}`, this.spanAt());
169
+ }
170
+ this.tokens.push({ type: "eof", value: "", span: this.spanAt() });
171
+ return this.tokens;
172
+ }
173
+
174
+ private readString() {
175
+ const start = this.position();
176
+ this.advance();
177
+ let value = "";
178
+ while (!this.done() && this.peek() !== "\"") {
179
+ const next = this.advance();
180
+ if (next === "\\") {
181
+ const escaped = this.advance();
182
+ value += escaped === "n" ? "\n" : escaped;
183
+ } else {
184
+ value += next;
185
+ }
186
+ }
187
+ if (this.peek() !== "\"") throw new PointCoreLexerError("Unterminated string", { start, end: this.position() });
188
+ this.advance();
189
+ this.tokens.push({ type: "string", value, span: { start, end: this.position() } });
190
+ }
191
+
192
+ private readNumber() {
193
+ const start = this.position();
194
+ let value = "";
195
+ while (/[0-9.]/.test(this.peek())) value += this.advance();
196
+ this.tokens.push({ type: "number", value, span: { start, end: this.position() } });
197
+ }
198
+
199
+ private readIdentifier() {
200
+ const start = this.position();
201
+ let value = "";
202
+ while (/[A-Za-z0-9_]/.test(this.peek())) value += this.advance();
203
+ this.tokens.push({ type: "identifier", value, span: { start, end: this.position() } });
204
+ }
205
+
206
+ private push(type: PointCoreTokenType, value: string) {
207
+ const end = this.position();
208
+ this.tokens.push({
209
+ type,
210
+ value,
211
+ span: {
212
+ start: { line: end.line, column: end.column - value.length, offset: end.offset - value.length },
213
+ end,
214
+ },
215
+ });
216
+ }
217
+
218
+ private advance() {
219
+ const char = this.source[this.index++] ?? "";
220
+ if (char === "\n") {
221
+ this.line += 1;
222
+ this.column = 1;
223
+ } else {
224
+ this.column += 1;
225
+ }
226
+ return char;
227
+ }
228
+
229
+ private peek(offset = 0) {
230
+ return this.source[this.index + offset] ?? "";
231
+ }
232
+
233
+ private done() {
234
+ return this.index >= this.source.length;
235
+ }
236
+
237
+ private position() {
238
+ return { line: this.line, column: this.column, offset: this.index };
239
+ }
240
+
241
+ private spanAt(): PointSourceSpan {
242
+ const position = this.position();
243
+ return { start: position, end: position };
244
+ }
245
+ }