@sheldev/oryn 0.0.0-stage → 0.1.0

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/dist/parser.js ADDED
@@ -0,0 +1,518 @@
1
+ import { tokenize } from "./lexer.js";
2
+ export class ParserError extends Error {
3
+ position;
4
+ constructor(message, position) {
5
+ super(`${message} at ${position.line}:${position.column}`);
6
+ this.position = position;
7
+ this.name = "ParserError";
8
+ }
9
+ }
10
+ export class Parser {
11
+ tokens;
12
+ current = 0;
13
+ constructor(tokens) {
14
+ this.tokens = tokens;
15
+ }
16
+ parse() {
17
+ const imports = [];
18
+ const body = [];
19
+ this.skipSeparators();
20
+ while (!this.isAtEnd()) {
21
+ if (this.matchKeyword("import")) {
22
+ imports.push(this.parseImport());
23
+ }
24
+ else if (this.matchKeyword("proc")) {
25
+ body.push(this.parseProc());
26
+ }
27
+ else if (this.matchKeyword("struct")) {
28
+ body.push(this.parseStruct());
29
+ }
30
+ else if (this.matchKeyword("enum")) {
31
+ body.push(this.parseEnum());
32
+ }
33
+ else if (this.matchKeyword("var") || this.matchKeyword("let") || this.matchKeyword("const")) {
34
+ body.push(this.parseVarDecl());
35
+ }
36
+ else {
37
+ const token = this.peek();
38
+ throw new ParserError(`Unexpected token ${JSON.stringify(token.value)}`, token.start);
39
+ }
40
+ this.skipSeparators();
41
+ }
42
+ return {
43
+ kind: "Program",
44
+ imports,
45
+ body,
46
+ };
47
+ }
48
+ parseImport() {
49
+ const token = this.consume("string", "Expected string literal after import");
50
+ return {
51
+ kind: "ImportDecl",
52
+ path: token.value,
53
+ };
54
+ }
55
+ parseStruct() {
56
+ const name = this.consume("identifier", "Expected struct name").value;
57
+ this.consumePunctuation("{");
58
+ this.skipSeparators();
59
+ const fields = [];
60
+ while (!this.checkPunctuation("}") && !this.isAtEnd()) {
61
+ const fieldName = this.consume("identifier", "Expected field name").value;
62
+ this.consumePunctuation(":");
63
+ const fieldType = this.parseTypeAnnotation();
64
+ fields.push({ name: fieldName, type: fieldType });
65
+ this.matchPunctuation(",");
66
+ this.skipSeparators();
67
+ }
68
+ this.consumePunctuation("}");
69
+ return { kind: "StructDecl", name, fields };
70
+ }
71
+ parseEnum() {
72
+ const name = this.consume("identifier", "Expected enum name").value;
73
+ this.consumePunctuation("{");
74
+ this.skipSeparators();
75
+ const members = [];
76
+ while (!this.checkPunctuation("}") && !this.isAtEnd()) {
77
+ const memberName = this.consume("identifier", "Expected enum member name").value;
78
+ let value;
79
+ if (this.matchOperator("=")) {
80
+ value = this.parseExpression();
81
+ }
82
+ members.push({ name: memberName, value });
83
+ this.matchPunctuation(",");
84
+ this.skipSeparators();
85
+ }
86
+ this.consumePunctuation("}");
87
+ return { kind: "EnumDecl", name, members };
88
+ }
89
+ parseTypeAnnotation() {
90
+ let typeStr = "";
91
+ if (this.matchOperator("^")) {
92
+ typeStr += "^";
93
+ }
94
+ const token = this.consume("identifier", "Expected type name");
95
+ typeStr += token.value;
96
+ return typeStr;
97
+ }
98
+ parseProc() {
99
+ const nameToken = this.consume("identifier", "Expected procedure name");
100
+ this.consumePunctuation("(");
101
+ const params = [];
102
+ if (!this.checkPunctuation(")")) {
103
+ do {
104
+ const pName = this.consume("identifier", "Expected parameter name").value;
105
+ this.consumePunctuation(":");
106
+ const pType = this.parseTypeAnnotation();
107
+ params.push({ name: pName, type: pType });
108
+ } while (this.matchPunctuation(","));
109
+ }
110
+ this.consumePunctuation(")");
111
+ const returnTypes = [];
112
+ if (this.matchOperator("->")) {
113
+ if (this.matchPunctuation("(")) {
114
+ if (!this.checkPunctuation(")")) {
115
+ do {
116
+ returnTypes.push(this.parseTypeAnnotation());
117
+ } while (this.matchPunctuation(","));
118
+ }
119
+ this.consumePunctuation(")");
120
+ }
121
+ else {
122
+ returnTypes.push(this.parseTypeAnnotation());
123
+ }
124
+ }
125
+ const body = this.parseBlock();
126
+ return {
127
+ kind: "ProcDecl",
128
+ name: nameToken.value,
129
+ params,
130
+ returnTypes,
131
+ body,
132
+ };
133
+ }
134
+ parseVarDecl() {
135
+ const keywordToken = this.previous();
136
+ const mutable = keywordToken.value !== "const";
137
+ const name = this.consume("identifier", "Expected variable name").value;
138
+ let type;
139
+ if (this.matchPunctuation(":")) {
140
+ type = this.parseTypeAnnotation();
141
+ }
142
+ let initializer;
143
+ if (this.matchOperator("=")) {
144
+ initializer = this.parseExpression();
145
+ }
146
+ return {
147
+ kind: "VarDecl",
148
+ name,
149
+ type,
150
+ initializer,
151
+ mutable,
152
+ };
153
+ }
154
+ parseBlock() {
155
+ this.consumePunctuation("{");
156
+ this.skipSeparators();
157
+ const statements = [];
158
+ while (!this.checkPunctuation("}") && !this.isAtEnd()) {
159
+ statements.push(this.parseStatement());
160
+ this.skipSeparators();
161
+ }
162
+ this.consumePunctuation("}");
163
+ return {
164
+ kind: "BlockStmt",
165
+ statements,
166
+ };
167
+ }
168
+ parseStatement() {
169
+ if (this.matchKeyword("var") || this.matchKeyword("let") || this.matchKeyword("const")) {
170
+ return this.parseVarDecl();
171
+ }
172
+ if (this.matchKeyword("return")) {
173
+ return this.parseReturn();
174
+ }
175
+ if (this.matchKeyword("if")) {
176
+ return this.parseIf();
177
+ }
178
+ if (this.matchKeyword("while")) {
179
+ return this.parseWhile();
180
+ }
181
+ if (this.matchKeyword("for")) {
182
+ return this.parseFor();
183
+ }
184
+ const expr = this.parseExpression();
185
+ if (this.isAssignmentOperator(this.peek().value)) {
186
+ const opToken = this.advance();
187
+ const value = this.parseExpression();
188
+ return {
189
+ kind: "AssignStmt",
190
+ target: expr,
191
+ operator: opToken.value,
192
+ value,
193
+ };
194
+ }
195
+ return expr;
196
+ }
197
+ parseReturn() {
198
+ const argumentsList = [];
199
+ if (!this.isSeparator() && !this.checkPunctuation("}")) {
200
+ do {
201
+ argumentsList.push(this.parseExpression());
202
+ } while (this.matchPunctuation(","));
203
+ }
204
+ return {
205
+ kind: "ReturnStmt",
206
+ arguments: argumentsList,
207
+ };
208
+ }
209
+ parseIf() {
210
+ const condition = this.parseExpression();
211
+ const consequent = this.parseBlock();
212
+ let alternate;
213
+ if (this.matchKeyword("else")) {
214
+ if (this.matchKeyword("if")) {
215
+ alternate = this.parseIf();
216
+ }
217
+ else {
218
+ alternate = this.parseBlock();
219
+ }
220
+ }
221
+ return {
222
+ kind: "IfStmt",
223
+ condition,
224
+ consequent,
225
+ alternate,
226
+ };
227
+ }
228
+ parseWhile() {
229
+ const condition = this.parseExpression();
230
+ const body = this.parseBlock();
231
+ return {
232
+ kind: "WhileStmt",
233
+ condition,
234
+ body,
235
+ };
236
+ }
237
+ parseFor() {
238
+ const condition = this.parseExpression();
239
+ const body = this.parseBlock();
240
+ return {
241
+ kind: "ForStmt",
242
+ condition,
243
+ body,
244
+ };
245
+ }
246
+ parseExpression() {
247
+ return this.parseOrHandling();
248
+ }
249
+ parseOrHandling() {
250
+ let expr = this.parseLambda();
251
+ while (this.matchKeyword("or_return") ||
252
+ this.matchKeyword("or_else") ||
253
+ this.matchKeyword("or_break")) {
254
+ const op = this.previous().value;
255
+ let fallback;
256
+ if (op === "or_else") {
257
+ fallback = this.parseLambda();
258
+ }
259
+ expr = {
260
+ kind: "OrHandleExpr",
261
+ expression: expr,
262
+ operator: op,
263
+ fallback,
264
+ };
265
+ }
266
+ return expr;
267
+ }
268
+ parseLambda() {
269
+ // Check for arrow lambda: (params) => body
270
+ if (this.checkPunctuation("(")) {
271
+ const saved = this.current;
272
+ try {
273
+ this.advance(); // consume '('
274
+ const params = [];
275
+ if (!this.checkPunctuation(")")) {
276
+ do {
277
+ const pName = this.consume("identifier", "Expected parameter name").value;
278
+ this.consumePunctuation(":");
279
+ const pType = this.parseTypeAnnotation();
280
+ params.push({ name: pName, type: pType });
281
+ } while (this.matchPunctuation(","));
282
+ }
283
+ this.consumePunctuation(")");
284
+ if (this.matchOperator("=>")) {
285
+ const body = this.parseExpression();
286
+ return { kind: "LambdaExpr", params, body };
287
+ }
288
+ }
289
+ catch {
290
+ // backtrack if not a lambda
291
+ }
292
+ this.current = saved;
293
+ }
294
+ return this.parseLogicalOr();
295
+ }
296
+ parseLogicalOr() {
297
+ let expr = this.parseLogicalAnd();
298
+ while (this.matchOperator("||") || this.matchKeyword("or")) {
299
+ const op = this.previous().value;
300
+ const right = this.parseLogicalAnd();
301
+ expr = { kind: "BinaryExpr", left: expr, operator: op, right };
302
+ }
303
+ return expr;
304
+ }
305
+ parseLogicalAnd() {
306
+ let expr = this.parseEquality();
307
+ while (this.matchOperator("&&") || this.matchKeyword("and")) {
308
+ const op = this.previous().value;
309
+ const right = this.parseEquality();
310
+ expr = { kind: "BinaryExpr", left: expr, operator: op, right };
311
+ }
312
+ return expr;
313
+ }
314
+ parseEquality() {
315
+ let expr = this.parseComparison();
316
+ while (this.matchOperator("==") || this.matchOperator("!=")) {
317
+ const op = this.previous().value;
318
+ const right = this.parseComparison();
319
+ expr = { kind: "BinaryExpr", left: expr, operator: op, right };
320
+ }
321
+ return expr;
322
+ }
323
+ parseComparison() {
324
+ let expr = this.parseTerm();
325
+ while (this.matchOperator("<") ||
326
+ this.matchOperator(">") ||
327
+ this.matchOperator("<=") ||
328
+ this.matchOperator(">=")) {
329
+ const op = this.previous().value;
330
+ const right = this.parseTerm();
331
+ expr = { kind: "BinaryExpr", left: expr, operator: op, right };
332
+ }
333
+ return expr;
334
+ }
335
+ parseTerm() {
336
+ let expr = this.parseFactor();
337
+ while (this.matchOperator("+") || this.matchOperator("-")) {
338
+ const op = this.previous().value;
339
+ const right = this.parseFactor();
340
+ expr = { kind: "BinaryExpr", left: expr, operator: op, right };
341
+ }
342
+ return expr;
343
+ }
344
+ parseFactor() {
345
+ let expr = this.parseUnary();
346
+ while (this.matchOperator("*") ||
347
+ this.matchOperator("/") ||
348
+ this.matchOperator("%")) {
349
+ const op = this.previous().value;
350
+ const right = this.parseUnary();
351
+ expr = { kind: "BinaryExpr", left: expr, operator: op, right };
352
+ }
353
+ return expr;
354
+ }
355
+ parseUnary() {
356
+ if (this.matchOperator("-") || this.matchOperator("!") || this.matchKeyword("not") || this.matchOperator("^")) {
357
+ const op = this.previous().value;
358
+ const argument = this.parseUnary();
359
+ return { kind: "UnaryExpr", operator: op, argument };
360
+ }
361
+ return this.parseCallMember();
362
+ }
363
+ parseCallMember() {
364
+ let expr = this.parsePrimary();
365
+ while (true) {
366
+ if (this.matchPunctuation("(")) {
367
+ expr = this.finishCall(expr);
368
+ }
369
+ else if (this.matchPunctuation(".")) {
370
+ const prop = this.consume("identifier", "Expected property name after '.'").value;
371
+ expr = {
372
+ kind: "MemberExpr",
373
+ object: expr,
374
+ property: prop,
375
+ };
376
+ }
377
+ else {
378
+ break;
379
+ }
380
+ }
381
+ return expr;
382
+ }
383
+ finishCall(callee) {
384
+ const args = [];
385
+ if (!this.checkPunctuation(")")) {
386
+ do {
387
+ args.push(this.parseExpression());
388
+ } while (this.matchPunctuation(","));
389
+ }
390
+ this.consumePunctuation(")");
391
+ return {
392
+ kind: "CallExpr",
393
+ callee,
394
+ args,
395
+ };
396
+ }
397
+ parsePrimary() {
398
+ if (this.match("number")) {
399
+ const token = this.previous();
400
+ return {
401
+ kind: "LiteralExpr",
402
+ value: Number(token.value.replace(/_/g, "")),
403
+ raw: token.value,
404
+ type: "number",
405
+ };
406
+ }
407
+ if (this.match("string")) {
408
+ const token = this.previous();
409
+ return {
410
+ kind: "LiteralExpr",
411
+ value: token.value.slice(1, -1),
412
+ raw: token.value,
413
+ type: "string",
414
+ };
415
+ }
416
+ if (this.peek().kind === "identifier" && (this.peek().value === "true" || this.peek().value === "false")) {
417
+ const token = this.advance();
418
+ return {
419
+ kind: "LiteralExpr",
420
+ value: token.value === "true",
421
+ raw: token.value,
422
+ type: "boolean",
423
+ };
424
+ }
425
+ if (this.match("identifier")) {
426
+ return {
427
+ kind: "IdentifierExpr",
428
+ name: this.previous().value,
429
+ };
430
+ }
431
+ if (this.matchPunctuation("(")) {
432
+ const expr = this.parseExpression();
433
+ this.consumePunctuation(")");
434
+ return expr;
435
+ }
436
+ const token = this.peek();
437
+ throw new ParserError(`Unexpected token ${JSON.stringify(token.value)}`, token.start);
438
+ }
439
+ peek() {
440
+ return this.tokens[this.current] ?? { kind: "eof", value: "", start: { offset: 0, line: 0, column: 0 }, end: { offset: 0, line: 0, column: 0 } };
441
+ }
442
+ previous() {
443
+ return this.tokens[this.current - 1];
444
+ }
445
+ isAtEnd() {
446
+ return this.peek().kind === "eof";
447
+ }
448
+ advance() {
449
+ if (!this.isAtEnd())
450
+ this.current++;
451
+ return this.previous();
452
+ }
453
+ match(kind) {
454
+ if (this.peek().kind === kind) {
455
+ this.advance();
456
+ return true;
457
+ }
458
+ return false;
459
+ }
460
+ matchKeyword(kw) {
461
+ const token = this.peek();
462
+ if ((token.kind === "keyword" || token.kind === "identifier") && token.value === kw) {
463
+ this.advance();
464
+ return true;
465
+ }
466
+ return false;
467
+ }
468
+ matchPunctuation(p) {
469
+ const token = this.peek();
470
+ if (token.kind === "punctuation" && token.value === p) {
471
+ this.advance();
472
+ return true;
473
+ }
474
+ return false;
475
+ }
476
+ checkPunctuation(p) {
477
+ const token = this.peek();
478
+ return token.kind === "punctuation" && token.value === p;
479
+ }
480
+ matchOperator(op) {
481
+ const token = this.peek();
482
+ if (token.kind === "operator" && token.value === op) {
483
+ this.advance();
484
+ return true;
485
+ }
486
+ return false;
487
+ }
488
+ consume(kind, message) {
489
+ if (this.peek().kind === kind) {
490
+ return this.advance();
491
+ }
492
+ const token = this.peek();
493
+ throw new ParserError(message, token.start);
494
+ }
495
+ consumePunctuation(p) {
496
+ if (this.checkPunctuation(p)) {
497
+ return this.advance();
498
+ }
499
+ const token = this.peek();
500
+ throw new ParserError(`Expected '${p}'`, token.start);
501
+ }
502
+ isAssignmentOperator(op) {
503
+ return ["=", "+=", "-=", "*=", "/=", "%="].includes(op);
504
+ }
505
+ isSeparator() {
506
+ return this.peek().kind === "newline" || this.peek().value === ";";
507
+ }
508
+ skipSeparators() {
509
+ while (!this.isAtEnd() && (this.peek().kind === "newline" || this.peek().value === ";")) {
510
+ this.advance();
511
+ }
512
+ }
513
+ }
514
+ export function parse(source) {
515
+ const tokens = tokenize(source);
516
+ return new Parser(tokens).parse();
517
+ }
518
+ //# sourceMappingURL=parser.js.map
@@ -0,0 +1,16 @@
1
+ export type TokenKind = "keyword" | "identifier" | "number" | "string" | "operator" | "punctuation" | "newline" | "eof";
2
+ export interface SourcePosition {
3
+ offset: number;
4
+ line: number;
5
+ column: number;
6
+ }
7
+ export interface Token {
8
+ kind: TokenKind;
9
+ value: string;
10
+ start: SourcePosition;
11
+ end: SourcePosition;
12
+ }
13
+ export declare class LexerError extends Error {
14
+ readonly position: SourcePosition;
15
+ constructor(message: string, position: SourcePosition);
16
+ }
package/dist/token.js ADDED
@@ -0,0 +1,9 @@
1
+ export class LexerError extends Error {
2
+ position;
3
+ constructor(message, position) {
4
+ super(`${message} at ${position.line}:${position.column}`);
5
+ this.position = position;
6
+ this.name = "LexerError";
7
+ }
8
+ }
9
+ //# sourceMappingURL=token.js.map
package/package.json CHANGED
@@ -1,6 +1,37 @@
1
1
  {
2
2
  "name": "@sheldev/oryn",
3
- "version": "0.0.0-stage",
4
- "stub": true,
5
- "description": "Temporary package placeholder for staged publishing"
6
- }
3
+ "version": "0.1.0",
4
+ "description": "Oryn-to-Odin transpiler CLI",
5
+ "type": "module",
6
+ "repository": {
7
+ "type": "git",
8
+ "url": "git+https://github.com/Sheldevv/oryn.git"
9
+ },
10
+ "homepage": "https://github.com/Sheldevv/oryn#readme",
11
+ "keywords": ["oryn", "odin", "transpiler", "compiler", "language"],
12
+ "bugs": { "url": "https://github.com/Sheldevv/oryn/issues" },
13
+ "bin": {
14
+ "oryn": "dist/cli.js"
15
+ },
16
+ "files": [
17
+ "dist/**/*.js",
18
+ "dist/**/*.d.ts",
19
+ "README.md",
20
+ "SPEC.md"
21
+ ],
22
+ "scripts": {
23
+ "build": "tsc -p tsconfig.json && node scripts/postbuild.mjs",
24
+ "dev": "tsx src/cli.ts",
25
+ "test": "tsx --test test/*.test.ts",
26
+ "prepublishOnly": "npm run build && npm test"
27
+ },
28
+ "engines": {
29
+ "node": ">=20"
30
+ },
31
+ "license": "MIT",
32
+ "devDependencies": {
33
+ "@types/node": "^22.15.0",
34
+ "tsx": "^4.19.0",
35
+ "typescript": "^5.8.3"
36
+ }
37
+ }