firefly-compiler 0.5.7 → 0.5.9

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.
@@ -160,7 +160,8 @@ extend self: JsEmitter {
160
160
  emitLetDefinition(definition: DLet, mutable: Bool, async: Bool): String {
161
161
  let mutability = if(mutable) {"let"} else {"const"}
162
162
  let valueCode = self.emitTerm(definition.value, async)
163
- mutability + " " + escapeKeyword(definition.name) + " = " + valueCode + ";"
163
+ let assignmentCode = if(!mutable || valueCode != "(void 0)") {" = " + valueCode} else {""}
164
+ mutability + " " + escapeKeyword(definition.name) + assignmentCode + ";"
164
165
  }
165
166
 
166
167
  emitExtendsDefinition(definition: DExtend): String {
@@ -352,80 +353,10 @@ extend self: JsEmitter {
352
353
  let c = if(await) {", $task"} else {""}
353
354
  let call = "(" + self.emitTerm(function, async) + ")(" + self.emitTerm(value, async) + c + ")"
354
355
  if(await) {"(await " + call + ")"} else {call}
355
- | ECall(at, StaticCall(operator, _, _), _, [], [value], _) {!operator.grabFirst().isAsciiLetter()} =>
356
- "(" + operator + self.emitArgument(at, value, async) + ")"
357
- | ECall(at, StaticCall(operator, _, _), _, [], [left, right], _) {!operator.grabFirst().isAsciiLetter()} =>
358
- "(" + self.emitArgument(at, left, async) + " " + operator + " " + self.emitArgument(at, right, async) + ")"
359
- | ECall(at, StaticCall("ff:core/List.List_grab", _, _), _, _, [Argument(_, _, EVariable(_, x1)), Argument(_, _, EVariable(_, x2))], _) =>
360
- "(" + escapeResolved(x1) + "[" + escapeResolved(x2) + "] ?? " +
361
- "ff_core_List.internalGrab_(" + escapeResolved(x1) + ", " + escapeResolved(x2) + "))"
362
- | ECall(at, StaticCall("ff:core/Array.Array_grab", _, _), _, _, [Argument(_, _, EVariable(_, x1)), Argument(_, _, EVariable(_, x2))], _) =>
363
- "(" + escapeResolved(x1) + ".array[" + escapeResolved(x2) + "] ?? " +
364
- "ff_core_Array.internalGrab_(" + escapeResolved(x1) + ", " + escapeResolved(x2) + "))"
365
- | ECall(at, StaticCall("ff:core/UnsafeJs.import", _, _), _, _, [Argument(_, _, EString(_, url))], _) =>
366
- self.jsImporter.add(url.replace("\"", ""))
367
- | ECall(at, StaticCall("ff:core/UnsafeJs.await", _, _), _, _, [Argument(_, _, body)], _) =>
368
- if(async) {
369
- "(await " + self.emitTerm(body, async) + "($task))"
370
- } else {
371
- self.emitTerm(invokeImmediately(body), async)
372
- }
373
- | ECall(at, StaticCall("ff:core/UnsafeJs.cancelled", _, _), _, _, [], _) =>
374
- if(async) {"$task.controller.signal.aborted"} else {"false"}
375
- | ECall(at, StaticCall("ff:core/UnsafeJs.throwIfCancelled", _, _), _, _, [], _) =>
376
- if(async) {"((() => ff_core_Task.Task_throwIfAborted($task))())"} else {""}
377
- | ECall(at, StaticCall("ff:core/UnsafeJs.inAsync", _, _), _, _, [], _) =>
378
- if(self.emittingAsync) {"true"} else {"false"}
379
- | ECall(at, StaticCall("ff:core/UnsafeJs.inBrowser", _, _), _, _, [], _) =>
380
- if(self.emitTarget == EmitBrowser) {"true"} else {"false"}
381
- | ECall(at, StaticCall("ff:core/UnsafeJs.inNode", _, _), _, _, [], _) =>
382
- if(self.emitTarget == EmitNode) {"true"} else {"false"}
383
- | ECall(at, StaticCall("ff:core/UnsafeJs.inBuild", _, _), _, _, [], _) =>
384
- if(self.emitTarget == EmitBuild) {"true"} else {"false"}
385
- | ECall(at, StaticCall("ff:core/UnsafeJs.value", _, _), _, _, [Argument(_, _, e)], _) =>
386
- self.emitTerm(e, async)
387
- | ECall(at, StaticCall("ff:core/UnsafeJs.fromValue", _, _), _, _, [Argument(_, _, e)], _) =>
388
- self.emitTerm(e, async)
389
- | ECall(at, StaticCall("ff:core/Equal.equals", _, _), _, _, [left, right], [Dictionary(_, _, _, typeName, [])]) {
390
- primitiveTypes.contains(typeName) || typeName == "ff:core/Ordering.Ordering"
391
- } =>
392
- "(" + self.emitArgument(at, left, async) + " === " + self.emitArgument(at, right, async) + ")"
393
- | ECall(at, StaticCall("ff:core/Equal.notEquals", _, _), _, _, [left, right], [Dictionary(_, _, _, typeName, [])]) {
394
- primitiveTypes.contains(typeName) || typeName == "ff:core/Ordering.Ordering"
395
- } =>
396
- "(" + self.emitArgument(at, left, async) + " !== " + self.emitArgument(at, right, async) + ")"
397
- | ECall(at, StaticCall("ff:core/Ordering.before", _, _), _, _, [left, right], [Dictionary(_, _, _, typeName, [])]) {
398
- primitiveTypes.contains(typeName)
399
- } =>
400
- "(" + self.emitArgument(at, left, async) + " < " + self.emitArgument(at, right, async) + ")"
401
- | ECall(at, StaticCall("ff:core/Ordering.notBefore", _, _), _, _, [left, right], [Dictionary(_, _, _, typeName, [])]) {
402
- primitiveTypes.contains(typeName)
403
- } =>
404
- "(" + self.emitArgument(at, left, async) + " >= " + self.emitArgument(at, right, async) + ")"
405
- | ECall(at, StaticCall("ff:core/Ordering.after", _, _), _, _, [left, right], [Dictionary(_, _, _, typeName, [])]) {
406
- primitiveTypes.contains(typeName)
407
- } =>
408
- "(" + self.emitArgument(at, left, async) + " > " + self.emitArgument(at, right, async) + ")"
409
- | ECall(at, StaticCall("ff:core/Ordering.notAfter", _, _), _, _, [left, right], [Dictionary(_, _, _, typeName, [])]) {
410
- primitiveTypes.contains(typeName)
356
+ | ECall(at, StaticCall(name, _, _), _, _, arguments, dictionaries) {
357
+ self.emitSpecialCall(term, async, name, arguments.map {_.value}, dictionaries) | Some(code)
411
358
  } =>
412
- "(" + self.emitArgument(at, left, async) + " <= " + self.emitArgument(at, right, async) + ")"
413
- | ECall(_, StaticCall("ff:core/List.fillBy", _, _), effect, _, [size, Argument(_, _, ELambda(at,
414
- Lambda(_, _, [MatchCase(_, [PVariable(_, name)], [], body)@c])@l
415
- ))], _) {
416
- !effectTypeIsAsync(effect)
417
- } =>
418
- let n = name.map {escapeResolved(_)}.else {"i"}
419
- let newAsync = self.emittingAsync && effectTypeIsAsync(effect)
420
- let await = if(newAsync) {"await "} else {""}
421
- await + "((() => {\n" +
422
- "const size = " + self.emitArgument(at, size, async) + ";\n" + // Not correct if async and body isn't
423
- "const result = [];\n" +
424
- "for(let " + n + " = 0; " + n + " < size; " + n + "++) {\n" +
425
- "result.push(" + self.emitTerm(body, newAsync) + ");\n" +
426
- "}\n" +
427
- "return result;\n" +
428
- "})())"
359
+ code
429
360
  | ECall(at, StaticCall(name, _, True), effect, typeArguments, arguments, dictionaries) =>
430
361
  let await = async && effectTypeIsAsync(effect)
431
362
  let dictionaryStrings = dictionaries.map {self.emitDictionary(_)}
@@ -485,6 +416,13 @@ extend self: JsEmitter {
485
416
  | _ =>
486
417
  "(function() {\n" + self.emitStatements(term, True, async) + "\n})()"
487
418
  }}
419
+
420
+ emitField(term: Term, async: Bool, dot: String = "."): String {
421
+ term.{
422
+ | EString(_, q) {safeBare(q) | Some(s)} => dot + s
423
+ | _ => "[" + self.emitTerm(term, async) + "]"
424
+ }
425
+ }
488
426
 
489
427
  emitDictionary(d: Dictionary): String {
490
428
  let m = if(d.moduleName != "") {
@@ -522,46 +460,6 @@ extend self: JsEmitter {
522
460
  | EAssignField(at, operator, record, field, value) =>
523
461
  self.emitTerm(record, async) + "." + escapeKeyword(field) + " " + operator + "= " +
524
462
  self.emitTerm(value, async)
525
- | ECall(at, StaticCall("ff:core/Core.while", _, _), _, _, [condition, body], _) =>
526
- "while(" + self.emitTerm(invokeImmediately(condition.value), async) + ") {\n" +
527
- self.emitStatements(invokeImmediately(body.value), False, async) + "\n}"
528
- | ECall(at, StaticCall("ff:core/Core.doWhile", _, _), _, _, [Argument(_, _, doWhileBody)], _) {
529
- invokeImmediately(doWhileBody) | ESequential(_, body, condition)
530
- } =>
531
- "while(true) {\n" +
532
- self.emitStatements(body, False, async) +
533
- "\nif(!" + self.emitTerm(condition, async) + ") break" +
534
- "\n}"
535
- | ECall(at, StaticCall("ff:core/Core.doWhile", _, _), _, _, [Argument(_, _, doWhileBody)], _) {
536
- invokeImmediately(doWhileBody) | body
537
- } =>
538
- "while(" + self.emitTerm(body, async) + ") {}"
539
- | ECall(at, StaticCall("ff:core/Core.if", _, _), _, _, [condition, body], _) =>
540
- "if(" + self.emitTerm(condition.value, async) + ") {\n" +
541
- if(last) {
542
- "return ff_core_Option.Some(" + self.emitTerm(invokeImmediately(body.value), async) +
543
- ")\n} else return ff_core_Option.None()"
544
- } else {
545
- self.emitStatements(invokeImmediately(body.value), False, async) + "\n}"
546
- }
547
- | ECall(at, StaticCall("ff:core/Core.throw", _, _), _, _, [argument], [dictionary]) =>
548
- let d = self.emitDictionary(dictionary)
549
- let a = self.emitArgument(at, argument, async)
550
- "throw Object.assign(new Error(), {ffException: ff_core_Any.toAny_(" + a + ", " + d + ")})"
551
- | ECall(at, StaticCall("ff:core/Try.Try_catch", _, _), _, _, _, _) {
552
- self.emitTryCatchFinally(term, last, async) | Some(code)
553
- } =>
554
- code
555
- | ECall(at, StaticCall("ff:core/Try.Try_catchAny", _, _), _, _, _, _) {
556
- self.emitTryCatchFinally(term, last, async) | Some(code)
557
- } =>
558
- code
559
- | ECall(at, StaticCall("ff:core/Try.Try_finally", _, _), _, _, _, _) {
560
- self.emitTryCatchFinally(term, last, async) | Some(code)
561
- } =>
562
- code
563
- | ECall(at, StaticCall("ff:core/UnsafeJs.throwIfCancelled", _, _), _, _, [], _) =>
564
- if(async) {"ff_core_Task.Task_throwIfAborted($task)"} else {""}
565
463
  | ECall(at, StaticCall(name, True, instanceCall), effect, _, arguments, _) =>
566
464
  if(instanceCall) {throw(CompileError(at, "Not yet implemented: Tail calls on trait methods."))}
567
465
  self.tailCallUsed = True
@@ -572,6 +470,10 @@ extend self: JsEmitter {
572
470
  ))
573
471
  }.collect {_}.unzip()
574
472
  "{\n" + pair.first.join("\n") + "\n" + pair.second.join("\n") + "\ncontinue _tailcall\n}"
473
+ | ECall(at, StaticCall(name, _, _), _, _, arguments, dictionaries) {
474
+ self.emitSpecialStatement(term, last, async, name, arguments.map {_.value}, dictionaries) | Some(code)
475
+ } =>
476
+ code
575
477
  | EPipe(at, value, _, ELambda(_, Lambda(_, _, cases))) =>
576
478
  Patterns.convertAndCheck(self.otherModules, cases)
577
479
  if(!last) {"do "}.else {""} +
@@ -601,6 +503,221 @@ extend self: JsEmitter {
601
503
  }
602
504
  }
603
505
  }
506
+
507
+ emitSpecialCall(
508
+ term: Term
509
+ async: Bool
510
+ name: String
511
+ arguments: List[Term]
512
+ dictionaries: List[Dictionary]
513
+ ): Option[String] {
514
+ name.{
515
+ | operator {!operator.grabFirst().isAsciiLetter()} {arguments | [value]} =>
516
+ Some("(" + operator + self.emitTerm(value, async) + ")")
517
+ | operator {!operator.grabFirst().isAsciiLetter()} {arguments | [left, right]} =>
518
+ Some("(" + self.emitTerm(left, async) + " " + operator + " " + self.emitTerm(right, async) + ")")
519
+ | "ff:core/List.List_grab" {arguments | [EVariable(_, x1), EVariable(_, x2)]} =>
520
+ Some(
521
+ "(" + escapeResolved(x1) + "[" + escapeResolved(x2) + "] ?? " +
522
+ "ff_core_List.internalGrab_(" + escapeResolved(x1) + ", " + escapeResolved(x2) + "))"
523
+ )
524
+ | "ff:core/Array.Array_grab" {arguments | [EVariable(_, x1), EVariable(_, x2)]} =>
525
+ Some(
526
+ "(" + escapeResolved(x1) + ".array[" + escapeResolved(x2) + "] ?? " +
527
+ "ff_core_Array.internalGrab_(" + escapeResolved(x1) + ", " + escapeResolved(x2) + "))"
528
+ )
529
+ | "ff:core/Equal.equals" {arguments | [left, right]} {dictionaries | [Dictionary(_, _, _, typeName, [])]} {
530
+ primitiveTypes.contains(typeName) || typeName == "ff:core/Ordering.Ordering"
531
+ } =>
532
+ Some("(" + self.emitTerm(left, async) + " === " + self.emitTerm(right, async) + ")")
533
+ | "ff:core/Equal.notEquals" {arguments | [left, right]} {dictionaries | [Dictionary(_, _, _, typeName, [])]} {
534
+ primitiveTypes.contains(typeName) || typeName == "ff:core/Ordering.Ordering"
535
+ } =>
536
+ Some("(" + self.emitTerm(left, async) + " !== " + self.emitTerm(right, async) + ")")
537
+ | "ff:core/Ordering.before" {arguments | [left, right]} {dictionaries | [Dictionary(_, _, _, typeName, [])]} {
538
+ primitiveTypes.contains(typeName)
539
+ } =>
540
+ Some("(" + self.emitTerm(left, async) + " < " + self.emitTerm(right, async) + ")")
541
+ | "ff:core/Ordering.notBefore" {arguments | [left, right]} {dictionaries | [Dictionary(_, _, _, typeName, [])]} {
542
+ primitiveTypes.contains(typeName)
543
+ } =>
544
+ Some("(" + self.emitTerm(left, async) + " >= " + self.emitTerm(right, async) + ")")
545
+ | "ff:core/Ordering.after" {arguments | [left, right]} {dictionaries | [Dictionary(_, _, _, typeName, [])]} {
546
+ primitiveTypes.contains(typeName)
547
+ } =>
548
+ Some("(" + self.emitTerm(left, async) + " > " + self.emitTerm(right, async) + ")")
549
+ | "ff:core/Ordering.notAfter" {arguments | [left, right]} {dictionaries | [Dictionary(_, _, _, typeName, [])]} {
550
+ primitiveTypes.contains(typeName)
551
+ } =>
552
+ Some("(" + self.emitTerm(left, async) + " <= " + self.emitTerm(right, async) + ")")
553
+ | "ff:core/List.fillBy" {term | ECall call} {arguments | [size, ELambda(at,
554
+ Lambda(_, _, [MatchCase(_, [PVariable(_, name)], [], body)@c])@l
555
+ )]} {
556
+ !effectTypeIsAsync(call.effect)
557
+ } =>
558
+ let n = name.map {escapeResolved(_)}.else {"i"}
559
+ let newAsync = self.emittingAsync && effectTypeIsAsync(call.effect)
560
+ let await = if(newAsync) {"await "} else {""}
561
+ Some(
562
+ await + "((() => {\n" +
563
+ "const size = " + self.emitTerm(size, async) + ";\n" + // Not correct if async and body isn't
564
+ "const result = [];\n" +
565
+ "for(let " + n + " = 0; " + n + " < size; " + n + "++) {\n" +
566
+ "result.push(" + self.emitTerm(body, newAsync) + ");\n" +
567
+ "}\n" +
568
+ "return result;\n" +
569
+ "})())"
570
+ )
571
+ | "ff:core/UnsafeJs.import" {arguments | [EString(_, url)]} =>
572
+ Some(self.jsImporter.add(url.replace("\"", "")))
573
+ | "ff:core/UnsafeJs.await" {arguments | [body]} =>
574
+ if(async) {
575
+ Some("(await " + self.emitTerm(body, async) + "($task))")
576
+ } else {
577
+ Some(self.emitTerm(invokeImmediately(body), async))
578
+ }
579
+ | "ff:core/UnsafeJs.cancelled" =>
580
+ Some(if(async) {"$task.controller.signal.aborted"} else {"false"})
581
+ | "ff:core/UnsafeJs.throwIfCancelled" =>
582
+ Some(if(async) {"((() => ff_core_Task.Task_throwIfAborted($task))())"} else {""})
583
+ | "ff:core/UnsafeJs.inAsync" =>
584
+ Some(if(self.emittingAsync) {"true"} else {"false"})
585
+ | "ff:core/UnsafeJs.inBrowser" =>
586
+ Some(if(self.emitTarget == EmitBrowser) {"true"} else {"false"})
587
+ | "ff:core/UnsafeJs.inNode" =>
588
+ Some(if(self.emitTarget == EmitNode) {"true"} else {"false"})
589
+ | "ff:core/UnsafeJs.inBuild" =>
590
+ Some(if(self.emitTarget == EmitBuild) {"true"} else {"false"})
591
+ | "ff:core/UnsafeJs.value" {arguments | [e]} =>
592
+ Some(self.emitTerm(e, async))
593
+ | "ff:core/UnsafeJs.fromValue" {arguments | [e]} =>
594
+ Some(self.emitTerm(e, async))
595
+ | "ff:core/JsValue.JsValue_get" {arguments | [e1, e2]} =>
596
+ Some(self.emitTerm(e1, async) + self.emitField(e2, async))
597
+ | name {name.removeFirst("ff:core/JsValue.JsValue_call") | Some(n)} {n.all {_.isAsciiDigit()}} {
598
+ arguments | [e1, e2, ...es]
599
+ } =>
600
+ let argumentCode = es.map {self.emitTerm(_, async)}.join(", ")
601
+ Some(self.emitTerm(e1, async) + self.emitField(e2, async) + "(" + argumentCode + ")")
602
+ | name {name.removeFirst("ff:core/JsValue.JsValue_callValue") | Some(n)} {n.all {_.isAsciiDigit()}} {
603
+ arguments | [e1, ...es]
604
+ } =>
605
+ let argumentCode = es.map {self.emitTerm(_, async)}.join(", ")
606
+ Some(self.emitTerm(e1, async) + "(" + argumentCode + ")")
607
+ | name {name.removeFirst("ff:core/JsValue.JsValue_new") | Some(n)} {n.all {_.isAsciiDigit()}} {
608
+ arguments | [e1, ...es]
609
+ } =>
610
+ let argumentCode = es.map {self.emitTerm(_, async)}.join(", ")
611
+ Some("(new " + self.emitTerm(e1, async) + "(" + argumentCode + ")" + ")")
612
+ | "ff:core/JsValue.JsValue_with" =>
613
+ function go(e: Term, fields: List[Pair[Term, Term]]): String {
614
+ e.{
615
+ | ECall(_, StaticCall("ff:core/JsValue.JsValue_with", _, _), _, _, [a1, a2, a3], _) =>
616
+ go(a1.value, [Pair(a2.value, a3.value), ...fields])
617
+ | ECall(_, StaticCall(name, _, _), _, _, [a], _) {
618
+ name == "ff:core/JsSystem.JsSystem_object" || name == "ff:core/JsSystem.JsSystem_new0"
619
+ } {
620
+ noSideEffects(a.value)
621
+ } =>
622
+ "{" + fields.map {p =>
623
+ self.emitField(p.first, async, dot = "") + ": " + self.emitTerm(p.second, async)
624
+ }.join(", ") + "}"
625
+ | _ =>
626
+ "{..." + self.emitTerm(e, async) + ", " + fields.map {p =>
627
+ self.emitField(p.first, async, dot = "") + ": " + self.emitTerm(p.second, async)
628
+ }.join(", ") + "}"
629
+ }
630
+ }
631
+ Some(go(term, []))
632
+ | name {name.removeFirst("ff:core/JsSystem.JsSystem_call") | Some(n)} {n.all {_.isAsciiDigit()}} {
633
+ arguments | [e1, EString(_, q)@e2, ...es]
634
+ } {noSideEffects(e1)} =>
635
+ let argumentCode = es.map {self.emitTerm(_, async)}.join(", ")
636
+ Some(safeBare(q).else {"globalThis[" + self.emitTerm(e2, async) + "]"} + "(" + argumentCode + ")")
637
+ | name {name.removeFirst("ff:core/JsSystem.JsSystem_function") | Some(n)} {n.all {_.isAsciiDigit()}} {
638
+ arguments | [e1, e2]
639
+ } {noSideEffects(e1)} {term | ECall call} {!effectTypeIsAsync(call.effect)} =>
640
+ Some(self.emitTerm(e2, async))
641
+ | "ff:core/JsSystem.JsSystem_get" {arguments | [e1, EString(_, q)@e2]} {noSideEffects(e1)} =>
642
+ Some(safeBare(q).else {"globalThis[" + self.emitTerm(e2, async) + "]"})
643
+ | "ff:core/JsSystem.JsSystem_object" {arguments | [e]} {noSideEffects(e)} =>
644
+ Some("{}")
645
+ | "ff:core/JsSystem.JsSystem_new0" {arguments | [e]} {noSideEffects(e)} =>
646
+ Some("{}")
647
+ | "ff:core/JsSystem.JsSystem_null" {arguments | [e]} {noSideEffects(e)} =>
648
+ Some("null")
649
+ | "ff:core/JsSystem.JsSystem_undefined" {arguments | [e]} {noSideEffects(e)} =>
650
+ Some("(void 0)")
651
+ | _ =>
652
+ None
653
+ }
654
+ }
655
+
656
+ emitSpecialStatement(
657
+ term: Term
658
+ last: Bool
659
+ async: Bool
660
+ name: String
661
+ arguments: List[Term]
662
+ dictionaries: List[Dictionary]
663
+ ): Option[String] {
664
+ name.{
665
+ | "ff:core/Core.while" {arguments | [condition, body]} =>
666
+ Some(
667
+ "while(" + self.emitComma(invokeImmediately(condition), async) + ") {\n" +
668
+ self.emitStatements(invokeImmediately(body), False, async) + "\n}"
669
+ )
670
+ | "ff:core/Core.doWhile" {arguments | [doWhileBody]} {
671
+ invokeImmediately(doWhileBody) | ESequential(_, body, condition)
672
+ } =>
673
+ Some(
674
+ "while(true) {\n" +
675
+ self.emitStatements(body, False, async) +
676
+ "\nif(!" + self.emitComma(condition, async) + ") break" +
677
+ "\n}"
678
+ )
679
+ | "ff:core/Core.doWhile" {arguments | [doWhileBody]} {
680
+ invokeImmediately(doWhileBody) | body
681
+ } =>
682
+ Some("while(" + self.emitComma(body, async) + ") {}")
683
+ | "ff:core/Core.if" {arguments | [condition, body]} =>
684
+ Some(
685
+ "if(" + self.emitComma(condition, async) + ") {\n" +
686
+ if(last) {
687
+ "return ff_core_Option.Some(" + self.emitTerm(invokeImmediately(body), async) +
688
+ ")\n} else return ff_core_Option.None()"
689
+ } else {
690
+ self.emitStatements(invokeImmediately(body), False, async) + "\n}"
691
+ }
692
+ )
693
+ | "ff:core/Core.throw" {term | ECall c} {c.arguments | [argument]} {dictionaries | [dictionary]} =>
694
+ let d = self.emitDictionary(dictionary)
695
+ let a = self.emitArgument(term.at, argument, async)
696
+ Some("throw Object.assign(new Error(), {ffException: ff_core_Any.toAny_(" + a + ", " + d + ")})")
697
+ | "ff:core/Try.Try_catch" {self.emitTryCatchFinally(term, last, async) | Some(code)} =>
698
+ Some(code)
699
+ | "ff:core/Try.Try_catchAny" {self.emitTryCatchFinally(term, last, async) | Some(code)} =>
700
+ Some(code)
701
+ | "ff:core/Try.Try_finally" {self.emitTryCatchFinally(term, last, async) | Some(code)} =>
702
+ Some(code)
703
+ | "ff:core/UnsafeJs.throwIfCancelled" =>
704
+ Some(if(async) {"ff_core_Task.Task_throwIfAborted($task)"} else {""})
705
+ | "ff:core/JsValue.JsValue_set" {arguments | [e1, e2, e3]} =>
706
+ Some(self.emitTerm(e1, async) + self.emitField(e2, async) + " = " + self.emitTerm(e3, async))
707
+ | "ff:core/JsValue.JsValue_increment" {arguments | [e1, e2, e3]} =>
708
+ Some(self.emitTerm(e1, async) + self.emitField(e2, async) + " += " + self.emitTerm(e3, async))
709
+ | "ff:core/JsValue.JsValue_decrement" {arguments | [e1, e2, e3]} =>
710
+ Some(self.emitTerm(e1, async) + self.emitField(e2, async) + " -= " + self.emitTerm(e3, async))
711
+ | "ff:core/JsSystem.JsSystem_set" {arguments | [e1, EString(_, q), e3]} {noSideEffects(e1)} {safeBare(q) | Some(s)} =>
712
+ Some(s + " = " + self.emitTerm(e3, async))
713
+ | "ff:core/JsSystem.JsSystem_increment" {arguments | [e1, EString(_, q), e3]} {noSideEffects(e1)} {safeBare(q) | Some(s)} =>
714
+ Some(s + " += " + self.emitTerm(e3, async))
715
+ | "ff:core/JsSystem.JsSystem_decrement" {arguments | [e1, EString(_, q), e3]} {noSideEffects(e1)} {safeBare(q) | Some(s)} =>
716
+ Some(s + " -= " + self.emitTerm(e3, async))
717
+ | _ =>
718
+ None
719
+ }
720
+ }
604
721
 
605
722
  emitTryCatchFinally(term: Term, last: Bool, async: Bool): Option[String] {
606
723
  function emitCatch(catchEffect: Type, cases: List[MatchCase]): String {
@@ -866,6 +983,22 @@ extend self: JsEmitter {
866
983
  self.emitTerm(value, async)
867
984
  }
868
985
  }
986
+
987
+ emitComma(term: Term, async: Bool): String {
988
+ term.{
989
+ | ESequential(_, ESequential(_, before1, before2), after) {
990
+ safeCommable(before1) && safeCommable(before2)
991
+ } =>
992
+ "(" + self.emitStatements(before1, False, async) + ", " +
993
+ self.emitStatements(before2, False, async) + ", " +
994
+ self.emitTerm(after, async) + ")"
995
+ | ESequential(_, before, after) {safeCommable(before)} =>
996
+ "(" + self.emitStatements(before, False, async) + ", " +
997
+ self.emitTerm(after, async) + ")"
998
+ | _ =>
999
+ self.emitTerm(term, async)
1000
+ }
1001
+ }
869
1002
 
870
1003
  }
871
1004
 
@@ -899,6 +1032,21 @@ invokeImmediately(function: Term): Term {
899
1032
  ECall(function.at, DynamicCall(function, False), effect, [], [], [])
900
1033
  }
901
1034
 
1035
+ safeCommable(term: Term): Bool {
1036
+ term.{
1037
+ | EField _ => True
1038
+ | EVariable _ => True
1039
+ | EAssign _ => True
1040
+ | EAssignField _ => True
1041
+ | ECall _ => True
1042
+ | EString(_, _) => True
1043
+ | EInt(_, _) => True
1044
+ | EChar(_, _) => True
1045
+ | EFloat(_, _) => True
1046
+ | _ => False
1047
+ }
1048
+ }
1049
+
902
1050
  extractTypeName(type: Type): String {
903
1051
  | TVariable(at, index) =>
904
1052
  fail(at, "Unexpected type variable: $" + index)
@@ -946,6 +1094,29 @@ effectTypeIsAsync(effect: Type): Bool {
946
1094
  | _ => False
947
1095
  }
948
1096
 
1097
+ safeBare(quotedString: String): Option[String] {
1098
+ // TODO: And not a reserved word in JS
1099
+ quotedString.removeFirst("\"").flatMap {_.removeLast("\"")}.filter {s =>
1100
+ s.first().any {_.isAsciiLetter()} && s.all {_.isAsciiLetterOrDigit()}
1101
+ }
1102
+ }
1103
+
1104
+ noSideEffects(term: Term): Bool {
1105
+ term.{
1106
+ | EField(_, _, e, _) => noSideEffects(e)
1107
+ | EVariable(_, _) => True
1108
+ | ECall(_, StaticCall("ff:core/BrowserSystem.BrowserSystem_js", _, _), _, _, [a], _) => noSideEffects(a.value)
1109
+ | ECall(_, StaticCall("ff:core/BuildSystem.BuildSystem_js", _, _), _, _, [a], _) => noSideEffects(a.value)
1110
+ | ECall(_, StaticCall("ff:core/NodeSystem.NodeSystem_js", _, _), _, _, [a], _) => noSideEffects(a.value)
1111
+ | ECall(_, StaticCall("ff:core/UnsafeJs.jsSystem", _, _), _, _, _, _) => True
1112
+ | EString(_, _) => True
1113
+ | EInt(_, _) => True
1114
+ | EChar(_, _) => True
1115
+ | EFloat(_, _) => True
1116
+ | _ => False
1117
+ }
1118
+ }
1119
+
949
1120
  primitiveTypes = [
950
1121
  "ff:core/Bool.Bool"
951
1122
  "ff:core/Char.Char"
package/core/JsValue.ff CHANGED
@@ -116,38 +116,37 @@ extend self: JsValue {
116
116
  target js sync "for(const value of self_) if(!body_(value)) break"
117
117
  target js async "for(const value of self_) if(!await body_(value, $task)) break"
118
118
 
119
-
120
- call(name: String, arguments: List[JsValue]): JsValue
119
+ call[A0: IsJsValue](name: A0, arguments: List[JsValue]): JsValue
121
120
  target js sync "return self_[name_].apply(this_, arguments_)"
122
121
 
123
- call0(name: String): JsValue
122
+ call0[A0: IsJsValue](name: A0): JsValue
124
123
  target js sync "return self_[name_].call(self_)"
125
124
 
126
- call1[A1: IsJsValue](name: String, a1: A1): JsValue
125
+ call1[A0: IsJsValue, A1: IsJsValue](name: A0, a1: A1): JsValue
127
126
  target js sync "return self_[name_].call(self_, a1_)"
128
127
 
129
- call2[A1: IsJsValue, A2: IsJsValue](name: String, a1: A1, a2: A2): JsValue
128
+ call2[A0: IsJsValue, A1: IsJsValue, A2: IsJsValue](name: A0, a1: A1, a2: A2): JsValue
130
129
  target js sync "return self_[name_].call(self_, a1_, a2_)"
131
130
 
132
- call3[A1: IsJsValue, A2: IsJsValue, A3: IsJsValue](name: String, a1: A1, a2: A2, a3: A3): JsValue
131
+ call3[A0: IsJsValue, A1: IsJsValue, A2: IsJsValue, A3: IsJsValue](name: A0, a1: A1, a2: A2, a3: A3): JsValue
133
132
  target js sync "return self_[name_].call(self_, a1_, a2_, a3_)"
134
133
 
135
- call4[A1: IsJsValue, A2: IsJsValue, A3: IsJsValue, A4: IsJsValue](name: String, a1: A1, a2: A2, a3: A3, a4: A4): JsValue
134
+ call4[A0: IsJsValue, A1: IsJsValue, A2: IsJsValue, A3: IsJsValue, A4: IsJsValue](name: A0, a1: A1, a2: A2, a3: A3, a4: A4): JsValue
136
135
  target js sync "return self_[name_].call(self_, a1_, a2_, a3_, a4_)"
137
136
 
138
- call5[A1: IsJsValue, A2: IsJsValue, A3: IsJsValue, A4: IsJsValue, A5: IsJsValue](name: String, a1: A1, a2: A2, a3: A3, a4: A4, a5: A5): JsValue
137
+ call5[A0: IsJsValue, A1: IsJsValue, A2: IsJsValue, A3: IsJsValue, A4: IsJsValue, A5: IsJsValue](name: A0, a1: A1, a2: A2, a3: A3, a4: A4, a5: A5): JsValue
139
138
  target js sync "return self_[name_].call(self_, a1_, a2_, a3_, a4_, a5_)"
140
139
 
141
- call6[A1: IsJsValue, A2: IsJsValue, A3: IsJsValue, A4: IsJsValue, A5: IsJsValue, A6: IsJsValue](name: String, a1: A1, a2: A2, a3: A3, a4: A4, a5: A5, a6: A6): JsValue
140
+ call6[A0: IsJsValue, A1: IsJsValue, A2: IsJsValue, A3: IsJsValue, A4: IsJsValue, A5: IsJsValue, A6: IsJsValue](name: A0, a1: A1, a2: A2, a3: A3, a4: A4, a5: A5, a6: A6): JsValue
142
141
  target js sync "return self_[name_].call(self_, a1_, a2_, a3_, a4_, a5_, a6_)"
143
142
 
144
- call7[A1: IsJsValue, A2: IsJsValue, A3: IsJsValue, A4: IsJsValue, A5: IsJsValue, A6: IsJsValue, A7: IsJsValue](name: String, a1: A1, a2: A2, a3: A3, a4: A4, a5: A5, a6: A6, a7: A7): JsValue
143
+ call7[A0: IsJsValue, A1: IsJsValue, A2: IsJsValue, A3: IsJsValue, A4: IsJsValue, A5: IsJsValue, A6: IsJsValue, A7: IsJsValue](name: A0, a1: A1, a2: A2, a3: A3, a4: A4, a5: A5, a6: A6, a7: A7): JsValue
145
144
  target js sync "return self_[name_].call(self_, a1_, a2_, a3_, a4_, a5_, a6_, a7_)"
146
145
 
147
- call8[A1: IsJsValue, A2: IsJsValue, A3: IsJsValue, A4: IsJsValue, A5: IsJsValue, A6: IsJsValue, A7: IsJsValue, A8: IsJsValue](name: String, a1: A1, a2: A2, a3: A3, a4: A4, a5: A5, a6: A6, a7: A7, a8: A8): JsValue
146
+ call8[A0: IsJsValue, A1: IsJsValue, A2: IsJsValue, A3: IsJsValue, A4: IsJsValue, A5: IsJsValue, A6: IsJsValue, A7: IsJsValue, A8: IsJsValue](name: A0, a1: A1, a2: A2, a3: A3, a4: A4, a5: A5, a6: A6, a7: A7, a8: A8): JsValue
148
147
  target js sync "return self_[name_].call(self_, a1_, a2_, a3_, a4_, a5_, a6_, a7_, a8_)"
149
148
 
150
- call9[A1: IsJsValue, A2: IsJsValue, A3: IsJsValue, A4: IsJsValue, A5: IsJsValue, A6: IsJsValue, A7: IsJsValue, A8: IsJsValue, A9: IsJsValue](name: String, a1: A1, a2: A2, a3: A3, a4: A4, a5: A5, a6: A6, a7: A7, a8: A8, a9: A9): JsValue
149
+ call9[A0: IsJsValue, A1: IsJsValue, A2: IsJsValue, A3: IsJsValue, A4: IsJsValue, A5: IsJsValue, A6: IsJsValue, A7: IsJsValue, A8: IsJsValue, A9: IsJsValue](name: A0, a1: A1, a2: A2, a3: A3, a4: A4, a5: A5, a6: A6, a7: A7, a8: A8, a9: A9): JsValue
151
150
  target js sync "return self_[name_].call(self_, a1_, a2_, a3_, a4_, a5_, a6_, a7_, a8_, a9_)"
152
151
 
153
152
 
@@ -155,34 +154,34 @@ extend self: JsValue {
155
154
  target js sync "return self_.apply(this_, arguments_)"
156
155
 
157
156
  callValue0(): JsValue
158
- target js sync "return self_.call(self_)"
157
+ target js sync "return self_.call(void 0)"
159
158
 
160
159
  callValue1[A1: IsJsValue](a1: A1): JsValue
161
- target js sync "return self_.call(self_, a1_)"
160
+ target js sync "return self_.call(void 0, a1_)"
162
161
 
163
162
  callValue2[A1: IsJsValue, A2: IsJsValue](a1: A1, a2: A2): JsValue
164
- target js sync "return self_.call(self_, a1_, a2_)"
163
+ target js sync "return self_.call(void 0, a1_, a2_)"
165
164
 
166
165
  callValue3[A1: IsJsValue, A2: IsJsValue, A3: IsJsValue](a1: A1, a2: A2, a3: A3): JsValue
167
- target js sync "return self_.call(self_, a1_, a2_, a3_)"
166
+ target js sync "return self_.call(void 0, a1_, a2_, a3_)"
168
167
 
169
168
  callValue4[A1: IsJsValue, A2: IsJsValue, A3: IsJsValue, A4: IsJsValue](a1: A1, a2: A2, a3: A3, a4: A4): JsValue
170
- target js sync "return self_.call(self_, a1_, a2_, a3_, a4_)"
169
+ target js sync "return self_.call(void 0, a1_, a2_, a3_, a4_)"
171
170
 
172
171
  callValue5[A1: IsJsValue, A2: IsJsValue, A3: IsJsValue, A4: IsJsValue, A5: IsJsValue](a1: A1, a2: A2, a3: A3, a4: A4, a5: A5): JsValue
173
- target js sync "return self_.call(self_, a1_, a2_, a3_, a4_, a5_)"
172
+ target js sync "return self_.call(void 0, a1_, a2_, a3_, a4_, a5_)"
174
173
 
175
174
  callValue6[A1: IsJsValue, A2: IsJsValue, A3: IsJsValue, A4: IsJsValue, A5: IsJsValue, A6: IsJsValue](a1: A1, a2: A2, a3: A3, a4: A4, a5: A5, a6: A6): JsValue
176
- target js sync "return self_.call(self_, a1_, a2_, a3_, a4_, a5_, a6_)"
175
+ target js sync "return self_.call(void 0, a1_, a2_, a3_, a4_, a5_, a6_)"
177
176
 
178
177
  callValue7[A1: IsJsValue, A2: IsJsValue, A3: IsJsValue, A4: IsJsValue, A5: IsJsValue, A6: IsJsValue, A7: IsJsValue](a1: A1, a2: A2, a3: A3, a4: A4, a5: A5, a6: A6, a7: A7): JsValue
179
- target js sync "return self_.call(self_, a1_, a2_, a3_, a4_, a5_, a6_, a7_)"
178
+ target js sync "return self_.call(void 0, a1_, a2_, a3_, a4_, a5_, a6_, a7_)"
180
179
 
181
180
  callValue8[A1: IsJsValue, A2: IsJsValue, A3: IsJsValue, A4: IsJsValue, A5: IsJsValue, A6: IsJsValue, A7: IsJsValue, A8: IsJsValue](a1: A1, a2: A2, a3: A3, a4: A4, a5: A5, a6: A6, a7: A7, a8: A8): JsValue
182
- target js sync "return self_.call(self_, a1_, a2_, a3_, a4_, a5_, a6_, a7_, a8_)"
181
+ target js sync "return self_.call(void 0, a1_, a2_, a3_, a4_, a5_, a6_, a7_, a8_)"
183
182
 
184
183
  callValue9[A1: IsJsValue, A2: IsJsValue, A3: IsJsValue, A4: IsJsValue, A5: IsJsValue, A6: IsJsValue, A7: IsJsValue, A8: IsJsValue, A9: IsJsValue](a1: A1, a2: A2, a3: A3, a4: A4, a5: A5, a6: A6, a7: A7, a8: A8, a9: A9): JsValue
185
- target js sync "return self_.call(self_, a1_, a2_, a3_, a4_, a5_, a6_, a7_, a8_, a9_)"
184
+ target js sync "return self_.call(void 0, a1_, a2_, a3_, a4_, a5_, a6_, a7_, a8_, a9_)"
186
185
 
187
186
 
188
187
  new(this: JsValue, arguments: List[JsValue]): JsValue
package/core/List.ff CHANGED
@@ -231,7 +231,7 @@ extend self[T]: List[T] {
231
231
  await body_(self_[i], $task)
232
232
  }
233
233
  """
234
-
234
+
235
235
  eachWhile(body: T => Bool): Unit
236
236
  target js sync "for(const value of self_) if(!body_(value)) break"
237
237
  target js async "for(const value of self_) if(!await body_(value, $task)) break"