@ugo-studio/jspp 0.2.5 → 0.2.6

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.
@@ -0,0 +1,55 @@
1
+ import { COLORS } from "./colors.js";
2
+ export class Spinner {
3
+ frames = ["⠋", "⠙", "⠹", "⠸", "⠼", "⠴", "⠦", "⠧", "⠇", "⠏"];
4
+ interval = null;
5
+ frameIndex = 0;
6
+ text;
7
+ constructor(text) {
8
+ this.text = text;
9
+ }
10
+ start() {
11
+ process.stdout.write("\x1b[?25l"); // Hide cursor
12
+ this.frameIndex = 0;
13
+ this.render();
14
+ this.interval = setInterval(() => {
15
+ this.frameIndex = (this.frameIndex + 1) % this.frames.length;
16
+ this.render();
17
+ }, 80);
18
+ }
19
+ update(text) {
20
+ this.text = text;
21
+ this.render();
22
+ }
23
+ stop(symbol = "", color = COLORS.reset) {
24
+ if (this.interval) {
25
+ clearInterval(this.interval);
26
+ this.interval = null;
27
+ }
28
+ this.clearLine();
29
+ process.stdout.write(`${color}${symbol} ${COLORS.reset} ${this.text}\n`);
30
+ process.stdout.write("\x1b[?25h"); // Show cursor
31
+ }
32
+ succeed(text) {
33
+ if (text)
34
+ this.text = text;
35
+ this.stop("✔", COLORS.green);
36
+ }
37
+ fail(text) {
38
+ if (text)
39
+ this.text = text;
40
+ this.stop("✖", COLORS.red);
41
+ }
42
+ info(text) {
43
+ if (text)
44
+ this.text = text;
45
+ this.stop("ℹ", COLORS.cyan);
46
+ }
47
+ render() {
48
+ this.clearLine();
49
+ const frame = this.frames[this.frameIndex];
50
+ process.stdout.write(`${COLORS.cyan}${frame} ${COLORS.reset} ${this.text}`);
51
+ }
52
+ clearLine() {
53
+ process.stdout.write("\r\x1b[K");
54
+ }
55
+ }
@@ -44,11 +44,11 @@ export function visitForStatement(node, context) {
44
44
  : DeclarationType.const;
45
45
  conditionContext.localScopeSymbols.add(name, {
46
46
  type: declType,
47
- checked: { initialized: true },
47
+ checks: { initialized: true },
48
48
  });
49
49
  statementContext.localScopeSymbols.add(name, {
50
50
  type: declType,
51
- checked: { initialized: true },
51
+ checks: { initialized: true },
52
52
  });
53
53
  if (typeInfo.needsHeapAllocation) {
54
54
  initializerCode =
@@ -1,4 +1,5 @@
1
1
  import ts from "typescript";
2
+ import { CompilerError } from "../error.js";
2
3
  import { CodeGenerator } from "./index.js";
3
4
  export function visitVariableDeclarationList(node, context) {
4
5
  return node.declarations
@@ -69,7 +70,14 @@ export function visitVariableDeclaration(node, context) {
69
70
  if (isLetOrConst) {
70
71
  // If there's no initializer, it should be assigned undefined.
71
72
  if (!initializer) {
72
- return `${nativeLambdaCode}${assignmentTarget} = jspp::Constants::UNDEFINED`;
73
+ // Constant variables must be initialized
74
+ const isConst = (varDecl.parent.flags & (ts.NodeFlags.Const)) !== 0;
75
+ if (isConst) {
76
+ throw new CompilerError(`The constant "${name}" must be initialized`, varDecl, "SyntaxError");
77
+ }
78
+ else {
79
+ return `${nativeLambdaCode}${assignmentTarget} = jspp::Constants::UNDEFINED`;
80
+ }
73
81
  }
74
82
  return `${nativeLambdaCode}${assignmentTarget}${initializer}`;
75
83
  }
@@ -1,4 +1,6 @@
1
1
  import ts from "typescript";
2
+ import { constants } from "../constants.js";
3
+ import { CompilerError } from "../error.js";
2
4
  import { CodeGenerator } from "./index.js";
3
5
  export function visitObjectPropertyName(node, context) {
4
6
  if (ts.isNumericLiteral(node)) {
@@ -190,7 +192,7 @@ export function visitPropertyAccessExpression(node, context) {
190
192
  const propAccess = node;
191
193
  if (propAccess.expression.kind === ts.SyntaxKind.SuperKeyword) {
192
194
  if (!context.superClassVar) {
193
- throw new Error("super.prop accessed but no super class variable found in context");
195
+ throw new CompilerError("super.prop accessed but no super class variable found in context", propAccess.expression, "SyntaxError");
194
196
  }
195
197
  const propName = propAccess.name.getText();
196
198
  return `jspp::AnyValue::resolve_property_for_read((${context.superClassVar}).get_own_property("prototype").get_own_property("${propName}"), ${this.globalThisVar}, "${this.escapeString(propName)}")`;
@@ -258,7 +260,11 @@ export function visitElementAccessExpression(node, context) {
258
260
  export function visitBinaryExpression(node, context) {
259
261
  const binExpr = node;
260
262
  const opToken = binExpr.operatorToken;
261
- let op = opToken.getText();
263
+ const op = opToken.getText();
264
+ const visitContext = {
265
+ ...context,
266
+ supportedNativeLiterals: undefined,
267
+ };
262
268
  const assignmentOperators = [
263
269
  ts.SyntaxKind.PlusEqualsToken,
264
270
  ts.SyntaxKind.MinusEqualsToken,
@@ -279,8 +285,8 @@ export function visitBinaryExpression(node, context) {
279
285
  if (assignmentOperators.includes(opToken.kind)) {
280
286
  if (opToken.kind ===
281
287
  ts.SyntaxKind.GreaterThanGreaterThanGreaterThanEqualsToken) {
282
- const leftText = this.visit(binExpr.left, context);
283
- const rightText = this.visit(binExpr.right, context);
288
+ const leftText = this.visit(binExpr.left, visitContext);
289
+ const rightText = this.visit(binExpr.right, visitContext);
284
290
  let target = leftText;
285
291
  if (ts.isIdentifier(binExpr.left)) {
286
292
  const scope = this.getScopeForNode(binExpr.left);
@@ -292,8 +298,8 @@ export function visitBinaryExpression(node, context) {
292
298
  }
293
299
  }
294
300
  if (opToken.kind === ts.SyntaxKind.AsteriskAsteriskEqualsToken) {
295
- const leftText = this.visit(binExpr.left, context);
296
- const rightText = this.visit(binExpr.right, context);
301
+ const leftText = this.visit(binExpr.left, visitContext);
302
+ const rightText = this.visit(binExpr.right, visitContext);
297
303
  let target = leftText;
298
304
  if (ts.isIdentifier(binExpr.left)) {
299
305
  const scope = this.getScopeForNode(binExpr.left);
@@ -308,8 +314,8 @@ export function visitBinaryExpression(node, context) {
308
314
  if (opToken.kind === ts.SyntaxKind.AmpersandAmpersandEqualsToken ||
309
315
  opToken.kind === ts.SyntaxKind.BarBarEqualsToken ||
310
316
  opToken.kind === ts.SyntaxKind.QuestionQuestionEqualsToken) {
311
- const leftText = this.visit(binExpr.left, context);
312
- const rightText = this.visit(binExpr.right, context);
317
+ const leftText = this.visit(binExpr.left, visitContext);
318
+ const rightText = this.visit(binExpr.right, visitContext);
313
319
  let target = leftText;
314
320
  if (ts.isIdentifier(binExpr.left)) {
315
321
  const scope = this.getScopeForNode(binExpr.left);
@@ -328,21 +334,21 @@ export function visitBinaryExpression(node, context) {
328
334
  }
329
335
  }
330
336
  }
331
- const leftText = this.visit(binExpr.left, context);
337
+ const leftText = this.visit(binExpr.left, visitContext);
332
338
  let rightText = ts.isNumericLiteral(binExpr.right)
333
339
  ? binExpr.right.getText()
334
- : this.visit(binExpr.right, context);
340
+ : this.visit(binExpr.right, visitContext);
335
341
  if (ts.isIdentifier(binExpr.right)) {
336
342
  const scope = this.getScopeForNode(binExpr.right);
337
343
  const typeInfo = this.typeAnalyzer.scopeManager.lookupFromScope(binExpr.right.getText(), scope);
338
- rightText = this.getDerefCode(rightText, this.getJsVarName(binExpr.right), context, typeInfo);
344
+ rightText = this.getDerefCode(rightText, this.getJsVarName(binExpr.right), visitContext, typeInfo);
339
345
  }
340
346
  let target = leftText;
341
347
  if (ts.isIdentifier(binExpr.left)) {
342
348
  const scope = this.getScopeForNode(binExpr.left);
343
349
  const typeInfo = this.typeAnalyzer.scopeManager.lookupFromScope(binExpr.left.getText(), scope);
344
350
  if (context.derefBeforeAssignment) {
345
- target = this.getDerefCode(leftText, leftText, context, typeInfo);
351
+ target = this.getDerefCode(leftText, leftText, visitContext, typeInfo);
346
352
  }
347
353
  else if (typeInfo.needsHeapAllocation) {
348
354
  target = `*${leftText}`;
@@ -350,8 +356,9 @@ export function visitBinaryExpression(node, context) {
350
356
  }
351
357
  return `${target} ${op} ${rightText}`;
352
358
  }
359
+ // Assignment expression `a = 1`
353
360
  if (opToken.kind === ts.SyntaxKind.EqualsToken) {
354
- let rightText = this.visit(binExpr.right, context);
361
+ let rightText = this.visit(binExpr.right, visitContext);
355
362
  if (ts.isPropertyAccessExpression(binExpr.left)) {
356
363
  const propAccess = binExpr.left;
357
364
  if (propAccess.expression.kind === ts.SyntaxKind.SuperKeyword) {
@@ -365,13 +372,13 @@ export function visitBinaryExpression(node, context) {
365
372
  if (rightTypeInfo &&
366
373
  !rightTypeInfo.isParameter &&
367
374
  !rightTypeInfo.isBuiltin) {
368
- finalRightText = this.getDerefCode(rightText, this.getJsVarName(binExpr.right), context, rightTypeInfo);
375
+ finalRightText = this.getDerefCode(rightText, this.getJsVarName(binExpr.right), visitContext, rightTypeInfo);
369
376
  }
370
377
  }
371
378
  // Approximate super assignment as setting property on 'this'
372
379
  return `(${this.globalThisVar}).set_own_property("${propName}", ${finalRightText})`;
373
380
  }
374
- const objExprText = this.visit(propAccess.expression, context);
381
+ const objExprText = this.visit(propAccess.expression, visitContext);
375
382
  const propName = propAccess.name.getText();
376
383
  let finalObjExpr = objExprText;
377
384
  if (ts.isIdentifier(propAccess.expression)) {
@@ -396,14 +403,14 @@ export function visitBinaryExpression(node, context) {
396
403
  }
397
404
  else if (ts.isElementAccessExpression(binExpr.left)) {
398
405
  const elemAccess = binExpr.left;
399
- const objExprText = this.visit(elemAccess.expression, context);
400
- let argText = visitObjectPropertyName.call(this, elemAccess.argumentExpression, { ...context, isBracketNotationPropertyAccess: true });
406
+ const objExprText = this.visit(elemAccess.expression, visitContext);
407
+ let argText = visitObjectPropertyName.call(this, elemAccess.argumentExpression, { ...visitContext, isBracketNotationPropertyAccess: true });
401
408
  let finalObjExpr = objExprText;
402
409
  if (ts.isIdentifier(elemAccess.expression)) {
403
410
  const scope = this.getScopeForNode(elemAccess.expression);
404
411
  const typeInfo = this.typeAnalyzer.scopeManager.lookupFromScope(elemAccess.expression.getText(), scope);
405
412
  if (typeInfo && !typeInfo.isParameter && !typeInfo.isBuiltin) {
406
- finalObjExpr = this.getDerefCode(objExprText, this.getJsVarName(elemAccess.expression), context, typeInfo);
413
+ finalObjExpr = this.getDerefCode(objExprText, this.getJsVarName(elemAccess.expression), visitContext, typeInfo);
407
414
  }
408
415
  }
409
416
  if (ts.isIdentifier(elemAccess.argumentExpression)) {
@@ -413,7 +420,7 @@ export function visitBinaryExpression(node, context) {
413
420
  if (argTypeInfo &&
414
421
  !argTypeInfo.isParameter &&
415
422
  !argTypeInfo.isBuiltin) {
416
- argText = this.getDerefCode(argText, this.getJsVarName(elemAccess.argumentExpression), context, argTypeInfo);
423
+ argText = this.getDerefCode(argText, this.getJsVarName(elemAccess.argumentExpression), visitContext, argTypeInfo);
417
424
  }
418
425
  }
419
426
  let finalRightText = rightText;
@@ -424,12 +431,12 @@ export function visitBinaryExpression(node, context) {
424
431
  if (rightTypeInfo &&
425
432
  !rightTypeInfo.isParameter &&
426
433
  !rightTypeInfo.isBuiltin) {
427
- finalRightText = this.getDerefCode(rightText, this.getJsVarName(binExpr.right), context, rightTypeInfo);
434
+ finalRightText = this.getDerefCode(rightText, this.getJsVarName(binExpr.right), visitContext, rightTypeInfo);
428
435
  }
429
436
  }
430
437
  return `${finalObjExpr}.set_own_property(${argText}, ${finalRightText})`;
431
438
  }
432
- const leftText = this.visit(binExpr.left, context);
439
+ const leftText = this.visit(binExpr.left, visitContext);
433
440
  const scope = this.getScopeForNode(binExpr.left);
434
441
  const typeInfo = this.typeAnalyzer.scopeManager.lookupFromScope(binExpr.left.text, scope);
435
442
  if (!typeInfo && !this.isBuiltinObject(binExpr.left)) {
@@ -442,7 +449,7 @@ export function visitBinaryExpression(node, context) {
442
449
  rightText = binExpr.right.getText();
443
450
  }
444
451
  const target = context.derefBeforeAssignment
445
- ? this.getDerefCode(leftText, leftText, context, typeInfo)
452
+ ? this.getDerefCode(leftText, leftText, visitContext, typeInfo)
446
453
  : (typeInfo.needsHeapAllocation ? `*${leftText}` : leftText);
447
454
  // Update scope symbols on variable re-assignment
448
455
  if (ts.isIdentifier(binExpr.left)) {
@@ -461,8 +468,9 @@ export function visitBinaryExpression(node, context) {
461
468
  }
462
469
  return `${target} ${op} ${rightText}`;
463
470
  }
464
- const leftText = this.visit(binExpr.left, context);
465
- const rightText = this.visit(binExpr.right, context);
471
+ const leftText = this.visit(binExpr.left, visitContext);
472
+ const rightText = this.visit(binExpr.right, visitContext);
473
+ // Generate lhs and rhs code
466
474
  let finalLeft = leftText;
467
475
  if (ts.isIdentifier(binExpr.left)) {
468
476
  const scope = this.getScopeForNode(binExpr.left);
@@ -471,7 +479,7 @@ export function visitBinaryExpression(node, context) {
471
479
  return `jspp::Exception::throw_unresolved_reference(${this.getJsVarName(binExpr.left)})`;
472
480
  }
473
481
  if (typeInfo && !typeInfo.isParameter && !typeInfo.isBuiltin) {
474
- finalLeft = this.getDerefCode(leftText, this.getJsVarName(binExpr.left), context, typeInfo);
482
+ finalLeft = this.getDerefCode(leftText, this.getJsVarName(binExpr.left), visitContext, typeInfo);
475
483
  }
476
484
  }
477
485
  let finalRight = rightText;
@@ -483,7 +491,7 @@ export function visitBinaryExpression(node, context) {
483
491
  return `jspp::Exception::throw_unresolved_reference(${this.getJsVarName(binExpr.right)})`;
484
492
  }
485
493
  if (typeInfo && !typeInfo.isParameter && !typeInfo.isBuiltin) {
486
- finalRight = this.getDerefCode(rightText, this.getJsVarName(binExpr.right), context, typeInfo);
494
+ finalRight = this.getDerefCode(rightText, this.getJsVarName(binExpr.right), visitContext, typeInfo);
487
495
  }
488
496
  }
489
497
  if (opToken.kind === ts.SyntaxKind.InKeyword) {
@@ -501,35 +509,63 @@ export function visitBinaryExpression(node, context) {
501
509
  if (opToken.kind === ts.SyntaxKind.QuestionQuestionToken) {
502
510
  return `jspp::nullish_coalesce(${finalLeft}, ${finalRight})`;
503
511
  }
504
- // Optimizations to prevent calling make_number multiple times
505
- if (ts.isNumericLiteral(binExpr.left)) {
506
- finalLeft = binExpr.left.getText();
507
- }
508
- if (ts.isNumericLiteral(binExpr.right)) {
509
- finalRight = binExpr.right.getText();
510
- }
512
+ const isLiteral = (n) => ts.isNumericLiteral(n);
513
+ const supportsNativeBoolean = context.supportedNativeLiterals?.includes("boolean") || false;
514
+ // Native values for lhs and rhs
515
+ const literalLeft = isLiteral(binExpr.left)
516
+ ? binExpr.left.getText()
517
+ : finalLeft;
518
+ const literalRight = isLiteral(binExpr.right)
519
+ ? binExpr.right.getText()
520
+ : finalRight;
521
+ // Operations that returns boolean should return the native boolean if supported
522
+ if (constants.booleanOperators.includes(opToken.kind) &&
523
+ supportsNativeBoolean) {
524
+ if (opToken.kind === ts.SyntaxKind.EqualsEqualsEqualsToken) {
525
+ return `jspp::is_strictly_equal_to_primitive(${literalLeft}, ${literalRight})`;
526
+ }
527
+ if (opToken.kind === ts.SyntaxKind.EqualsEqualsToken) {
528
+ return `jspp::is_equal_to_primitive(${literalLeft}, ${literalRight})`;
529
+ }
530
+ if (opToken.kind === ts.SyntaxKind.ExclamationEqualsEqualsToken) {
531
+ return `!jspp::is_strictly_equal_to_primitive(${literalLeft}, ${literalRight})`;
532
+ }
533
+ if (opToken.kind === ts.SyntaxKind.ExclamationEqualsToken) {
534
+ return `!jspp::is_equal_to_primitive(${literalLeft}, ${literalRight})`;
535
+ }
536
+ if (isLiteral(binExpr.left) && isLiteral(binExpr.right)) {
537
+ return `(${literalLeft} ${op} ${literalRight})`;
538
+ }
539
+ else
540
+ return `(${literalLeft} ${op} ${literalRight}).as_boolean()`;
541
+ }
542
+ // Return boxed value
511
543
  if (opToken.kind === ts.SyntaxKind.EqualsEqualsEqualsToken) {
512
- return `jspp::is_strictly_equal_to(${finalLeft}, ${finalRight})`;
544
+ return `jspp::is_strictly_equal_to(${literalLeft}, ${literalRight})`;
513
545
  }
514
546
  if (opToken.kind === ts.SyntaxKind.EqualsEqualsToken) {
515
- return `jspp::is_equal_to(${finalLeft}, ${finalRight})`;
547
+ return `jspp::is_equal_to(${literalLeft}, ${literalRight})`;
516
548
  }
517
549
  if (opToken.kind === ts.SyntaxKind.ExclamationEqualsEqualsToken) {
518
- return `jspp::not_strictly_equal_to(${finalLeft}, ${finalRight})`;
550
+ return `jspp::not_strictly_equal_to(${literalLeft}, ${literalRight})`;
519
551
  }
520
552
  if (opToken.kind === ts.SyntaxKind.ExclamationEqualsToken) {
521
- return `jspp::not_equal_to(${finalLeft}, ${finalRight})`;
553
+ return `jspp::not_equal_to(${literalLeft}, ${literalRight})`;
522
554
  }
523
555
  if (opToken.kind === ts.SyntaxKind.AsteriskAsteriskToken) {
524
- return `jspp::pow(${finalLeft}, ${finalRight})`;
556
+ return `jspp::pow(${literalLeft}, ${literalRight})`;
525
557
  }
526
558
  if (opToken.kind === ts.SyntaxKind.GreaterThanGreaterThanGreaterThanToken) {
527
- return `jspp::unsigned_right_shift(${finalLeft}, ${finalRight})`;
559
+ return `jspp::unsigned_right_shift(${literalLeft}, ${literalRight})`;
560
+ }
561
+ // Use literal for at most one side
562
+ if (isLiteral(binExpr.left)) {
563
+ finalLeft = binExpr.left.getText();
528
564
  }
529
- // Optimizations to prevent calling make_number multiple times
530
- if (ts.isNumericLiteral(binExpr.left) && ts.isNumericLiteral(binExpr.right)) {
531
- return `jspp::AnyValue::make_number(${finalLeft} ${op} ${finalRight})`;
565
+ else if (isLiteral(binExpr.right)) {
566
+ finalRight = binExpr.right.getText();
532
567
  }
568
+ // For other arithmetic and bitwise operations, use native operations if possible
533
569
  if (op === "+" ||
534
570
  op === "-" ||
535
571
  op === "*" ||
@@ -546,7 +582,12 @@ export function visitBinaryExpression(node, context) {
546
582
  }
547
583
  export function visitConditionalExpression(node, context) {
548
584
  const condExpr = node;
549
- const condition = this.visit(condExpr.condition, context);
585
+ const isBinaryExpression = ts.isBinaryExpression(condExpr.condition) &&
586
+ constants.booleanOperators.includes(condExpr.condition.operatorToken.kind);
587
+ const condition = this.visit(condExpr.condition, {
588
+ ...context,
589
+ supportedNativeLiterals: isBinaryExpression ? ["boolean"] : undefined,
590
+ });
550
591
  const whenTrueStmt = this.visit(condExpr.whenTrue, {
551
592
  ...context,
552
593
  isFunctionBody: false,
@@ -555,6 +596,9 @@ export function visitConditionalExpression(node, context) {
555
596
  ...context,
556
597
  isFunctionBody: false,
557
598
  });
599
+ if (isBinaryExpression) {
600
+ return `${condition} ? ${whenTrueStmt} : ${whenFalseStmt}`;
601
+ }
558
602
  return `jspp::is_truthy(${condition}) ? ${whenTrueStmt} : ${whenFalseStmt}`;
559
603
  }
560
604
  export function visitCallExpression(node, context) {
@@ -562,7 +606,7 @@ export function visitCallExpression(node, context) {
562
606
  const callee = callExpr.expression;
563
607
  if (callee.kind === ts.SyntaxKind.SuperKeyword) {
564
608
  if (!context.superClassVar) {
565
- throw new Error("super() called but no super class variable found in context");
609
+ throw new CompilerError("super() called but no super class variable found in context", callee, "SyntaxError");
566
610
  }
567
611
  const args = callExpr.arguments.map((arg) => this.visit(arg, context))
568
612
  .join(", ");
@@ -593,7 +637,7 @@ export function visitCallExpression(node, context) {
593
637
  const propAccess = callee;
594
638
  if (propAccess.expression.kind === ts.SyntaxKind.SuperKeyword) {
595
639
  if (!context.superClassVar) {
596
- throw new Error("super.method() called but no super class variable found in context");
640
+ throw new CompilerError("super.method() called but no super class variable found in context", propAccess.expression, "SyntaxError");
597
641
  }
598
642
  const propName = propAccess.name.getText();
599
643
  return `(${context.superClassVar}).get_own_property("prototype").get_own_property("${propName}").call(${this.globalThisVar}, ${argsSpan}, "${this.escapeString(propName)}")`;
@@ -827,3 +871,15 @@ export function visitDeleteExpression(node, context) {
827
871
  }
828
872
  return "jspp::Constants::TRUE"; // delete on non-property is true in JS
829
873
  }
874
+ export function visitAsExpression(node, context) {
875
+ return this.visit(node.expression, context);
876
+ }
877
+ export function visitTypeAssertionExpression(node, context) {
878
+ return this.visit(node.expression, context);
879
+ }
880
+ export function visitNonNullExpression(node, context) {
881
+ return this.visit(node.expression, context);
882
+ }
883
+ export function visitSatisfiesExpression(node, context) {
884
+ return this.visit(node.expression, context);
885
+ }
@@ -1,5 +1,6 @@
1
1
  import ts from "typescript";
2
- import { DeclaredSymbols } from "../../ast/symbols.js";
2
+ import { DeclarationType, DeclaredSymbols } from "../../ast/symbols.js";
3
+ import { CompilerError } from "../error.js";
3
4
  import { collectFunctionScopedDeclarations } from "./helpers.js";
4
5
  import { CodeGenerator } from "./index.js";
5
6
  export function generateLambda(node, context, options) {
@@ -84,17 +85,27 @@ export function generateLambda(node, context, options) {
84
85
  // So subsequent code using `argsName` and `visit` (using `globalThisVar`) works correctly.
85
86
  const paramExtractor = (parameters) => {
86
87
  let code = "";
87
- parameters.forEach((p, i) => {
88
+ parameters.filter((p) => this.isTypescript && p.name.getText() === "this" ? false : true // Ignore "this" parameters
89
+ ).forEach((p, i) => {
88
90
  const name = p.name.getText();
89
91
  const defaultValue = p.initializer
90
92
  ? this.visit(p.initializer, visitContext)
91
93
  : "jspp::Constants::UNDEFINED";
94
+ // Catch invalid parameters
95
+ if (name === "this") {
96
+ throw new CompilerError(`Cannot use '${name}' as a parameter name.`, p, "SyntaxError");
97
+ }
98
+ // Add paramerter to local context
99
+ visitContext.localScopeSymbols.add(name, {
100
+ type: DeclarationType.let,
101
+ checks: { initialized: true },
102
+ });
92
103
  const scope = this.getScopeForNode(p);
93
104
  const typeInfo = this.typeAnalyzer.scopeManager.lookupFromScope(name, scope);
94
105
  // Handle rest parameters
95
106
  if (!!p.dotDotDotToken) {
96
107
  if (parameters.length - 1 !== i) {
97
- throw new SyntaxError("Rest parameter must be last formal parameter.");
108
+ throw new CompilerError("Rest parameter must be last formal parameter.", p, "SyntaxError");
98
109
  }
99
110
  if (typeInfo.needsHeapAllocation) {
100
111
  code +=
@@ -1,6 +1,7 @@
1
1
  import ts from "typescript";
2
2
  import { BUILTIN_OBJECTS, Scope } from "../../analysis/scope.js";
3
3
  import { DeclarationType, DeclaredSymbols } from "../../ast/symbols.js";
4
+ import { CompilerError } from "../error.js";
4
5
  import { CodeGenerator } from "./index.js";
5
6
  export function isBuiltinObject(node) {
6
7
  return BUILTIN_OBJECTS.values().some((obj) => obj.name === node.text);
@@ -20,6 +21,10 @@ export function getDeclaredSymbols(node) {
20
21
  // Handles class declarations
21
22
  symbols.add(child.name.getText());
22
23
  }
24
+ else if (ts.isEnumDeclaration(child) && child.name) {
25
+ // Handles enum declarations
26
+ symbols.add(child.name.getText());
27
+ }
23
28
  else if (ts.isParameter(child)) {
24
29
  // Handles function parameters
25
30
  symbols.add(child.name.getText());
@@ -60,7 +65,7 @@ export function getScopeForNode(node) {
60
65
  }
61
66
  const rootScope = this.typeAnalyzer.scopeManager.getAllScopes()[0];
62
67
  if (!rootScope) {
63
- throw new Error("Compiler bug: Could not find a root scope.");
68
+ throw new CompilerError("Could not find a root scope.", node, "CompilerBug");
64
69
  }
65
70
  return rootScope;
66
71
  }
@@ -88,7 +93,7 @@ export function getDerefCode(nodeText, varName, context, typeInfo) {
88
93
  const symbolName = varName.slice(1).slice(0, -1);
89
94
  const symbol = context.localScopeSymbols.get(symbolName) ??
90
95
  context.globalScopeSymbols.get(symbolName);
91
- const isInitialized = symbol?.checked.initialized ||
96
+ const isInitialized = symbol?.checks.initialized ||
92
97
  false;
93
98
  // Mark the symbol as checked
94
99
  this.markSymbolAsInitialized(symbolName, context.globalScopeSymbols, context.localScopeSymbols);
@@ -109,12 +114,12 @@ export function getDerefCode(nodeText, varName, context, typeInfo) {
109
114
  export function markSymbolAsInitialized(name, topLevel, local) {
110
115
  if (topLevel.has(name)) {
111
116
  topLevel.update(name, {
112
- checked: { initialized: true },
117
+ checks: { initialized: true },
113
118
  });
114
119
  }
115
120
  else if (local.has(name)) {
116
121
  local.update(name, {
117
- checked: { initialized: true },
122
+ checks: { initialized: true },
118
123
  });
119
124
  }
120
125
  }
@@ -138,16 +143,19 @@ export function hoistDeclaration(decl, hoistedSymbols, scopeNode) {
138
143
  ? DeclarationType.function
139
144
  : ts.isClassDeclaration(decl)
140
145
  ? DeclarationType.class
141
- : DeclarationType.var;
146
+ : ts.isEnumDeclaration(decl)
147
+ ? DeclarationType.enum
148
+ : DeclarationType.var;
142
149
  if (hoistedSymbols.has(name)) {
143
150
  const existingSymbol = hoistedSymbols.get(name);
144
- // Don't allow multiple declaration of `let`,`const`,`function` or `class` variables
151
+ // Don't allow multiple declaration of `let`,`const`,`function`, `class` or `enum` variables
145
152
  if (existingSymbol?.type === DeclarationType.let ||
146
153
  existingSymbol?.type === DeclarationType.const ||
147
154
  existingSymbol?.type === DeclarationType.function ||
148
155
  existingSymbol?.type === DeclarationType.class ||
156
+ existingSymbol?.type === DeclarationType.enum ||
149
157
  existingSymbol?.type !== declType) {
150
- throw new SyntaxError(`Identifier '${name}' has already been declared.\n\n${" ".repeat(6) + decl.getText()}\n`);
158
+ throw new CompilerError(`Identifier '${name}' has already been declared.`, decl, "SyntaxError");
151
159
  }
152
160
  // `var` variables can be declared multiple times
153
161
  return "";
@@ -203,10 +211,24 @@ export function prepareScopeSymbolsForVisit(topLevel, local) {
203
211
  // Join the top and local scopes
204
212
  return new DeclaredSymbols(topLevel, local);
205
213
  }
214
+ export function shouldIgnoreStatement(stmt) {
215
+ // Ignore variable statements with 'declare' modifier
216
+ if (ts.isVariableStatement(stmt)) {
217
+ if (stmt.modifiers &&
218
+ stmt.modifiers.some((m) => m.kind === ts.SyntaxKind.DeclareKeyword)) {
219
+ return true;
220
+ }
221
+ }
222
+ return false;
223
+ }
206
224
  export function collectFunctionScopedDeclarations(node) {
207
225
  const decls = [];
208
226
  function visit(n) {
209
227
  if (ts.isVariableStatement(n)) {
228
+ // Ignore Declare modifier
229
+ if (shouldIgnoreStatement(n))
230
+ return;
231
+ // Only collect let/const declarations
210
232
  const isLetOrConst = (n.declarationList.flags &
211
233
  (ts.NodeFlags.Let | ts.NodeFlags.Const)) !== 0;
212
234
  if (!isLetOrConst) {
@@ -257,6 +279,10 @@ export function collectBlockScopedDeclarations(statements) {
257
279
  const decls = [];
258
280
  for (const stmt of statements) {
259
281
  if (ts.isVariableStatement(stmt)) {
282
+ // Ignore Declare modifier
283
+ if (shouldIgnoreStatement(stmt))
284
+ continue;
285
+ // Only collect let/const declarations
260
286
  const isLetOrConst = (stmt.declarationList.flags &
261
287
  (ts.NodeFlags.Let | ts.NodeFlags.Const)) !== 0;
262
288
  if (isLetOrConst) {
@@ -2,10 +2,11 @@ import { DeclaredSymbols } from "../../ast/symbols.js";
2
2
  import { generateFullLambdaExpression, generateLambda, } from "./function-handlers.js";
3
3
  import { escapeString, generateUniqueExceptionName, generateUniqueName, getDeclaredSymbols, getDerefCode, getJsVarName, getReturnCommand, getScopeForNode, hoistDeclaration, indent, isAsyncFunction, isBuiltinObject, isFunctionUsedAsValue, isFunctionUsedBeforeDeclaration, isGeneratorFunction, markSymbolAsInitialized, prepareScopeSymbolsForVisit, } from "./helpers.js";
4
4
  import { visit } from "./visitor.js";
5
- const MODULE_NAME = "__main_function__";
5
+ const MODULE_NAME = "__entry_point__";
6
6
  export class CodeGenerator {
7
7
  indentationLevel = 0;
8
8
  typeAnalyzer;
9
+ isTypescript = false;
9
10
  globalThisVar;
10
11
  uniqueNameCounter = 0;
11
12
  // visitor
@@ -34,8 +35,9 @@ export class CodeGenerator {
34
35
  /**
35
36
  * Main entry point for the code generation process.
36
37
  */
37
- generate(ast, analyzer) {
38
+ generate(ast, analyzer, isTypescript) {
38
39
  this.typeAnalyzer = analyzer;
40
+ this.isTypescript = isTypescript;
39
41
  this.globalThisVar = this.generateUniqueName("__this_val__", this.getDeclaredSymbols(ast));
40
42
  const declarations = `#include "index.hpp"\n\n`;
41
43
  let containerCode = `jspp::JsPromise ${MODULE_NAME}() {\n`;
@@ -68,10 +70,10 @@ export class CodeGenerator {
68
70
  mainCode += ` jspp::Scheduler::instance().run();\n`;
69
71
  mainCode += ` } catch (const std::exception& ex) {\n`;
70
72
  mainCode +=
71
- " auto error = std::make_shared<jspp::AnyValue>(jspp::Exception::exception_to_any_value(ex));\n {\n";
73
+ " auto error = std::make_shared<jspp::AnyValue>(jspp::Exception::exception_to_any_value(ex));\n";
72
74
  mainCode +=
73
- ` console.call_own_property("error", std::span<const jspp::AnyValue>((const jspp::AnyValue[]){*error}, 1));\n`;
74
- mainCode += ` return 1;\n }\n`;
75
+ ` console.call_own_property("error", std::span<const jspp::AnyValue>((const jspp::AnyValue[]){*error}, 1));\n`;
76
+ mainCode += ` return 1;\n`;
75
77
  mainCode += ` }\n`;
76
78
  mainCode += " return 0;\n}";
77
79
  return declarations + containerCode + mainCode;