firefly-compiler 0.4.7 → 0.4.8

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (90) hide show
  1. package/compiler/Compiler.ff +2 -0
  2. package/compiler/Deriver.ff +7 -0
  3. package/core/Array.ff +17 -4
  4. package/core/AssetSystem.ff +1 -1
  5. package/core/BrowserSystem.ff +27 -0
  6. package/core/List.ff +4 -0
  7. package/core/Lock.ff +2 -2
  8. package/core/Random.ff +148 -0
  9. package/core/SourceLocation.ff +27 -0
  10. package/core/Task.ff +3 -0
  11. package/core/WebSocket.ff +127 -0
  12. package/httpserver/HttpServer.ff +1 -1
  13. package/lux/Css.ff +648 -0
  14. package/lux/CssTest.ff +48 -0
  15. package/lux/Lux.ff +33 -18
  16. package/lux/Main.ff +4 -1
  17. package/output/js/ff/compiler/Builder.mjs +4 -0
  18. package/output/js/ff/compiler/Compiler.mjs +5 -1
  19. package/output/js/ff/compiler/Dependencies.mjs +4 -0
  20. package/output/js/ff/compiler/Deriver.mjs +10 -2
  21. package/output/js/ff/compiler/Dictionaries.mjs +6 -0
  22. package/output/js/ff/compiler/Environment.mjs +10 -0
  23. package/output/js/ff/compiler/Inference.mjs +4 -0
  24. package/output/js/ff/compiler/JsEmitter.mjs +14 -0
  25. package/output/js/ff/compiler/JsImporter.mjs +4 -0
  26. package/output/js/ff/compiler/LspHook.mjs +6 -0
  27. package/output/js/ff/compiler/Main.mjs +16 -0
  28. package/output/js/ff/compiler/Parser.mjs +8 -0
  29. package/output/js/ff/compiler/Patterns.mjs +8 -0
  30. package/output/js/ff/compiler/Resolver.mjs +4 -0
  31. package/output/js/ff/compiler/Substitution.mjs +4 -0
  32. package/output/js/ff/compiler/Syntax.mjs +132 -0
  33. package/output/js/ff/compiler/Token.mjs +56 -0
  34. package/output/js/ff/compiler/Tokenizer.mjs +4 -0
  35. package/output/js/ff/compiler/Unification.mjs +10 -0
  36. package/output/js/ff/compiler/Wildcards.mjs +4 -0
  37. package/output/js/ff/compiler/Workspace.mjs +8 -0
  38. package/output/js/ff/core/Any.mjs +4 -0
  39. package/output/js/ff/core/Array.mjs +27 -1
  40. package/output/js/ff/core/AssetSystem.mjs +6 -2
  41. package/output/js/ff/core/Atomic.mjs +4 -0
  42. package/output/js/ff/core/Bool.mjs +4 -0
  43. package/output/js/ff/core/Box.mjs +4 -0
  44. package/output/js/ff/core/BrowserSystem.mjs +55 -0
  45. package/output/js/ff/core/Buffer.mjs +4 -0
  46. package/output/js/ff/core/BuildSystem.mjs +4 -0
  47. package/output/js/ff/core/Channel.mjs +4 -0
  48. package/output/js/ff/core/Char.mjs +4 -0
  49. package/output/js/ff/core/Core.mjs +4 -0
  50. package/output/js/ff/core/Duration.mjs +4 -0
  51. package/output/js/ff/core/Equal.mjs +4 -0
  52. package/output/js/ff/core/Error.mjs +4 -0
  53. package/output/js/ff/core/FileHandle.mjs +4 -0
  54. package/output/js/ff/core/Float.mjs +4 -0
  55. package/output/js/ff/core/HttpClient.mjs +4 -0
  56. package/output/js/ff/core/Instant.mjs +4 -0
  57. package/output/js/ff/core/Int.mjs +4 -0
  58. package/output/js/ff/core/IntMap.mjs +4 -0
  59. package/output/js/ff/core/JsSystem.mjs +4 -0
  60. package/output/js/ff/core/JsValue.mjs +4 -0
  61. package/output/js/ff/core/List.mjs +12 -0
  62. package/output/js/ff/core/Lock.mjs +6 -2
  63. package/output/js/ff/core/Log.mjs +4 -0
  64. package/output/js/ff/core/Map.mjs +4 -0
  65. package/output/js/ff/core/NodeSystem.mjs +4 -0
  66. package/output/js/ff/core/Nothing.mjs +4 -0
  67. package/output/js/ff/core/Option.mjs +8 -0
  68. package/output/js/ff/core/Ordering.mjs +4 -0
  69. package/output/js/ff/core/Pair.mjs +6 -0
  70. package/output/js/ff/core/Path.mjs +4 -0
  71. package/output/js/ff/core/Random.mjs +340 -0
  72. package/output/js/ff/core/RbMap.mjs +4 -0
  73. package/output/js/ff/core/Serializable.mjs +4 -0
  74. package/output/js/ff/core/Set.mjs +4 -0
  75. package/output/js/ff/core/Show.mjs +4 -0
  76. package/output/js/ff/core/SourceLocation.mjs +45 -1
  77. package/output/js/ff/core/Stack.mjs +4 -0
  78. package/output/js/ff/core/Stream.mjs +4 -0
  79. package/output/js/ff/core/String.mjs +4 -0
  80. package/output/js/ff/core/StringMap.mjs +4 -0
  81. package/output/js/ff/core/Task.mjs +7 -1
  82. package/output/js/ff/core/Try.mjs +4 -0
  83. package/output/js/ff/core/Unit.mjs +4 -0
  84. package/output/js/ff/core/WebSocket.mjs +198 -0
  85. package/package.json +1 -1
  86. package/vscode/package.json +1 -1
  87. package/webserver/.firefly/include/package-lock.json +16 -0
  88. package/webserver/.firefly/include/package.json +5 -0
  89. package/webserver/.firefly/package.ff +2 -0
  90. package/webserver/WebServer.ff +608 -0
@@ -94,6 +94,7 @@ coreImports: List[DImport] =
94
94
  "Ordering"
95
95
  "Pair"
96
96
  "Path"
97
+ "Random"
97
98
  "Serializable"
98
99
  "Set"
99
100
  "Show"
@@ -105,6 +106,7 @@ coreImports: List[DImport] =
105
106
  "Task"
106
107
  "Try"
107
108
  "Unit"
109
+ "WebSocket"
108
110
  ].map {moduleName =>
109
111
  DImport(
110
112
  at = Location("<prelude>", 1, 1)
@@ -516,6 +516,12 @@ extend self: Deriver {
516
516
  let variantName = modulePrefix + "." + variant.name
517
517
  let fields = [...declaration.commonFields, ...variant.fields]
518
518
  let updateChecksum = self.makeUpdateChecksum(at, variantName, declaration, variant)
519
+ let autoResize = self.makeMethodCall(
520
+ at = at
521
+ target = EVariable(at, "serialization")
522
+ methodName = "autoResize"
523
+ arguments = [EInt(at, "1")]
524
+ )
519
525
  let setVariantIndex = self.makeMethodCall(
520
526
  at = at
521
527
  target = EField(at, False, EVariable(at, "serialization"), "buffer")
@@ -533,6 +539,7 @@ extend self: Deriver {
533
539
  patterns = [wildcardPattern, PVariantAs(at, variantName, at, Some("v"))]
534
540
  guards = []
535
541
  body = [
542
+ autoResize
536
543
  setVariantIndex
537
544
  EAssignField(at, "+", EVariable(at, "serialization"), "offset", EInt(at, "1"))
538
545
  ...fieldSerializations
package/core/Array.ff CHANGED
@@ -14,6 +14,13 @@ fillBy[T](size: Int, body: Int => T): Array[T]
14
14
  target js sync """
15
15
  return Array.from({length: size_}, (_, i) => body_(i));
16
16
  """
17
+ target js async """
18
+ const array = new Array(size_);
19
+ for(let i = 0; i < size_; i++) {
20
+ array[i] = await(body_(_i));
21
+ }
22
+ return array;
23
+ """
17
24
 
18
25
  range(size: Int): Array[Int]
19
26
  target js sync """
@@ -46,13 +53,19 @@ extend self[T]: Array[T] {
46
53
  return self_[index_]
47
54
  """
48
55
 
49
- first(): Option[T] { self.get(0) }
56
+ first(): Option[T] {self.get(0)}
57
+
58
+ last(): Option[T] {self.get(self.size() - 1)}
59
+
60
+ grabFirst(): T {self.grab(0)}
50
61
 
51
- last(): Option[T] { self.get(self.size() - 1) }
62
+ grabLast(): T {self.grab(self.size() - 1)}
52
63
 
53
- grabFirst(): T { self.grab(0) }
64
+ takeFirst(count: Int = 1): Array[T]
65
+ target js sync "return self_.slice(0, count_)"
54
66
 
55
- grabLast(): T { self.grab(self.size() - 1) }
67
+ takeLast(count: Int = 1): Array[T]
68
+ target js sync "return self_.slice(-count_)"
56
69
 
57
70
  dropFirst(count: Int = 1): Array[T]
58
71
  target js sync "return self_.slice(count_)"
@@ -22,7 +22,7 @@ extend self: AssetSystem {
22
22
  assets(path: String): AssetSystem {
23
23
  let prefix = if(path.endsWith("/")) {path} else {path + "/"}
24
24
  let streams = self.files.pairs().collect {
25
- | Pair(p, s) {p.startsWith(prefix)} => Some(Pair(p.dropFirst(prefix.size()), s))
25
+ | Pair(p, s) {p.startsWith(prefix)} => Some(Pair(p.dropFirst(prefix.size() - 1), s))
26
26
  | _ => None
27
27
  }
28
28
  AssetSystem(streams.toMap())
@@ -5,10 +5,37 @@ extend self: BrowserSystem {
5
5
  httpClient(): HttpClient
6
6
  target js async "return null"
7
7
 
8
+ webSocket(url: String): WebSocket {
9
+ WebSocket.internalOpenBrowserWebSocket(self, url)
10
+ }
11
+
8
12
  mainTask(): Task
9
13
  target js async "return self_.task_"
10
14
 
11
15
  js(): JsSystem
12
16
  target js async "return typeof globalThis !== 'undefined' ? globalThis : window"
17
+
18
+ url(): String
19
+ target js async """
20
+ return location.href;
21
+ """
22
+
23
+ urlPath(): String
24
+ target js async """
25
+ return location.pathname;
26
+ """
13
27
 
28
+ urlQuery(name: String): Option[String]
29
+ target js async """
30
+ const param = new URLSearchParams(location.search).get(name_)
31
+ if(param == null) return ff_core_Option.None();
32
+ return ff_core_Option.Some(param);
33
+ """
34
+
35
+ urlFragment(): Option[String]
36
+ target js async """
37
+ if(!location.hash.startsWith('#')) return ff_core_Option.None();
38
+ return ff_core_Option.Some(location.hash.slice(1));
39
+ """
40
+
14
41
  }
package/core/List.ff CHANGED
@@ -142,6 +142,10 @@ extend self[T]: List[T] {
142
142
  tailcall tail.each(body)
143
143
  }
144
144
  }
145
+
146
+ eachWhile(body: T => Bool): Bool {
147
+ self.all(body)
148
+ }
145
149
 
146
150
  all(body: T => Bool): Bool {
147
151
  self.{
package/core/Lock.ff CHANGED
@@ -76,7 +76,7 @@ extend self: LockCondition {
76
76
  }
77
77
  const level = self_.lock.level
78
78
  self_.lock.level = 1
79
- await ff_core.Lock_release$(self_.lock)
79
+ await ff_core_Lock.Lock_release$(self_.lock)
80
80
  try {
81
81
  await new Promise((resolve, reject) => {
82
82
  $task.controller.signal.addEventListener('abort', reject)
@@ -92,7 +92,7 @@ extend self: LockCondition {
92
92
  let acquired = false
93
93
  while(!acquired) {
94
94
  try {
95
- await ff_core.Lock_acquire$(self_.lock)
95
+ await ff_core_Lock.Lock_acquire$(self_.lock)
96
96
  self_.lock.level = level
97
97
  acquired = true
98
98
  } catch(e) {
package/core/Random.ff ADDED
@@ -0,0 +1,148 @@
1
+ class Random {}
2
+
3
+ // Using Alea PRNG by Johannes Baagøe <baagoe@baagoe.com>, 2010
4
+ // Typical use: Random.seedInstant(system.task().now())
5
+
6
+ seedInt(seed: Int): Random {
7
+ seedFloat(seed.toFloat())
8
+ }
9
+
10
+ seedFloat(seed: Float): Random {
11
+ let buffer = Buffer.make(8)
12
+ buffer.setFloat64(0, seed)
13
+ seedBuffer(buffer)
14
+ }
15
+
16
+ seedInstant(seed: Instant): Random {
17
+ seedFloat(seed.since1970.seconds)
18
+ }
19
+
20
+ seedBuffer(buffer: Buffer): Random
21
+ target js sync """
22
+ var n = 0xefc8249d;
23
+ function mash(data) {
24
+ for(var i = 0; i < data.byteLength; i++) {
25
+ n += data.getUint8(i);
26
+ var h = 0.02519603282416938 * n;
27
+ n = h >>> 0;
28
+ h -= n;
29
+ h *= n;
30
+ n = h >>> 0;
31
+ h -= n;
32
+ n += h * 0x100000000; // 2^32
33
+ }
34
+ return (n >>> 0) * 2.3283064365386963e-10; // 2^-32
35
+ }
36
+ var space = new DataView(new Uint8Array([32]).buffer);
37
+ var r = {
38
+ s0: mash(space),
39
+ s1: mash(space),
40
+ s2: mash(space),
41
+ c: 1,
42
+ spareGauss: NaN
43
+ };
44
+ r.s0 -= mash(buffer_);
45
+ if(r.s0 < 0) r.s0 += 1;
46
+ r.s1 -= mash(buffer_);
47
+ if(r.s1 < 0) r.s1 += 1;
48
+ r.s2 -= mash(buffer_);
49
+ if(r.s2 < 0) r.s2 += 1;
50
+ return r;
51
+ """
52
+
53
+ extend self: Random {
54
+
55
+ copy(): Random
56
+ target js sync """
57
+ return {...self_};
58
+ """
59
+
60
+ nextInt(from: Int, until: Int): Int
61
+ target js sync """
62
+ return Random_nextFloat(self_, from_, until_) | 0;
63
+ """
64
+
65
+ nextFloat(from: Float, until: Float): Float
66
+ target js sync """
67
+ var t = 2091639 * self_.s0 + self_.c * 2.3283064365386963e-10; // 2^-32
68
+ self_.s0 = self_.s1;
69
+ self_.s1 = self_.s2;
70
+ var uniform = self_.s2 = t - (self_.c = t | 0);
71
+ return from_ + uniform * (until_ - from_);
72
+ """
73
+
74
+ nextBool(): Bool {
75
+ self.nextInt(0, 2) == 0
76
+ }
77
+
78
+ nextBytes(buffer: Buffer, start: Int, stop: Int): Unit {
79
+ start.until(stop).each {i =>
80
+ buffer.setUint8(i, self.nextInt(0, 256))
81
+ }
82
+ }
83
+
84
+ nextGauss(mean: Float, standardDeviation: Float): Float
85
+ target js sync """
86
+ if(!isNaN(self_.spareGauss)) {
87
+ const result = self_.spareGauss * standardDeviation_ + mean_;
88
+ self_.spareGauss = NaN;
89
+ return result;
90
+ } else {
91
+ let u = 0.5, v = 0.5, s = 0.5;
92
+ do {
93
+ u = Random_nextFloat(self_, 0.0, 1.0) * 2 - 1;
94
+ v = Random_nextFloat(self_, 0.0, 1.0) * 2 - 1;
95
+ s = u * u + v * v;
96
+ } while(s >= 1 || s == 0);
97
+ s = Math.sqrt(-2.0 * Math.log(s) / s);
98
+ self_.spareGauss = v * s;
99
+ return mean_ + standardDeviation_ * u * s;
100
+ }
101
+ """
102
+
103
+ shuffleStack[T](stack: Stack[T]): Unit {
104
+ 0.until(stack.size() - 1).each {i =>
105
+ let j = self.nextInt(0, stack.size() - i) + i
106
+ let value = stack.grab(i)
107
+ stack.set(i, stack.grab(j))
108
+ stack.set(j, value)
109
+ }
110
+ }
111
+
112
+ shuffleArray[T](array: Array[T]): Array[T] {
113
+ let stack = array.toStack()
114
+ self.shuffleStack(array.toStack())
115
+ stack.drain()
116
+ }
117
+
118
+ shuffleList[T](list: List[T]): List[T] {
119
+ let stack = list.toStack()
120
+ self.shuffleStack(list.toStack())
121
+ stack.toList()
122
+ }
123
+
124
+ sampleStack[T](count: Int, stack: Stack[T], body: T => Unit): Unit {
125
+ self.shuffleArray(stack.toArray()).takeFirst(count).each {body(_)}
126
+ }
127
+
128
+ sampleArray[T](count: Int, array: Array[T]): Array[T] {
129
+ self.shuffleArray(array).takeFirst(count)
130
+ }
131
+
132
+ sampleList[T](count: Int, list: List[T]): List[T] {
133
+ self.shuffleList(list).takeFirst(count)
134
+ }
135
+
136
+ grabStack[T](stack: Stack[T]): T {
137
+ stack.grab(self.nextInt(0, stack.size()))
138
+ }
139
+
140
+ grabArray[T](array: Array[T]): T {
141
+ array.grab(self.nextInt(0, array.size()))
142
+ }
143
+
144
+ grabList[T](list: Stack[T]): T {
145
+ list.grab(self.nextInt(0, list.size()))
146
+ }
147
+
148
+ }
@@ -39,3 +39,30 @@ extend self: SourceLocation {
39
39
  }
40
40
 
41
41
  }
42
+
43
+ instance SourceLocation: Show {
44
+ show(self: SourceLocation): String {
45
+ self.location
46
+ }
47
+ }
48
+
49
+ instance SourceLocation: Equal {
50
+ equals(a: SourceLocation, b: SourceLocation): Bool {
51
+ a.location == b.location
52
+ }
53
+ }
54
+
55
+ instance SourceLocation: Order {
56
+ compare(a: SourceLocation, b: SourceLocation): Ordering {
57
+ Ordering.compare(a.location, b.location)
58
+ }
59
+ }
60
+
61
+ instance SourceLocation: Serializable {
62
+ serializeUsing(serialization: Serialization, self: SourceLocation) {
63
+ Serializable.serializeUsing(serialization, self.location)
64
+ }
65
+ deserializeUsing(serialization: Serialization): SourceLocation {
66
+ SourceLocation(Serializable.deserializeUsing(serialization))
67
+ }
68
+ }
package/core/Task.ff CHANGED
@@ -46,6 +46,9 @@ extend self: Task {
46
46
  target js async """
47
47
  self_.controller.abort()
48
48
  """
49
+ target js sync """
50
+ self_.controller.abort()
51
+ """
49
52
 
50
53
  // Create a blocking channel with the specified capacity. The channel is not tied to the task.
51
54
  channel[T](capacity: Int = 0): Channel[T]
@@ -0,0 +1,127 @@
1
+ capability WebSocket {}
2
+
3
+ extend self: WebSocket {
4
+
5
+ readText(encoding: String = "utf8"): Option[String] {
6
+ self.readAny {_} {_.toString(encoding)}
7
+ }
8
+
9
+ readBuffer(): Option[Buffer] {
10
+ self.readAny {_.toBuffer()} {_}
11
+ }
12
+
13
+ readAny[T](fromText: String => T, fromBuffer: Buffer => T): Option[T]
14
+ target browser async """
15
+ ff_core_Task.Task_throwIfAborted($task);
16
+ while(self_.ffFront.length === 0) {
17
+ while(self_.ffBack.length !== 0) {
18
+ self_.ffFront.push(self_.ffBack.pop());
19
+ }
20
+ if(self_.ffFront.length !== 0) break;
21
+ if(self_.readyState === 3) return ff_core_Option.None();
22
+ let abort = null;
23
+ try {
24
+ await new Promise((resolve, reject) => {
25
+ self_.ffListeners.push(resolve);
26
+ if($task !== self_.ffTask) {
27
+ abort = () => {
28
+ self_.ffListeners = self_.ffListeners.filter(l => l != resolve);
29
+ reject();
30
+ };
31
+ $task.controller.signal.addEventListener('abort', abort);
32
+ }
33
+ });
34
+ } finally {
35
+ if($task !== self_.ffTask) {
36
+ $task.controller.signal.removeEventListener('abort', abort);
37
+ }
38
+ }
39
+ }
40
+ const message = self_.ffFront.pop();
41
+ if(!(message instanceof MessageEvent)) throw new Error("WebSocket error");
42
+ const data = message.data;
43
+ if(typeof data === 'string') return ff_core_Option.Some(await fromText_(data));
44
+ return ff_core_Option.Some(await fromBuffer_(new DataView(data)));
45
+ """
46
+
47
+ writeBuffer(data: Buffer): Unit
48
+ target browser async """
49
+ ff_core_Task.Task_throwIfAborted($task);
50
+ self_.send(data_);
51
+ """
52
+
53
+ writeText(data: String): Unit
54
+ target browser async """
55
+ ff_core_Task.Task_throwIfAborted($task);
56
+ self_.send(data_);
57
+ """
58
+
59
+ close(code: Int = 1000, reason: String = ""): Unit
60
+ target browser async """
61
+ if(self_.readyState === 2 || self_.readyState === 3) return;
62
+ await new Promise((resolve, reject) => {
63
+ self_.addEventListener('close', resolve);
64
+ self_.close(code_, reason !== "" ? reason : void 0);
65
+ });
66
+ return;
67
+ """
68
+
69
+ isOpen(): Bool
70
+ target browser async """
71
+ return self_.readyState === 1;
72
+ """
73
+
74
+ isClosing(): Bool
75
+ target browser async """
76
+ return self_.readyState === 2;
77
+ """
78
+
79
+ isClosed(): Bool
80
+ target browser async """
81
+ return self_.readyState === 3;
82
+ """
83
+
84
+ }
85
+
86
+ internalOpenBrowserWebSocket(browserSystem: BrowserSystem, url: String): WebSocket
87
+ target browser async """
88
+ if(typeof location !== 'undefined' && !url_.includes("://")) {
89
+ if(location.href && location.href.startsWith("http")) {
90
+ url_ = new URL(url_, location.href).href.replace(/^http/, 'ws');
91
+ }
92
+ }
93
+ const socket = new WebSocket(url_);
94
+ socket.binaryType = "arraybuffer";
95
+ socket.ffFront = [];
96
+ socket.ffBack = [];
97
+ socket.ffListeners = [];
98
+ socket.ffTask = $task;
99
+ $task.controller.signal.addEventListener('message', m => socket.ffBack.push(m));
100
+ const abort = () => socket.close();
101
+ try {
102
+ await new Promise((resolve, reject) => {
103
+ socket.onopen = resolve;
104
+ socket.onerror = reject;
105
+ $task.controller.signal.addEventListener('abort', abort);
106
+ });
107
+ } finally {
108
+ socket.onopen = null;
109
+ socket.onerror = null;
110
+ }
111
+ socket.addEventListener('close', () => {
112
+ $task.controller.signal.removeEventListener('abort', abort);
113
+ for(let i = 0; i < socket.ffListeners.length; i++) socket.ffListeners[i]();
114
+ socket.ffListeners.length = 0;
115
+ });
116
+ socket.addEventListener('message', m => {
117
+ socket.ffBack.push(m);
118
+ for(let i = 0; i < socket.ffListeners.length; i++) socket.ffListeners[i]();
119
+ socket.ffListeners.length = 0;
120
+ });
121
+ socket.addEventListener('error', e => {
122
+ socket.ffBack.push(e);
123
+ for(let i = 0; i < socket.ffListeners.length; i++) socket.ffListeners[i]();
124
+ socket.ffListeners.length = 0;
125
+ });
126
+ return socket;
127
+ """
@@ -146,7 +146,7 @@ extend self: HttpResponse {
146
146
  target node async """
147
147
  import * as http from 'http'
148
148
  ff_core_Task.Task_throwIfAborted($task)
149
- if(!self_.write(buffer_)) {
149
+ if(!self_.write(Buffer.from(buffer_.buffer))) {
150
150
  let reject = null;
151
151
  await new Promise((resolve, reject) => {
152
152
  const abort = () => {